rom 2.0.2 → 3.0.0

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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -7
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile +17 -2
  6. data/Rakefile +7 -2
  7. data/lib/rom/array_dataset.rb +44 -0
  8. data/lib/rom/association_set.rb +11 -5
  9. data/lib/rom/auto_curry.rb +55 -0
  10. data/lib/rom/command.rb +331 -47
  11. data/lib/rom/command_registry.rb +7 -18
  12. data/lib/rom/commands/class_interface.rb +120 -6
  13. data/lib/rom/commands/composite.rb +0 -1
  14. data/lib/rom/commands/graph.rb +7 -15
  15. data/lib/rom/commands/lazy/update.rb +1 -1
  16. data/lib/rom/configuration.rb +2 -0
  17. data/lib/rom/configuration_dsl/command.rb +6 -8
  18. data/lib/rom/configuration_dsl/mapper.rb +2 -3
  19. data/lib/rom/configuration_dsl/mapper_dsl.rb +0 -1
  20. data/lib/rom/configuration_dsl/relation.rb +4 -4
  21. data/lib/rom/configuration_dsl.rb +0 -4
  22. data/lib/rom/constants.rb +7 -1
  23. data/lib/rom/container.rb +11 -17
  24. data/lib/rom/create_container.rb +0 -2
  25. data/lib/rom/data_proxy.rb +94 -0
  26. data/lib/rom/enumerable_dataset.rb +68 -0
  27. data/lib/rom/gateway.rb +74 -32
  28. data/lib/rom/global/plugin_dsl.rb +0 -2
  29. data/lib/rom/global.rb +0 -2
  30. data/lib/rom/initializer.rb +26 -0
  31. data/lib/rom/lint/gateway.rb +17 -0
  32. data/lib/rom/mapper_registry.rb +1 -1
  33. data/lib/rom/memory/commands.rb +0 -2
  34. data/lib/rom/memory/dataset.rb +1 -2
  35. data/lib/rom/memory/relation.rb +14 -1
  36. data/lib/rom/memory/schema.rb +13 -0
  37. data/lib/rom/plugin_registry.rb +1 -1
  38. data/lib/rom/plugins/command/schema.rb +2 -2
  39. data/lib/rom/plugins/configuration/configuration_dsl.rb +6 -2
  40. data/lib/rom/plugins/relation/key_inference.rb +4 -2
  41. data/lib/rom/plugins/relation/registry_reader.rb +5 -1
  42. data/lib/rom/registry.rb +50 -0
  43. data/lib/rom/relation/class_interface.rb +142 -30
  44. data/lib/rom/relation/curried.rb +15 -15
  45. data/lib/rom/relation/view_dsl.rb +31 -0
  46. data/lib/rom/relation.rb +101 -41
  47. data/lib/rom/schema/attribute.rb +367 -0
  48. data/lib/rom/schema/dsl.rb +14 -10
  49. data/lib/rom/schema.rb +337 -19
  50. data/lib/rom/setup/auto_registration.rb +20 -17
  51. data/lib/rom/setup/auto_registration_strategies/base.rb +8 -3
  52. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +4 -3
  53. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +5 -4
  54. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +3 -3
  55. data/lib/rom/setup/finalize/finalize_commands.rb +1 -1
  56. data/lib/rom/setup/finalize/finalize_mappers.rb +1 -1
  57. data/lib/rom/setup/finalize/finalize_relations.rb +4 -2
  58. data/lib/rom/setup/finalize.rb +1 -1
  59. data/lib/rom/transaction.rb +24 -0
  60. data/lib/rom/types.rb +9 -1
  61. data/lib/rom/version.rb +1 -1
  62. data/lib/rom.rb +4 -8
  63. data/rom.gemspec +5 -4
  64. data/spec/integration/command_registry_spec.rb +1 -14
  65. data/spec/integration/commands/create_spec.rb +5 -25
  66. data/spec/integration/commands/delete_spec.rb +1 -1
  67. data/spec/integration/commands/error_handling_spec.rb +1 -1
  68. data/spec/integration/commands/graph_spec.rb +20 -14
  69. data/spec/integration/commands/update_spec.rb +4 -27
  70. data/spec/integration/commands_spec.rb +1 -1
  71. data/spec/integration/{repositories → gateways}/extending_relations_spec.rb +1 -1
  72. data/spec/integration/{repositories → gateways}/setting_logger_spec.rb +2 -2
  73. data/spec/integration/mappers/combine_spec.rb +1 -1
  74. data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
  75. data/spec/integration/mappers/definition_dsl_spec.rb +1 -1
  76. data/spec/integration/mappers/embedded_spec.rb +1 -1
  77. data/spec/integration/mappers/exclude_spec.rb +1 -1
  78. data/spec/integration/mappers/fold_spec.rb +1 -1
  79. data/spec/integration/mappers/group_spec.rb +1 -1
  80. data/spec/integration/mappers/overwrite_attributes_value_spec.rb +1 -1
  81. data/spec/integration/mappers/prefix_separator_spec.rb +1 -1
  82. data/spec/integration/mappers/prefix_spec.rb +1 -1
  83. data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
  84. data/spec/integration/mappers/registering_custom_mappers_spec.rb +1 -1
  85. data/spec/integration/mappers/renaming_attributes_spec.rb +1 -1
  86. data/spec/integration/mappers/reusing_mappers_spec.rb +1 -1
  87. data/spec/integration/mappers/step_spec.rb +1 -1
  88. data/spec/integration/mappers/symbolizing_attributes_spec.rb +1 -1
  89. data/spec/integration/mappers/unfold_spec.rb +1 -1
  90. data/spec/integration/mappers/ungroup_spec.rb +2 -2
  91. data/spec/integration/mappers/unwrap_spec.rb +2 -2
  92. data/spec/integration/mappers/wrap_spec.rb +1 -1
  93. data/spec/integration/memory/commands/create_spec.rb +1 -1
  94. data/spec/integration/memory/commands/delete_spec.rb +1 -1
  95. data/spec/integration/memory/commands/update_spec.rb +1 -1
  96. data/spec/integration/multi_env_spec.rb +1 -1
  97. data/spec/integration/multi_repo_spec.rb +1 -1
  98. data/spec/integration/relations/default_dataset_spec.rb +1 -1
  99. data/spec/integration/relations/reading_spec.rb +1 -1
  100. data/spec/integration/relations/registry_dsl_spec.rb +1 -1
  101. data/spec/integration/setup_spec.rb +10 -4
  102. data/spec/shared/command_graph.rb +8 -4
  103. data/spec/shared/enumerable_dataset.rb +1 -1
  104. data/spec/spec_helper.rb +7 -9
  105. data/spec/support/schema.rb +14 -0
  106. data/spec/unit/rom/array_dataset_spec.rb +59 -0
  107. data/spec/unit/rom/association_set_spec.rb +4 -0
  108. data/spec/unit/rom/auto_curry_spec.rb +63 -0
  109. data/spec/unit/rom/commands/graph_spec.rb +12 -11
  110. data/spec/unit/rom/commands/lazy_spec.rb +8 -5
  111. data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +336 -0
  112. data/spec/unit/rom/commands/result_spec.rb +1 -1
  113. data/spec/unit/rom/commands_spec.rb +26 -3
  114. data/spec/unit/rom/configuration_spec.rb +1 -1
  115. data/spec/unit/rom/container_spec.rb +15 -5
  116. data/spec/unit/rom/create_container_spec.rb +1 -1
  117. data/spec/unit/rom/enumerable_dataset_spec.rb +15 -0
  118. data/spec/unit/rom/gateway_spec.rb +1 -1
  119. data/spec/unit/rom/mapper_registry_spec.rb +1 -1
  120. data/spec/unit/rom/memory/commands_spec.rb +1 -1
  121. data/spec/unit/rom/memory/dataset_spec.rb +1 -1
  122. data/spec/unit/rom/memory/{repository_spec.rb → gateway_spec.rb} +1 -1
  123. data/spec/unit/rom/memory/inheritance_spec.rb +32 -0
  124. data/spec/unit/rom/memory/relation_spec.rb +15 -3
  125. data/spec/unit/rom/memory/storage_spec.rb +1 -1
  126. data/spec/unit/rom/plugin_spec.rb +1 -1
  127. data/spec/unit/rom/plugins/command/schema_spec.rb +5 -5
  128. data/spec/unit/rom/plugins/relation/key_inference_spec.rb +1 -1
  129. data/spec/unit/rom/registry_spec.rb +86 -0
  130. data/spec/unit/rom/relation/attribute_reader_spec.rb +17 -0
  131. data/spec/unit/rom/relation/call_spec.rb +51 -0
  132. data/spec/unit/rom/relation/composite_spec.rb +1 -1
  133. data/spec/unit/rom/relation/graph_spec.rb +1 -1
  134. data/spec/unit/rom/relation/lazy/combine_spec.rb +1 -1
  135. data/spec/unit/rom/relation/lazy_spec.rb +1 -1
  136. data/spec/unit/rom/relation/loaded_spec.rb +1 -1
  137. data/spec/unit/rom/relation/schema_spec.rb +50 -6
  138. data/spec/unit/rom/relation/view_spec.rb +122 -0
  139. data/spec/unit/rom/relation_spec.rb +20 -5
  140. data/spec/unit/rom/schema/accessing_attributes_spec.rb +52 -0
  141. data/spec/unit/rom/schema/append_spec.rb +17 -0
  142. data/spec/unit/rom/schema/exclude_spec.rb +15 -0
  143. data/spec/unit/rom/schema/finalize_spec.rb +59 -0
  144. data/spec/unit/rom/schema/key_predicate_spec.rb +15 -0
  145. data/spec/unit/rom/schema/merge_spec.rb +17 -0
  146. data/spec/unit/rom/schema/prefix_spec.rb +16 -0
  147. data/spec/unit/rom/schema/project_spec.rb +15 -0
  148. data/spec/unit/rom/schema/rename_spec.rb +22 -0
  149. data/spec/unit/rom/schema/type_spec.rb +49 -0
  150. data/spec/unit/rom/schema/uniq_spec.rb +21 -0
  151. data/spec/unit/rom/schema/wrap_spec.rb +17 -0
  152. data/spec/unit/rom/schema_spec.rb +2 -2
  153. metadata +79 -17
  154. data/lib/rom/plugins/relation/view/dsl.rb +0 -32
  155. data/lib/rom/plugins/relation/view.rb +0 -95
  156. data/spec/unit/rom/plugins/relation/view_spec.rb +0 -51
