rom 3.0.0.rc1 → 3.0.0.rc2

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
  SHA1:
3
- metadata.gz: 3b8faaf70d24fe2d5212f777e8023dfc4b6a2e34
4
- data.tar.gz: 4fe312831401a4a99c9837ef1948af386b558762
3
+ metadata.gz: c09afbd9407f30670628cfcfaebf9d903ca6619c
4
+ data.tar.gz: a94d330cd2a0ecb12c500042b9659ec5ee48c568
5
5
  SHA512:
6
- metadata.gz: e46223303dd3c701780526df9184c5c2c03cec5aee5a5cc3d3ba64ebd6e962619c2b04af8c309db4410d19bbf999e41c2ef813a667bccd5d5c8c468d69ff9fef
7
- data.tar.gz: 09b1af7a72438045fc804872a8cfa63ac8d9c2c3add1123a71f6e6117d7f61c65e005c241e35757a25b3d2ec19b877f97516ba6bae74b973083e5f013d2bb21f
6
+ metadata.gz: cf5b188aca3186e6f402f4e246edc6555821fb0d0a24e771715267806a923fd3bd19ec4a0270a572b4fd7c48ee8d9ff380a2aca407202d6d84c1846f884b81e7
7
+ data.tar.gz: 552d6f7c1436ae873f1926feafd38f218090ec3ba0a109b45fa931eb3578452f4611745426adb2fbeca537c9ed36ba66c4b99b5ac00faf815e0e17a921811039
data/lib/rom/command.rb CHANGED
@@ -21,7 +21,7 @@ module ROM
21
21
  #
22
22
  # @abstract
23
23
  #
24
- # @private
24
+ # @api public
25
25
  class Command
26
26
  extend Initializer
27
27
  include Dry::Equalizer(:relation, :options)
@@ -31,20 +31,199 @@ module ROM
31
31
  extend Dry::Core::ClassAttributes
32
32
  extend ClassInterface
33
33
 
34
- defines :adapter, :relation, :result, :input, :register_as, :restrictable
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
35
59
 
36
- # @attr_reader [Relation] relation The command's relation
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
86
+
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
113
+
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
141
+
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
165
+ #
166
+ # @api public
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
193
+
194
+ # @!attribute [r] relation
195
+ # @return [Relation] Command's relation
37
196
  param :relation
38
197
 
39
198
  CommandType = Types::Strict::Symbol.enum(:create, :update, :delete)
40
199
  Result = Types::Strict::Symbol.enum(:one, :many)
41
200
 
201
+ # @!attribute [r] type
202
+ # @return [Symbol] The command type, one of :create, :update or :delete
42
203
  option :type, type: CommandType, optional: true
204
+
205
+ # @!attribute [r] source
206
+ # @return [Relation] The source relation
43
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
44
211
  option :result, reader: true, type: Result
212
+
213
+ # @!attribute [r] input
214
+ # @return [Proc, #call] Tuple processing function, typically uses Relation#input_schema
45
215
  option :input, reader: true
216
+
217
+ # @!attribute [r] curry_args
218
+ # @return [Array] Curried args
46
219
  option :curry_args, reader: true, default: -> _ { EMPTY_ARRAY }
220
+
221
+ # @!attribute [r] before
222
+ # @return [Array<Hash>] An array with before hooks configuration
47
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
48
227
  option :after, Types::Coercible::Array, reader: true, as: :after_hooks, default: proc { EMPTY_ARRAY }
49
228
 
50
229
  input Hash
@@ -84,6 +263,8 @@ module ROM
84
263
 
85
264
  # Call the command and return one or many tuples
86
265
  #
266
+ # This method will apply before/after hooks automatically
267
+ #
87
268
  # @api public
88
269
  def call(*args, &block)
89
270
  tuples =
@@ -98,7 +279,13 @@ module ROM
98
279
  result = prepared ? execute(prepared, &block) : execute(&block)
99
280
 
100
281
  if curried?
101
- apply_hooks(after_hooks, result, *args)
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
102
289
  else
103
290
  apply_hooks(after_hooks, result, *args[1..args.size-1])
104
291
  end
@@ -116,9 +303,10 @@ module ROM
116
303
 
117
304
  # Curry this command with provided args
118
305
  #
119
- # 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.
120
308
  #
121
- # @return [Command]
309
+ # @return [Command, Lazy]
122
310
  #
123
311
  # @api public
124
312
  def curry(*args)
