glue_gun_dsl 0.1.17 → 0.1.19

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: '0962a5666114730f40d7b78a9e70e2762480f318cf4790641c47e8406d6af3aa'
4
- data.tar.gz: 161576a11d2ff360e5c4fc34327c9db3ecf3201fe9f81fd864e84c34ce223c12
3
+ metadata.gz: 68fc0793c49beee7168734f15f10ef57334c0fbbb0f4373c74cfdad16b416a2f
4
+ data.tar.gz: 34ba98c7f1518de7b16ae4c2d1e4cd9f5c8224ad911c3d7bc14ed21dec139fe8
5
5
  SHA512:
6
- metadata.gz: 3231f0758ae02faea304068b16bd9da17b6910a2f2f3ccd3ddec5c08ee6c9af651b36fc376e9e9a80d330d24448c4d18389cab87a76ade9fbd5db52eeaabb1d6
7
- data.tar.gz: 1f5f68fd1879dc28a708214a794d0d2f39410b912e0659d63a67c2244dffb82b89dea4084bfa4db028c0e0aef46a501a4b017a000c64c7fc8027798a669c303e
6
+ metadata.gz: 72d8a6751b6ed9d9ea22f8dfb8e89386b2217bbdfc17f922716a208ca9fce8a290762ceb43dcb301d32e9733058c1ea7433b8435a8b6f3b9feb96db83ceaeaaa
7
+ data.tar.gz: 4cece38fc3e06acd4c285a4e8d04e19d69c01b39ac80af8815f967b96df7f863ef225bad172fb4e5231552a107ae092445feee107b3419225db3e6c9d26f59b1
data/lib/glue_gun/dsl.rb CHANGED
@@ -107,13 +107,14 @@ module GlueGun
107
107
  options = {}
108
108
  end
109
109
 
110
+ dependency_builder = DependencyBuilder.new(component_type, options)
111
+
110
112
  if factory_class.present?
111
- dependency_definitions[component_type] = { factory_class: factory_class }
113
+ dependency_builder.set_factory_class(factory_class)
112
114
  else
113
- dependency_builder = DependencyBuilder.new(component_type)
114
115
  dependency_builder.instance_eval(&block)
115
- dependency_definitions[component_type] = { builder: dependency_builder }
116
116
  end
117
+ dependency_definitions[component_type] = dependency_builder
117
118
 
118
119
  # Define singleton method to allow hardcoding dependencies in subclasses
119
120
  define_singleton_method component_type do |option = nil, options = {}|
@@ -176,72 +177,38 @@ module GlueGun
176
177
  end
177
178
  end
178
179
 
179
- def allowed_configurations(init_args, definition)
180
- if definition[:factory_class]
181
- factory_instance = definition[:factory_class].new
182
- dep_defs = factory_instance.dependency_definitions
183
- definition = dep_defs[dep_defs.keys.first]
184
- return allowed_configurations(init_args, definition)
185
- elsif definition[:builder]
186
- builder = definition[:builder]
187
- allowed_configs = builder.option_configs.keys
188
- end
189
-
190
- allowed_configs
191
- end
192
-
193
- def is_hash?(init_args, definition)
194
- return false unless init_args.is_a?(Hash)
195
-
196
- allowed_configs = allowed_configurations(init_args, definition)
197
- return false if allowed_configs.count == 1 && allowed_configs == [:default]
198
-
199
- if init_args.key?(:option_name)
200
- allowed_configs.exclude?(init_args[:option_name])
201
- else
202
- init_args.keys.none? { |k| allowed_configs.include?(k) }
203
- end
204
- end
205
-
206
- def validate_hash_dependencies(init_args, definition, component_type)
207
- allowed_configs = allowed_configurations(init_args, definition)
208
-
209
- init_args.each do |_named_key, configuration|
210
- next unless configuration.is_a?(Hash)
180
+ def initialize_dependency(component_type, init_args = nil, definition = nil)
181
+ definition ||= self.class.dependency_definitions[component_type]
182
+ is_array = init_args.is_a?(Array)
183
+ is_hash = definition.is_hash?(init_args)
211
184
 
