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.
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]