@@ -130,68 +318,130 @@ module ROM
130
318
  end
131
319
  alias_method :with, :curry
132
320
 
321
+ # Compose this command with other commands
322
+ #
323
+ # Composed commands can handle nested input
324
+ #
325
+ # @return [Command::Graph]
326
+ #
327
+ # @api public
328
+ def combine(*others)
329
+ Graph.new(self, others)
330
+ end
331
+
332
+ # Check if this command is curried
333
+ #
334
+ # @return [TrueClass, FalseClass]
335
+ #
133
336
  # @api public
134
337
  def curried?
135
338
  curry_args.size > 0
136
339
  end
137
340
 
138
- # @api private
139
- def hooks?
140
- before_hooks.size > 0 || after_hooks.size > 0
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))
141
350
  end
142
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
+ #
143
358
  # @api public
144
- def combine(*others)
145
- Graph.new(self, others)
359
+ def before(*hooks)
360
+ self.class.new(relation, options.merge(before: before_hooks + hooks))
146
361
  end
147
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
+ #
148
396
  # @api private
149
397
  def lazy?
150
398
  false
151
399
  end
152
400
 
401
+ # Check if this command is a graph
402
+ #
403
+ # @return [false]
404
+ #
153
405
  # @api private
154
406
  def graph?
155
407
  false
156
408
  end
157
409
 
410
+ # Check if this command returns a single tuple
411
+ #
412
+ # @return [TrueClass,FalseClass]
413
+ #
158
414
  # @api private
159
415
  def one?
160
416
  result.equal?(:one)
161
417
  end
162
418
 
419
+ # Check if this command returns many tuples
420
+ #
421
+ # @return [TrueClass,FalseClass]
422
+ #
163
423
  # @api private
164
424
  def many?
165
425
  result.equal?(:many)
166
426
  end
167
427
 
168
- # @api private
169
- def new(new_relation)
170
- self.class.build(new_relation, options.merge(source: relation))
171
- end
172
-
173
- # @api public
174
- def with_opts(new_opts)
175
- self.class.new(relation, options.merge(new_opts))
176
- end
177
-
178
- # @api public
179
- def before(*hooks)
180
- self.class.new(relation, options.merge(before: before_hooks + hooks))
181
- end
182
-
183
- # @api public
184
- def after(*hooks)
185
- self.class.new(relation, options.merge(after: after_hooks + hooks))
186
- end
187
-
188
428
  private
189
429
 
430
+ # Hook called by Pipeline to get composite class for commands
431
+ #
432
+ # @return [Class]
433
+ #
190
434
  # @api private
191
435
  def composite_class
192
436
  Command::Composite
193
437
  end
194
438
 
439
+ # Apply provided hooks
440
+ #
441
+ # Used by #call
442
+ #
443
+ # @return [Array<Hash>]
444
+ #
195
445
  # @api private
196
446
  def apply_hooks(hooks, tuples, *args)
197
447
  hooks.reduce(tuples) do |a, e|
data/lib/rom/container.rb CHANGED
@@ -11,14 +11,14 @@ module ROM
11
11
  #
12
12
  # There are 3 types of container setup:
13
13
  #
14
- # * in-line setup - a simple block-based configuration which allows configuring
14
+ # * Setup DSL - a simple block-based configuration which allows configuring
15
15
  # all components and gives you back a container instance. This type is suitable
16
16
  # for small scripts, or in some cases rake tasks
17
- # * multi-step setup - this type requires creating a configuration object,
17
+ # * Explicit setup - this type requires creating a configuration object,
18
18
  # registering component classes (ie relation classes) and passing the config
19
19
  # to container builder function. This type is suitable when your environment
20
20
  # is not typical and you need full control over component registration
21
- # * multi-step setup with auto-registration - same as multi-step but allows
21
+ # * Explicit setup with auto-registration - same as explicit setup but allows
22
22
  # you to configure auto-registration mechanism which will register component
23
23
  # classes for you, based on dir/file naming conventions. This is the most
24
24
  # common type of setup that's used by framework integrations
@@ -99,24 +99,20 @@ module ROM
99
99
  class Container
100
100
  include Dry::Equalizer(:gateways, :relations, :mappers, :commands)
101
101
 
102
- # @return [Hash] configured gateways
103
- #
104
- # @api public
102
+ # @!attribute [r] gateways
103
+ # @return [Hash] A hash with configured gateways
105
104
  attr_reader :gateways