212
- key = configuration.keys.first
213
- if key.nil? || allowed_configs.exclude?(key)
214
- raise ArgumentError,
215
- "Unknown #{component_type} option: #{init_args.keys.first}."
185
+ if init_args.nil? && definition.default_option_name.nil?
186
+ return nil if definition.lazy?
187
+ if definition.when_block.nil?
188
+ raise "No default option or when block present for component_type #{component_type}. Don't know how to build!"
216
189
  end
217
190
  end
218
- end
219
-
220
- def initialize_dependency(component_type, init_args = {}, definition = nil)
221
- definition ||= self.class.dependency_definitions[component_type]
222
- is_array = init_args.is_a?(Array)
223
- is_hash = is_hash?(init_args, definition)
224
191
 
225
192
  if is_array
226
193
  dep = []
227
194
  config = []
228
195
  Array(init_args).each do |args|
229
- d, c = initialize_single_dependency(component_type, args, definition)
196
+ d, c = definition.initialize_single_dependency(args, self)
230
197
  dep.push(d)
231
198
  config.push(c)
232
199
  end
233
200
  elsif is_hash
234
201
  dep = {}
235
202
  config = {}
236
- validate_hash_dependencies(init_args, definition, component_type)
203
+ definition.validate_hash_dependencies(init_args)
237
204
 
238
205
  init_args.each do |key, args|
239
- d, c = initialize_single_dependency(component_type, args, definition)
206
+ d, c = definition.initialize_single_dependency(args, self)
240
207
  dep[key] = d
241
208
  config[key] = c
242
209
  end
243
210
  else
244
- dep, config = initialize_single_dependency(component_type, init_args, definition)
211
+ dep, config = definition.initialize_single_dependency(init_args, self)
245
212
  end
246
213
 
