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
@@ -2,7 +2,9 @@ require 'dry/equalizer'
2
2
 
3
3
  module ROM
4
4
  class Relation
5
- # Materializes a relation and exposes interface to access the data
5
+ # Materializes a relation and exposes interface to access the data.
6
+ #
7
+ # This relation type is returned when a lazy relation is called
6
8
  #
7
9
  # @api public
8
10
  class Loaded
@@ -41,8 +43,8 @@ module ROM
41
43
  # @yield [Hash]
42
44
  #
43
45
  # @api public
44
- def each(&block)
45
- return to_enum unless block
46
+ def each
47
+ return to_enum unless block_given?
46
48
  collection.each { |tuple| yield(tuple) }
47
49
  end
48
50
 
@@ -86,7 +88,8 @@ module ROM
86
88
  # @param [Symbol] key The key name
87
89
  #
88
90
  # @return [Array]
89
- # @raises KeyError when provided key doesn't exist in any of the tuples
91
+ #
92
+ # @raise KeyError when provided key doesn't exist in any of the tuples
90
93
  #
91
94
  # @api public
92
95
  def pluck(key)
@@ -19,8 +19,8 @@ module ROM
19
19
  # @yield [Hash,Object]
20
20
  #
21
21
  # @api public
22
- def each(&block)
23
- return to_enum unless block
22
+ def each
23
+ return to_enum unless block_given?
24
24
  to_a.each { |tuple| yield(tuple) }
25
25
  end
26
26
 
@@ -4,7 +4,8 @@ module ROM
4
4
  #
5
5
  # This is used to establish pre-defined relation views with explicit schemas.
6
6
  # Such views can be used to compose relations together, even from multiple
7
- # adapters.
7
+ # adapters. In advanced adapters like rom-sql using view DSL is not required though,
8
+ # as relation schemas are dynamic and they always represent current tuple structure.
8
9
  #
9
10
  # @api public
10
11
  class ViewDSL
@@ -7,13 +7,23 @@ module ROM
7
7
  #
8
8
  # @api public
9
9
  class Wrap < Graph
10
+ # Wrap more relations
11
+ #
12
+ # @see Relation#wrap
13
+ #
14
+ # @return [Wrap]
15
+ #
10
16
  # @api public
11
17
  def wrap(*args)
12
18
  self.class.new(root, nodes + root.wrap(*args).nodes)
13
19
  end
14
20
 
21
+ # Materialize a wrap
22
+ #
15
23
  # @see Relation#call
16
24
  #
25
+ # @return [Loaded]
26
+ #
17
27
  # @api public
18
28
  def call(*args)
19
29
  if auto_map?
@@ -23,6 +33,10 @@ module ROM
23
33
  end
24
34
  end
25
35
 
36
+ # Return an adapter-specific relation representing a wrap
37
+ #
38
+ # @abstract
39
+ #
26
40
  # @api private
27
41
  def relation
28
42
  raise NotImplementedError
@@ -1,7 +1,9 @@
1
1
  require 'rom/registry'
2
2
 
3
3
  module ROM
4
+ # @api private
4
5
  class RelationRegistry < Registry
6
+ # @api private
5
7
  def initialize(elements = {}, options = {})
6
8
  super
7
9
  yield(self, elements) if block_given?
data/lib/rom/schema.rb CHANGED
@@ -85,6 +85,10 @@ module ROM
85
85
  # @api private
86
86
  option :relations, default: -> { EMPTY_HASH }
87
87
 
88
+ # @!attribute [r] canonical
89
+ # @return [Symbol] The canonical schema which is carried in all schema instances
90
+ option :canonical, default: -> { self }
91
+
88
92
  # @api private
89
93
  option :attr_class, default: -> { Attribute }
90
94
 
@@ -137,7 +141,7 @@ module ROM
137
141
  #
138
142
  # Default implementation is a no-op and it simply returns back untouched relation
