Class: Bootinq

Inherits:
Object
  • Object
show all
Extended by:
SingleForwardable
Includes:
Mixins, Singleton
Defined in:
lib/bootinq.rb,
lib/bootinq/mixins.rb,
lib/bootinq/switch.rb,
lib/bootinq/railtie.rb,
lib/bootinq/version.rb,
lib/bootinq/component.rb

Overview

Bootinq

Installation

Ruby on Rails

  1. insert require "bootinq" on top of config/application.rb;
  2. find and replace Bundler.require(*Rails.groups) with Bootinq.require

Other frameworks

  1. locate Bundler.require(…) in your app and insert require "bootinq" above it;
  2. replace previosly located Bundler.require(…) line with the Bootinq.require(…).

Examples:

Grape

# config/application.rb

require 'boot'
require 'bootinq'

# Bundler.require :default, ENV['RACK_ENV']
Bootinq.require :default, ENV['RACK_ENV'], verbose: true

config/bootinq.yml

env_key: BOOTINQ
default: a

parts:
  s: :shared

mount:
  a: :api
  f: :engine

deps:
  shared:
    in: af

Defined Under Namespace

Modules: Mixins Classes: Component, Mountable, Railtie, Switch

Constant Summary collapse

DEFAULT =
{
  "env_key" => 'BOOTINQ',
  "default" => '',
  "parts"   => {},
  "mount"   => {},
  "deps"    => {}
}.freeze
ALL =
%i[* all].freeze
VERSION =
"2.0.1"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeself



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/bootinq.rb', line 160

def initialize
  config = self.class.deserialized_config
  config.merge!(DEFAULT) { |_, l, r| l.nil? ? r : l }

  @_orig_value = ENV.fetch(config['env_key']) { config['default'] }
  @_neg, @_value = FilterNegValue[@_orig_value, config]

  @_deps = config['deps']

  @flags      = []
  @components = []

  config['parts'].each { |flag, name| enable_component(name, flag: flag.to_s) }
  config['mount'].each { |flag, name| enable_component(name, flag: flag.to_s, as: Mountable) }
end

Instance Attribute Details

#componentsObject (readonly)

Returns the value of attribute components.



157
158
159
# File 'lib/bootinq.rb', line 157

def components
  @components
end

#flagsObject (readonly)

Returns the value of attribute flags.



152
153
154
# File 'lib/bootinq.rb', line 152

def flags
  @flags
end

Class Method Details

.deserialized_config(path: nil) ⇒ Hash

Reads config

Parameters:

  • path (String) (defaults to: nil)

    path to yaml config (default: ENV['BOOTINQ_PATH'])

Returns:

  • (Hash)

    deserializes yaml config



131
132
133
134
# File 'lib/bootinq.rb', line 131

def self.deserialized_config(path: nil)
  bootinq_yaml = File.read(path || ENV.fetch('BOOTINQ_PATH'))
  psych_safe_load(bootinq_yaml, [Symbol])
end

.init(verbose: false, on_ready: ) ⇒ instance .init(verbose: false, &on_ready) ⇒ instance

Sets BOOTINQ_PATH enviroment variable if it is missing & initializes itself

Parameters:

  • verbose (Boolean) (defaults to: false)

    track inquired components

  • on_ready (Proc) (defaults to: nil)

    optional ready callback proc

Returns:

  • (instance)


114
115
116
117
118
119
120
121
122
123
124
# File 'lib/bootinq.rb', line 114

def self.init(verbose: false, on_ready: nil, &block)
  ENV['BOOTINQ_PATH'] ||= File.expand_path('../bootinq.yml', caller_locations(2, 1)[0].path)

  instance
  on_ready = block.to_proc if on_ready.nil? && block_given?
  instance.instance_variable_set(:@_on_ready, on_ready.to_proc) if on_ready

  puts "Bootinq: loading components #{instance.components.join(', ')}" if verbose

  instance.ready!
end

.require(*groups, **options, &on_ready)

This method returns an undefined value.

Invokes the init method with the given options and block, then calls Bundler.require with the enabled groups.

Parameters:

  • groups (Array<Symbol>)
  • options (Hash)

    initialization options

Options Hash (**options):

  • verbose (Boolean)

    track inquired components

  • on_ready (Proc)

    optional ready callback proc

See Also:

  • init
  • Bundler.require


83
84
85
86
# File 'lib/bootinq.rb', line 83

def self.require(*groups, **options, &on_ready)
  init(**options, &on_ready)
  Bundler.require(*instance.groups(*groups))
end

.setup(*groups, **options) {|instance| ... }

This method returns an undefined value.

Invokes the init method with the given options and block, then calls Bundler.require with the enabled groups.

Parameters:

  • groups (Array<Symbol>)
  • options (Hash)

    initialization options

