bootinq 1.8 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70b36fe43d2052568352c6afd05fdc32f761bdb232dadaed96d3ba05f3a93f6b
4
- data.tar.gz: 28d7999b1f29b41c87944da9b564b5641ba0582465ae703b8d582aa8814a8eca
3
+ metadata.gz: 6e377cd962a0467e2e6ea056674bae230612b823f667d780c9982d76ff4bbf0c
4
+ data.tar.gz: 402aef98700c975d26ef975c8e21fef639138dc03b5adb93c54f8e85e3b5a44f
5
5
  SHA512:
6
- metadata.gz: 85a46c57dee491a7cd41997e79992befbe184439e0d49af40e980cf222d8f513eeda9c5874fdf8006eed7561715ab41cbe36f1de24a9a4efbe081601f1dbc69f
7
- data.tar.gz: 66d7bbe52c5835ac53bd11e3413c26b7af08eabf7c06f19b3ccc88f4627d52c1e90341381ba2b5a180504580ed171afff4f5cbf4a58f03757f6722ea73f285fb
6
+ metadata.gz: 78dffcca879fd25017dc6d09fdd33f5bfdbd5878922a7c705d10f0ccd07cd703bede9e2d5e2e06ccc8c18bcdc21e091fc9efb7611e8f7d0b69193bfe4f951676
7
+ data.tar.gz: e6aeda4ed1293fb720a9998d79dd877bc36cff136fc78b9bcf420b8dd2f9a322fe573014303c4572c0bde882c327d6d66d7c127ec0983403fa0693ab43047d0e
data/.yardopts ADDED
@@ -0,0 +1,8 @@
1
+ --db .yardoc \
2
+ --markup-provider commonmarker \
3
+ --markup markdown \
4
+ --no-single-db \
5
+ --no-cache \
6
+ --no-document \
7
+ --embed-mixins \
8
+ --hide-void-return
data/Gemfile CHANGED
@@ -26,6 +26,8 @@ end
26
26
  group :development do
27
27
  # You don't need these, but I use them
28
28
  gem "awesome_print"
29
+ gem "commonmarker", require: false
30
+ gem "yard"
29
31
  end
30
32
 
31
33
  group :shared_boot do
@@ -2,90 +2,306 @@
2
2
 
3
3
  class Bootinq
4
4
  class Component
5
- attr_reader :intern, :id2name, :group
5
+ # @!attribute [r] intern
6
+ # @return [Symbol]
6
7
 
7
- alias :to_sym :intern
8
- alias :to_s :id2name
9
- alias :gem_name :id2name
10
- alias :name :id2name
8
+ attr_reader :intern
11
9
 
10
+ # @!attribute [r] id2name
11
+ # @return [Symbol]
12
+
13
+ attr_reader :id2name
14
+
15
+ # @!attribute [r] group
16
+ # @return [Symbol] Bundle group name
17
+
18
+ attr_reader :group
19
+
20
+ alias_method :to_sym, :intern
21
+ alias_method :to_s, :id2name
22
+ alias_method :gem_name, :id2name
23
+ alias_method :name, :id2name
24
+
25
+ # @see #initialize
26
+ # @param intern [String, Symbol]
27
+ # @return [Bootinq::Component] frozen
28
+ def self.new(intern)
29
+ super.freeze
30
+ end
31
+
32
+ # @param intern [String, Symbol]
33
+ # @return [self]
12
34
  def initialize(intern)
13
- @intern = intern.to_sym
35
+ @intern = intern.to_sym
14
36
  @id2name = intern.to_s.freeze
15
- @group = :"#@id2name\_boot"
16
- freeze
37
+ @group = :"#{@id2name}_boot"
17
38
  end
18
39
 
40
+ # @return [Boolean]
19
41
  def mountable?
20
42
  false
21
43
  end
22
44
 
45
+ # @return [Symbol]
23
46
  def module_name
24
47
  @id2name.camelcase.to_sym
25
48
  end
26
49
 
50
+ # @return [Module]
51
+ def namespace
52
+ Object.const_get(module_name)
53
+ end
54
+
55
+ # @return [void]
27
56
  def engine
28
57
  end
29
58
 
59
+ # @param klass [Class]
60
+ # @return [Boolean]
30
61
  def kind_of?(klass)
31
62
  super || @intern.kind_of?(klass)
32
63
  end
33
64
 
34
- def == other
65
+ ##
66
+ # @!group Coercing methods
67
+
68
+ # Coerces self to other value klass.
69
+ # @overload coerce_to(string)
70
+ # @param string [String]
71
+ # @return [String] {#id2name}
72
+ # @overload coerce_to(symbol)
73
+ # @param symbol [Symbol]
74
+ # @return [Symbol] {#intern}
75
+ # @overload coerce_to(other)
76
+ # @param other [Any]
77
+ # @return [self]
78
+ def coerce_to(other)
35
79
  case other
36
- when String then other == @id2name
37
- when Symbol then other == @intern
38
- else super
80
+ when String; @id2name
81
+ when Symbol; @intern
82
+ else self
39
83
  end