139
143
  #
140
- # @param [Relation]
144
+ # @param [Relation] relation
141
145
  #
142
146
  # @return [Relation]
143
147
  #
@@ -293,7 +297,7 @@ module ROM
293
297
  #
294
298
  # This returns a new schema instance
295
299
  #
296
- # @param [*Array<Attribute>]
300
+ # @param [Array<Attribute>] new_attributes
297
301
  #
298
302
  # @return [Schema]
299
303
  #
@@ -304,8 +308,6 @@ module ROM
304
308
 
305
309
  # Return a new schema with uniq attributes
306
310
  #
307
- # @param [*Array<Attribute>]
308
- #
309
311
  # @return [Schema]
310
312
  #
311
313
  # @api public
@@ -328,6 +330,19 @@ module ROM
328
330
  ! attributes.detect { |attr| attr.name == name }.nil?
329
331
  end
330
332
 
333
+ # Return if a schema is canonical
334
+ #
335
+ # @return [Boolean]
336
+ #
337
+ # @api public
338
+ def canonical?
339
+ self.equal?(canonical)
340
+ end
341
+
342
+ # Finalize a schema
343
+ #
344
+ # @return [self]
345
+ #
331
346
  # @api private
332
347
  def finalize!(**opts)
333
348
  return self if frozen?
@@ -352,6 +367,12 @@ module ROM
352
367
  self
353
368
  end
354
369
 
370
+ # Finalize associations defined in a schema
371
+ #
372
+ # @param [RelationRegistry] relations
373
+ #
374
+ # @return [self]
375
+ #
355
376
  # @api private
356
377
  def finalize_associations!(relations:)
357
378
  set!(:associations, yield) if associations.any?
@@ -30,16 +30,25 @@ module ROM
30
30
  # @example using relation identifier
31
31
  # has_many :tasks
32
32
  #
33
+ # @example setting custom foreign key name
34
+ # has_many :tasks, foreign_key: :assignee_id
35
+ #
33
36
  # @example with a :through option
34
37
  # # this establishes many-to-many association
35
38
  # has_many :tasks, through: :users_tasks
36
39
  #
40
+ # @example using a custom view which overrides default one
41
+ # has_many :posts, view: :published, override: true
42
+ #
37
43
  # @example using aliased association with a custom view
38
44
  # has_many :posts, as: :published_posts, view: :published
39
45
  #
40
46
  # @example using custom target relation
41
47
  # has_many :user_posts, relation: :posts
42
48
  #
49
+ # @example using custom target relation and an alias
50
+ # has_many :user_posts, relation: :posts, as: :published, view: :published
51
+ #
43
52
  # @param [Symbol] target The target relation identifier
44
53
  # @param [Hash] options A hash with additional options
45
54
  #
@@ -5,10 +5,9 @@ require 'rom/attribute'
5
5
  require 'rom/schema/associations_dsl'
6
6
 
7
7
  module ROM
8
- # Relation schema
9
- #
10
- # @api public
11
8
  class Schema
9
+ # Schema DSL exposed as `schema { .. }` in relation classes
10
+ #
12
11
  # @api public
13
12
  class DSL < BasicObject
14
13
  KERNEL_METHODS = %i(extend method).freeze
@@ -16,17 +15,41 @@ module ROM
16
15
 
17
16
  extend Initializer
18
17
 
18
+ # @!attribute [r] relation
19
+ # @return [Relation::Name] The name of the schema's relation
19
20
  param :relation
20
21
 
22
+ # @!attribute [r] inferrer
23
+ # @return [Inferrer] Optional attribute inferrer
21
24
  option :inferrer, default: -> { DEFAULT_INFERRER }
22
25
 
26
+ # @!attribute [r] schema_class
27
+ # @return [Class] Schema class that should be instantiated
23
28
  option :schema_class, default: -> { Schema }
24
29
 
