rom-core 4.0.0.beta3 → 4.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -1
- data/lib/rom/association_set.rb +3 -0
- data/lib/rom/associations/abstract.rb +72 -1
- data/lib/rom/associations/definitions/abstract.rb +22 -6
- data/lib/rom/associations/definitions/many_to_many.rb +3 -0
- data/lib/rom/associations/definitions/many_to_one.rb +1 -0
- data/lib/rom/associations/definitions/one_to_many.rb +1 -0
- data/lib/rom/associations/definitions/one_to_one.rb +1 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +1 -0
- data/lib/rom/associations/many_to_many.rb +44 -0
- data/lib/rom/associations/many_to_one.rb +26 -0
- data/lib/rom/associations/one_to_many.rb +26 -0
- data/lib/rom/associations/one_to_one.rb +3 -0
- data/lib/rom/associations/one_to_one_through.rb +3 -0
- data/lib/rom/attribute.rb +2 -2
- data/lib/rom/auto_curry.rb +11 -0
- data/lib/rom/cache.rb +29 -0
- data/lib/rom/command_compiler.rb +4 -4
- data/lib/rom/command_registry.rb +9 -5
- data/lib/rom/commands/class_interface.rb +7 -7
- data/lib/rom/commands/graph/input_evaluator.rb +33 -3
- data/lib/rom/commands/lazy.rb +4 -0
- data/lib/rom/commands/lazy/create.rb +10 -0
- data/lib/rom/commands/lazy/delete.rb +10 -0
- data/lib/rom/commands/lazy/update.rb +10 -0
- data/lib/rom/configuration.rb +34 -14
- data/lib/rom/configuration_dsl.rb +0 -2
- data/lib/rom/constants.rb +10 -0
- data/lib/rom/container.rb +16 -0
- data/lib/rom/create_container.rb +7 -0
- data/lib/rom/environment.rb +3 -2
- data/lib/rom/gateway.rb +16 -1
- data/lib/rom/global.rb +1 -1
- data/lib/rom/global/plugin_dsl.rb +3 -1
- data/lib/rom/initializer.rb +25 -13
- data/lib/rom/mapper_registry.rb +4 -1
- data/lib/rom/memory/dataset.rb +29 -2
- data/lib/rom/memory/schema.rb +7 -0
- data/lib/rom/plugin_base.rb +1 -1
- data/lib/rom/plugin_registry.rb +2 -2
- data/lib/rom/plugins/command/schema.rb +7 -0
- data/lib/rom/plugins/relation/instrumentation.rb +10 -0
- data/lib/rom/plugins/relation/registry_reader.rb +0 -3
- data/lib/rom/registry.rb +15 -3
- data/lib/rom/relation.rb +38 -23
- data/lib/rom/relation/class_interface.rb +15 -6
- data/lib/rom/relation/combined.rb +7 -2
- data/lib/rom/relation/curried.rb +23 -0
- data/lib/rom/relation/graph.rb +25 -14
- data/lib/rom/relation/loaded.rb +7 -4
- data/lib/rom/relation/materializable.rb +2 -2
- data/lib/rom/relation/view_dsl.rb +2 -1
- data/lib/rom/relation/wrap.rb +14 -0
- data/lib/rom/relation_registry.rb +2 -0
- data/lib/rom/schema.rb +25 -4
- data/lib/rom/schema/associations_dsl.rb +9 -0
- data/lib/rom/schema/dsl.rb +27 -4
- data/lib/rom/setup.rb +20 -7
- data/lib/rom/setup/auto_registration.rb +27 -0
- data/lib/rom/setup/auto_registration_strategies/base.rb +7 -2
- data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +17 -0
- data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +11 -0
- data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +9 -0
- data/lib/rom/setup/finalize/finalize_mappers.rb +4 -2
- data/lib/rom/setup/finalize/finalize_relations.rb +1 -1
- data/lib/rom/support/configurable.rb +19 -0
- data/lib/rom/support/notifications.rb +29 -2
- data/lib/rom/types.rb +53 -8
- data/lib/rom/version.rb +1 -1
- metadata +7 -13
@@ -1,6 +1,8 @@
|
|
1
1
|
module ROM
|
2
2
|
module Plugins
|
3
3
|
module Command
|
4
|
+
# Command plugin which sets input processing function via relation schema
|
5
|
+
#
|
4
6
|
# @api private
|
5
7
|
module Schema
|
6
8
|
def self.included(klass)
|
@@ -10,7 +12,12 @@ module ROM
|
|
10
12
|
|
11
13
|
# @api private
|
12
14
|
module ClassInterface
|
15
|
+
# Build a command and set it input to relation's input_schema
|
16
|
+
#
|
13
17
|
# @see Command.build
|
18
|
+
#
|
19
|
+
# @return [Command]
|
20
|
+
#
|
14
21
|
# @api public
|
15
22
|
def build(relation, options = {})
|
16
23
|
if options.key?(:input) || !relation.schema?
|
@@ -24,7 +24,15 @@ module ROM
|
|
24
24
|
defines :mixin
|
25
25
|
mixin Module.new
|
26
26
|
|
27
|
+
# Instrumentation extension for relation classes
|
28
|
+
#
|
29
|
+
# @api private
|
27
30
|
module ClassInterface
|
31
|
+
# Configure provided methods for instrumentation
|
32
|
+
#
|
33
|
+
# @param [Array<Symbol>] methods A list of method names
|
34
|
+
#
|
35
|
+
# @api public
|
28
36
|
def instrument(*methods)
|
29
37
|
(methods - Instrumentation.mixin.instance_methods).each do |meth|
|
30
38
|
Instrumentation.mixin.send(:define_method, meth) do
|
@@ -34,6 +42,8 @@ module ROM
|
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
45
|
+
# Execute a block using instrumentation
|
46
|
+
#
|
37
47
|
# @api public
|
38
48
|
def instrument(&block)
|
39
49
|
notifications.instrument(self.class.adapter, name: name.relation, **notification_payload(self), &block)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'dry/core/cache'
|
2
1
|
require 'rom/constants'
|
3
2
|
|
4
3
|
module ROM
|
@@ -10,8 +9,6 @@ module ROM
|
|
10
9
|
#
|
11
10
|
# @api public
|
12
11
|
class RegistryReader < Module
|
13
|
-
extend Dry::Core::Cache
|
14
|
-
|
15
12
|
EMPTY_REGISTRY = RelationRegistry.new(EMPTY_HASH).freeze
|
16
13
|
|
17
14
|
# @api private
|
data/lib/rom/registry.rb
CHANGED
@@ -12,10 +12,15 @@ module ROM
|
|
12
12
|
include Enumerable
|
13
13
|
include Dry::Equalizer(:elements)
|
14
14
|
|
15
|
+
# @!attribute [r] elements
|
16
|
+
# @return [Hash] Internal hash for storing registry objects
|
15
17
|
param :elements
|
16
18
|
|
17
|
-
|
19
|
+
# @!attribute [r] cache
|
20
|
+
# @return [Cache] local cache instance
|
21
|
+
option :cache, default: -> { Cache.new }
|
18
22
|
|
23
|
+
# @api private
|
19
24
|
def self.new(*args)
|
20
25
|
case args.size
|
21
26
|
when 0
|
@@ -27,10 +32,12 @@ module ROM
|
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
35
|
+
# @api private
|
30
36
|
def self.element_not_found_error
|
31
37
|
ElementNotFoundError
|
32
38
|
end
|
33
39
|
|
40
|
+
# @api private
|
34
41
|
def map(&block)
|
35
42
|
new_elements = elements.each_with_object({}) do |(name, element), h|
|
36
43
|
h[name] = yield(element)
|
@@ -38,15 +45,18 @@ module ROM
|
|
38
45
|
self.class.new(new_elements, options)
|
39
46
|
end
|
40
47
|
|
41
|
-
|
42
|
-
|
48
|
+
# @api private
|
49
|
+
def each
|
50
|
+
return to_enum unless block_given?
|
43
51
|
elements.each { |element| yield(element) }
|
44
52
|
end
|
45
53
|
|
54
|
+
# @api private
|
46
55
|
def key?(name)
|
47
56
|
!name.nil? && elements.key?(name.to_sym)
|
48
57
|
end
|
49
58
|
|
59
|
+
# @api private
|
50
60
|
def fetch(key)
|
51
61
|
raise ArgumentError.new('key cannot be nil') if key.nil?
|
52
62
|
|
@@ -58,12 +68,14 @@ module ROM
|
|
58
68
|
end
|
59
69
|
alias_method :[], :fetch
|
60
70
|
|
71
|
+
# @api private
|
61
72
|
def respond_to_missing?(name, include_private = false)
|
62
73
|
elements.key?(name) || super
|
63
74
|
end
|
64
75
|
|
65
76
|
private
|
66
77
|
|
78
|
+
# @api private
|
67
79
|
def method_missing(name, *)
|
68
80
|
elements.fetch(name) { super }
|
69
81
|
end
|
data/lib/rom/relation.rb
CHANGED
@@ -30,19 +30,11 @@ module ROM
|
|
30
30
|
#
|
31
31
|
# Relation is a proxy for the dataset object provided by the gateway. It
|
32
32
|
# can forward methods to the dataset, which is why the "native" interface of
|
33
|
-
# the underlying gateway is available in the relation
|
34
|
-
# however, is considered private and should not be used outside of the
|
35
|
-
# relation instance.
|
33
|
+
# the underlying gateway is available in the relation
|
36
34
|
#
|
37
35
|
# Individual adapters sets up their relation classes and provide different APIs
|
38
36
|
# depending on their persistence backend.
|
39
37
|
#
|
40
|
-
# Vanilla Relation class doesn't have APIs that are specific to ROM container setup.
|
41
|
-
# When adapter Relation class inherits from this class, these APIs are added automatically,
|
42
|
-
# so that they can be registered within a container.
|
43
|
-
#
|
44
|
-
# @see ROM::Relation::ClassInterface
|
45
|
-
#
|
46
38
|
# @api public
|
47
39
|
class Relation
|
48
40
|
# Default no-op output schema which is called in `Relation#each`
|
@@ -223,8 +215,8 @@ module ROM
|
|
223
215
|
# @return [Enumerator] if block is not provided
|
224
216
|
#
|
225
217
|
# @api public
|
226
|
-
def each
|
227
|
-
return to_enum unless
|
218
|
+
def each
|
219
|
+
return to_enum unless block_given?
|
228
220
|
|
229
221
|
if auto_struct?
|
230
222
|
mapper.(dataset.map { |tuple| output_schema[tuple] }).each { |struct| yield(struct) }
|
@@ -267,15 +259,19 @@ module ROM
|
|
267
259
|
when Symbol
|
268
260
|
acc << node(arg)
|
269
261
|
when Hash
|
270
|
-
acc
|
271
|
-
root.node(name).combine(*opts)
|
272
|
-
end
|
262
|
+
acc.concat(arg.map { |name, opts| node(name).combine(opts) })
|
273
263
|
when Array
|
274
264
|
acc.concat(arg.map { |opts| nodes(opts) }.reduce(:concat))
|
275
265
|
end
|
276
266
|
end
|
277
267
|
end
|
278
268
|
|
269
|
+
# Create a graph node for a given association identifier
|
270
|
+
#
|
271
|
+
# @param [Symbol, Relation::Name]
|
272
|
+
#
|
273
|
+
# @return [Relation]
|
274
|
+
#
|
279
275
|
# @api public
|
280
276
|
def node(name)
|
281
277
|
assoc = associations[name]
|
@@ -283,6 +279,12 @@ module ROM
|
|
283
279
|
other.eager_load(assoc)
|
284
280
|
end
|
285
281
|
|
282
|
+
# Return a graph node prepared by the given association
|
283
|
+
#
|
284
|
+
# @param [Association] An association object
|
285
|
+
#
|
286
|
+
# @return [Relation]
|
287
|
+
#
|
286
288
|
# @api public
|
287
289
|
def eager_load(assoc)
|
288
290
|
relation = assoc.prepare(self)
|
@@ -294,6 +296,12 @@ module ROM
|
|
294
296
|
end
|
295
297
|
end
|
296
298
|
|
299
|
+
# Preload other relation via association
|
300
|
+
#
|
301
|
+
# This is used internally when relations are composed
|
302
|
+
#
|
303
|
+
# @return [Relation::Curried]
|
304
|
+
#
|
297
305
|
# @api private
|
298
306
|
def preload_assoc(assoc, other)
|
299
307
|
assoc.preload(self, other)
|
@@ -304,9 +312,9 @@ module ROM
|
|
304
312
|
# @example
|
305
313
|
# tasks.wrap(:owner)
|
306
314
|
#
|
307
|
-
# @param [
|
315
|
+
# @param [Array<Symbol>] names A list with association identifiers
|
308
316
|
#
|
309
|
-
# @return [
|
317
|
+
# @return [Wrap]
|
310
318
|
#
|
311
319
|
# @api public
|
312
320
|
def wrap(*names)
|
@@ -315,6 +323,8 @@ module ROM
|
|
315
323
|
|
316
324
|
# Wrap around other relations
|
317
325
|
#
|
326
|
+
# @param [Array<Relation>] others Other relations
|
327
|
+
#
|
318
328
|
# @return [Relation::Wrap]
|
319
329
|
#
|
320
330
|
# @api public
|
@@ -322,7 +332,7 @@ module ROM
|
|
322
332
|
wrap_class.new(self, others)
|
323
333
|
end
|
324
334
|
|
325
|
-
# Loads relation
|
335
|
+
# Loads a relation
|
326
336
|
#
|
327
337
|
# @return [Relation::Loaded]
|
328
338
|
#
|
@@ -408,16 +418,17 @@ module ROM
|
|
408
418
|
end
|
409
419
|
|
410
420
|
undef_method :with
|
421
|
+
|
411
422
|
# Returns a new instance with the same dataset but new options
|
412
423
|
#
|
413
424
|
# @example
|
414
425
|
# users.with(output_schema: -> tuple { .. })
|
415
426
|
#
|
416
|
-
# @param
|
427
|
+
# @param [Hash] opts New options
|
417
428
|
#
|
418
429
|
# @return [Relation]
|
419
430
|
#
|
420
|
-
# @api
|
431
|
+
# @api public
|
421
432
|
def with(opts)
|
422
433
|
new_options =
|
423
434
|
if opts.key?(:meta)
|
@@ -523,7 +534,7 @@ module ROM
|
|
523
534
|
#
|
524
535
|
# @api public
|
525
536
|
def map_to(klass, **opts)
|
526
|
-
with(opts.merge(meta: { model: klass }))
|
537
|
+
with(opts.merge(auto_struct: true, meta: { model: klass }))
|
527
538
|
end
|
528
539
|
|
529
540
|
# Return a new relation with an aliased name
|
@@ -531,9 +542,9 @@ module ROM
|
|
531
542
|
# @example
|
532
543
|
# users.as(:people)
|
533
544
|
#
|
534
|
-
# @param [
|
545
|
+
# @param [Symbol] aliaz Aliased name
|
535
546
|
#
|
536
|
-
# @return [Relation
|
547
|
+
# @return [Relation]
|
537
548
|
#
|
538
549
|
# @api public
|
539
550
|
def as(aliaz)
|
@@ -586,7 +597,7 @@ module ROM
|
|
586
597
|
|
587
598
|
# Return a new relation configured with the provided struct namespace
|
588
599
|
#
|
589
|
-
# @param [Module] namespace
|
600
|
+
# @param [Module] ns Custom namespace module for auto-structs
|
590
601
|
#
|
591
602
|
# @return [Relation]
|
592
603
|
#
|
@@ -614,6 +625,10 @@ module ROM
|
|
614
625
|
Relation::Composite
|
615
626
|
end
|
616
627
|
|
628
|
+
# Return configured "wrap" relation class used in Relation#wrap
|
629
|
+
#
|
630
|
+
# @return [Class]
|
631
|
+
#
|
617
632
|
# @api private
|
618
633
|
def wrap_class
|
619
634
|
self.class.wrap_class
|
@@ -10,6 +10,8 @@ require 'rom/support/notifications'
|
|
10
10
|
|
11
11
|
module ROM
|
12
12
|
class Relation
|
13
|
+
# Global class-level API for relation classes
|
14
|
+
#
|
13
15
|
# @api public
|
14
16
|
module ClassInterface
|
15
17
|
extend Notifications::Listener
|
@@ -78,8 +80,8 @@ module ROM
|
|
78
80
|
# end
|
79
81
|
# end
|
80
82
|
#
|
81
|
-
# # access schema
|
82
|
-
#
|
83
|
+
# # access schema from a finalized relation
|
84
|
+
# users.schema
|
83
85
|
#
|
84
86
|
# @return [Schema]
|
85
87
|
#
|
@@ -112,6 +114,12 @@ module ROM
|
|
112
114
|
end
|
113
115
|
end
|
114
116
|
|
117
|
+
# Assign a schema to a relation class
|
118
|
+
#
|
119
|
+
# @param [Schema] schema
|
120
|
+
#
|
121
|
+
# @return [Schema]
|
122
|
+
#
|
115
123
|
# @api private
|
116
124
|
def set_schema!(schema)
|
117
125
|
@schema = schema
|
@@ -129,10 +137,10 @@ module ROM
|
|
129
137
|
|
130
138
|
# Define a relation view with a specific schema
|
131
139
|
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
140
|
+
# This method should only be used in cases where a given adapter doesn't
|
141
|
+
# support automatic schema projection at run-time.
|
142
|
+
#
|
143
|
+
# **It's not needed in rom-sql**
|
136
144
|
#
|
137
145
|
# @overload view(name, schema, &block)
|
138
146
|
# @example View with the canonical schema
|
@@ -279,6 +287,7 @@ module ROM
|
|
279
287
|
end
|
280
288
|
end
|
281
289
|
|
290
|
+
# @api private
|
282
291
|
def name
|
283
292
|
super || superclass.name
|
284
293
|
end
|
@@ -25,7 +25,7 @@ module ROM
|
|
25
25
|
|
26
26
|
# Combine this graph with more nodes
|
27
27
|
#
|
28
|
-
# @param [Array<Relation
|
28
|
+
# @param [Array<Relation>] others A list of relations
|
29
29
|
#
|
30
30
|
# @return [Graph]
|
31
31
|
#
|
@@ -34,8 +34,13 @@ module ROM
|
|
34
34
|
self.class.new(root, nodes + others)
|
35
35
|
end
|
36
36
|
|
37
|
-
#
|
37
|
+
# Combine with other relations
|
38
|
+
#
|
38
39
|
# @see Relation#combine
|
40
|
+
#
|
41
|
+
# @return [Combined]
|
42
|
+
#
|
43
|
+
# @api public
|
39
44
|
def combine(*args)
|
40
45
|
self.class.new(root, nodes + root.combine(*args).nodes)
|
41
46
|
end
|
data/lib/rom/relation/curried.rb
CHANGED
@@ -8,6 +8,14 @@ require 'rom/relation/materializable'
|
|
8
8
|
|
9
9
|
module ROM
|
10
10
|
class Relation
|
11
|
+
# Curried relation is a special relation proxy used by auto-curry mechanism.
|
12
|
+
#
|
13
|
+
# When a relation view method is called without all arguments, a curried proxy
|
14
|
+
# is returned that can be fully applied later on.
|
15
|
+
#
|
16
|
+
# Curried relations are typically used for relation composition
|
17
|
+
#
|
18
|
+
# @api public
|
11
19
|
class Curried
|
12
20
|
extend Initializer
|
13
21
|
|
@@ -17,10 +25,20 @@ module ROM
|
|
17
25
|
|
18
26
|
undef :map_with
|
19
27
|
|
28
|
+
# @!attribute [r] relation
|
29
|
+
# @return [Relation] The source relation that is curried
|
20
30
|
param :relation
|
21
31
|
|
32
|
+
# @!attribute [r] view
|
33
|
+
# @return [Symbol] The name of relation's view method
|
22
34
|
option :view, type: Types::Strict::Symbol
|
35
|
+
|
36
|
+
# @!attribute [r] arity
|
37
|
+
# @return [Integer] View's arity
|
23
38
|
option :arity, type: Types::Strict::Int
|
39
|
+
|
40
|
+
# @!attribute [r] curry_args
|
41
|
+
# @return [Array] Arguments that will be passed to curried view
|
24
42
|
option :curry_args, default: -> { EMPTY_ARRAY }
|
25
43
|
|
26
44
|
# Load relation if args match the arity
|
@@ -45,6 +63,11 @@ module ROM
|
|
45
63
|
end
|
46
64
|
alias_method :[], :call
|
47
65
|
|
66
|
+
# Relations are coercible to an array but a curried relation cannot be coerced
|
67
|
+
# When something tries to do this, an exception will be raised
|
68
|
+
#
|
69
|
+
# @raise ArgumentError
|
70
|
+
#
|
48
71
|
# @api public
|
49
72
|
def to_a
|
50
73
|
raise(
|
data/lib/rom/relation/graph.rb
CHANGED
@@ -18,8 +18,12 @@ module ROM
|
|
18
18
|
|
19
19
|
include Memoizable
|
20
20
|
|
21
|
+
# @!attribute [r] root
|
22
|
+
# @return [Relation] The root relation
|
21
23
|
param :root
|
22
24
|
|
25
|
+
# @!attribute [r] nodes
|
26
|
+
# @return [Array<Relation>] An array with relation nodes
|
23
27
|
param :nodes
|
24
28
|
|
25
29
|
include Dry::Equalizer(:root, :nodes)
|
@@ -27,23 +31,16 @@ module ROM
|
|
27
31
|
include Pipeline
|
28
32
|
include Pipeline::Proxy
|
29
33
|
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# @return [Relation]
|
33
|
-
#
|
34
|
-
# @api private
|
35
|
-
attr_reader :root
|
36
|
-
|
37
|
-
# Child relation nodes
|
38
|
-
#
|
39
|
-
# @return [Array<Relation>]
|
40
|
-
#
|
41
|
-
# @api private
|
42
|
-
attr_reader :nodes
|
43
|
-
|
34
|
+
# for compatibility with the pipeline
|
44
35
|
alias_method :left, :root
|
45
36
|
alias_method :right, :nodes
|
46
37
|
|
38
|
+
# Rebuild a graph with new nodes
|
39
|
+
#
|
40
|
+
# @param [Array<Relation>] nodes
|
41
|
+
#
|
42
|
+
# @return [Graph]
|
43
|
+
#
|
47
44
|
# @api public
|
48
45
|
def with_nodes(nodes)
|
49
46
|
self.class.new(root, nodes)
|
@@ -58,16 +55,30 @@ module ROM
|
|
58
55
|
true
|
59
56
|
end
|
60
57
|
|
58
|
+
# Map graph tuples via custom mappers
|
59
|
+
#
|
60
|
+
# @see Relation#map_with
|
61
|
+
#
|
62
|
+
# @return [Graph]
|
63
|
+
#
|
61
64
|
# @api public
|
62
65
|
def map_with(*args)
|
63
66
|
self.class.new(root.map_with(*args), nodes)
|
64
67
|
end
|
65
68
|
|
69
|
+
# Map graph tuples to custom objects
|
70
|
+
#
|
71
|
+
# @see Relation#map_to
|
72
|
+
#
|
73
|
+
# @return [Graph]
|
74
|
+
#
|
66
75
|
# @api public
|
67
76
|
def map_to(klass)
|
68
77
|
self.class.new(root.map_to(klass), nodes)
|
69
78
|
end
|
70
79
|
|
80
|
+
# @see Relation#mapper
|
81
|
+
#
|
71
82
|
# @api private
|
72
83
|
def mapper
|
73
84
|
mappers[to_ast]
|