106
105
 
107
- # @return [RelationRegistry] relation registry
108
- #
109
- # @api public
106
+ # @!attribute [r] relations
107
+ # @return [RelationRegistry] The relation registry
110
108
  attr_reader :relations
111
109
 
112
- # @return [Registry] command registry
113
- #
114
- # @api public
110
+ # @!attribute [r] gateways
111
+ # @return [CommandRegistry] The command registry
115
112
  attr_reader :commands
116
113
 
117
- # @return [Registry] mapper registry
118
- #
119
- # @api public
114
+ # @!attribute [r] mappers
115
+ # @return [Hash] A hash with configured custom mappers
120
116
  attr_reader :mappers
121
117
 
122
118
  # @api private
data/lib/rom/gateway.rb CHANGED
@@ -5,49 +5,61 @@ require 'rom/transaction'
5
5
  module ROM
6
6
  # Abstract gateway class
7
7
  #
8
+ # Every adapter needs to inherit from this class and implement
9
+ # required interface
10
+ #
11
+ # @abstract
12
+ #
8
13
  # @api public
9
14
  class Gateway
10
15
  extend Dry::Core::ClassAttributes
11
16
 
12
17
  defines :adapter
13
18
 
14
- # Return connection object
15
- #
16
- # @return [Object] type varies depending on the gateway
17
- #
18
- # @api public
19
+ # @!attribute [r] connection
20
+ # @return [Object] The gateway's connection object (type varies across adapters)
19
21
  attr_reader :connection
20
22
 
21
- # Setup a gateway
23
+ # Set up a gateway
22
24
  #
23
25
  # @overload setup(type, *args)
24
26
  # Sets up a single-gateway given a gateway type.
25
27
  # For custom gateways, create an instance and pass it directly.
26
28
  #
27
- # @param [Symbol] type
28
- # @param [Array] *args
29
+ # @example
30
+ # module SuperDB
31
+ # class Gateway < ROM::Gateway
32
+ # def initialize(options)
33
+ # end
34
+ # end
35
+ # end
29
36
  #
30
- # @overload setup(gateway)
31
- # @param [Gateway] gateway
37
+ # ROM.register_adapter(:super_db, SuperDB)
32
38
  #
33
- # @return [Gateway] a specific gateway subclass
39
+ # Gateway.setup(:super_db, some: 'options')
40
+ # # SuperDB::Gateway.new(some: 'options') is called
41
+ #
42
+ # @param [Symbol] type Registered gateway identifier
43
+ # @param [Array] args Additional gateway options
34
44
  #
35
- # @example
36
- # module SuperDB
37
- # class Gateway < ROM::Gateway
38
- # def initialize(options)
45
+ # @overload setup(gateway)
46
+ # Set up a gateway instance
47
+ #
48
+ # @example
49
+ # module SuperDB
50
+ # class Gateway < ROM::Gateway
51
+ # def initialize(options)
52
+ # end
39
53
  # end
40
54
  # end
41
- # end
42
55
  #
43
- # ROM.register_adapter(:super_db, SuperDB)
56
+ # ROM.register_adapter(:super_db, SuperDB)
57
+ #
58
+ # Gateway.setup(SuperDB::Gateway.new(some: 'options'))
44
59
  #
45
- # Gateway.setup(:super_db, some: 'options')
46
- # # SuperDB::Gateway.new(some: 'options') is called
60
+ # @param [Gateway] gateway
47
61
  #
48
- # # or alternatively
49
- # super_db = Gateway.setup(SuperDB::Gateway.new(some: 'options'))
50
- # Gateway.setup(super_db)
62
+ # @return [Gateway] a specific gateway subclass
51
63
  #
52
64
  # @api public
53
65
  def self.setup(gateway_or_scheme, *args)
@@ -76,7 +88,7 @@ module ROM
76
88
 
77
89
  # Get gateway subclass for a specific adapter
78
90
  #
79
- # @param [Symbol] type adapter identifier
91
+ # @param [Symbol] type Adapter identifier
80
92
  #
81
93
  # @return [Class]
82
94
  #
@@ -109,6 +121,10 @@ module ROM
109
121
 
110
122
  # A generic interface for setting up a logger
111
123
  #
124
+ # This is not a required interface, it's a no-op by default
125
+ #
126
+ # @abstract
127
+ #
112
128
  # @api public
113
129
  def use_logger(*)
114
130
  # noop
