rom-core 4.0.0.beta3 → 4.0.0.rc1
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 +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]
|