30
+ # @!attribute [r] attr_class
31
+ # @return [Class] Attribute class that should be used
25
32
  option :attr_class, default: -> { Attribute }
26
33
 
34
+ # @!attribute [r] adapter
35
+ # @return [Symbol] The adapter identifier used in gateways
27
36
  option :adapter, default: -> { :default }
28
37
 
29
- attr_reader :attributes, :plugins, :definition, :associations_dsl
38
+ # @!attribute [r] attributes
39
+ # @return [Hash] A hash with attributes defined by the DSL
40
+ attr_reader :attributes
41
+
42
+ # @!attribute [r] plugins
43
+ # @return [Hash] A hash with schema plugins enabled in a schema
44
+ attr_reader :plugins
45
+
46
+ # @!attribute [r] definition
47
+ # @return [Proc] Definition block passed to DSL
48
+ attr_reader :definition
49
+
50
+ # @!attribute [r] associations_dsl
51
+ # @return [AssociationDSL] Associations defined within a block
52
+ attr_reader :associations_dsl
30
53
 
31
54
  # @api private
32
55
  def initialize(*, &block)
data/lib/rom/setup.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'rom/setup/auto_registration'
2
2
 
3
3
  module ROM
4
+ # Setup objects collect component classes during setup/finalization process
5
+ #
6
+ # @api public
4
7
  class Setup
5
8
  # @return [Array] registered relation subclasses
6
9
  #
@@ -32,6 +35,23 @@ module ROM
32
35
  @notifications = notifications
33
36
  end
34
37
 
38
+ # Enable auto-registration for a given setup object
39
+ #
40
+ # @param [String, Pathname] directory The root path to components
41
+ # @param [Hash] options
42
+ # @option options [Boolean, String] :namespace Enable/disable namespace or provide a custom namespace name
43
+ #
44
+ # @return [Setup]
45
+ #
46
+ # @api public
47
+ def auto_registration(directory, options = {})
48
+ auto_registration = AutoRegistration.new(directory, options)
49
+ auto_registration.relations.map { |r| register_relation(r) }
50
+ auto_registration.commands.map { |r| register_command(r) }
51
+ auto_registration.mappers.map { |r| register_mapper(r) }
52
+ self
53
+ end
54
+
35
55
  # Relation sub-classes are being registered with this method during setup
36
56
  #
37
57
  # @api private
@@ -57,12 +77,5 @@ module ROM
57
77
  def register_plugin(plugin)
58
78
  plugins << plugin
59
79
  end
60
-
61
- def auto_registration(directory, options = {})
62
- auto_registration = AutoRegistration.new(directory, options)
63
- auto_registration.relations.map { |r| register_relation(r) }
64
- auto_registration.commands.map { |r| register_command(r) }
65
- auto_registration.mappers.map { |r| register_mapper(r) }
66
- end
67
80
  end
68
81
  end
@@ -9,23 +9,38 @@ require 'rom/setup/auto_registration_strategies/with_namespace'
9
9
  require 'rom/setup/auto_registration_strategies/custom_namespace'
10
10
 
11
11
  module ROM
12
+ # AutoRegistration is used to load component files automatically from the provided directory path
13
+ #
14
+ # @api public
12
15
  class AutoRegistration
13
16
  extend Initializer
14
17
 
15
18
  NamespaceType = Types::Strict::Bool | Types::Strict::String
19
+
16
20
  PathnameType = Types.Constructor(Pathname, &Kernel.method(:Pathname))
21
+
17
22
  DEFAULT_MAPPING = {
18
23
  relations: :relations,
19
24
  mappers: :mappers,
20
25
  commands: :commands
21
26
  }.freeze
22
27
 
28
+ # @!attribute [r] directory
29
+ # @return [Pathname] The root path
23
30
  param :directory, type: PathnameType
24
31
 
