rom 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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