class Bootinq
# 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(…)`.
@example Grape
# config/application.rb require 'boot' require 'bootinq' # Bundler.require :default, ENV['RACK_ENV'] Bootinq.require :default, ENV['RACK_ENV'], verbose: true
@example config/bootinq.yml
env_key: BOOTINQ default: a parts: s: :shared mount: a: :api f: :engine deps: shared: in: af
Constants
- ALL
- DEFAULT
- FilterNegValue
- VERSION
Attributes
@!attribute components [r]
@return [Array<String>]
@!attribute flags [r]
@return [Array<String>]
Public Class Methods
Reads config @param path [String]
path to yaml config (default: ENV['BOOTINQ_PATH'])
@return [Hash]
deserializes yaml config
# 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
Sets ‘BOOTINQ_PATH` enviroment variable if it is missing & initializes itself @overload init(verbose: false, on_ready:) @overload init(verbose: false, &on_ready) @param verbose [Boolean]
track inquired components
@param on_ready [Proc]
optional ready callback proc
@return [instance]
# 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
@return [self]
# 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
Invokes the {init} method with the given options and block, then calls {Bundler.require} with the enabled groups. @see init @see Bundler.require @param groups [Array<Symbol>] @param options [Hash]
initialization options
@option options [Boolean] verbose
track inquired components
@option options [Proc] on_ready
optional ready callback proc
@return [void]
# File lib/bootinq.rb, line 83 def self.require(*groups, **options, &on_ready) init(**options, &on_ready) Bundler.require(*instance.groups(*groups)) end
Invokes the {init} method with the given options and block, then calls {Bundler.require} with the enabled groups. @see init @see Bundler.setup @param groups [Array<Symbol>] @param options [Hash]
initialization options
@option options [Boolean] verbose
track inquired components
@option options [Proc] on_ready
optional ready callback proc
@yield [instance] @return [void]
# 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
Private Class Methods
# File lib/bootinq.rb, line 138 def self.psych_safe_load(path, permitted_classes) YAML.safe_load(path, permitted_classes: permitted_classes) end
Public Instance Methods
@param name [String, Symbol] @return [Bootinq::Component]
# File lib/bootinq.rb, line 224 def component(name) @components[@components.index(name)] end
Checks if a component with the given name (i.e. the same gem group) is disabled @return [Boolean]
# File lib/bootinq.rb, line 232 def disabled?(name) !@components.include?(name) end
Enumerates enabled mountable components @overload each_mountable
() @overload each_mountable
(&block)
@yield [component]
@return [Enumerator]
# 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
Enables the given component if it is required by flag or when another enabled component depends it. @param name [String]
of the component
@param flag [String]
the component's assigned char flag
@param as [Class]
the component's constructor class
@yield [name, is_enabled] @return [void]
# 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
Checks if a component with the given name (i.e. the same gem group) is enabled @return [Boolean]
# File lib/bootinq.rb, line 218 def enabled?(name) ALL.include?(name) || @components.include?(name) end
@api private
# File lib/bootinq.rb, line 405 def freeze @_value.freeze @_neg @flags.freeze @components.freeze super end
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}. @param groups [Array<String, Symbol>] @return [Array<String, Symbol>] merged groups
# 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
Checks if the named component is dependent by another enabled one. @param name [String, Symbol] @return [Boolean]
# 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
@overload not(name)
@yield [void] (if component is disabled) @param name [Symbol] single component's name
@overload not(any:)
@see not_any @yield [void] (if _any_ matching component is disabled) @param any [Array<Symbol>] list of components' names
@overload not(all:)
@see not_all @yield [void] (if _all_ matching components are disabled) @param all [Array<Symbol>] list of components' names
@return [Boolean] matching status
@example single
Bootinq.not(:frontend) { puts 'not frontend' }
@example any
Bootinq.not(any: %i[frontend backend]) { puts 'neither frontend nor backend' }
@example all
Bootinq.on(all: %i[frontend backend]) { puts 'both disabled' }
# 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
@yield [void]
if _all_ matching components are disabled
@param parts [Array<String, Symbol>]
list of components' names
@return [Boolean]
matching status
# 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
@yield [void]
if _any_ matching component is disabled
@param parts [Array<String, Symbol>]
list of components' names
@return [Boolean]
matching status
# 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
@overload on(name)
@yield [void] (if component is enabled) @param name [Symbol] single component's name
@overload on(any:)
@see on_any @yield [void] (if _any_ matching component is enabled) @param any [Array<Symbol>] list of components' names
@overload on(all:)
@see on_all @yield [void] (if _all_ matching components are enabled) @param all [Array<Symbol>] list of components' names
@return [Boolean] matching status
@example single
Bootinq.on(:frontend) { puts 'frontend' }
@example any
Bootinq.on(any: %i[frontend backend]) { puts 'frontend or backend' }
@example all
Bootinq.on(all: %i[frontend backend]) { puts 'both' }
# 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
@yield [void]
if _all_ matching components are enabled
@param parts [Array<String, Symbol>]
list of components' names
@return [Boolean]
matching status
# 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
@yield [void]
if _any_ matching component is enabled
@param parts [Array<String, Symbol>]
list of components' names
@return [Boolean]
matching status
# 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
Once-only set {Bootinq} to ready state firing the ‘@_on_ready` callback. @return [self] on the first call @return [void] after
# 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
@return [Boolean]
# File lib/bootinq.rb, line 177 def ready? !!@ready end
Collector method. @example
Bootinq.switch do |part| part.frontend { … } part.backend { … } end
@yield [switch] @see Bootinq::Switch
@return [void]
# File lib/bootinq.rb, line 391 def switch yield(Switch.new) nil end