32
+ # @!attribute [r] namespace
33
+ # @return [Boolean,String]
34
+ # The name of the top level namespace or true/false which
35
+ # enables/disables default top level namespace inferred from the dir name
25
36
  option :namespace, type: NamespaceType, default: -> { true }
26
37
 
38
+ # @!attribute [r] component_dirs
39
+ # @return [Hash] component => dir-name map
27
40
  option :component_dirs, type: Types::Strict::Hash, default: -> { DEFAULT_MAPPING }
28
41
 
42
+ # @!attribute [r] globs
43
+ # @return [Hash] File globbing functions for each component dir
29
44
  option :globs, default: -> {
30
45
  Hash[
31
46
  component_dirs.map { |component, path|
@@ -34,20 +49,32 @@ module ROM
34
49
  ]
35
50
  }
36
51
 
52
+ # Load relation files
53
+ #
54
+ # @api private
37
55
  def relations
38
56
  load_entities(:relations)
39
57
  end
40
58
 
59
+ # Load command files
60
+ #
61
+ # @api private
41
62
  def commands
42
63
  load_entities(:commands)
43
64
  end
44
65
 
66
+ # Load mapper files
67
+ #
68
+ # @api private
45
69
  def mappers
46
70
  load_entities(:mappers)
47
71
  end
48
72
 
49
73
  private
50
74
 
75
+ # Load given component files
76
+ #
77
+ # @api private
51
78
  def load_entities(entity)
52
79
  Dir[globs[entity]].map do |file|
53
80
  require file
@@ -3,14 +3,19 @@ require 'rom/initializer'
3
3
 
4
4
  module ROM
5
5
  module AutoRegistrationStrategies
6
+ # Base class for registration strategies
7
+ #
8
+ # @api private
6
9
  class Base
7
10
  extend Initializer
8
11
 
9
12
  PathnameType = Types.Definition(Pathname).constrained(type: Pathname)
10
13
 
11
- option :file, type: Types::Strict::String
12
-
13
14
  EXTENSION_REGEX = /\.rb\z/
15
+
16
+ # @!attribute [r] file
17
+ # @return [String] Name of a component file
18
+ option :file, type: Types::Strict::String
14
19
  end
15
20
  end
16
21
  end
@@ -6,10 +6,22 @@ require 'rom/setup/auto_registration_strategies/base'
6
6
 
7
7
  module ROM
8
8
  module AutoRegistrationStrategies
9
+ # Custom namespace strategy loads components and assumes they are defined
10
+ # within the provided namespace
11
+ #
12
+ # @api private
9
13
  class CustomNamespace < Base
14
+ # @!attribute [r] directory
15
+ # @return [Pathname] The path to dir with components
10
16
  option :directory, type: PathnameType
17
+
18
+ # @!attribute [r] namespace
19
+ # @return [String] Name of a namespace
11
20
  option :namespace, type: Types::Strict::String
12
21
 
22
+ # Loads components
23
+ #
24
+ # @api private
13
25
  def call
14
26
  potential = []
15
27
  attempted = []
@@ -37,24 +49,29 @@ module ROM
37
49
 
38
50
  private
39
51
 
52
+ # @api private
40
53
  def name_error_message(attempted)
41
54
  "required file does not define expected constant name; either " \
42
55
  "register your constant explicitly of try following the path" \
43
56
  "naming convention like:\n\n\t- #{attempted.join("\n\t- ")}\n"
44
57
  end
45
58
 
59
+ # @api private
46
60
  def filename
47
61
  Pathname(file).basename('.rb')
48
62
  end
49
63
 
64
+ # @api private
50
65
  def ns_const
51
66
  @namespace_constant ||= Dry::Core::Inflector.constantize(namespace)
52
67
  end
53
68
 
69
+ # @api private
54
70
  def path_arr
55
71
  file_path << filename
56
72
  end
57
73
 
74
+ # @api private
58
75
  def file_path
59
76
  File.dirname(file).split("/") - directory.to_s.split("/")
60
77
  end