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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -1
  3. data/lib/rom/association_set.rb +3 -0
  4. data/lib/rom/associations/abstract.rb +72 -1
  5. data/lib/rom/associations/definitions/abstract.rb +22 -6
  6. data/lib/rom/associations/definitions/many_to_many.rb +3 -0
  7. data/lib/rom/associations/definitions/many_to_one.rb +1 -0
  8. data/lib/rom/associations/definitions/one_to_many.rb +1 -0
  9. data/lib/rom/associations/definitions/one_to_one.rb +1 -0
  10. data/lib/rom/associations/definitions/one_to_one_through.rb +1 -0
  11. data/lib/rom/associations/many_to_many.rb +44 -0
  12. data/lib/rom/associations/many_to_one.rb +26 -0
  13. data/lib/rom/associations/one_to_many.rb +26 -0
  14. data/lib/rom/associations/one_to_one.rb +3 -0
  15. data/lib/rom/associations/one_to_one_through.rb +3 -0
  16. data/lib/rom/attribute.rb +2 -2
  17. data/lib/rom/auto_curry.rb +11 -0
  18. data/lib/rom/cache.rb +29 -0
  19. data/lib/rom/command_compiler.rb +4 -4
  20. data/lib/rom/command_registry.rb +9 -5
  21. data/lib/rom/commands/class_interface.rb +7 -7
  22. data/lib/rom/commands/graph/input_evaluator.rb +33 -3
  23. data/lib/rom/commands/lazy.rb +4 -0
  24. data/lib/rom/commands/lazy/create.rb +10 -0
  25. data/lib/rom/commands/lazy/delete.rb +10 -0
  26. data/lib/rom/commands/lazy/update.rb +10 -0
  27. data/lib/rom/configuration.rb +34 -14
  28. data/lib/rom/configuration_dsl.rb +0 -2
  29. data/lib/rom/constants.rb +10 -0
  30. data/lib/rom/container.rb +16 -0
  31. data/lib/rom/create_container.rb +7 -0
  32. data/lib/rom/environment.rb +3 -2
  33. data/lib/rom/gateway.rb +16 -1
  34. data/lib/rom/global.rb +1 -1
  35. data/lib/rom/global/plugin_dsl.rb +3 -1
  36. data/lib/rom/initializer.rb +25 -13
  37. data/lib/rom/mapper_registry.rb +4 -1
  38. data/lib/rom/memory/dataset.rb +29 -2
  39. data/lib/rom/memory/schema.rb +7 -0
  40. data/lib/rom/plugin_base.rb +1 -1
  41. data/lib/rom/plugin_registry.rb +2 -2
  42. data/lib/rom/plugins/command/schema.rb +7 -0
  43. data/lib/rom/plugins/relation/instrumentation.rb +10 -0
  44. data/lib/rom/plugins/relation/registry_reader.rb +0 -3
  45. data/lib/rom/registry.rb +15 -3
  46. data/lib/rom/relation.rb +38 -23
  47. data/lib/rom/relation/class_interface.rb +15 -6
  48. data/lib/rom/relation/combined.rb +7 -2
  49. data/lib/rom/relation/curried.rb +23 -0
  50. data/lib/rom/relation/graph.rb +25 -14
  51. data/lib/rom/relation/loaded.rb +7 -4
  52. data/lib/rom/relation/materializable.rb +2 -2
  53. data/lib/rom/relation/view_dsl.rb +2 -1
  54. data/lib/rom/relation/wrap.rb +14 -0
  55. data/lib/rom/relation_registry.rb +2 -0
  56. data/lib/rom/schema.rb +25 -4
  57. data/lib/rom/schema/associations_dsl.rb +9 -0
  58. data/lib/rom/schema/dsl.rb +27 -4
  59. data/lib/rom/setup.rb +20 -7
  60. data/lib/rom/setup/auto_registration.rb +27 -0
  61. data/lib/rom/setup/auto_registration_strategies/base.rb +7 -2
  62. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +17 -0
  63. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +11 -0
  64. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +9 -0
  65. data/lib/rom/setup/finalize/finalize_mappers.rb +4 -2
  66. data/lib/rom/setup/finalize/finalize_relations.rb +1 -1
  67. data/lib/rom/support/configurable.rb +19 -0
  68. data/lib/rom/support/notifications.rb +29 -2
  69. data/lib/rom/types.rb +53 -8
  70. data/lib/rom/version.rb +1 -1
  71. 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
- option :cache, reader: true, default: -> { Cache.new }
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
- def each(&block)
42
- return to_enum unless block
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. This interface,
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(&block)
227
- return to_enum unless block
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 << arg.reduce(self) do |root, (name, *opts)|
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 [Hash] options
315
+ # @param [Array<Symbol>] names A list with association identifiers
308
316
  #
309
- # @return [Relation]
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 new_options [Hash]
427
+ # @param [Hash] opts New options
417
428
  #
418
429
  # @return [Relation]
419
430
  #
420
- # @api private
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 [Class] klass Your custom model class
545
+ # @param [Symbol] aliaz Aliased name
535
546
  #
536
- # @return [Relation::Composite]
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
- # Users.schema
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
- # Explicit relation views allow relation composition with auto-mapping
133
- # in repositories. It's useful for cases like defining custom views
134
- # for associations where relations (even from different databases) can
135
- # be composed together and automatically mapped in memory to structs.
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::Lazy>]
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
- # @api public
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
@@ -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(
@@ -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
- # Root aka parent relation
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]