data/lib/rom/command.rb CHANGED
@@ -1,5 +1,8 @@
1
- require 'rom/support/deprecations'
2
- require 'rom/support/options'
1
+ require 'dry/core/deprecations'
2
+ require 'dry/core/class_attributes'
3
+
4
+ require 'rom/types'
5
+ require 'rom/initializer'
3
6
  require 'rom/pipeline'
4
7
 
5
8
  require 'rom/commands/class_interface'
@@ -18,58 +21,213 @@ module ROM
18
21
  #
19
22
  # @abstract
20
23
  #
21
- # @private
24
+ # @api public
22
25
  class Command
23
- DEFAULT_VALIDATOR = proc {}
24
-
26
+ extend Initializer
25
27
  include Dry::Equalizer(:relation, :options)
26
28
  include Commands
27
29
  include Pipeline::Operator
28
30
 
29
- extend ClassMacros
31
+ extend Dry::Core::ClassAttributes
30
32
  extend ClassInterface
31
33
 
32
- include Options
34
+ # @!method self.adapter
35
+ # Get or set adapter identifier
36
+ #
37
+ # @overload adapter
38
+ # Get adapter identifier
39
+ #
40
+ # @example
41
+ # ROM::Memory::Commands::Create.adapter
42
+ # # => :memory
43
+ #
44
+ # @return [Symbol]
45
+ #
46
+ # @overload adapter(identifier)
47
+ # Set adapter identifier. This must always match actual adapter identifier
48
+ # that was used to register an adapter.
49
+ #
50
+ # @example
51
+ # module MyAdapter
52
+ # class CreateCommand < ROM::Commands::Memory::Create
53
+ # adapter :my_adapter
54
+ # end
55
+ # end
56
+ #
57
+ # @api public
58
+ defines :adapter
33
59
 