Options Hash (**options):

  • verbose (Boolean)

    track inquired components

  • on_ready (Proc)

    optional ready callback proc

Yields:

  • (instance)

See Also:

  • init
  • Bundler.setup


101
102
103
104
# File 'lib/bootinq.rb', line 101

def self.setup(*groups, **options, &on_ready) # :yields: Bootinq.instance
  init(**options, &on_ready)
  Bundler.setup(*instance.groups(*groups))
end

Instance Method Details

#component(name) ⇒ Bootinq::Component Also known as: []

Parameters:

  • name (String, Symbol)

Returns:



224
225
226
# File 'lib/bootinq.rb', line 224

def component(name)
  @components[@components.index(name)]
end

#disabled?(name) ⇒ Boolean

Checks if a component with the given name (i.e. the same gem group) is disabled

Returns:

  • (Boolean)


232
233
234
# File 'lib/bootinq.rb', line 232

def disabled?(name)
  !@components.include?(name)
end

#each_mountableEnumerator #each_mountable {|component| ... } ⇒ Enumerator

Enumerates enabled mountable components

Overloads:

  • #each_mountable {|component| ... } ⇒ Enumerator

    Yields:

Returns:

  • (Enumerator)


241
242
243
244
245
246
247
# File 'lib/bootinq.rb', line 241

def each_mountable
  return enum_for(:each_mountable) unless block_given?

  @components.each do |component|
    yield(component) if component.mountable?
  end
end

#enable_component(name, flag:, as: Component) {|name, is_enabled| ... }

This method returns an undefined value.

Enables the given component if it is required by flag or when another enabled component depends it.

Parameters:

  • name (String)

    of the component

  • flag (String)

    the component's assigned char flag

  • as (Class) (defaults to: Component)

    the component's constructor class

Yields:

  • (name, is_enabled)


204
205
206
207
208
209
210
211
212
213
214
# File 'lib/bootinq.rb', line 204

def enable_component(name, flag:, as: Component)
  if is_dependency?(name) || @_value.include?(flag)
    @flags      << flag
    @components << as.new(name)
    yield(name, true) if block_given?
  else
    yield(name, false) if block_given?
  end

  nil
end

#enabled?(name) ⇒ Boolean

Checks if a component with the given name (i.e. the same gem group) is enabled

Returns:

  • (Boolean)


218
219
220
# File 'lib/bootinq.rb', line 218

def enabled?(name)
  ALL.include?(name) || @components.include?(name)
end

#freezeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



405
406
407
408
409
410
411
# File 'lib/bootinq.rb', line 405

def freeze
  @_value.freeze
  @_neg
  @flags.freeze
  @components.freeze
  super
end

#groups(*groups) ⇒ Array<String, Symbol>

Merges groups of enabled components with the given ones. When loaded with Rails, it passes them to Rails.groups method, otherwise just returns the merged list to use it with Bundler.require.

Parameters:

  • groups (Array<String, Symbol>)

Returns:

  • (Array<String, Symbol>)

    merged groups



254
255
256
257
258
259
260
261
# File 'lib/bootinq.rb', line 254

def groups(*groups)
  @components.each do |component|
    next if groups.include?(component.group)
    groups.unshift(component.group)
  end

  defined?(Rails) ? Rails.groups(*groups) : groups
end

#is_dependency?(name) ⇒ Boolean

Checks if the named component is dependent by another enabled one.

Parameters:

  • name (String, Symbol)

Returns:

  • (Boolean)


399
400
401
402
# File 'lib/bootinq.rb', line 399

def is_dependency?(name)
  @_deps.key?(name.to_s) &&
  @_value.count(@_deps.dig(name.to_s, 'in').to_s) > 0
end

#not(name) {|void| ... } ⇒ Boolean #not(any: ) {|void| ... } ⇒ Boolean #not(all: ) {|void| ... } ⇒ Boolean

Returns matching status.

Examples:

single

Bootinq.not(:frontend) { puts 'not frontend' }

any

Bootinq.not(any: %i[frontend backend]) { puts 'neither frontend nor backend' }

all

Bootinq.on(all: %i[frontend backend]) { puts 'both disabled' }

Overloads:

  • #not(name) {|void| ... } ⇒ Boolean

    Parameters:

    • name (Symbol)

      single component's name

    Yields:

    • (void)

      (if component is disabled)

  • #not(any: ) {|void| ... } ⇒ Boolean

    Parameters:

    • any (Array<Symbol>) (defaults to: )

      list of components' names

    Yields:

    • (void)

      (if any matching component is disabled)

    See Also:

  • #not(all: ) {|void| ... } ⇒ Boolean

    Parameters:

    • all (Array<Symbol>) (defaults to: )

      list of components' names

    Yields:

    • (void)

      (if all matching components are disabled)

    See Also:

