rom 3.0.0.rc1 → 3.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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