@@ -116,6 +132,11 @@ module ROM
116
132
 
117
133
  # A generic interface for returning default logger
118
134
  #
135
+ # Adapters should implement this method as handling loggers is different
136
+ # across adapters. This is a no-op by default and returns nil.
137
+ #
138
+ # @return [NilClass]
139
+ #
119
140
  # @api public
120
141
  def logger
121
142
  # noop
@@ -123,8 +144,10 @@ module ROM
123
144
 
124
145
  # Extension hook for adding gateway-specific behavior to a command class
125
146
  #
126
- # @param [Class] klass command class
127
- # @param [Object] _dataset dataset that will be used with this command class
147
+ # This simply returns back the class by default
148
+ #
149
+ # @param [Class] klass The command class
150
+ # @param [Object] _dataset The dataset that will be used with this command class
128
151
  #
129
152
  # @return [Class]
130
153
  #
@@ -137,7 +160,7 @@ module ROM
137
160
  #
138
161
  # Every gateway that supports schema inference should implement this method
139
162
  #
140
- # @return [Array] array with datasets and their names
163
+ # @return [Array] An array with dataset names
141
164
  #
142
165
  # @api private
143
166
  def schema
@@ -163,6 +186,8 @@ module ROM
163
186
  transaction_runner(opts).run(opts, &block)
164
187
  end
165
188
 
189
+ private
190
+
166
191
  # @api private
167
192
  def transaction_runner(_)
168
193
  Transaction::NoOp
data/lib/rom/relation.rb CHANGED
@@ -18,18 +18,23 @@ module ROM
18
18
  # Base relation class
19
19
  #
20
20
  # Relation is a proxy for the dataset object provided by the gateway. It
21
- # forwards every method to the dataset, which is why the "native" interface of
21
+ # can forward methods to the dataset, which is why the "native" interface of
22
22
  # the underlying gateway is available in the relation. This interface,
23
23
  # however, is considered private and should not be used outside of the
24
24
  # relation instance.
25
25
  #
26
- # ROM builds sub-classes of this class for every relation defined in the
27
- # environment for easy inspection and extensibility - every gateway can
28
- # provide extensions for those sub-classes but there is always a vanilla
29
- # relation instance stored in the schema registry.
26
+ # Individual adapters sets up their relation classes and provide different APIs
27
+ # depending on their persistence backend.
28
+ #
29
+ # Vanilla Relation class doesn't have APIs that are specific to ROM container setup.
30
+ # When adapter Relation class inherits from this class, these APIs are added automatically,
31
+ # so that they can be registered within a container.
32
+ #
33
+ # @see ROM::Relation::ClassInterface
30
34
  #
31
35
  # @api public
32
36
  class Relation
37
+ # Default no-op output schema which is called in `Relation#each`
33
38
  NOOP_OUTPUT_SCHEMA = -> tuple { tuple }.freeze
34
39
 
35
40
  extend Initializer
@@ -71,6 +76,15 @@ module ROM
71
76
 
72
77
  # Return schema attribute
73
78
  #
79
+ # @example accessing canonical attribute
80
+ # users[:id]
81
+ # # => #<ROM::SQL::Attribute[Integer] primary_key=true name=:id source=ROM::Relation::Name(users)>
82
+ #
83
+ # @example accessing joined attribute
84
+ # tasks_with_users = tasks.join(users).select_append(tasks[:title])
85
+ # tasks_with_users[:title, :tasks]
86
+ # # => #<ROM::SQL::Attribute[String] primary_key=false name=:title source=ROM::Relation::Name(tasks)>
87
+ #
74
88
  # @return [Schema::Attribute]
75
89
  #
76
90
  # @api public
@@ -80,7 +94,10 @@ module ROM
80
94
 
81
95
  # Yields relation tuples
82
96
  #
97
+ # Every tuple is processed through Relation#output_schema, it's a no-op by default
98
+ #
83
99
  # @yield [Hash]
100
+ #
84
101
  # @return [Enumerator] if block is not provided
85
102
  #
86
103
  # @api public
@@ -91,7 +108,7 @@ module ROM
91
108
 
92
109
  # Composes with other relations
93
110
  #
94
- # @param *others [Array<Relation>] The other relation(s) to compose with
111
+ # @param [Array<Relation>] others The other relation(s) to compose with
95
112
  #
96
113
  # @return [Relation::Graph]
97
114
  #
@@ -147,6 +164,18 @@ module ROM
147
164
 