40
84
  end
41
85
 
42
- def ===(other)
86
+ # Coerces other value
87
+ # @overload coerce(symbol)
88
+ # @param symbol [Symbol]
89
+ # @return [Symbol] symbol
90
+ # @overload coerce(other)
91
+ # @param other [String, Any]
92
+ # @return [String] other
93
+ def coerce(other)
43
94
  case other
44
- when String then other === @id2name
45
- when Symbol then other === @intern
46
- else super
95
+ when String, Symbol; other
96
+ else other.to_s
47
97
  end
48
98
  end
49
99
 
100
+ # @!endgroup
101
+ ##
102
+
103
+ ##
104
+ # @!group Comparation methods
105
+
106
+ # @param other [Any]
107
+ # @return [Boolean]
108
+ def ==(other)
109
+ other = coerce(other)
110
+ other == coerce_to(other)
111
+ end
112
+
113
+ # @param other [Any]
114
+ # @return [Boolean]
115
+ def ===(other)
116
+ other = coerce(other)
117
+ other === coerce_to(other)
118
+ end
119
+
120
+ # @param other [Any]
121
+ # @return [Boolean]
50
122
  def casecmp(other)
51
- case other
52
- when String then @id2name.casecmp(other)
53
- when Symbol then @intern.casecmp(other)
54
- when self.class then casecmp(other.to_s)
55
- end
123
+ other = coerce(other)
124
+ coerce_to(other).casecmp(other)
56
125
  end
57
126
 
127
+ # @param other [Any]
128
+ # @return [Boolean]
58
129
  def casecmp?(other)
59
- case other
60
- when String then @id2name.casecmp?(other)
61
- when Symbol then @intern.casecmp?(other)
62
- when self.class then casecmp?(other.to_s)
63
- end
130
+ other = coerce(other)
131
+ coerce_to(other).casecmp?(other)
132
+ end
133
+
134
+ # @!endgroup
135
+ ##
136
+
137
+ ##
138
+ # @!group Symbol-delegated methods
139
+
140
+ # @return [String] representation of {#intern} as a symbol literal.
141
+ def inspect
142
+ @intern.inspect
143
+ end
144
+
145
+ # @return [Proc] responded to the given method by {#intern}
146
+ def to_proc
147
+ @intern.to_proc
64
148
  end
65
149
 