Returns:

  • (Boolean)

    matching status



347
348
349
350
351
352
353
354
355
356
# File 'lib/bootinq.rb', line 347

def not(name = nil, any: nil, all: nil)
  is_matched =
    name ? disabled?(name) :
    any  ? not_any(*any) :
    all  ? not_all(*all) : false

  yield if is_matched

  is_matched
end

#not_all(*parts) {|void| ... } ⇒ Boolean

Returns matching status.

Parameters:

  • parts (Array<String, Symbol>)

    list of components' names

Yields:

  • (void)

    if all matching components are disabled

Returns:

  • (Boolean)

    matching status



364
365
366
367
368
# File 'lib/bootinq.rb', line 364

def not_all(*parts) # :yields:
  is_matched = parts.reduce(true) { |m, part| m && disabled?(part) }
  yield if is_matched && block_given?
  is_matched
end

#not_any(*parts) {|void| ... } ⇒ Boolean

Returns matching status.

Parameters:

  • parts (Array<String, Symbol>)

    list of components' names

Yields:

  • (void)

    if any matching component is disabled

Returns:

  • (Boolean)

    matching status



376
377
378
379
380
# File 'lib/bootinq.rb', line 376

def not_any(*parts) # :yields:
  is_matched = parts.reduce(false) { |m, part| m || disabled?(part) }
  yield if is_matched && block_given?
  is_matched
end

#on(name) {|void| ... } ⇒ Boolean #on(any: ) {|void| ... } ⇒ Boolean #on(all: ) {|void| ... } ⇒ Boolean

Returns matching status.

Examples:

single

Bootinq.on(:frontend) { puts 'frontend' }

any

Bootinq.on(any: %i[frontend backend]) { puts 'frontend or backend' }

all

Bootinq.on(all: %i[frontend backend]) { puts 'both' }

Overloads:

  • #on(name) {|void| ... } ⇒ Boolean

    Parameters:

    • name (Symbol)

      single component's name

    Yields:

    • (void)

      (if component is enabled)

  • #on(any: ) {|void| ... } ⇒ Boolean

    Parameters:

    • any (Array<Symbol>) (defaults to: )

      list of components' names

    Yields:

    • (void)

      (if any matching component is enabled)

    See Also:

  • #on(all: ) {|void| ... } ⇒ Boolean

    Parameters:

    • all (Array<Symbol>) (defaults to: )

      list of components' names

    Yields:

    • (void)

      (if all matching components are enabled)

    See Also:

Returns:

  • (Boolean)

    matching status



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/bootinq.rb', line 285

def on(name = nil, any: nil, all: nil)
  if name && ALL.include?(name)
    yield
    return true
  end

  is_matched =
    name ? enabled?(name) :
    any  ? on_any(*any) :
    all  ? on_all(*all) : false

  yield if is_matched

  is_matched
end

#on_all(*parts) {|void| ... } ⇒ Boolean

Returns matching status.

Parameters:

  • parts (Array<String, Symbol>)

    list of components' names

Yields:

  • (void)

    if all matching components are enabled

Returns:

  • (Boolean)

    matching status



307
308
309
310
311
# File 'lib/bootinq.rb', line 307

def on_all(*parts) # :yields:
  is_matched = parts.reduce(true) { |m, part| m && enabled?(part) }
  yield if is_matched && block_given?
  is_matched
end

#on_any(*parts) {|void| ... } ⇒ Boolean

Returns matching status.

Parameters:

  • parts (Array<String, Symbol>)

    list of components' names

Yields:

  • (void)

    if any matching component is enabled

Returns:

  • (Boolean)

    matching status



319
320
321
322
323
# File 'lib/bootinq.rb', line 319

def on_any(*parts) # :yields:
  is_matched = parts.reduce(false) { |m, part| m || enabled?(part) }
  yield if is_matched && block_given?
  is_matched
end

#ready!self, void

Once-only set Bootinq to ready state firing the @_on_ready callback.

Returns:

  • (self)

    on the first call

  • (void)

    after



184
185
186
187
188
189
190
191
192
# File 'lib/bootinq.rb', line 184

def ready!
  return if ready?
  @ready = true
  if defined?(@_on_ready)
    Bootinq.class_exec(&@_on_ready)
    remove_instance_variable :@_on_ready
  end
  freeze
end

#ready?Boolean

Returns:

  • (Boolean)


177
178
179
# File 'lib/bootinq.rb', line 177

def ready?
  !!@ready
end

#switch {|switch| ... }

This method returns an undefined value.

Collector method.

Examples:

Bootinq.switch do |part|
  part.frontend {  }
  part.backend {  }
end

Yields:

See Also:



391
392
393
394
# File 'lib/bootinq.rb', line 391

def switch
  yield(Switch.new)
  nil
end