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
@@ -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