148
165
  # Return a new relation with provided dataset and additional options
149
166
  #
167
+ # Use this method whenever you need to use dataset API to get a new dataset
168
+ # and you want to return a relation back. Typically relation API should be
169
+ # enough though. If you find yourself using this method, it might be worth
170
+ # to consider reporting an issue that some dataset functionality is not available
171
+ # through relation API.
172
+ #
173
+ # @example with a new dataset
174
+ # users.new(users.dataset.some_method)
175
+ #
176
+ # @example with a new dataset and options
177
+ # users.new(users.dataset.some_method, other: 'options')
178
+ #
150
179
  # @param [Object] dataset
151
180
  # @param [Hash] new_opts Additional options
152
181
  #
@@ -157,6 +186,9 @@ module ROM
157
186
 
158
187
  # Returns a new instance with the same dataset but new options
159
188
  #
189
+ # @example
190
+ # users.with(output_schema: -> tuple { .. })
191
+ #
160
192
  # @param new_options [Hash]
161
193
  #
162
194
  # @return [Relation]
@@ -168,6 +200,8 @@ module ROM
168
200
 
169
201
  # Return all registered relation schemas
170
202
  #
203
+ # This holds all schemas defined via `view` DSL
204
+ #
171
205
  # @return [Hash<Symbol=>Schema>]
172
206
  #
173
207
  # @api public
@@ -186,6 +220,10 @@ module ROM
186
220
 
187
221
  private
188
222
 
223
+ # Hook used by `Pipeline` to get the class that should be used for composition
224
+ #
225
+ # @return [Class]
226
+ #
189
227
  # @api private
190
228
  def composite_class
191
229
  Relation::Composite
data/lib/rom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ROM
2
- VERSION = '3.0.0.rc1'.freeze
2
+ VERSION = '3.0.0.rc2'.freeze
3
3
  end
@@ -94,7 +94,7 @@ RSpec.describe ROM::Commands::Create[:memory], 'before/after hooks' do
94
94
  end
95
95
  end
96
96
 
97
- context 'with curried args' do
97
+ context 'with one curried arg' do
98
98
  subject(:command) do
99
99
  Class.new(ROM::Commands::Create[:memory]) do
100
100
  result :many
@@ -142,6 +142,54 @@ RSpec.describe ROM::Commands::Create[:memory], 'before/after hooks' do
142
142
  end
143
143
  end
144
144
 
145
+ context 'with 2 curried args' do
146
+ subject(:command) do
147
+ Class.new(ROM::Commands::Create[:memory]) do
148
+ result :many
149
+ before :prepare
150
+ after :finalize
151
+
152
+ def execute(tuples)
153
+ input = tuples.map.with_index { |tuple, idx| tuple.merge(id: idx + 1) }
154
+ relation.insert(input)
155
+ input
156
+ end
157
+
158
+ def prepare(tuples, name)
159
+ tuples.map.with_index { |tuple, idx| tuple.merge(name: "#{name} #{idx + 1}") }
160
+ end
161
+
162
+ def finalize(tuples, *)
163
+ tuples.map { |tuple| tuple.merge(finalized: true) }
164
+ end
165
+ end.build(relation)
166
+ end
167
+
168
+ let(:tuples) do
169
+ [{ email: 'user-1@test.com' }, { email: 'user-2@test.com' }]
170
+ end
171
+
172
+ let(:relation) do
173
+ spy(:relation)
174
+ end
175
+
176
+ it 'applies before/after hooks' do
177
+ insert_tuples = [
178
+ { id: 1, email: 'user-1@test.com', name: 'User 1' },
179
+ { id: 2, email: 'user-2@test.com', name: 'User 2' }
180
+ ]
181
+
182
+ result = [
183
+ { id: 1, email: 'user-1@test.com', name: 'User 1', finalized: true },
184
+ { id: 2, email: 'user-2@test.com', name: 'User 2', finalized: true }
185
+ ]
186
+
187
+ expect(command.with(tuples, 'User').call).to eql(result)
188
+
189
+ expect(relation).to have_received(:insert).with(insert_tuples)
190
+ end
191
+ end
192
+
145
193
  context 'with pre-set opts' do
146
194
  subject(:command) do
147
195
  Class.new(ROM::Commands::Create[:memory]) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rom
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.rc1
4
+ version: 3.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-24 00:00:00.000000000 Z
11
+ date: 2017-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby