glue_gun_dsl 0.1.16 → 0.1.18

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: cc6aaef2a48bc81e0383a4b88f0afc691a3f99097e12877579c7d579953cfea7
4
- data.tar.gz: ceb228b7381a0fbc7e2b3c07fe650cc05d89524dcec7199490fafb8813117bcc
3
+ metadata.gz: 1cf96906f1af3fd62ba4c0e7aa061d9d109aee45ced7284e7776cb74d7dca313
4
+ data.tar.gz: 47f2454a802ce7a9bd335e7413fd2851551eae2437d4de9bebb0f1f31ad82b53
5
5
  SHA512:
6
- metadata.gz: 7624649374c85a5822bb6a74096e50c69947db98bd1097ec020e3e82edde37c35a9057905534271da63385ef1c37d43b479712d0442247837151c0ab23ef9cbc
7
- data.tar.gz: 1f18555d5938dc3fb16bed600e3216502601e0b97c8a7c0998b22dd636cea40263f5ec7225767232e873778690c5ca0d752786c4e5017b371c35b17d191277aa
6
+ metadata.gz: 8f4be572fe72494ab001d7ea71ec0f35064d1914a7982ab026ffd12f40126dd01051c1594e9baf78ae3a6415b3bfb44d7524f512986af53dcaa0db3ab5924ba3
7
+ data.tar.gz: fcf9a586af6236a3595b6a215daca49239a5e7407ab4086626dbcc105f755a76a82ae45de536047a7a8464be0035a89b81e8c4ca5bb11dd1c7aca31eae049b97
data/lib/glue_gun/dsl.rb CHANGED
@@ -106,16 +106,15 @@ module GlueGun
106
106
  factory_class = options
107
107
  options = {}
108
108
  end
109
- is_array = options[:array] || false
110
- is_hash = options[:hash] || false
109
+
110
+ dependency_builder = DependencyBuilder.new(component_type)
111
111
 
112
112
  if factory_class.present?
113
- dependency_definitions[component_type] = { factory_class: factory_class, array: is_array, hash: is_hash }
113
+ dependency_builder.set_factory_class(factory_class)
114
114
  else
115
- dependency_builder = DependencyBuilder.new(component_type)
116
115
  dependency_builder.instance_eval(&block)
117
- dependency_definitions[component_type] = { builder: dependency_builder, array: is_array, hash: is_hash }
118
116
  end
117
+ dependency_definitions[component_type] = dependency_builder
119
118
 
120
119
  # Define singleton method to allow hardcoding dependencies in subclasses
121
120
  define_singleton_method component_type do |option = nil, options = {}|
@@ -178,29 +177,33 @@ module GlueGun
178
177
  end
179
178
  end
180
179
 
181
- def initialize_dependency(component_type, init_args = {}, definition = nil)
180
+ def initialize_dependency(component_type, init_args = nil, definition = nil)
182
181
  definition ||= self.class.dependency_definitions[component_type]
183
- is_array = definition[:array]
184
- is_hash = definition[:hash]
182
+ is_array = init_args.is_a?(Array)
183
+ is_hash = definition.is_hash?(init_args)
184
+
185
+ return nil if init_args.nil? && definition.default_option_name.nil?
185
186
 
186
187
  if is_array
187
188
  dep = []
188
189
  config = []
189
190
  Array(init_args).each do |args|
190
- d, c = initialize_single_dependency(component_type, args, definition)
191
+ d, c = definition.initialize_single_dependency(args, self)
191
192
  dep.push(d)
192
193
  config.push(c)
193
194
  end
194
195
  elsif is_hash
195
196
  dep = {}
196
197
  config = {}
198
+ definition.validate_hash_dependencies(init_args)
199
+
197
200
  init_args.each do |key, args|
198
- d, c = initialize_single_dependency(component_type, args, definition)
201
+ d, c = definition.initialize_single_dependency(args, self)
199
202
  dep[key] = d
200
203
  config[key] = c
201
204
  end
202
205
  else
203
- dep, config = initialize_single_dependency(component_type, init_args, definition)
206
+ dep, config = definition.initialize_single_dependency(init_args, self)
204
207
  end
205
208
 