247
214
  dependencies[component_type] = {
@@ -252,126 +219,6 @@ module GlueGun
252
219
  dep
253
220
  end
254
221
 
255
- def initialize_factory_dependency(component_type, init_args, definition)
256
- factory_instance = definition[:factory_class].new
257
-
258
- # Pass the parent instance to the factory
259
- factory_instance.instance_variable_set(:@parent, self)
260
-
261
- dep_defs = factory_instance.dependency_definitions
262
- definition = dep_defs[dep_defs.keys.first]
263
-
264
- if dep_defs.key?(component_type)
265
- factory_instance.send(:initialize_single_dependency, component_type, init_args, definition)
266
- elsif dep_defs.keys.one?
267
- factory_instance.send(:initialize_single_dependency, dep_defs.keys.first, init_args, definition)
268
- else
269
- raise ArgumentError,
270
- "Don't know how to use Factory #{factory_instance.class} to build dependency '#{component_type}'"
271
- end
272
- end
273
-
274
- def initialize_builder_dependency(component_type, init_args, definition)
275
- dependency_builder = definition[:builder]
276
-
277
- if init_args && init_args.is_a?(Hash) && init_args.key?(:option_name)
278
- option_name = init_args[:option_name]
279
- init_args = init_args[:value]
280
- else
281
- option_name, init_args = determine_option_name(component_type, init_args)
282
- end
283
-
284
- option_config = dependency_builder.option_configs[option_name]
285
-
286
- raise ArgumentError, "Unknown #{component_type} option '#{option_name}'" unless option_config
287
-
288
- [instantiate_dependency(option_config, init_args), option_config]
289
- end
290
-
291
- def initialize_single_dependency(component_type, init_args, definition)
292
- if dependency_injected?(component_type, init_args)
293
- dep = init_args
294
- option_config = injected_dependency(component_type, init_args)
295
- elsif definition[:factory_class]
296
- dep, option_config = initialize_factory_dependency(component_type, init_args, definition)
297
- else
298
- dep, option_config = initialize_builder_dependency(component_type, init_args, definition)
299
- end
300
-
301
- [dep, option_config]
302
- end
303
-
304
- def build_dependency_attributes(option_config, dep_attributes)
305
- option_config.attributes.each do |attr_name, attr_config|
306
- if dep_attributes.key?(attr_name)
307
- value = dep_attributes[attr_name]
308
- else
309
- value = if attr_config.source && respond_to?(attr_config.source)
310
- send(attr_config.source)
311
- elsif respond_to?(attr_name)
312
- send(attr_name)
313
- elsif instance_variable_defined?(:@parent) && @parent.respond_to?(attr_name)
314
- @parent.send(attr_name)
315
- else
316
- attr_config.default
317
- end
318
- value = attr_config.process_value(value, self) if attr_config.respond_to?(:process_value)
319
- dep_attributes[attr_name] = value
320
- end
321
- end
322
-
323
- dep_attributes
324
- end
325
-
326
- def determine_option_name(component_type, init_args)
327
- dependency_builder = self.class.dependency_definitions[component_type][:builder]
328
-
329
- option_name = nil
330
-
331
- # Use when block if defined
332
- if dependency_builder.when_block
333
- result = instance_exec(init_args, &dependency_builder.when_block)
334
- if result.is_a?(Hash) && result[:option]
335
- option_name = result[:option]
336
- as_attr = result[:as]
337
- init_args = { as_attr => init_args } if as_attr && init_args
338
- end
339
- end
340
-
341
- # Detect option from user input
342
- if option_name.nil? && (init_args.is_a?(Hash) && init_args.keys.size == 1)
343
- if dependency_builder.option_configs.key?(init_args.keys.first)
344
- option_name = init_args.keys.first
345
- init_args = init_args[option_name] # Extract the inner value
346
- else
347
- default_option = dependency_builder.get_option(dependency_builder.default_option_name)
348
- raise ArgumentError, "Unknown #{component_type} option: #{init_args.keys.first}." unless default_option.only?
349
- unless default_option.attributes.keys.include?(init_args.keys.first)
350
- raise ArgumentError, "#{default_option.class_name} does not respond to #{init_args.keys.first}"
351
- end
352
- end
353
- end
354
-
355
- # Use default option if none determined
356
- option_name ||= dependency_builder.default_option_name
357
-
358
- [option_name, init_args]
359
- end
360
-
361
- def instantiate_dependency(option_config, init_args)
362
- dep_attributes = init_args.is_a?(Hash) ? init_args : {}
363
-
364
- # Build dependency attributes, including sourcing from parent
365
- dep_attributes = build_dependency_attributes(option_config, dep_attributes)
366
-
367
- if dep_attributes.key?(:id)
368
- raise ArgumentError,
369
- "cannot bind attribute 'id' between #{self.class.name} and #{option_config.class_name}. ID is reserved for primary keys in Ruby on Rails"
370
- end
371
- dependency_class = option_config.class_name
372
- dependency_class.new(dep_attributes)
373
- end
374
-
375
222
  def propagate_changes
376
223
  changed_attributes.each do |attr_name, _old_value|
377
224
  new_value = read_attribute(attr_name)
@@ -421,26 +268,6 @@ module GlueGun
421
268
  end
422
269
  end
423
270
 
424
- def injected_dependency(component_type, value)
425
- definition = self.class.dependency_definitions[component_type]
426
- builder = definition[:builder]
427
- factory = definition[:factory_class]
428
-
429
- option_configs = if builder
430
- builder.option_configs
431
- else
432
- factory.dependency_definitions.values.first.values.first.option_configs
433
- end
434
- option_configs.values.detect do |option|
435
- option_class = option.class_name
436
- value.is_a?(option_class)
437
- end
438
- end
439
-
440
- def dependency_injected?(component_type, value)
441
- injected_dependency(component_type, value).present?
442
- end
443
-
444
271
  def dependencies
445
272
  @dependencies ||= {}
446
273
  end
@@ -490,14 +317,193 @@ module GlueGun
490
317
  end
491
318
 
492
319
  class DependencyBuilder
493
- attr_reader :component_type, :option_configs, :when_block, :is_only
320
+ attr_reader :component_type, :option_configs, :when_block, :is_only, :factory_class, :lazy, :parent
494
321
 
495
- def initialize(component_type)
322
+ def initialize(component_type, options)
496
323
  @component_type = component_type
497
324
  @option_configs = {}
498
325
  @default_option_name = nil
499
326
  @single_option = nil
500
327
  @is_only = false
328
+ @lazy = options.key?(:lazy) ? options.dig(:lazy) : true
329
+ end
330
+
331
+ def set_factory_class(factory_class)
332
+ @factory_class = factory_class
333
+ end
334
+
335
+ def builder
336
+ if factory?
337
+ factory_instance = factory_class.new
338
+ dep_defs = factory_instance.dependency_definitions
339
+ dep_defs[dep_defs.keys.first]
340
+ else
341
+ self
342
+ end
343
+ end
344
+
345
+ def get_option_configs
346
+ builder.option_configs
347
+ end
348
+
349
+ def allowed_configurations
350
+ get_option_configs.keys
351
+ end
352
+
353
+ def allowed_classes
354
+ get_option_configs.values
355
+ end
356
+
357
+ def factory?
358
+ @factory_class.present?
359
+ end
360
+
361
+ def builder?
362
+ !factory?
363
+ end
364
+
365
+ def lazy?
366
+ @lazy == true
367
+ end
368
+
369
+ def is_hash?(init_args)
370
+ return false unless init_args.is_a?(Hash)
371
+
372
+ allowed_configs = allowed_configurations
373
+ return false if allowed_configs.count == 1 && allowed_configs == [:default]
374
+
375
+ if init_args.key?(:option_name)
376
+ allowed_configs.exclude?(init_args[:option_name])
377
+ else
378
+ init_args.keys.none? { |k| allowed_configs.include?(k) }
379
+ end
380
+ end
381
+
382
+ def initialize_factory_dependency(init_args, instance)
383
+ builder.initialize_single_dependency(init_args, instance)
384
+ end
385
+
386
+ def initialize_builder_dependency(init_args, instance)
387
+ if init_args && init_args.is_a?(Hash) && init_args.key?(:option_name)
388
+ option_name = init_args[:option_name]
389
+ init_args = init_args[:value]
390
+ else
391
+ option_name, init_args = determine_option_name(init_args, instance)
392
+ end
393
+
394
+ option_config = option_configs[option_name]
395
+
396
+ raise ArgumentError, "Unknown #{component_type} option '#{option_name}'" unless option_config
397
+
398
+ [instantiate_dependency(option_config, init_args, instance), option_config]
399
+ end
400
+
401
+ def initialize_single_dependency(init_args, instance)
402
+ if dependency_injected?(init_args)
403
+ dep = init_args
404
+ option_config = injected_dependency(init_args)
405
+ elsif factory?
406
+ dep, option_config = initialize_factory_dependency(init_args, instance)
407
+ else
408
+ dep, option_config = initialize_builder_dependency(init_args, instance)
409
+ end
410
+
411
+ [dep, option_config]
412
+ end
413
+
414
+ def build_dependency_attributes(option_config, dep_attributes, parent)
415
+ option_config.attributes.each do |attr_name, attr_config|
416
+ if dep_attributes.key?(attr_name)
417
+ value = dep_attributes[attr_name]
418
+ else
419
+ value = if attr_config.source && parent.respond_to?(attr_config.source)
420
+ parent.send(attr_config.source)
421
+ elsif parent.respond_to?(attr_name)
422
+ parent.send(attr_name)
423
+ else
424
+ attr_config.default
425
+ end
426
+ value = attr_config.process_value(value, self) if attr_config.respond_to?(:process_value)
427
+ dep_attributes[attr_name] = value
428
+ end
429
+ end
430
+
431
+ dep_attributes
432
+ end
433
+
434
+ def determine_option_name(init_args, instance)
435
+ option_name = nil
436
+
437
+ # Use when block if defined
438
+ if when_block
439
+ result = instance.instance_exec(init_args, &when_block)
440
+ if result.is_a?(Hash) && result[:option]
441
+ option_name = result[:option]
442
+ as_attr = result[:as]
443
+ init_args = { as_attr => init_args } if as_attr && init_args
444
+ end
445
+ end
446
+
447
+ # Detect option from user input
448
+ if option_name.nil? && (init_args.is_a?(Hash) && init_args.keys.size == 1)
449
+ if option_configs.key?(init_args.keys.first)
450
+ option_name = init_args.keys.first
451
+ init_args = init_args[option_name] # Extract the inner value
452
+ else
453
+ default_option = get_option(default_option_name)
454
+ unless default_option.only?
455
+ raise ArgumentError,
456
+ "Unknown #{component_type} option: #{init_args.keys.first}."
457
+ end
458
+ unless default_option.attributes.keys.include?(init_args.keys.first)
459
+ raise ArgumentError, "#{default_option.class_name} does not respond to #{init_args.keys.first}"
460
+ end
461
+ end
462
+ end
463
+
464
+ # Use default option if none determined
465
+ option_name ||= default_option_name
466
+
467
+ [option_name, init_args]
468
+ end
469
+
470
+ def instantiate_dependency(option_config, init_args, parent)
471
+ dep_attributes = init_args.is_a?(Hash) ? init_args : {}
472
+
473
+ # Build dependency attributes, including sourcing from parent
474
+ dep_attributes = build_dependency_attributes(option_config, dep_attributes, parent)
475
+
476
+ if dep_attributes.key?(:id)
477
+ raise ArgumentError,
478
+ "cannot bind attribute 'id' between #{parent.class.name} and #{option_config.class_name}. ID is reserved for primary keys in Ruby on Rails"
479
+ end
480
+ dependency_class = option_config.class_name
481
+ dependency_class.new(dep_attributes)
482
+ end
483
+
484
+ def injected_dependency(value)
485
+ allowed_classes.detect do |option|
486
+ option_class = option.class_name
487
+ value.is_a?(option_class)
488
+ end
489
+ end
490
+
491
+ def dependency_injected?(value)
492
+ injected_dependency(value).present?
493
+ end
494
+
495
+ def validate_hash_dependencies(init_args)
496
+ allowed_configs = allowed_configurations
497
+
498
+ init_args.each do |_named_key, configuration|
499
+ next unless configuration.is_a?(Hash)
500
+
501
+ key = configuration.keys.first
502
+ if key.nil? || allowed_configs.exclude?(key)
503
+ raise ArgumentError,
504
+ "Unknown #{component_type} option: #{init_args.keys.first}."
505
+ end
506
+ end
501
507
  end
502
508
 
503
509
  # Support set_class and attribute for single-option dependencies
@@ -523,7 +529,11 @@ module GlueGun
523
529
  end
524
530
 
525
531
  def default_option_name
526
- @default_option_name || (@single_option ? :default : nil)
532
+ if factory?
533
+ builder.default_option_name
534
+ else
535
+ @default_option_name || (@single_option ? :default : nil)
536
+ end
527
537
  end
528
538
 
529
539
  def when(&block)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GlueGun
4
- VERSION = "0.1.17"
4
+ VERSION = "0.1.19"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glue_gun_dsl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.17
4
+ version: 0.1.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Shollenberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-16 00:00:00.000000000 Z
11
+ date: 2024-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel