glue_gun_dsl 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0962a5666114730f40d7b78a9e70e2762480f318cf4790641c47e8406d6af3aa'
4
- data.tar.gz: 161576a11d2ff360e5c4fc34327c9db3ecf3201fe9f81fd864e84c34ce223c12
3
+ metadata.gz: 1cf96906f1af3fd62ba4c0e7aa061d9d109aee45ced7284e7776cb74d7dca313
4
+ data.tar.gz: 47f2454a802ce7a9bd335e7413fd2851551eae2437d4de9bebb0f1f31ad82b53
5
5
  SHA512:
6
- metadata.gz: 3231f0758ae02faea304068b16bd9da17b6910a2f2f3ccd3ddec5c08ee6c9af651b36fc376e9e9a80d330d24448c4d18389cab87a76ade9fbd5db52eeaabb1d6
7
- data.tar.gz: 1f5f68fd1879dc28a708214a794d0d2f39410b912e0659d63a67c2244dffb82b89dea4084bfa4db028c0e0aef46a501a4b017a000c64c7fc8027798a669c303e
6
+ metadata.gz: 8f4be572fe72494ab001d7ea71ec0f35064d1914a7982ab026ffd12f40126dd01051c1594e9baf78ae3a6415b3bfb44d7524f512986af53dcaa0db3ab5924ba3
7
+ data.tar.gz: fcf9a586af6236a3595b6a215daca49239a5e7407ab4086626dbcc105f755a76a82ae45de536047a7a8464be0035a89b81e8c4ca5bb11dd1c7aca31eae049b97
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)
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,33 @@ 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)
211
-
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}."
216
- end
217
- end
218
- end
219
-
220
- def initialize_dependency(component_type, init_args = {}, definition = nil)
180
+ def initialize_dependency(component_type, init_args = nil, definition = nil)
221
181
  definition ||= self.class.dependency_definitions[component_type]
222
182
  is_array = init_args.is_a?(Array)
223
- is_hash = is_hash?(init_args, definition)
183
+ is_hash = definition.is_hash?(init_args)
184
+
185
+ return nil if init_args.nil? && definition.default_option_name.nil?
224
186
 
225
187
  if is_array
226
188
  dep = []
227
189
  config = []
228
190
  Array(init_args).each do |args|
229
- d, c = initialize_single_dependency(component_type, args, definition)
191
+ d, c = definition.initialize_single_dependency(args, self)
230
192
  dep.push(d)
231
193
  config.push(c)
232
194
  end
233
195
  elsif is_hash
234
196
  dep = {}
235
197
  config = {}
236
- validate_hash_dependencies(init_args, definition, component_type)
198
+ definition.validate_hash_dependencies(init_args)
237
199
 
238
200
  init_args.each do |key, args|
239
- d, c = initialize_single_dependency(component_type, args, definition)
201
+ d, c = definition.initialize_single_dependency(args, self)
240
202
  dep[key] = d
241
203
  config[key] = c
242
204
  end
243
205
  else
244
- dep, config = initialize_single_dependency(component_type, init_args, definition)
206
+ dep, config = definition.initialize_single_dependency(init_args, self)
245
207
  end
246
208
 