206
209
  dependencies[component_type] = {
@@ -211,126 +214,6 @@ module GlueGun
211
214
  dep
212
215
  end
213
216
 
214
- def initialize_factory_dependency(component_type, init_args, definition)
215
- factory_instance = definition[:factory_class].new
216
-
217
- # Pass the parent instance to the factory
218
- factory_instance.instance_variable_set(:@parent, self)
219
-
220
- dep_defs = factory_instance.dependency_definitions
221
- definition = dep_defs[dep_defs.keys.first]
222
-
223
- if dep_defs.key?(component_type)
224
- factory_instance.send(:initialize_single_dependency, component_type, init_args, definition)
225
- elsif dep_defs.keys.one?
226
- factory_instance.send(:initialize_single_dependency, dep_defs.keys.first, init_args, definition)
227
- else
228
- raise ArgumentError,
229
- "Don't know how to use Factory #{factory_instance.class} to build dependency '#{component_type}'"
230
- end
231
- end
232
-
233
- def initialize_builder_dependency(component_type, init_args, definition)
234
- dependency_builder = definition[:builder]
235
-
236
- if init_args && init_args.is_a?(Hash) && init_args.key?(:option_name)
237
- option_name = init_args[:option_name]
238
- init_args = init_args[:value]
239
- else
240
- option_name, init_args = determine_option_name(component_type, init_args)
241
- end
242
-
243
- option_config = dependency_builder.option_configs[option_name]
244
-
245
- raise ArgumentError, "Unknown #{component_type} option '#{option_name}'" unless option_config
246
-
247
- [instantiate_dependency(option_config, init_args), option_config]
248
- end
249
-
250
- def initialize_single_dependency(component_type, init_args, definition)
251
- if dependency_injected?(component_type, init_args)
252
- dep = init_args
253
- option_config = injected_dependency(component_type, init_args)
254
- elsif definition[:factory_class]
255
- dep, option_config = initialize_factory_dependency(component_type, init_args, definition)
256
- else
257
- dep, option_config = initialize_builder_dependency(component_type, init_args, definition)
258
- end
259
-
260
- [dep, option_config]
261
- end
262
-
263
- def build_dependency_attributes(option_config, dep_attributes)
264
- option_config.attributes.each do |attr_name, attr_config|
265
- if dep_attributes.key?(attr_name)
266
- value = dep_attributes[attr_name]
267
- else
268
- value = if attr_config.source && respond_to?(attr_config.source)
269
- send(attr_config.source)
270
- elsif respond_to?(attr_name)
271
- send(attr_name)
272
- elsif instance_variable_defined?(:@parent) && @parent.respond_to?(attr_name)
273
- @parent.send(attr_name)
274
- else
275
- attr_config.default
276
- end
277
- value = attr_config.process_value(value, self) if attr_config.respond_to?(:process_value)
278
- dep_attributes[attr_name] = value
279
- end
280
- end
281
-
282
- dep_attributes
283
- end
284
-
285
- def determine_option_name(component_type, init_args)
286
- dependency_builder = self.class.dependency_definitions[component_type][:builder]
287
-
288
- option_name = nil
289
-
290
- # Use when block if defined
291
- if dependency_builder.when_block
292
- result = instance_exec(init_args, &dependency_builder.when_block)
293
- if result.is_a?(Hash) && result[:option]
294
- option_name = result[:option]
295
- as_attr = result[:as]
296
- init_args = { as_attr => init_args } if as_attr && init_args
297
- end
298
- end
299
-
300
- # Detect option from user input
301
- if option_name.nil? && (init_args.is_a?(Hash) && init_args.keys.size == 1)
302
- if dependency_builder.option_configs.key?(init_args.keys.first)
303
- option_name = init_args.keys.first
304
- init_args = init_args[option_name] # Extract the inner value
305
- else
306
- default_option = dependency_builder.get_option(dependency_builder.default_option_name)
307
- raise ArgumentError, "Unknown #{component_type} option: #{init_args.keys.first}." unless default_option.only?
308
- unless default_option.attributes.keys.include?(init_args.keys.first)
309
- raise ArgumentError, "#{default_option.class_name} does not respond to #{init_args.keys.first}"
310
- end
311
- end
312
- end
313
-
314
- # Use default option if none determined
315
- option_name ||= dependency_builder.default_option_name
316
-
317
- [option_name, init_args]
318
- end
319
-
320
- def instantiate_dependency(option_config, init_args)
321
- dep_attributes = init_args.is_a?(Hash) ? init_args : {}
322
-
323
- # Build dependency attributes, including sourcing from parent
324
- dep_attributes = build_dependency_attributes(option_config, dep_attributes)
325
-
326
- if dep_attributes.key?(:id)
327
- raise ArgumentError,
328
- "cannot bind attribute 'id' between #{self.class.name} and #{option_config.class_name}. ID is reserved for primary keys in Ruby on Rails"
329
- end
330
- dependency_class = option_config.class_name
331
- dependency_class.new(dep_attributes)
332
- end
333
-
334
217
  def propagate_changes
335
218
  changed_attributes.each do |attr_name, _old_value|
336
219
  new_value = read_attribute(attr_name)
@@ -380,26 +263,6 @@ module GlueGun
380
263
  end
381
264
  end
382
265
 
383
- def injected_dependency(component_type, value)
384
- definition = self.class.dependency_definitions[component_type]
385
- builder = definition[:builder]
386
- factory = definition[:factory_class]
387
-
388
- option_configs = if builder
389
- builder.option_configs
390
- else
391
- factory.dependency_definitions.values.first.values.first.option_configs
392
- end
393
- option_configs.values.select do |option|
394
- option_class = option.class_name
395
- value.is_a?(option_class)
396
- end
397
- end
398
-
399
- def dependency_injected?(component_type, value)
400
- injected_dependency(component_type, value).any?
401
- end
402
-
403
266
  def dependencies
404
267
  @dependencies ||= {}
405
268
  end
@@ -449,7 +312,7 @@ module GlueGun
449
312
  end
450
313
 
451
314
  class DependencyBuilder
452
- attr_reader :component_type, :option_configs, :when_block, :is_only
315
+ attr_reader :component_type, :option_configs, :when_block, :is_only, :factory_class
453
316
 
454
317
  def initialize(component_type)
455
318
  @component_type = component_type
@@ -459,6 +322,180 @@ module GlueGun
459
322
  @is_only = false
460
323
  end
461
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
+
462
499
  # Support set_class and attribute for single-option dependencies
463
500
  def set_class(class_name)
464
501
  single_option.set_class(class_name)
@@ -482,7 +519,11 @@ module GlueGun
482
519
  end
483
520
 
484
521
  def default_option_name
485
- @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
486
527
  end
487
528
 
488
529
  def when(&block)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GlueGun
4
- VERSION = "0.1.16"
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.16
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