66
- %i(inspect to_proc __id__ hash).
67
- each { |sym| class_eval %(def #{sym}; @intern.#{sym}; end), __FILE__, __LINE__ + 1 }
150
+ # @return [Integer] identifier for {#intern}
151
+ def __id__
152
+ @intern.__id__
153
+ end
68
154
 
69
- %i(encoding empty? length).
70
- each { |sym| class_eval %(def #{sym}; @id2name.#{sym}; end), __FILE__, __LINE__ + 1 }
155
+ # @return [Integer] {#intern}'s hash
156
+ def hash
157
+ @intern.hash
158
+ end
71
159
 
72
- %i(match match? =~ []).
73
- each { |sym| class_eval %(def #{sym}(*args); @id2name.#{sym}(*args); end), __FILE__, __LINE__ + 1 }
160
+ # @!endgroup
161
+ ##
74
162
 
75
- %i(upcase downcase capitalize swapcase succ next).
76
- each { |sym| class_eval %(def #{sym}; self.class.new(@intern.#{sym}); end), __FILE__, __LINE__ + 1 }
163
+ ##
164
+ # @!group String-delegated methods
77
165
 
78
- alias :slice :[]
79
- alias :size :length
166
+ # @return [Encoding] of {#id2name}
167
+ def encoding
168
+ @id2name.encoding
169
+ end
170
+
171
+ # @return [Boolean]
172
+ def empty?
173
+ @id2name.empty?
174
+ end
175
+
176
+ # @return [Integer] length of {#id2name}
177
+ def length
178
+ @id2name.length
179
+ end
180
+
181
+ # @param pattern [Regexp, String]
182
+ # @param start_from [Integer] position in {#id2name} to begin the search
183
+ # @yield [MatchData] if match succeed
184
+ # @yieldreturn [Any]
185
+ # @return [MatchData] unless block given
186
+ # @return [Any] returned by the given block
187
+ def match(*args, &block)
188
+ @id2name.match(*args, &block)
189
+ end
190
+
191
+ # @param pattern [Regexp, String]
192
+ # @param start_from [Integer] position in {#id2name} to begin the search
193
+ # @return [Boolean] indicates whether the regexp is matched or not
194
+ def match?(*args)
195
+ @id2name.match?(*args)
196
+ end
197
+
198
+ # @overload =~(pattern)
199
+ # @param pattern [Regexp]
200
+ # @return [Integer] if matched, the position the match starts
201
+ # @return [nil] if there is no match
202
+ # @overload =~(arg)
203
+ # @param arg [Object]
204
+ # @see Object#=~
205
+ def =~(arg)
206
+ @id2name =~ arg
207
+ end
208
+
209
+ # Element reference
210
+ # @see String#slice
211
+ def slice(*args)
212
+ @id2name[*args]
213
+ end
214
+
215
+ alias_method :[], :slice
216
+
217
+ # @!endgroup
218
+ ##
219
+
220
+ ##
221
+ # @!group Symbol-delegated mutation methods
222
+
223
+ # @see Symbol#upcase
224
+ # @return [Bootinq::Component] new instance with upcased {#intern}
225
+ def upcase
226
+ self.class.new(@intern.upcase)
227
+ end
228
+
229
+ # @see Symbol#downcase
230
+ # @return [Bootinq::Component] new instance with downcased {#intern}
231
+ def downcase
232
+ self.class.new(@intern.downcase)
233
+ end
234
+
235
+ # @see Symbol#capitalize
236
+ # @return [Bootinq::Component] new instance with capitalized {#intern}
237
+ def capitalize
238
+ self.class.new(@intern.capitalize)
239
+ end
240
+
241
+ # @see Symbol#swapcase
242
+ # @return [Bootinq::Component] new instance with swapcased {#intern}
243
+ def swapcase
244
+ self.class.new(@intern.swapcase)
245
+ end
246
+
247
+ # @see Symbol#succ
248
+ # @return [Bootinq::Component] new instance with the successor {#intern}
249
+ def succ
250
+ self.class.new(@intern.succ)
251
+ end
252
+
253
+ # @see Symbol#next
254
+ # @return [Bootinq::Component] new instance with the next {#intern}
255
+ def next
256
+ self.class.new(@intern.next)
257
+ end
258
+
259
+ # @endgroup
260
+ ##
80
261
  end
81
262
 
82
263
  class Mountable < Component
264
+ # @!attribute [r] module_name
265
+ # @return [Symbol]
266
+
267
+ # @!attribute [r] namespace
268
+ # @return [Module]
269
+
270
+ def initialize(intern)
271
+ super
272
+ @module_name = module_name()
273
+ @namespace = namespace()
274
+ end
275
+
83
276
  def mountable?
84
277
  true
85
278
  end
86
279
 
280
+ def module_name
281
+ return @module_name if frozen? || defined?(@module_name)
282
+ super
283
+ end
284
+
285
+ def namespace
286
+ if frozen? || defined?(@namespace)
287
+ @namespace.is_a?(Proc) ? @namespace.call : @namespace
288
+ elsif namespace_defined?
289
+ super
290
+ else
291
+ proc { super }
292
+ end
293
+ end
294
+
295
+ # @return [Class]
87
296
  def engine
88
- Object.const_get(module_name)::Engine
297
+ namespace::Engine
298
+ end
299
+
300
+ private
301
+
302
+ # @api private
303
+ def namespace_defined?
304
+ Object.const_defined?(module_name)
89
305
  end
90
306
  end
91
307
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Bootinq
4
+ # When just required, hooks {Bootinq#enable_component} method to
5
+ # generate fast inline wrapping methods.
6
+ #
7
+ # @see Mixins#enable_component
8
+ #
9
+ # @example Usage
10
+ # require 'bootinq'
11
+ # require 'bootinq/mixins'
12
+ module Mixins
13
+ # @api private
14
+ module ComputeNameMethod
15
+ DASH = '_'
16
+
17
+ private_constant :DASH
18
+
19
+ def compute_name(component_name)
20
+ component_name.to_s.split(DASH).
21
+ each(&:capitalize!).
22
+ join << @name_suffix
23
+ end
24
+ end
25
+
26
+ private_constant :ComputeNameMethod
27
+
28
+ # @api private
29
+ class Enabled < ::Module
30
+ @name_suffix = 'EnabledMixin'
31
+ extend ComputeNameMethod
32
+
33
+ def initialize(module_name, component_name)
34
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
35
+ # Yields the block due to component is enabled
36
+ # @yield [void]
37
+ def on_#{component_name}(*)
38
+ yield
39
+ end
40
+
41
+ # Does nothing due to component is enabled
42
+ # @return [void]
43
+ def not_#{component_name}(*)
44
+ end
45
+ RUBY
46
+ end
47
+ end
48
+
49
+ private_constant :Enabled
50
+
51
+ # @api private
52
+ class Disabled < ::Module
53
+ @name_suffix = 'DisabledMixin'
54
+ extend ComputeNameMethod
55
+
56
+ def initialize(module_name, component_name)
57
+ define_method(:name, module_name.method(:itself))
58
+
59
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
60
+ # Does nothing due to component is disabled
61
+ # @return [void]
62
+ def on_#{component_name}(*)
63
+ end
64
+
65
+ # Yields the block due to component is disabled
66
+ # @yield [void]
67
+ def not_#{component_name}(*)
68
+ yield
69
+ end
70
+ RUBY
71
+ end
72
+ end
73
+
74
+ private_constant :Disabled
75
+
76
+ Builder = -> (component_name, enabled) do
77
+ klass = enabled ? Enabled : Disabled
78
+ module_name = klass.compute_name(component_name).freeze
79
+
80
+ if Bootinq.const_defined?(module_name)
81
+ Bootinq.const_get(module_name)
82
+ else
83
+ Bootinq.const_set(module_name, klass.new(module_name, component_name))
84
+ end
85
+ end
86
+
87
+ private_constant :Builder
88
+
89
+ # Generates {Enabled} or {Disabled} mixin and sets it to a constant once,
90
+ # bypassing if it has been already defined.
91
+ # @yield [component_name, enabled]
92
+ # @return [void]
93
+ def enable_component(name, **opts)
94
+ super(name, **opts) do |component_name, enabled|
95
+ Bootinq.extend Builder[component_name, enabled]
96
+ yield(component_name, enabled) if block_given?
97
+ end
98
+ end
99
+ end
100
+
101
+ prepend Mixins
102
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ class Bootinq
6
+ # Require `bootinq/railtie` in the `before_configuration` block of your
7
+ # application definition to allow load component-scoped config paths
8
+ # only when the named component is enabled:
9
+ #
10
+ # - `config/routes.rb` → `config/routes.component.rb`
11
+ # - `config/locales` → `config/locales.component`
12
+ # - `config/initializers` → `config/initializers.component`
13
+ #
14
+ # It doesn't affect on the default paths without suffix.
15
+ #
16
+ # @example
17
+ # # config/application.rb
18
+ # module Example
19
+ # class Application < Rails::Application
20
+ # config.before_configuration do
21
+ # require 'bootinq/railtie'
22
+ # end
23
+ # end
24
+ # end
25
+ class Railtie < ::Rails::Railtie
26
+ initializer 'bootinq.add_locales', before: :add_locales do |app|
27
+ Bootinq.components.each do |component|
28
+ app.paths["config/locales"] << "config/locales.#{component.name}"
29
+ end
30
+ end
31
+
32
+ initializer 'bootinq.load_config_initializers', before: :load_config_initializers do |app|
33
+ Bootinq.components.each do |component|
34
+ app.paths["config/initializers"] << "config/initializers.#{component.name}"
35
+ end
36
+ end
37
+
38
+ initializer 'bootinq.add_routing_paths', before: :add_routing_paths do |app|
39
+ Bootinq.components.each do |component|
40
+ app.paths["config/routes.rb"] << "config/routes.#{component.name}.rb"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Bootinq
4
- VERSION = "1.8"
4
+ VERSION = "2.0.1"
5
5
  end
data/lib/bootinq.rb CHANGED
@@ -6,25 +6,21 @@ require "forwardable"
6
6
  require "bootinq/component"
7
7
  require "bootinq/switch"
8
8
 
9
- # = Bootinq
9
+ # # Bootinq
10
10
  #
11
- # == Installation
11
+ # ## Installation
12
12
  #
13
- # === Ruby on Rails
13
+ # ### Ruby on Rails
14
14
  #
15
- # 1. Insert <tt>require "bootinq"</tt> in the top of <tt>config/application.rb</tt>
15
+ # 1. insert `require "bootinq"` on top of `config/application.rb`;
16
+ # 2. find and replace `Bundler.require(*Rails.groups)` with `Bootinq.require`
16
17
  #
17
- # 2. Find <tt>Bundler.require(*Rails.groups)</tt> line below and replace it
18
- # with the <tt>Bootinq.require</tt>.
18
+ # ### Other frameworks
19
19
  #
20
- # === Other
21
- #
22
- # 1. Locate <tt>Bundler.require(...)</tt> in your app and insert <tt>require "bootinq"</tt> above.
23
- #
24
- # 2. Replace located <tt>Bundler.require(...)</tt> line with the <tt>Bootinq.require(...)</tt>.
25
- #
26
- # For example, if you are using Grape:
20
+ # 1. locate `Bundler.require(…)` in your app and insert `require "bootinq"` above it;
21
+ # 2. replace previosly located `Bundler.require(…)` line with the `Bootinq.require(…)`.
27
22
  #
23
+ # @example Grape
28
24
  # # config/application.rb
29
25
  #
30
26
  # require 'boot'
@@ -32,10 +28,8 @@ require "bootinq/switch"
32
28
  #
33
29
  # # Bundler.require :default, ENV['RACK_ENV']
34
30
  # Bootinq.require :default, ENV['RACK_ENV'], verbose: true
35
- # ...
36
- #
37
- # == Example <tt>config/bootinq.yml</tt>:
38
31
  #
32
+ # @example config/bootinq.yml
39
33
  # env_key: BOOTINQ
40
34
  # default: a
41
35
  #
@@ -49,7 +43,6 @@ require "bootinq/switch"
49
43
  # deps:
50
44
  # shared:
51
45
  # in: af
52
- #
53
46
  class Bootinq
54
47
  include Singleton
55
48
 
@@ -75,47 +68,72 @@ class Bootinq
75
68
 
76
69
  private_constant :FilterNegValue
77
70
 
78
- # :call-seq:
79
- # Bootinq.require(*groups, verbose: false, &block)
80
- #
81
- # Invokes the <tt>Bootinq.init</tt> method with the given verbose key argument & block,
82
- # and, finally, makes Bundler to require the given groups.
83
- def self.require(*groups, verbose: false, &block) # :yields: Bootinq.instance
84
- init(verbose: verbose, &block)
71
+ # Invokes the {init} method with the given options and block,
72
+ # then calls {Bundler.require} with the enabled groups.
73
+ # @see init
74
+ # @see Bundler.require
75
+ # @param groups [Array<Symbol>]
76
+ # @param options [Hash]
77
+ # initialization options
78
+ # @option options [Boolean] verbose
79
+ # track inquired components
80
+ # @option options [Proc] on_ready
81
+ # optional ready callback proc
82
+ # @return [void]
83
+ def self.require(*groups, **options, &on_ready)
84
+ init(**options, &on_ready)
85
85
  Bundler.require(*instance.groups(*groups))
86
86
  end
87
87
 
88
- # :call-seq:
89
- # Bootinq.setup(*groups, verbose: false, &block)
90
- #
91
- # Invokes the <tt>Bootinq.init</tt> method with the given verbose key argument & block,
92
- # and, finally, makes Bundler to setup the given groups.
93
- def self.setup(*groups, verbose: false, &block) # :yields: Bootinq.instance
94
- init(verbose: verbose, &block)
88
+ # Invokes the {init} method with the given options and block,
89
+ # then calls {Bundler.require} with the enabled groups.
90
+ # @see init
91
+ # @see Bundler.setup
92
+ # @param groups [Array<Symbol>]
93
+ # @param options [Hash]
94
+ # initialization options
95
+ # @option options [Boolean] verbose
96
+ # track inquired components
97
+ # @option options [Proc] on_ready
98
+ # optional ready callback proc
99
+ # @yield [instance]
100
+ # @return [void]
101
+ def self.setup(*groups, **options, &on_ready) # :yields: Bootinq.instance
102
+ init(**options, &on_ready)
95
103
  Bundler.setup(*instance.groups(*groups))
96
104
  end
97
105
 
98
- # :call-seq:
99
- # Bootinq.init(verbose: false, &block) -> true or false
100
- #
101
- # Initializes itself. Sets the BOOTINQ_PATH enviroment variable if it is missing.
102
- # To track inquired components use <tt>verbose: true</tt> key argument.
103
- # Optionally yields block within the own instance's binding.
104
- def self.init(verbose: false, &block) # :yields: Bootinq.instance
106
+ # Sets `BOOTINQ_PATH` enviroment variable if it is missing & initializes itself
107
+ # @overload init(verbose: false, on_ready:)
108
+ # @overload init(verbose: false, &on_ready)
109
+ # @param verbose [Boolean]
110
+ # track inquired components
111
+ # @param on_ready [Proc]
112
+ # optional ready callback proc
113
+ # @return [instance]
114
+ def self.init(verbose: false, on_ready: nil, &block)
105
115
  ENV['BOOTINQ_PATH'] ||= File.expand_path('../bootinq.yml', caller_locations(2, 1)[0].path)
106
116
 
107
117
  instance
108
- instance.instance_variable_set(:@_on_ready, block.to_proc) if block_given?
118
+ on_ready = block.to_proc if on_ready.nil? && block_given?
119
+ instance.instance_variable_set(:@_on_ready, on_ready.to_proc) if on_ready
120
+
109
121
  puts "Bootinq: loading components #{instance.components.join(', ')}" if verbose
122
+
110
123
  instance.ready!
111
124
  end
112
125
 
113
- # Reads config from the given or default path, deserializes it and returns as a hash.
126
+ # Reads config
127
+ # @param path [String]
128
+ # path to yaml config (default: ENV['BOOTINQ_PATH'])
129
+ # @return [Hash]
130
+ # deserializes yaml config
114
131
  def self.deserialized_config(path: nil)
115
132
  bootinq_yaml = File.read(path || ENV.fetch('BOOTINQ_PATH'))
116
133
  psych_safe_load(bootinq_yaml, [Symbol])
117
134
  end
118
135
 
136
+ # @api private
119
137
  if RUBY_VERSION >= '3.1.0'
120
138
  def self.psych_safe_load(path, permitted_classes)
121
139
  YAML.safe_load(path, permitted_classes: permitted_classes)
@@ -126,10 +144,20 @@ class Bootinq
126
144
  end
127
145
  end
128
146
 
147
+ private_class_method :psych_safe_load
148
+
149
+ # @!attribute flags [r]
150
+ # @return [Array<String>]
151
+
129
152
  attr_reader :flags
153
+
154
+ # @!attribute components [r]
155
+ # @return [Array<String>]
156
+
130
157
  attr_reader :components
131
158
 
132
- def initialize # :no-doc:
159
+ # @return [self]
160
+ def initialize
133
161
  config = self.class.deserialized_config
134
162
  config.merge!(DEFAULT) { |_, l, r| l.nil? ? r : l }
135
163
 
@@ -145,172 +173,235 @@ class Bootinq
145
173
  config['mount'].each { |flag, name| enable_component(name, flag: flag.to_s, as: Mountable) }
146
174
  end
147
175
 
148
- def ready? # :no-doc:
176
+ # @return [Boolean]
177
+ def ready?
149
178
  !!@ready
150
179
  end
151
180
 
152
- # :call-seq:
153
- # Bootinq.ready! -> nil or self
154
- #
155
- # At the first call marks Bootinq as ready and returns the instance,
156
- # otherwise returns nil.
181
+ # Once-only set {Bootinq} to ready state firing the `@_on_ready` callback.
182
+ # @return [self] on the first call
183
+ # @return [void] after
157
184
  def ready!
158
185
  return if ready?
159
186
  @ready = true
160
187
  if defined?(@_on_ready)
161
- instance_exec(&@_on_ready)
188
+ Bootinq.class_exec(&@_on_ready)
162
189
  remove_instance_variable :@_on_ready
163
190
  end
164
191
  freeze
165
192
  end
166
193
 
167
- # :call-seq:
168
- # Bootinq.enable_component(name, flag: [, as: Component])
169
- #
194
+ # Enables the given component if it is required by flag or
195
+ # when another enabled component depends it.
196
+ # @param name [String]
197
+ # of the component
198
+ # @param flag [String]
199
+ # the component's assigned char flag
200
+ # @param as [Class]
201
+ # the component's constructor class
202
+ # @yield [name, is_enabled]
203
+ # @return [void]
170
204
  def enable_component(name, flag:, as: Component)
171
205
  if is_dependency?(name) || @_value.include?(flag)
172
206
  @flags << flag
173
207
  @components << as.new(name)
208
+ yield(name, true) if block_given?
209
+ else
210
+ yield(name, false) if block_given?
174
211
  end
212
+
213
+ nil
175
214
  end
176
215
 
177
- # :call-seq:
178
- # Bootinq.enabled?(name) -> true or false
179
- #
180
- # Checks if a component with the given name (i.e. the same gem group)
181
- # is enabled
216
+ # Checks if a component with the given name (i.e. the same gem group) is enabled
217
+ # @return [Boolean]
182
218
  def enabled?(name)
183
- ALL.include?(name) || components.include?(name)
219
+ ALL.include?(name) || @components.include?(name)
184
220
  end
185
221
 
186
- # :call-seq:
187
- # Bootinq.component(name) -> Bootinq::Component
188
- # Bootinq[name] -> Bootinq::Component
189
- #
190
- # Returns a <tt>Bootinq::Component</tt> object by its name
222
+ # @param name [String, Symbol]
223
+ # @return [Bootinq::Component]
191
224
  def component(name)
192
- components[components.index(name)]
225
+ @components[@components.index(name)]
193
226
  end
194
227
 
195
- alias :[] :component
228
+ alias_method :[], :component
196
229
 
197
- # :call-seq:
198
- # Bootinq.each_mountable { |part| block } -> Array
199
- # Bootinq.each_mountable -> Enumerator
200
- #
201
- # Calls the given block once for each enabled mountable component
202
- # passing that part as a parameter. Returns the array of all mountable components.
203
- #
204
- # If no block is given, an Enumerator is returned.
205
- def each_mountable(&block) # :yields: part
206
- components.select(&:mountable?).each(&block)
230
+ # Checks if a component with the given name (i.e. the same gem group) is disabled
231
+ # @return [Boolean]
232
+ def disabled?(name)
233
+ !@components.include?(name)
207
234
  end
208
235
 
209
- # :call-seq:
210
- # Bootinq.groups(*groups)
211
- #
212
- # Merges enabled Bootinq's groups with the given groups and, if loaded with Rails,
213
- # passes them to <tt>Rails.groups</tt> method, otherwise just returns the merged list
214
- # to use with <tt>Bundler.require</tt>.
236
+ # Enumerates enabled mountable components
237
+ # @overload each_mountable()
238
+ # @overload each_mountable(&block)
239
+ # @yield [component]
240
+ # @return [Enumerator]
241
+ def each_mountable
242
+ return enum_for(:each_mountable) unless block_given?
243
+
244
+ @components.each do |component|
245
+ yield(component) if component.mountable?
246
+ end
247
+ end
248
+
249
+ # Merges groups of enabled components with the given ones.
250
+ # When loaded with Rails, it passes them to {Rails.groups} method,
251
+ # otherwise just returns the merged list to use it with {Bundler.require}.
252
+ # @param groups [Array<String, Symbol>]
253
+ # @return [Array<String, Symbol>] merged groups
215
254
  def groups(*groups)
216
- groups.unshift(*components.map(&:group))
217
- if defined?(Rails)
218
- Rails.groups(*groups)
219
- else
220
- groups
255
+ @components.each do |component|
256
+ next if groups.include?(component.group)
257
+ groups.unshift(component.group)
221
258
  end
259
+
260
+ defined?(Rails) ? Rails.groups(*groups) : groups
222
261
  end
223
262
 
224
- # :call-seq:
225
- # Bootinq.on(name) { block } -> true or false
226
- # Bootinq.on(any: [names]) { block } -> true or false
227
- # Bootinq.on(all: [names]) { block } -> true or false
263
+ # @overload on(name)
264
+ # @yield [void] (if component is enabled)
265
+ # @param name [Symbol] single component's name
228
266
  #
229
- # Takes a component's name or single-key options hash as an argument and
230
- # yields a given block if the target components are enabled.
267
+ # @overload on(any:)
268
+ # @see on_any
269
+ # @yield [void] (if _any_ matching component is enabled)
270
+ # @param any [Array<Symbol>] list of components' names
231
271
  #
232
- # See examples for a usage.
272
+ # @overload on(all:)
273
+ # @see on_all
274
+ # @yield [void] (if _all_ matching components are enabled)
275
+ # @param all [Array<Symbol>] list of components' names
233
276
  #
234
- # ==== Example:
277
+ # @return [Boolean] matching status
235
278
  #
236
- # Bootinq.on :frontend do
237
- # # make frontend thing...
238
- # end
239
- #
240
- # Bootinq.on any: %i(frontend backend) do
241
- # # do something when frontend or backend is enabled
242
- # end
243
- #
244
- # Bootinq.on all: %i(frontend backend) do
245
- # # do something when frontend and backend are enabled
246
- # end
247
- def on(name = nil, any: nil, all: nil) # :yields:
248
- if ALL.include?(name)
279
+ # @example single
280
+ # Bootinq.on(:frontend) { puts 'frontend' }
281
+ # @example any
282
+ # Bootinq.on(any: %i[frontend backend]) { puts 'frontend or backend' }
283
+ # @example all
284
+ # Bootinq.on(all: %i[frontend backend]) { puts 'both' }
285
+ def on(name = nil, any: nil, all: nil)
286
+ if name && ALL.include?(name)
249
287
  yield
250
288
  return true
251
289
  end
252
290
 
253
- if name.nil? && any.nil? && all.nil?
254
- raise ArgumentError, "wrong arguments (given 0, expected 1)"
255
- elsif (any && all) || (name && (any || all))
256
- raise ArgumentError, "expected single argument or one of keywords: `all' or `any'"
257
- end
258
-
259
291
  is_matched =
260
292
  name ? enabled?(name) :
261
293
  any ? on_any(*any) :
262
294
  all ? on_all(*all) : false
295
+
263
296
  yield if is_matched
297
+
264
298
  is_matched
265
299
  end
266
300
 
267
- # :call-seq:
268
- # Bootinq.on_all(*names) { block } -> true or false
269
- #
270
- # Takes a list of component names and yields a given block (optionally)
271
- # if all of them are enabled. Returns boolean matching status.
301
+ # @yield [void]
302
+ # if _all_ matching components are enabled
303
+ # @param parts [Array<String, Symbol>]
304
+ # list of components' names
305
+ # @return [Boolean]
306
+ # matching status
272
307
  def on_all(*parts) # :yields:
273
- is_matched = parts.all? { |part| enabled?(part) }
308
+ is_matched = parts.reduce(true) { |m, part| m && enabled?(part) }
274
309
  yield if is_matched && block_given?
275
310
  is_matched
276
311
  end
277
312
 
278
- # :call-seq:
279
- # Bootinq.on_all(*names) { block } -> true or false
280
- #
281
- # Takes a list of component names and yields a given block (optionally)
282
- # if any of them are enabled. Returns boolean matching status.
313
+ # @yield [void]
314
+ # if _any_ matching component is enabled
315
+ # @param parts [Array<String, Symbol>]
316
+ # list of components' names
317
+ # @return [Boolean]
318
+ # matching status
283
319
  def on_any(*parts) # :yields:
284
- is_matched = parts.any? { |part| enabled?(part) }
320
+ is_matched = parts.reduce(false) { |m, part| m || enabled?(part) }
285
321
  yield if is_matched && block_given?
286
322
  is_matched
287
323
  end
288
324
 
289
- # :call-seq:
290
- # Bootinq.switch(*parts) { block } -> nil
325
+ # @overload not(name)
326
+ # @yield [void] (if component is disabled)
327
+ # @param name [Symbol] single component's name
291
328
  #
292
- # Collector method.
329
+ # @overload not(any:)
330
+ # @see not_any
331
+ # @yield [void] (if _any_ matching component is disabled)
332
+ # @param any [Array<Symbol>] list of components' names
293
333
  #
294
- # Example:
334
+ # @overload not(all:)
335
+ # @see not_all
336
+ # @yield [void] (if _all_ matching components are disabled)
337
+ # @param all [Array<Symbol>] list of components' names
295
338
  #
339
+ # @return [Boolean] matching status
340
+ #
341
+ # @example single
342
+ # Bootinq.not(:frontend) { puts 'not frontend' }
343
+ # @example any
344
+ # Bootinq.not(any: %i[frontend backend]) { puts 'neither frontend nor backend' }
345
+ # @example all
346
+ # Bootinq.on(all: %i[frontend backend]) { puts 'both disabled' }
347
+ def not(name = nil, any: nil, all: nil)
348
+ is_matched =
349
+ name ? disabled?(name) :
350
+ any ? not_any(*any) :
351
+ all ? not_all(*all) : false
352
+
353
+ yield if is_matched
354
+
355
+ is_matched
356
+ end
357
+
358
+ # @yield [void]
359
+ # if _all_ matching components are disabled
360
+ # @param parts [Array<String, Symbol>]
361
+ # list of components' names
362
+ # @return [Boolean]
363
+ # matching status
364
+ def not_all(*parts) # :yields:
365
+ is_matched = parts.reduce(true) { |m, part| m && disabled?(part) }
366
+ yield if is_matched && block_given?
367
+ is_matched
368
+ end
369
+
370
+ # @yield [void]
371
+ # if _any_ matching component is disabled
372
+ # @param parts [Array<String, Symbol>]
373
+ # list of components' names
374
+ # @return [Boolean]
375
+ # matching status
376
+ def not_any(*parts) # :yields:
377
+ is_matched = parts.reduce(false) { |m, part| m || disabled?(part) }
378
+ yield if is_matched && block_given?
379
+ is_matched
380
+ end
381
+
382
+ # Collector method.
383
+ # @example
296
384
  # Bootinq.switch do |part|
297
385
  # part.frontend { … }
298
386
  # part.backend { … }
299
387
  # end
300
- def switch # :yields: Bootinq::Switch.new
388
+ # @yield [switch]
389
+ # @see Bootinq::Switch
390
+ # @return [void]
391
+ def switch
301
392
  yield(Switch.new)
302
393
  nil
303
394
  end
304
395
 
305
- # :call-seq:
306
- # is_dependency?(part_name) -> true or false
307
- #
308
- # Checks if the named component is a dependency of the enabled one.
396
+ # Checks if the named component is dependent by another enabled one.
397
+ # @param name [String, Symbol]
398
+ # @return [Boolean]
309
399
  def is_dependency?(name)
310
- @_deps.key?(name) && @_value.count(@_deps[name]['in'].to_s) > 0
400
+ @_deps.key?(name.to_s) &&
401
+ @_value.count(@_deps.dig(name.to_s, 'in').to_s) > 0
311
402
  end
312
403
 
313
- # Freezes every instance variables and the instance itself.
404
+ # @api private
314
405
  def freeze
315
406
  @_value.freeze
316
407
  @_neg
@@ -321,20 +412,22 @@ class Bootinq
321
412
 
322
413
  extend SingleForwardable
323
414
 
324
- delegate %I[
325
- component
326
- components
327
- each_mountable
328
- enabled?
329
- enable_component
330
- flags
331
- groups
332
- on
333
- on_all
334
- on_any
335
- ready!
336
- ready?
337
- switch
338
- []
339
- ] => :instance
415
+ def_delegator :instance, :component
416
+ def_delegator :instance, :components
417
+ def_delegator :instance, :each_mountable
418
+ def_delegator :instance, :enabled?
419
+ def_delegator :instance, :enable_component
420
+ def_delegator :instance, :disabled?
421
+ def_delegator :instance, :flags
422
+ def_delegator :instance, :groups
423
+ def_delegator :instance, :on
424
+ def_delegator :instance, :on_all
425
+ def_delegator :instance, :on_any
426
+ def_delegator :instance, :not
427
+ def_delegator :instance, :not_all
428
+ def_delegator :instance, :not_any
429
+ def_delegator :instance, :ready!
430
+ def_delegator :instance, :ready?
431
+ def_delegator :instance, :switch
432
+ def_delegator :instance, :[]
340
433
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootinq
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.8'
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-30 00:00:00.000000000 Z
11
+ date: 2022-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -48,6 +48,7 @@ files:
48
48
  - ".gitignore"
49
49
  - ".rspec"
50
50
  - ".travis.yml"
51
+ - ".yardopts"
51
52
  - Gemfile
52
53
  - LICENSE.txt
53
54
  - README.md
@@ -58,6 +59,8 @@ files:
58
59
  - lib/bootinq.rb
59
60
  - lib/bootinq.yml
60
61
  - lib/bootinq/component.rb
62
+ - lib/bootinq/mixins.rb
63
+ - lib/bootinq/railtie.rb
61
64
  - lib/bootinq/switch.rb
62
65
  - lib/bootinq/version.rb
63
66
  homepage: https://github.com/estum/bootinq
@@ -79,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
82
  - !ruby/object:Gem::Version
80
83
  version: '0'
81
84
  requirements: []
82
- rubygems_version: 3.3.7
85
+ rubygems_version: 3.0.3
83
86
  signing_key:
84
87
  specification_version: 4
85
88
  summary: Rails Boot Inquirer