34
- defines :adapter, :relation, :result, :input, :validator, :register_as, :restrictable
60
+ # @!method self.relation
61
+ # Get or set relation identifier
62
+ #
63
+ # @overload relation
64
+ # Get relation identifier
65
+ #
66
+ # @example
67
+ # module CreateUser < ROM::Commands::Create[:memory]
68
+ # relation :users
69
+ # end
70
+ #
71
+ # CreateUser.relation
72
+ # # => :users
73
+ #
74
+ # @return [Symbol]
75
+ #
76
+ # @overload relation(identifier)
77
+ # Set relation identifier.
78
+ #
79
+ # @example
80
+ # module CreateUser < ROM::Commands::Create[:memory]
81
+ # relation :users
82
+ # end
83
+ #
84
+ # @api public
85
+ defines :relation
35
86
 
36
- option :type, allow: [:create, :update, :delete]
37
- option :source, reader: true
38
- option :result, reader: true, allow: [:one, :many]
39
- option :validator, reader: true
40
- option :input, reader: true
41
- option :curry_args, type: Array, reader: true, default: EMPTY_ARRAY
87
+ # @!method self.relation
88
+ # Get or set result type
89
+ #
90
+ # @overload result
91
+ # Get result type
92
+ #
93
+ # @example
94
+ # module CreateUser < ROM::Commands::Create[:memory]
95
+ # result :one
96
+ # end
97
+ #
98
+ # CreateUser.result
99
+ # # => :one
100
+ #
101
+ # @return [Symbol]
102
+ #
103
+ # @overload relation(identifier)
104
+ # Set result type
105
+ #
106
+ # @example
107
+ # module CreateUser < ROM::Commands::Create[:memory]
108
+ # result :one
109
+ # end
110
+ #
111
+ # @api public
112
+ defines :result
42
113
 
43
- input Hash
44
- validator DEFAULT_VALIDATOR
45
- result :many
114
+ # @!method self.relation
115
+ # Get or set input processing function. This is typically set during setup
116
+ # to relation's input_schema
117
+ #
118
+ # @overload input
119
+ # Get input processing function
120
+ #
121
+ # @example
122
+ # module CreateUser < ROM::Commands::Create[:memory]
123
+ # input -> tuple { .. }
124
+ # end
125
+ #
126
+ # CreateUser.input
127
+ # # Your custom function
128
+ #
129
+ # @return [Proc,#call]
130
+ #
131
+ # @overload input(identifier)
132
+ # Set input processing function
133
+ #
134
+ # @example
135
+ # module CreateUser < ROM::Commands::Create[:memory]
136
+ # input -> tuple { .. }
137
+ # end
138
+ #
139
+ # @api public
140
+ defines :input
46
141
 
47
- # @deprecated
142
+ # @!method self.register_as
143
+ # Get or set identifier that should be used to register a command in a container
144
+ #
145
+ # @overload register_as
146
+ # Get registration identifier
147
+ #
148
+ # @example
149
+ # module CreateUser < ROM::Commands::Create[:memory]
150
+ # register_as :create_user
151
+ # end
152
+ #
153
+ # CreateUser.register_as
154
+ # # => :create_user
155
+ #
156
+ # @return [Symbol]
157
+ #
158
+ # @overload register_as(identifier)
159
+ # Set registration identifier
160
+ #
161
+ # @example
162
+ # module CreateUser < ROM::Commands::Create[:memory]
163
+ # register_as :create_user
164
+ # end
48
165
  #
49
166
  # @api public
50
- def self.validator(vp = nil)
51
- if defined?(@validator) && vp.nil?
52
- @validator
53
- else
54
- unless vp.equal?(DEFAULT_VALIDATOR)
55
- Deprecations.announce(
56
- "#{name}.validator",
57
- 'Please handle validation before calling commands'
58
- )
59
- end
60
- super
61
- end
62
- end
167
+ defines :register_as
168
+
169
+ # @!method self.restrictable
170
+ # @overload restrictable
171
+ # Check if a command class is restrictable
172
+ #
173
+ # @example
174
+ # module UpdateUser < ROM::Commands::Update[:memory]
175
+ # restrictable true
176
+ # end
177
+ #
178
+ # CreateUser.restrictable
179
+ # # => true
180
+ #
181
+ # @return [FalseClass, TrueClass]
182
+ #
183
+ # @overload restrictable(value)
184
+ # Set if a command is restrictable
185
+ #
186
+ # @example
187
+ # module UpdateUser < ROM::Commands::Update[:memory]
188
+ # restrictable true
189
+ # end
190
+ #
191
+ # @api public
192
+ defines :restrictable
63
193
 
64
- # @attr_reader [Relation] relation The command's relation
65
- attr_reader :relation
194
+ # @!attribute [r] relation
195
+ # @return [Relation] Command's relation
196
+ param :relation
66
197
 
67
- # @api private
68
- def initialize(relation, options = EMPTY_HASH)
69
- super
70
- @relation = relation
71
- @source = options[:source] || relation
72
- end
198
+ CommandType = Types::Strict::Symbol.enum(:create, :update, :delete)
199
+ Result = Types::Strict::Symbol.enum(:one, :many)
200
+
201
+ # @!attribute [r] type
202
+ # @return [Symbol] The command type, one of :create, :update or :delete
203
+ option :type, type: CommandType, optional: true
204
+
205
+ # @!attribute [r] source
206
+ # @return [Relation] The source relation
207
+ option :source, reader: true, optional: true, default: -> c { c.relation }
208
+
209
+ # @!attribute [r] type
210
+ # @return [Symbol] Result type, either :one or :many
211
+ option :result, reader: true, type: Result
212
+
213
+ # @!attribute [r] input
214
+ # @return [Proc, #call] Tuple processing function, typically uses Relation#input_schema
215
+ option :input, reader: true
216
+
217
+ # @!attribute [r] curry_args
218
+ # @return [Array] Curried args
219
+ option :curry_args, reader: true, default: -> _ { EMPTY_ARRAY }
220
+
221
+ # @!attribute [r] before
222
+ # @return [Array<Hash>] An array with before hooks configuration
223
+ option :before, Types::Coercible::Array, reader: true, as: :before_hooks, default: proc { EMPTY_ARRAY }
224
+
225
+ # @!attribute [r] before
226
+ # @return [Array<Hash>] An array with after hooks configuration
227
+ option :after, Types::Coercible::Array, reader: true, as: :after_hooks, default: proc { EMPTY_ARRAY }
228
+
229
+ input Hash
230
+ result :many
73
231
 
74
232
  # Return name of this command's relation
75
233
  #
@@ -105,9 +263,35 @@ module ROM
105
263
 
106
264
  # Call the command and return one or many tuples
107
265
  #
266
+ # This method will apply before/after hooks automatically
267
+ #
108
268
  # @api public
109
269
  def call(*args, &block)
110
- tuples = execute(*(curry_args + args), &block)
270
+ tuples =
271
+ if hooks?
272
+ prepared =
273
+ if curried?
274
+ apply_hooks(before_hooks, *(curry_args + args))
275
+ else
276
+ apply_hooks(before_hooks, *args)
277
+ end
278
+
279
+ result = prepared ? execute(prepared, &block) : execute(&block)
280
+
281
+ if curried?
282
+ if args.size > 0
283
+ apply_hooks(after_hooks, result, *args)
284
+ elsif curry_args.size > 1
285
+ apply_hooks(after_hooks, result, curry_args[1])
286
+ else
287
+ apply_hooks(after_hooks, result)
288
+ end
289
+ else
290
+ apply_hooks(after_hooks, result, *args[1..args.size-1])
291
+ end
292
+ else
293
+ execute(*(curry_args + args), &block)
294
+ end
111
295
 
112
296
  if one?
113
297
  tuples.first
@@ -119,9 +303,10 @@ module ROM
119
303
 
120
304
  # Curry this command with provided args
121
305
  #
122
- # Curried command can be called without args
306
+ # Curried command can be called without args. If argument is a graph input processor,
307
+ # lazy command will be returned, which is used for handling nested input hashes.
123
308
  #
124
- # @return [Command]
309
+ # @return [Command, Lazy]
125
310
  #
126
311
  # @api public
127
312
  def curry(*args)
@@ -133,41 +318,140 @@ module ROM
133
318
  end
134
319
  alias_method :with, :curry
135
320
 
321
+ # Compose this command with other commands
322
+ #
323
+ # Composed commands can handle nested input
324
+ #
325
+ # @return [Command::Graph]
326
+ #
136
327
  # @api public
137
328
  def combine(*others)
138
329
  Graph.new(self, others)
139
330
  end
140
331
 
332
+ # Check if this command is curried
333
+ #
334
+ # @return [TrueClass, FalseClass]
335
+ #
336
+ # @api public
337
+ def curried?
338
+ curry_args.size > 0
339
+ end
340
+
341
+ # Return a new command with new options
342
+ #
343
+ # @param [Hash] new_opts A hash with new options
344
+ #
345
+ # @return [Command]
346
+ #
347
+ # @api public
348
+ def with_opts(new_opts)
349
+ self.class.new(relation, options.merge(new_opts))
350
+ end
351
+
352
+ # Return a new command with appended before hooks
353
+ #
354
+ # @param [Array<Hash>] hooks A list of before hooks configurations
355
+ #
356
+ # @return [Command]
357
+ #
358
+ # @api public
359
+ def before(*hooks)
360
+ self.class.new(relation, options.merge(before: before_hooks + hooks))
361
+ end
362
+
363
+ # Return a new command with appended after hooks
364
+ #
365
+ # @param [Array<Hash>] hooks A list of after hooks configurations
366
+ #
367
+ # @return [Command]
368
+ #
369
+ # @api public
370
+ def after(*hooks)
371
+ self.class.new(relation, options.merge(after: after_hooks + hooks))
372
+ end
373
+
374
+ # Return a new command with other source relation
375
+ #
376
+ # This can be used to restrict command with a specific relation
377
+ #
378
+ # @return [Command]
379
+ #
380
+ # @api public
381
+ def new(new_relation)
382
+ self.class.build(new_relation, options.merge(source: relation))
383
+ end
384
+
385
+ # Check if this command has any hooks
386
+ #
387
+ # @api private
388
+ def hooks?
389
+ before_hooks.size > 0 || after_hooks.size > 0
390
+ end
391
+
392
+ # Check if this command is lazy
393
+ #
394
+ # @return [false]
395
+ #
141
396
  # @api private
142
397
  def lazy?
143
398
  false
144
399
  end
145
400
 
401
+ # Check if this command is a graph
402
+ #
403
+ # @return [false]
404
+ #
146
405
  # @api private
147
406
  def graph?
148
407
  false
149
408
  end
150
409
 
410
+ # Check if this command returns a single tuple
411
+ #
412
+ # @return [TrueClass,FalseClass]
413
+ #
151
414
  # @api private
152
415
  def one?
153
416
  result.equal?(:one)
154
417
  end
155
418
 
419
+ # Check if this command returns many tuples
420
+ #
421
+ # @return [TrueClass,FalseClass]
422
+ #
156
423
  # @api private
157
424
  def many?
158
425
  result.equal?(:many)
159
426
  end
160
427
 
161
- # @api private
162
- def new(new_relation)
163
- self.class.build(new_relation, options.merge(source: relation))
164
- end
165
-
166
428
  private
167
429
 
430
+ # Hook called by Pipeline to get composite class for commands
431
+ #
432
+ # @return [Class]
433
+ #
168
434
  # @api private
169
435
  def composite_class
170
436
  Command::Composite
171
437
  end
438
+
439
+ # Apply provided hooks
440
+ #
441
+ # Used by #call
442
+ #
443
+ # @return [Array<Hash>]
444
+ #
445
+ # @api private
446
+ def apply_hooks(hooks, tuples, *args)
447
+ hooks.reduce(tuples) do |a, e|
448
+ if e.is_a?(Hash)
449
+ hook_meth, hook_args = e.to_a.flatten(1)
450
+ __send__(hook_meth, a, *args, **hook_args)
451
+ else
452
+ __send__(e, a, *args)
453
+ end
454
+ end
455
+ end
172
456
  end
173
457
  end
@@ -1,3 +1,4 @@
1
+ require 'rom/types'
1
2
  require 'rom/commands/result'
2
3
 
3
4
  module ROM
@@ -5,40 +6,28 @@ module ROM
5
6
  #
6
7
  # @api public
7
8
  class CommandRegistry
9
+ extend Initializer
8
10
  include Commands
9
- include Options
10
11
 
11
12
  CommandNotFoundError = Class.new(KeyError)
13
+ RegistryType = Types.Definition(Registry) | Types.Constructor(Registry) { |r| Registry.new(r, self.class.name) }
12
14
 
13
15
  # Name of the relation from which commands are under
14
16
  #
15
17
  # @return [String]
16
18
  #
17
19
  # @api private
18
- attr_reader :relation_name
20
+ param :relation_name
19
21
 
20
22
  # Internal command registry
21
23
  #
22
24
  # @return [Registry]
23
25
  #
24
26
  # @api private
25
- attr_reader :registry
27
+ param :registry, type: RegistryType
26
28
 
27
- option :mappers, reader: true
28
- option :mapper, reader: true
29
-
30
- # @api private
31
- def initialize(relation_name, elements, options = EMPTY_HASH)
32
- super
33
-
34
- @relation_name = relation_name
35
- @registry =
36
- if elements.is_a?(Registry)
37
- elements
38
- else
39
- Registry.new(elements, self.class.name)
40
- end
41
- end
29
+ option :mappers, reader: true, optional: true
30
+ option :mapper, reader: true, optional: true
42
31
 
43
32
  # Try to execute a command in a block
44
33
  #
@@ -1,4 +1,5 @@
1
- require 'rom/support/class_builder'
1
+ require 'dry/core/class_builder'
2
+ require 'dry/core/inflector'
2
3
 
3
4
  module ROM
4
5
  # Base command class with factory class-level interface and setup-related logic
@@ -6,6 +7,15 @@ module ROM
6
7
  # @private
7
8
  class Command
8
9
  module ClassInterface
10
+ # This hook sets up default class state
11
+ #
12
+ # @api private
13
+ def inherited(klass)
14
+ super
15
+ klass.instance_variable_set(:'@before', before ? before.dup : [])
16
+ klass.instance_variable_set(:'@after', before ? after.dup : [])
17
+ end
18
+
9
19
  # Return adapter specific sub-class based on the adapter identifier
10
20
  #
11
21
  # This is a syntax sugar to make things consistent
@@ -20,7 +30,7 @@ module ROM
20
30
  #
21
31
  # @api public
22
32
  def [](adapter)
23
- adapter_namespace(adapter).const_get(Inflector.demodulize(name))
33
+ adapter_namespace(adapter).const_get(Dry::Core::Inflector.demodulize(name))
24
34
  end
25
35
 
26
36
  # Return namespaces that contains command subclasses of a specific adapter
@@ -65,8 +75,8 @@ module ROM
65
75
  #
66
76
  # @api public
67
77
  def create_class(name, type, &block)
68
- klass = ClassBuilder
69
- .new(name: "#{Inflector.classify(type)}[:#{name}]", parent: type)
78
+ klass = Dry::Core::ClassBuilder
79
+ .new(name: "#{Dry::Core::Inflector.classify(type)}[:#{name}]", parent: type)
70
80
  .call
71
81
 
72
82
  if block
@@ -105,6 +115,110 @@ module ROM
105
115
  include(relation_methods_mod(relation.class))
106
116
  end
107
117
 
118
+ # Set before-execute hooks
119
+ #
120
+ # @overload before(hook)
121
+ # Set an before hook as a method name
122
+ #
123
+ # @example
124
+ # class CreateUser < ROM::Commands::Create[:sql]
125
+ # relation :users
126
+ # register_as :create
127
+ #
128
+ # before :my_hook
129
+ #
130
+ # def my_hook(tuple, *)
131
+ # puts "hook called#
132
+ # end
133
+ # end
134
+ #
135
+ # @overload before(hook_opts)
136
+ # Set an before hook as a method name with arguments
137
+ #
138
+ # @example
139
+ # class CreateUser < ROM::Commands::Create[:sql]
140
+ # relation :users
141
+ # register_as :create
142
+ #
143
+ # before my_hook: { arg1: 1, arg1: 2 }
144
+ #
145
+ # def my_hook(tuple, arg1:, arg2:)
146
+ # puts "hook called with args: #{arg1} and #{arg2}"
147
+ # end
148
+ # end
149
+ #
150
+ # @param [Hash<Symbol=>Hash>] hook Options with method name and pre-set args
151
+ #
152
+ # @return [Array<Hash, Symbol>] A list of all configured before hooks
153
+ #
154
+ # @api public
155
+ def before(*hooks)
156
+ if hooks.size > 0
157
+ set_hooks(:before, hooks)
158
+ else
159
+ @before
160
+ end
161
+ end
162
+
163
+ # Set after-execute hooks
164
+ #
165
+ # @overload after(hook)
166
+ # Set an after hook as a method name
167
+ #
168
+ # @example
169
+ # class CreateUser < ROM::Commands::Create[:sql]
170
+ # relation :users
171
+ # register_as :create
172
+ #
173
+ # after :my_hook
174
+ #
175
+ # def my_hook(tuple, *)
176
+ # puts "hook called#
177
+ # end
178
+ # end
179
+ #
180
+ # @overload after(hook_opts)
181
+ # Set an after hook as a method name with arguments
182
+ #
183
+ # @example
184
+ # class CreateUser < ROM::Commands::Create[:sql]
185
+ # relation :users
186
+ # register_as :create
187
+ #
188
+ # after my_hook: { arg1: 1, arg1: 2 }
189
+ #
190
+ # def my_hook(tuple, arg1:, arg2:)
191
+ # puts "hook called with args: #{arg1} and #{arg2}"
192
+ # end
193
+ # end
194
+ #
195
+ # @param [Hash<Symbol=>Hash>] hook Options with method name and pre-set args
196
+ #
197
+ # @return [Array<Hash, Symbol>] A list of all configured after hooks
198
+ #
199
+ # @api public
200
+ def after(*hooks)
201
+ if hooks.size > 0
202
+ set_hooks(:after, hooks)
203
+ else
204
+ @after
205
+ end
206
+ end
207
+
208
+ # Set new or more hooks
209
+ #
210
+ # @api private
211
+ def set_hooks(type, hooks)
212
+ ivar = :"@#{type}"
213
+ value = instance_variable_get(ivar)
214
+
215
+ if value.empty?
216
+ instance_variable_set(ivar, hooks)
217
+ else
218
+ value.concat(hooks)
219
+ end
220
+ end
221
+
108
222
  # Return default name of the command class based on its name
109
223
  #
110
224
  # During setup phase this is used by defalut as `register_as` option
@@ -113,7 +227,7 @@ module ROM
113
227
  #
114
228
  # @api private
115
229
  def default_name
116
- Inflector.underscore(Inflector.demodulize(name)).to_sym
230
+ Dry::Core::Inflector.underscore(Dry::Core::Inflector.demodulize(name)).to_sym
117
231
  end
118
232
 
119
233
  # Return default options based on class macros
@@ -122,7 +236,7 @@ module ROM
122
236
  #
123
237
  # @api private
124
238
  def options
125
- { input: input, validator: validator, result: result }
239
+ { input: input, result: result, before: before, after: after }
126
240
  end
127
241
 
128
242
  # @api private
@@ -1,5 +1,4 @@
1
1
  require 'rom/pipeline'
2
- require 'rom/support/constants'
3
2
 
4
3
  module ROM
5
4
  module Commands