247
209
  dependencies[component_type] = {
@@ -252,126 +214,6 @@ module GlueGun
252
214
  dep
253
215
  end
254
216
 
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
217
  def propagate_changes
376
218
  changed_attributes.each do |attr_name, _old_value|
377
219
  new_value = read_attribute(attr_name)
@@ -421,26 +263,6 @@ module GlueGun
421
263
  end
422
264
  end
423
265
 
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
266
  def dependencies
445
267
  @dependencies ||= {}
446
268
  end
@@ -490,7 +312,7 @@ module GlueGun
490
312
  end
491
313
 
492
314
  class DependencyBuilder
493
- attr_reader :component_type, :option_configs, :when_block, :is_only
315
+ attr_reader :component_type, :option_configs, :when_block, :is_only, :factory_class
494
316
 
495
317
  def initialize(component_type)
496
318
  @component_type = component_type
@@ -500,6 +322,180 @@ module GlueGun
500
322
  @is_only = false
501
323
  end
502
324
 
325
+ def set_factory_class(factory_class)
326
+ @factory_class = factory_class
327
+ end
328
+
329
+ def builder
330
+ if factory?
331
+ factory_instance = factory_class.new
332
+ dep_defs = factory_instance.dependency_definitions
333
+ dep_defs[dep_defs.keys.first]
334
+ else
335
+ self
336
+ end
337
+ end
338
+
339
+ def get_option_configs
340
+ builder.option_configs
341
+ end
342
+
343
+ def allowed_configurations
344
+ get_option_configs.keys
345
+ end
346
+
347
+ def allowed_classes
348
+ get_option_configs.values
349
+ end
350
+
351
+ def factory?
352
+ @factory_class.present?
353
+ end
354
+
355
+ def builder?
356
+ !factory?
357
+ end
358
+
359
+ def is_hash?(init_args)
360
+ return false unless init_args.is_a?(Hash)
361
+
362
+ allowed_configs = allowed_configurations
363
+ return false if allowed_configs.count == 1 && allowed_configs == [:default]
364
+
365
+ if init_args.key?(:option_name)
366
+ allowed_configs.exclude?(init_args[:option_name])
367
+ else
368
+ init_args.keys.none? { |k| allowed_configs.include?(k) }
369
+ end
370
+ end
371
+
372
+ def initialize_factory_dependency(init_args, parent)
373
+ builder.initialize_single_dependency(init_args, parent)
374
+ end
375
+
376
+ def initialize_builder_dependency(init_args, parent)
377
+ if init_args && init_args.is_a?(Hash) && init_args.key?(:option_name)
378
+ option_name = init_args[:option_name]
379
+ init_args = init_args[:value]
380
+ else
381
+ option_name, init_args = determine_option_name(init_args)
382
+ end
383
+
384
+ option_config = option_configs[option_name]
385
+
386
+ raise ArgumentError, "Unknown #{component_type} option '#{option_name}'" unless option_config
387
+
388
+ [instantiate_dependency(option_config, init_args, parent), option_config]
389
+ end
390
+
391
+ def initialize_single_dependency(init_args, parent)
392
+ if dependency_injected?(init_args)
393
+ dep = init_args
394
+ option_config = injected_dependency(init_args)
395
+ elsif factory?
396
+ dep, option_config = initialize_factory_dependency(init_args, parent)
397
+ else
398
+ dep, option_config = initialize_builder_dependency(init_args, parent)
399
+ end
400
+
401
+ [dep, option_config]
402
+ end
403
+
404
+ def build_dependency_attributes(option_config, dep_attributes, parent)
405
+ option_config.attributes.each do |attr_name, attr_config|
406
+ if dep_attributes.key?(attr_name)
407
+ value = dep_attributes[attr_name]
408
+ else
409
+ value = if attr_config.source && parent.respond_to?(attr_config.source)
410
+ parent.send(attr_config.source)
411
+ elsif parent.respond_to?(attr_name)
412
+ parent.send(attr_name)
413
+ else
414
+ attr_config.default
415
+ end
416
+ value = attr_config.process_value(value, self) if attr_config.respond_to?(:process_value)
417
+ dep_attributes[attr_name] = value
418
+ end
419
+ end
420
+
421
+ dep_attributes
422
+ end
423
+
424
+ def determine_option_name(init_args)
425
+ option_name = nil
426
+
427
+ # Use when block if defined
428
+ if when_block
429
+ result = instance_exec(init_args, &when_block)
430
+ if result.is_a?(Hash) && result[:option]
431
+ option_name = result[:option]
432
+ as_attr = result[:as]
433
+ init_args = { as_attr => init_args } if as_attr && init_args
434
+ end
435
+ end
436
+
437
+ # Detect option from user input
438
+ if option_name.nil? && (init_args.is_a?(Hash) && init_args.keys.size == 1)
439
+ if option_configs.key?(init_args.keys.first)
440
+ option_name = init_args.keys.first
441
+ init_args = init_args[option_name] # Extract the inner value
442
+ else
443
+ default_option = get_option(default_option_name)
444
+ unless default_option.only?
445
+ raise ArgumentError,
446
+ "Unknown #{component_type} option: #{init_args.keys.first}."
447
+ end
448
+ unless default_option.attributes.keys.include?(init_args.keys.first)
449
+ raise ArgumentError, "#{default_option.class_name} does not respond to #{init_args.keys.first}"
450
+ end
451
+ end
452
+ end
453
+
454
+ # Use default option if none determined
455
+ option_name ||= default_option_name
456
+
457
+ [option_name, init_args]
458
+ end
459
+
460
+ def instantiate_dependency(option_config, init_args, parent)
461
+ dep_attributes = init_args.is_a?(Hash) ? init_args : {}
462
+
463
+ # Build dependency attributes, including sourcing from parent
464
+ dep_attributes = build_dependency_attributes(option_config, dep_attributes, parent)
465
+
466
+ if dep_attributes.key?(:id)
467
+ raise ArgumentError,
468
+ "cannot bind attribute 'id' between #{parent.class.name} and #{option_config.class_name}. ID is reserved for primary keys in Ruby on Rails"
469
+ end
470
+ dependency_class = option_config.class_name
471
+ dependency_class.new(dep_attributes)
472
+ end
473
+
474
+ def injected_dependency(value)
475
+ allowed_classes.detect do |option|
476
+ option_class = option.class_name
477
+ value.is_a?(option_class)
478
+ end
479
+ end
480
+
481
+ def dependency_injected?(value)
482
+ injected_dependency(value).present?
483
+ end
484
+
485
+ def validate_hash_dependencies(init_args)
486
+ allowed_configs = allowed_configurations
487
+
488
+ init_args.each do |_named_key, configuration|
489
+ next unless configuration.is_a?(Hash)
490
+
491
+ key = configuration.keys.first
492
+ if key.nil? || allowed_configs.exclude?(key)
493
+ raise ArgumentError,
494
+ "Unknown #{component_type} option: #{init_args.keys.first}."
495
+ end
496
+ end
497
+ end
498
+
503
499
  # Support set_class and attribute for single-option dependencies
504
500
  def set_class(class_name)
505
501
  single_option.set_class(class_name)
@@ -523,7 +519,11 @@ module GlueGun
523
519
  end
524
520
 
525
521
  def default_option_name
526
- @default_option_name || (@single_option ? :default : nil)
522
+ if factory?
523
+ builder.default_option_name
524
+ else
525
+ @default_option_name || (@single_option ? :default : nil)
526
+ end
527
527
  end
528
528
 
529
529
  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.18"
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.18
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