rom-core 4.0.0.beta2 → 4.0.0.beta3
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 +4 -4
- data/CHANGELOG.md +2 -1
- data/lib/rom/attribute.rb +427 -0
- data/lib/rom/auto_curry.rb +1 -1
- data/lib/rom/command.rb +9 -0
- data/lib/rom/command_proxy.rb +10 -2
- data/lib/rom/constants.rb +8 -0
- data/lib/rom/core.rb +0 -2
- data/lib/rom/gateway.rb +0 -11
- data/lib/rom/plugins/command/schema.rb +2 -4
- data/lib/rom/relation/class_interface.rb +13 -2
- data/lib/rom/relation/combined.rb +10 -4
- data/lib/rom/relation/commands.rb +28 -8
- data/lib/rom/relation/composite.rb +1 -3
- data/lib/rom/relation/curried.rb +10 -14
- data/lib/rom/relation/graph.rb +8 -0
- data/lib/rom/relation/materializable.rb +0 -7
- data/lib/rom/relation/name.rb +2 -2
- data/lib/rom/relation/wrap.rb +8 -27
- data/lib/rom/relation.rb +78 -15
- data/lib/rom/schema/dsl.rb +4 -2
- data/lib/rom/schema/inferrer.rb +4 -1
- data/lib/rom/schema.rb +13 -12
- data/lib/rom/setup/finalize/finalize_mappers.rb +5 -4
- data/lib/rom/setup/finalize/finalize_relations.rb +7 -10
- data/lib/rom/support/notifications.rb +92 -0
- data/lib/rom/version.rb +1 -1
- metadata +10 -5
- data/lib/rom/plugins/configuration/configuration_dsl.rb +0 -21
- data/lib/rom/schema/attribute.rb +0 -419
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8a152a49542a83c5195f02e61f08823c7207f23
|
4
|
+
data.tar.gz: d4513f31be747f8f01e7c581084782afc3662a6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 652ebe10f678636a8dfe0dd78e1f31fbba58499a9e65eaf1589e46c3abc87e730ee773fdf60435146725e73ca7596e519bc3888dd5498a3d81a90783740a7b40
|
7
|
+
data.tar.gz: 95f2e25a3eac0195a6d9d35697b619b52364255b7fefcbfd1c68a647f9aefecc21a489f9a945346f6834d2936d81235b447451c9131d2b86be40a5c834144e00
|
data/CHANGELOG.md
CHANGED
@@ -24,7 +24,7 @@ Previous `rom` gem was renamed to `rom-core`
|
|
24
24
|
* Works with MRI >= 2.2
|
25
25
|
* [BREAKING] Inferring relations from database schema **has been removed**. You need to define relations explicitly now (solnic)
|
26
26
|
* [BREAKING] Relations have `auto_map` **turned on by default**. This means that wraps and graphs return nested data structures automatically (solnic)
|
27
|
-
* [BREAKING] `Relation#combine` behavior from previous versions is now provided by `Relation#
|
27
|
+
* [BREAKING] `Relation#combine` behavior from previous versions is now provided by `Relation#combine_with` (solnic)
|
28
28
|
* [BREAKING] `Relation#as` now returns a new relation with aliased name, use `Relation#map_with(*list-of-mapper-ids)` or `Relation#map_to(model)` if you just want to map to custom models (solnic)
|
29
29
|
* [BREAKING] `Relation.register_as(:bar)` is removed in favor of `schema(:foo, as: :bar)` (solnic)
|
30
30
|
* [BREAKING] `Relation.dataset(:foo)` is removed in favor of `schema(:foo)`. Passing a block still works like before (solnic)
|
@@ -38,6 +38,7 @@ Previous `rom` gem was renamed to `rom-core`
|
|
38
38
|
|
39
39
|
* [BREAKING] `Relation::Curried#name` was renamed to `Relation::Curried#view` (solnic)
|
40
40
|
* [BREAKING] `Association::Name` was removed in favor of using `Relation::Name` (solnic)
|
41
|
+
* [BREAKING] `ROM::Schema::Attribute` was renamed to `ROM::Attribute` (solnic)
|
41
42
|
* Relations no longer use `method_missing` for accessing other relations from the registry (solnic)
|
42
43
|
|
43
44
|
## Fixed
|
@@ -0,0 +1,427 @@
|
|
1
|
+
require 'dry/equalizer'
|
2
|
+
|
3
|
+
require 'rom/initializer'
|
4
|
+
require 'rom/support/memoizable'
|
5
|
+
|
6
|
+
module ROM
|
7
|
+
# Schema attributes provide meta information about types and an API
|
8
|
+
# for additional operations. This class can be extended by adapters to provide
|
9
|
+
# database-specific features. In example rom-sql provides SQL::Attribute
|
10
|
+
# with more features like creating SQL expressions for queries.
|
11
|
+
#
|
12
|
+
# Schema attributes are accessible through canonical relation schemas and
|
13
|
+
# instance-level schemas.
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
class Attribute
|
17
|
+
include Dry::Equalizer(:type, :options)
|
18
|
+
include Memoizable
|
19
|
+
|
20
|
+
extend Initializer
|
21
|
+
|
22
|
+
# @!attribute [r] type
|
23
|
+
# @return [Dry::Types::Definition, Dry::Types::Sum, Dry::Types::Constrained]
|
24
|
+
param :type
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
def [](input)
|
28
|
+
type[input]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return true if this attribute type is a primary key
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# class Users < ROM::Relation[:memory]
|
35
|
+
# schema do
|
36
|
+
# attribute :id, Types::Int
|
37
|
+
# attribute :name, Types::String
|
38
|
+
#
|
39
|
+
# primary_key :id
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# Users.schema[:id].primary_key?
|
44
|
+
# # => true
|
45
|
+
#
|
46
|
+
# Users.schema[:name].primary_key?
|
47
|
+
# # => false
|
48
|
+
#
|
49
|
+
# @return [TrueClass,FalseClass]
|
50
|
+
#
|
51
|
+
# @api public
|
52
|
+
def primary_key?
|
53
|
+
meta[:primary_key].equal?(true)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return true if this attribute type is a foreign key
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# class Tasks < ROM::Relation[:memory]
|
60
|
+
# schema do
|
61
|
+
# attribute :id, Types::Int
|
62
|
+
# attribute :user_id, Types.ForeignKey(:users)
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# Users.schema[:user_id].foreign_key?
|
67
|
+
# # => true
|
68
|
+
#
|
69
|
+
# Users.schema[:id].foreign_key?
|
70
|
+
# # => false
|
71
|
+
#
|
72
|
+
# @return [TrueClass,FalseClass]
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def foreign_key?
|
76
|
+
meta[:foreign_key].equal?(true)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return true if this attribute type is a foreign key
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# class Tasks < ROM::Relation[:memory]
|
83
|
+
# schema do
|
84
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
85
|
+
# attribute :name, Types::String
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# Users.schema[:user_id].aliased?
|
90
|
+
# # => true
|
91
|
+
#
|
92
|
+
# Users.schema[:name].aliased?
|
93
|
+
# # => false
|
94
|
+
#
|
95
|
+
# @return [TrueClass,FalseClass]
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
def aliased?
|
99
|
+
!meta[:alias].nil?
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return source relation of this attribute type
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
# class Tasks < ROM::Relation[:memory]
|
106
|
+
# schema do
|
107
|
+
# attribute :id, Types::Int
|
108
|
+
# attribute :user_id, Types.ForeignKey(:users)
|
109
|
+
# end
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# Users.schema[:id].source
|
113
|
+
# # => :tasks
|
114
|
+
#
|
115
|
+
# Users.schema[:user_id].source
|
116
|
+
# # => :tasks
|
117
|
+
#
|
118
|
+
# @return [Symbol, Relation::Name]
|
119
|
+
#
|
120
|
+
# @api public
|
121
|
+
def source
|
122
|
+
meta[:source]
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return target relation of this attribute type
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# class Tasks < ROM::Relation[:memory]
|
129
|
+
# schema do
|
130
|
+
# attribute :id, Types::Int
|
131
|
+
# attribute :user_id, Types.ForeignKey(:users)
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# Users.schema[:id].target
|
136
|
+
# # => nil
|
137
|
+
#
|
138
|
+
# Users.schema[:user_id].target
|
139
|
+
# # => :users
|
140
|
+
#
|
141
|
+
# @return [NilClass, Symbol, Relation::Name]
|
142
|
+
#
|
143
|
+
# @api public
|
144
|
+
def target
|
145
|
+
meta[:target]
|
146
|
+
end
|
147
|
+
|
148
|
+
# Return the canonical name of this attribute name
|
149
|
+
#
|
150
|
+
# This *always* returns the name that is used in the datastore, even when
|
151
|
+
# an attribute is aliased
|
152
|
+
#
|
153
|
+
# @example
|
154
|
+
# class Tasks < ROM::Relation[:memory]
|
155
|
+
# schema do
|
156
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
157
|
+
# attribute :name, Types::String
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# Users.schema[:id].name
|
162
|
+
# # => :id
|
163
|
+
#
|
164
|
+
# Users.schema[:user_id].name
|
165
|
+
# # => :user_id
|
166
|
+
#
|
167
|
+
# @return [Symbol]
|
168
|
+
#
|
169
|
+
# @api public
|
170
|
+
def name
|
171
|
+
meta[:name]
|
172
|
+
end
|
173
|
+
|
174
|
+
# Return tuple key
|
175
|
+
#
|
176
|
+
# When schemas are projected with aliased attributes, we need a simple access to tuple keys
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# class Tasks < ROM::Relation[:memory]
|
180
|
+
# schema do
|
181
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
182
|
+
# attribute :name, Types::String
|
183
|
+
# end
|
184
|
+
# end
|
185
|
+
#
|
186
|
+
# Users.schema[:id].key
|
187
|
+
# # :id
|
188
|
+
#
|
189
|
+
# Users.schema.project(Users.schema[:id].aliased(:user_id)).key
|
190
|
+
# # :user_id
|
191
|
+
#
|
192
|
+
# @return [Symbol]
|
193
|
+
#
|
194
|
+
# @api public
|
195
|
+
def key
|
196
|
+
meta[:alias] || name
|
197
|
+
end
|
198
|
+
|
199
|
+
# Return attribute's alias
|
200
|
+
#
|
201
|
+
# @example
|
202
|
+
# class Tasks < ROM::Relation[:memory]
|
203
|
+
# schema do
|
204
|
+
# attribute :user_id, Types::Int.meta(alias: :id)
|
205
|
+
# attribute :name, Types::String
|
206
|
+
# end
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# Users.schema[:user_id].alias
|
210
|
+
# # => :user_id
|
211
|
+
#
|
212
|
+
# Users.schema[:name].alias
|
213
|
+
# # => nil
|
214
|
+
#
|
215
|
+
# @return [NilClass,Symbol]
|
216
|
+
#
|
217
|
+
# @api public
|
218
|
+
def alias
|
219
|
+
meta[:alias]
|
220
|
+
end
|
221
|
+
|
222
|
+
# Return new attribute type with provided alias
|
223
|
+
#
|
224
|
+
# @example
|
225
|
+
# class Tasks < ROM::Relation[:memory]
|
226
|
+
# schema do
|
227
|
+
# attribute :user_id, Types::Int
|
228
|
+
# attribute :name, Types::String
|
229
|
+
# end
|
230
|
+
# end
|
231
|
+
#
|
232
|
+
# aliased_user_id = Users.schema[:user_id].aliased(:id)
|
233
|
+
#
|
234
|
+
# aliased_user_id.aliased?
|
235
|
+
# # => true
|
236
|
+
#
|
237
|
+
# aliased_user_id.name
|
238
|
+
# # => :user_id
|
239
|
+
#
|
240
|
+
# aliased_user_id.alias
|
241
|
+
# # => :id
|
242
|
+
#
|
243
|
+
# @param [Symbol] name The alias
|
244
|
+
#
|
245
|
+
# @return [Attribute]
|
246
|
+
#
|
247
|
+
# @api public
|
248
|
+
def aliased(name)
|
249
|
+
meta(alias: name)
|
250
|
+
end
|
251
|
+
alias_method :as, :aliased
|
252
|
+
|
253
|
+
# Return new attribute type with an alias using provided prefix
|
254
|
+
#
|
255
|
+
# @example
|
256
|
+
# class Users < ROM::Relation[:memory]
|
257
|
+
# schema do
|
258
|
+
# attribute :id, Types::Int
|
259
|
+
# attribute :name, Types::String
|
260
|
+
# end
|
261
|
+
# end
|
262
|
+
#
|
263
|
+
# prefixed_id = Users.schema[:id].prefixed
|
264
|
+
#
|
265
|
+
# prefixed_id.aliased?
|
266
|
+
# # => true
|
267
|
+
#
|
268
|
+
# prefixed_id.name
|
269
|
+
# # => :id
|
270
|
+
#
|
271
|
+
# prefixed_id.alias
|
272
|
+
# # => :users_id
|
273
|
+
#
|
274
|
+
# prefixed_id = Users.schema[:id].prefixed(:user)
|
275
|
+
#
|
276
|
+
# prefixed_id.alias
|
277
|
+
# # => :user_id
|
278
|
+
#
|
279
|
+
# @param [Symbol] prefix The prefix (defaults to source.dataset)
|
280
|
+
#
|
281
|
+
# @return [Attribute]
|
282
|
+
#
|
283
|
+
# @api public
|
284
|
+
def prefixed(prefix = source.dataset)
|
285
|
+
aliased(:"#{prefix}_#{name}")
|
286
|
+
end
|
287
|
+
|
288
|
+
# Return if the attribute type is from a wrapped relation
|
289
|
+
#
|
290
|
+
# Wrapped attributes are used when two schemas from different relations
|
291
|
+
# are merged together. This way we can identify them easily and handle
|
292
|
+
# correctly in places like auto-mapping.
|
293
|
+
#
|
294
|
+
# @api public
|
295
|
+
def wrapped?
|
296
|
+
meta[:wrapped].equal?(true)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Return attribute type wrapped for the specified relation name
|
300
|
+
#
|
301
|
+
# @param [Symbol] name The name of the source relation (defaults to source.dataset)
|
302
|
+
#
|
303
|
+
# @return [Attribute]
|
304
|
+
#
|
305
|
+
# @api public
|
306
|
+
def wrapped(name = source.dataset)
|
307
|
+
prefixed(name).meta(wrapped: true)
|
308
|
+
end
|
309
|
+
|
310
|
+
# Return attribute type with additional meta information
|
311
|
+
#
|
312
|
+
# Return meta information hash if no opts are provided
|
313
|
+
#
|
314
|
+
# @param [Hash] opts The meta options
|
315
|
+
#
|
316
|
+
# @return [Attribute]
|
317
|
+
#
|
318
|
+
# @api public
|
319
|
+
def meta(opts = nil)
|
320
|
+
if opts
|
321
|
+
self.class.new(type.meta(opts))
|
322
|
+
else
|
323
|
+
type.meta
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# Return string representation of the attribute type
|
328
|
+
#
|
329
|
+
# @return [String]
|
330
|
+
#
|
331
|
+
# @api public
|
332
|
+
def inspect
|
333
|
+
%(#<#{self.class}[#{type.name}] #{meta.map { |k, v| "#{k}=#{v.inspect}" }.join(' ')}>)
|
334
|
+
end
|
335
|
+
alias_method :pretty_inspect, :inspect
|
336
|
+
|
337
|
+
# Check if the attribute type is equal to another
|
338
|
+
#
|
339
|
+
# @param [Dry::Type, Attribute]
|
340
|
+
#
|
341
|
+
# @return [TrueClass,FalseClass]
|
342
|
+
#
|
343
|
+
# @api public
|
344
|
+
def eql?(other)
|
345
|
+
other.is_a?(self.class) ? super : type.eql?(other)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Return if this attribute type has additional attribute type for reading
|
349
|
+
# tuple values
|
350
|
+
#
|
351
|
+
# @return [TrueClass, FalseClass]
|
352
|
+
#
|
353
|
+
# @api private
|
354
|
+
def read?
|
355
|
+
! meta[:read].nil?
|
356
|
+
end
|
357
|
+
|
358
|
+
# Return read type or self
|
359
|
+
#
|
360
|
+
# @return [Attribute]
|
361
|
+
#
|
362
|
+
# @api private
|
363
|
+
def to_read_type
|
364
|
+
read? ? meta[:read] : type
|
365
|
+
end
|
366
|
+
|
367
|
+
# Return nullable attribute
|
368
|
+
#
|
369
|
+
# @return [Attribute]
|
370
|
+
#
|
371
|
+
# @api public
|
372
|
+
def optional
|
373
|
+
sum = self.class.new(super, options)
|
374
|
+
read? ? sum.meta(read: meta[:read].optional) : sum
|
375
|
+
end
|
376
|
+
|
377
|
+
# @api private
|
378
|
+
def respond_to_missing?(name, include_private = false)
|
379
|
+
type.respond_to?(name) || super
|
380
|
+
end
|
381
|
+
|
382
|
+
# Return AST for the type
|
383
|
+
#
|
384
|
+
# @return [Array]
|
385
|
+
#
|
386
|
+
# @api public
|
387
|
+
def to_ast
|
388
|
+
[:attribute, [name, type.to_ast(meta: false), meta_ast]]
|
389
|
+
end
|
390
|
+
|
391
|
+
# Return AST for the read type
|
392
|
+
#
|
393
|
+
# @return [Array]
|
394
|
+
#
|
395
|
+
# @api public
|
396
|
+
def to_read_ast
|
397
|
+
[:attribute, [name, to_read_type.to_ast(meta: false), meta_ast]]
|
398
|
+
end
|
399
|
+
|
400
|
+
# @api private
|
401
|
+
def meta_ast
|
402
|
+
meta_keys = %i(wrapped alias primary_key)
|
403
|
+
ast = meta.select { |k, _| meta_keys.include?(k) }
|
404
|
+
ast[:source] = source.to_sym if source
|
405
|
+
ast
|
406
|
+
end
|
407
|
+
|
408
|
+
memoize :to_ast, :to_read_ast, :meta_ast
|
409
|
+
|
410
|
+
private
|
411
|
+
|
412
|
+
# @api private
|
413
|
+
def method_missing(meth, *args, &block)
|
414
|
+
if type.respond_to?(meth)
|
415
|
+
response = type.__send__(meth, *args, &block)
|
416
|
+
|
417
|
+
if response.is_a?(type.class)
|
418
|
+
self.class.new(response, options)
|
419
|
+
else
|
420
|
+
response
|
421
|
+
end
|
422
|
+
else
|
423
|
+
super
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
data/lib/rom/auto_curry.rb
CHANGED
data/lib/rom/command.rb
CHANGED
@@ -432,6 +432,15 @@ module ROM
|
|
432
432
|
result.equal?(:many)
|
433
433
|
end
|
434
434
|
|
435
|
+
# Check if this command is restrictible through relation
|
436
|
+
#
|
437
|
+
# @return [TrueClass,FalseClass]
|
438
|
+
#
|
439
|
+
# @api private
|
440
|
+
def restrictible?
|
441
|
+
self.class.restrictable.equal?(true)
|
442
|
+
end
|
443
|
+
|
435
444
|
private
|
436
445
|
|
437
446
|
# Hook called by Pipeline to get composite class for commands
|
data/lib/rom/command_proxy.rb
CHANGED
@@ -8,17 +8,25 @@ module ROM
|
|
8
8
|
class CommandProxy
|
9
9
|
attr_reader :command, :root
|
10
10
|
|
11
|
-
|
11
|
+
# @api private
|
12
|
+
def initialize(command, root = Dry::Core::Inflector.singularize(command.name.relation).to_sym)
|
12
13
|
@command = command
|
13
|
-
@root =
|
14
|
+
@root = root
|
14
15
|
end
|
15
16
|
|
17
|
+
# @api private
|
16
18
|
def call(input)
|
17
19
|
command.call(root => input)
|
18
20
|
end
|
19
21
|
|
22
|
+
# @api private
|
20
23
|
def >>(other)
|
21
24
|
self.class.new(command >> other)
|
22
25
|
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def restrictible?
|
29
|
+
command.restrictible?
|
30
|
+
end
|
23
31
|
end
|
24
32
|
end
|
data/lib/rom/constants.rb
CHANGED
@@ -19,12 +19,20 @@ module ROM
|
|
19
19
|
RelationAlreadyDefinedError = Class.new(StandardError)
|
20
20
|
MapperAlreadyDefinedError = Class.new(StandardError)
|
21
21
|
NoRelationError = Class.new(StandardError)
|
22
|
+
InvalidRelationName = Class.new(StandardError)
|
22
23
|
CommandError = Class.new(StandardError)
|
23
24
|
KeyMissing = Class.new(ROM::CommandError)
|
24
25
|
TupleCountMismatchError = Class.new(CommandError)
|
25
26
|
UnknownPluginError = Class.new(StandardError)
|
26
27
|
UnsupportedRelationError = Class.new(StandardError)
|
27
28
|
MissingAdapterIdentifierError = Class.new(StandardError)
|
29
|
+
AttributeAlreadyDefinedError = Class.new(StandardError)
|
30
|
+
|
31
|
+
class InvalidRelationName < StandardError
|
32
|
+
def initialize(relation)
|
33
|
+
super("Relation name: #{relation} is a protected word, please use another relation name")
|
34
|
+
end
|
35
|
+
end
|
28
36
|
|
29
37
|
class ElementNotFoundError < KeyError
|
30
38
|
def initialize(key, registry)
|
data/lib/rom/core.rb
CHANGED
@@ -26,7 +26,6 @@ require 'rom/container'
|
|
26
26
|
require 'rom/create_container'
|
27
27
|
|
28
28
|
# register core plugins
|
29
|
-
require 'rom/plugins/configuration/configuration_dsl'
|
30
29
|
require 'rom/plugins/relation/registry_reader'
|
31
30
|
require 'rom/plugins/relation/instrumentation'
|
32
31
|
require 'rom/plugins/command/schema'
|
@@ -37,7 +36,6 @@ module ROM
|
|
37
36
|
|
38
37
|
plugins do
|
39
38
|
register :mappers, ROM::Mapper::ConfigurationPlugin, type: :configuration
|
40
|
-
register :macros, ROM::ConfigurationPlugins::ConfigurationDSL, type: :configuration
|
41
39
|
register :timestamps, ROM::Plugins::Schema::Timestamps, type: :schema
|
42
40
|
register :registry_reader, ROM::Plugins::Relation::RegistryReader, type: :relation
|
43
41
|
register :instrumentation, ROM::Plugins::Relation::Instrumentation, type: :relation
|
data/lib/rom/gateway.rb
CHANGED
@@ -144,17 +144,6 @@ module ROM
|
|
144
144
|
# noop
|
145
145
|
end
|
146
146
|
|
147
|
-
# Schema inference hook
|
148
|
-
#
|
149
|
-
# Every gateway that supports schema inference should implement this method
|
150
|
-
#
|
151
|
-
# @return [Array] An array with dataset names
|
152
|
-
#
|
153
|
-
# @api private
|
154
|
-
def schema
|
155
|
-
[]
|
156
|
-
end
|
157
|
-
|
158
147
|
# Disconnect is optional and it's a no-op by default
|
159
148
|
#
|
160
149
|
# @api public
|
@@ -19,12 +19,10 @@ module ROM
|
|
19
19
|
default_input = options.fetch(:input, input)
|
20
20
|
|
21
21
|
input_handler =
|
22
|
-
if default_input != Hash
|
22
|
+
if default_input != Hash
|
23
23
|
-> tuple { relation.input_schema[input[tuple]] }
|
24
|
-
elsif relation.schema?
|
25
|
-
relation.input_schema
|
26
24
|
else
|
27
|
-
|
25
|
+
relation.input_schema
|
28
26
|
end
|
29
27
|
|
30
28
|
super(relation, options.merge(input: input_handler))
|
@@ -27,6 +27,9 @@ module ROM
|
|
27
27
|
end
|
28
28
|
|
29
29
|
DEFAULT_DATASET_PROC = -> * { self }.freeze
|
30
|
+
INVALID_RELATIONS_NAMES = [
|
31
|
+
:relations
|
32
|
+
].freeze
|
30
33
|
|
31
34
|
# Return adapter-specific relation subclass
|
32
35
|
#
|
@@ -93,6 +96,8 @@ module ROM
|
|
93
96
|
ds_name = dataset || schema_opts.fetch(:dataset, default_name.dataset)
|
94
97
|
relation = as || schema_opts.fetch(:relation, ds_name)
|
95
98
|
|
99
|
+
raise InvalidRelationName.new(relation) if invalid_relation_name?(relation)
|
100
|
+
|
96
101
|
@relation_name = Name[relation, ds_name]
|
97
102
|
|
98
103
|
@schema_proc = proc do |*args, &inner_block|
|
@@ -173,7 +178,7 @@ module ROM
|
|
173
178
|
# @api public
|
174
179
|
def view(*args, &block)
|
175
180
|
if args.size == 1 && block.arity > 0
|
176
|
-
raise ArgumentError, "
|
181
|
+
raise ArgumentError, "schema attribute names must be provided as the second argument"
|
177
182
|
end
|
178
183
|
|
179
184
|
name, new_schema_fn, relation_block =
|
@@ -247,7 +252,7 @@ module ROM
|
|
247
252
|
ancestor_methods = ancestors.reject { |klass| klass == self }
|
248
253
|
.map(&:instance_methods).flatten(1)
|
249
254
|
|
250
|
-
instance_methods - ancestor_methods + auto_curried_methods
|
255
|
+
instance_methods - ancestor_methods + auto_curried_methods.to_a
|
251
256
|
end
|
252
257
|
|
253
258
|
# @api private
|
@@ -277,6 +282,12 @@ module ROM
|
|
277
282
|
def name
|
278
283
|
super || superclass.name
|
279
284
|
end
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def invalid_relation_name?(relation)
|
289
|
+
INVALID_RELATIONS_NAMES.include?(relation.to_sym)
|
290
|
+
end
|
280
291
|
end
|
281
292
|
end
|
282
293
|
end
|
@@ -103,13 +103,19 @@ module ROM
|
|
103
103
|
end
|
104
104
|
|
105
105
|
# @api public
|
106
|
-
def
|
107
|
-
|
106
|
+
def command(type, *args)
|
107
|
+
if type == :create
|
108
|
+
super
|
109
|
+
else
|
110
|
+
raise NotImplementedError, "#{self.class}#command doesn't work with #{type.inspect} command type yet"
|
111
|
+
end
|
108
112
|
end
|
109
113
|
|
114
|
+
private
|
115
|
+
|
110
116
|
# @api private
|
111
|
-
def
|
112
|
-
|
117
|
+
def decorate?(other)
|
118
|
+
super || other.is_a?(Wrap)
|
113
119
|
end
|
114
120
|
end
|
115
121
|
end
|
@@ -2,18 +2,38 @@ module ROM
|
|
2
2
|
class Relation
|
3
3
|
# Extensions for relation classes which provide access to commands
|
4
4
|
#
|
5
|
-
# @api
|
5
|
+
# @api public
|
6
6
|
module Commands
|
7
|
+
# Return a command for the relation
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# users.command(:create)
|
11
|
+
#
|
12
|
+
# @param type [Symbol] The command type (:create, :update or :delete)
|
13
|
+
# @option :mapper [ROM::Mapper] An optional mapper applied to the command result
|
14
|
+
# @option :use [Array<Symbol>] A list of command plugins
|
15
|
+
# @option :result [:one, :many] Whether the command result has one or more rows.
|
16
|
+
# :one is default
|
17
|
+
#
|
18
|
+
# @return [ROM::Command]
|
19
|
+
#
|
7
20
|
# @api public
|
8
21
|
def command(type, mapper: nil, use: EMPTY_ARRAY, **opts)
|
9
|
-
|
22
|
+
base_command = commands[type, adapter, to_ast, use, opts]
|
10
23
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
mappers.
|
15
|
-
|
16
|
-
|
24
|
+
command =
|
25
|
+
if mapper
|
26
|
+
base_command >> mappers[mapper]
|
27
|
+
elsif mappers.any? && !base_command.is_a?(CommandProxy)
|
28
|
+
mappers.reduce(base_command) { |a, (_, e)| a >> e }
|
29
|
+
elsif auto_struct? || auto_map?
|
30
|
+
base_command >> self.mapper
|
31
|
+
else
|
32
|
+
base_command
|
33
|
+
end
|
34
|
+
|
35
|
+
if command.restrictible?
|
36
|
+
command.new(self)
|
17
37
|
else
|
18
38
|
command
|
19
39
|
end
|