rom 2.0.2 → 3.0.0.beta1

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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -4
  3. data/CHANGELOG.md +34 -1
  4. data/Gemfile +16 -2
  5. data/Rakefile +7 -2
  6. data/lib/rom/array_dataset.rb +44 -0
  7. data/lib/rom/association_set.rb +11 -5
  8. data/lib/rom/auto_curry.rb +55 -0
  9. data/lib/rom/command.rb +70 -41
  10. data/lib/rom/command_registry.rb +7 -18
  11. data/lib/rom/commands/class_interface.rb +7 -6
  12. data/lib/rom/commands/composite.rb +0 -1
  13. data/lib/rom/commands/graph.rb +7 -15
  14. data/lib/rom/commands/lazy/update.rb +1 -1
  15. data/lib/rom/configuration_dsl/command.rb +6 -8
  16. data/lib/rom/configuration_dsl/mapper.rb +2 -3
  17. data/lib/rom/configuration_dsl/mapper_dsl.rb +0 -1
  18. data/lib/rom/configuration_dsl/relation.rb +4 -4
  19. data/lib/rom/configuration_dsl.rb +0 -4
  20. data/lib/rom/constants.rb +1 -1
  21. data/lib/rom/container.rb +0 -2
  22. data/lib/rom/create_container.rb +0 -2
  23. data/lib/rom/data_proxy.rb +94 -0
  24. data/lib/rom/enumerable_dataset.rb +68 -0
  25. data/lib/rom/gateway.rb +23 -6
  26. data/lib/rom/global/plugin_dsl.rb +0 -2
  27. data/lib/rom/global.rb +0 -2
  28. data/lib/rom/initializer.rb +26 -0
  29. data/lib/rom/lint/gateway.rb +17 -0
  30. data/lib/rom/mapper_registry.rb +1 -1
  31. data/lib/rom/memory/commands.rb +0 -2
  32. data/lib/rom/memory/dataset.rb +1 -2
  33. data/lib/rom/memory/relation.rb +14 -1
  34. data/lib/rom/memory/schema.rb +13 -0
  35. data/lib/rom/plugin_registry.rb +1 -1
  36. data/lib/rom/plugins/configuration/configuration_dsl.rb +6 -2
  37. data/lib/rom/plugins/relation/key_inference.rb +4 -2
  38. data/lib/rom/plugins/relation/registry_reader.rb +5 -1
  39. data/lib/rom/registry.rb +50 -0
  40. data/lib/rom/relation/class_interface.rb +94 -26
  41. data/lib/rom/relation/curried.rb +15 -15
  42. data/lib/rom/relation/view_dsl.rb +31 -0
  43. data/lib/rom/relation.rb +49 -34
  44. data/lib/rom/schema/dsl.rb +7 -9
  45. data/lib/rom/schema/type.rb +115 -0
  46. data/lib/rom/schema.rb +218 -18
  47. data/lib/rom/setup/auto_registration.rb +20 -17
  48. data/lib/rom/setup/auto_registration_strategies/base.rb +8 -3
  49. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +4 -3
  50. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +5 -4
  51. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +3 -3
  52. data/lib/rom/setup/finalize/finalize_commands.rb +1 -1
  53. data/lib/rom/setup/finalize/finalize_mappers.rb +1 -1
  54. data/lib/rom/setup/finalize/finalize_relations.rb +3 -1
  55. data/lib/rom/setup/finalize.rb +1 -1
  56. data/lib/rom/transaction.rb +24 -0
  57. data/lib/rom/types.rb +9 -1
  58. data/lib/rom/version.rb +1 -1
  59. data/lib/rom.rb +4 -8
  60. data/rom.gemspec +4 -3
  61. data/spec/integration/command_registry_spec.rb +1 -14
  62. data/spec/integration/commands/create_spec.rb +5 -25
  63. data/spec/integration/commands/delete_spec.rb +1 -1
  64. data/spec/integration/commands/error_handling_spec.rb +1 -1
  65. data/spec/integration/commands/graph_spec.rb +20 -14
  66. data/spec/integration/commands/update_spec.rb +4 -27
  67. data/spec/integration/commands_spec.rb +1 -1
  68. data/spec/integration/{repositories → gateways}/extending_relations_spec.rb +1 -1
  69. data/spec/integration/{repositories → gateways}/setting_logger_spec.rb +2 -2
  70. data/spec/integration/mappers/combine_spec.rb +1 -1
  71. data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
  72. data/spec/integration/mappers/definition_dsl_spec.rb +1 -1
  73. data/spec/integration/mappers/embedded_spec.rb +1 -1
  74. data/spec/integration/mappers/exclude_spec.rb +1 -1
  75. data/spec/integration/mappers/fold_spec.rb +1 -1
  76. data/spec/integration/mappers/group_spec.rb +1 -1
  77. data/spec/integration/mappers/overwrite_attributes_value_spec.rb +1 -1
  78. data/spec/integration/mappers/prefix_separator_spec.rb +1 -1
  79. data/spec/integration/mappers/prefix_spec.rb +1 -1
  80. data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
  81. data/spec/integration/mappers/registering_custom_mappers_spec.rb +1 -1
  82. data/spec/integration/mappers/renaming_attributes_spec.rb +1 -1
  83. data/spec/integration/mappers/reusing_mappers_spec.rb +1 -1
  84. data/spec/integration/mappers/step_spec.rb +1 -1
  85. data/spec/integration/mappers/symbolizing_attributes_spec.rb +1 -1
  86. data/spec/integration/mappers/unfold_spec.rb +1 -1
  87. data/spec/integration/mappers/ungroup_spec.rb +1 -1
  88. data/spec/integration/mappers/unwrap_spec.rb +2 -2
  89. data/spec/integration/mappers/wrap_spec.rb +1 -1
  90. data/spec/integration/memory/commands/create_spec.rb +1 -1
  91. data/spec/integration/memory/commands/delete_spec.rb +1 -1
  92. data/spec/integration/memory/commands/update_spec.rb +1 -1
  93. data/spec/integration/multi_env_spec.rb +1 -1
  94. data/spec/integration/multi_repo_spec.rb +1 -1
  95. data/spec/integration/relations/default_dataset_spec.rb +1 -1
  96. data/spec/integration/relations/reading_spec.rb +1 -1
  97. data/spec/integration/relations/registry_dsl_spec.rb +1 -1
  98. data/spec/integration/setup_spec.rb +10 -4
  99. data/spec/shared/command_graph.rb +8 -4
  100. data/spec/shared/enumerable_dataset.rb +1 -1
  101. data/spec/spec_helper.rb +7 -9
  102. data/spec/support/schema.rb +14 -0
  103. data/spec/unit/rom/array_dataset_spec.rb +59 -0
  104. data/spec/unit/rom/association_set_spec.rb +4 -0
  105. data/spec/unit/rom/auto_curry_spec.rb +63 -0
  106. data/spec/unit/rom/commands/graph_spec.rb +12 -11
  107. data/spec/unit/rom/commands/lazy_spec.rb +8 -5
  108. data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +269 -0
  109. data/spec/unit/rom/commands/result_spec.rb +1 -1
  110. data/spec/unit/rom/commands_spec.rb +9 -3
  111. data/spec/unit/rom/configuration_spec.rb +1 -1
  112. data/spec/unit/rom/container_spec.rb +11 -5
  113. data/spec/unit/rom/create_container_spec.rb +1 -1
  114. data/spec/unit/rom/enumerable_dataset_spec.rb +15 -0
  115. data/spec/unit/rom/gateway_spec.rb +1 -1
  116. data/spec/unit/rom/mapper_registry_spec.rb +1 -1
  117. data/spec/unit/rom/memory/commands_spec.rb +1 -1
  118. data/spec/unit/rom/memory/dataset_spec.rb +1 -1
  119. data/spec/unit/rom/memory/{repository_spec.rb → gateway_spec.rb} +1 -1
  120. data/spec/unit/rom/memory/inheritance_spec.rb +32 -0
  121. data/spec/unit/rom/memory/relation_spec.rb +15 -3
  122. data/spec/unit/rom/memory/storage_spec.rb +1 -1
  123. data/spec/unit/rom/plugin_spec.rb +1 -1
  124. data/spec/unit/rom/plugins/relation/key_inference_spec.rb +1 -1
  125. data/spec/unit/rom/registry_spec.rb +86 -0
  126. data/spec/unit/rom/relation/attribute_reader_spec.rb +17 -0
  127. data/spec/unit/rom/relation/composite_spec.rb +1 -1
  128. data/spec/unit/rom/relation/graph_spec.rb +1 -1
  129. data/spec/unit/rom/relation/lazy/combine_spec.rb +1 -1
  130. data/spec/unit/rom/relation/lazy_spec.rb +1 -1
  131. data/spec/unit/rom/relation/loaded_spec.rb +1 -1
  132. data/spec/unit/rom/relation/schema_spec.rb +10 -6
  133. data/spec/unit/rom/relation/view_spec.rb +112 -0
  134. data/spec/unit/rom/relation_spec.rb +16 -2
  135. data/spec/unit/rom/schema/accessing_attributes_spec.rb +52 -0
  136. data/spec/unit/rom/schema/exclude_spec.rb +15 -0
  137. data/spec/unit/rom/schema/finalize_spec.rb +59 -0
  138. data/spec/unit/rom/schema/key_predicate_spec.rb +15 -0
  139. data/spec/unit/rom/schema/merge_spec.rb +17 -0
  140. data/spec/unit/rom/schema/prefix_spec.rb +16 -0
  141. data/spec/unit/rom/schema/project_spec.rb +15 -0
  142. data/spec/unit/rom/schema/rename_spec.rb +22 -0
  143. data/spec/unit/rom/schema/type_spec.rb +49 -0
  144. data/spec/unit/rom/schema/wrap_spec.rb +17 -0
  145. data/spec/unit/rom/schema_spec.rb +2 -2
  146. metadata +69 -17
  147. data/lib/rom/plugins/relation/view/dsl.rb +0 -32
  148. data/lib/rom/plugins/relation/view.rb +0 -95
  149. data/spec/unit/rom/plugins/relation/view_spec.rb +0 -51
@@ -1,5 +1,3 @@
1
- require 'rom/support/constants'
2
-
3
1
  require 'rom/configuration_dsl/relation'
4
2
  require 'rom/configuration_dsl/mapper_dsl'
5
3
  require 'rom/configuration_dsl/command_dsl'
@@ -55,13 +53,11 @@ module ROM
55
53
  # setup.commands(:users) do
56
54
  # define(:create) do
57
55
  # input NewUserParams
58
- # validator NewUserValidator
59
56
  # result :one
60
57
  # end
61
58
  #
62
59
  # define(:update) do
63
60
  # input UserParams
64
- # validator UserValidator
65
61
  # result :many
66
62
  # end
67
63
  #
data/lib/rom/constants.rb CHANGED
@@ -23,7 +23,7 @@ module ROM
23
23
  MissingAdapterIdentifierError = Class.new(StandardError)
24
24
 
25
25
  DuplicateConfigurationError = Class.new(StandardError)
26
- DuplicateContainerError = Class.new(StandardError)
26
+ DuplicateContainerError = Class.new(StandardError)
27
27
 
28
28
  InvalidOptionValueError = Class.new(StandardError)
29
29
  InvalidOptionKeyError = Class.new(StandardError)
data/lib/rom/container.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'rom/relation/loaded'
2
2
  require 'rom/commands/graph'
3
3
  require 'rom/commands/graph/builder'
4
- require 'rom/support/publisher'
5
4
 
6
5
  module ROM
7
6
  # ROM container is an isolated environment with no global state where all
@@ -98,7 +97,6 @@ module ROM
98
97
  #
99
98
  # @api public
100
99
  class Container
101
- include ROM::Support::Publisher
102
100
  include Dry::Equalizer(:gateways, :relations, :mappers, :commands)
103
101
 
104
102
  # @return [Hash] configured gateways
@@ -5,8 +5,6 @@ require 'rom/setup/finalize'
5
5
 
6
6
  module ROM
7
7
  class CreateContainer
8
- include ROM::Support::Publisher
9
-
10
8
  attr_reader :container
11
9
 
12
10
  def initialize(environment, setup)
@@ -0,0 +1,94 @@
1
+ module ROM
2
+ # Helper module for dataset classes
3
+ #
4
+ # It provides a constructor accepting data, header and an optional row_proc.
5
+ # This module is used internally by EnumerableDataset and ArrayDataset.
6
+ #
7
+ # @private
8
+ module DataProxy
9
+ NON_FORWARDABLE = [
10
+ :each, :to_a, :to_ary, :kind_of?, :instance_of?, :is_a?
11
+ ].freeze
12
+
13
+ # Wrapped data array
14
+ #
15
+ # @return [Object] Data object for the iterator
16
+ #
17
+ # @api private
18
+ attr_reader :data
19
+
20
+ # @return [Proc] tuple processing proc
21
+ #
22
+ # @api private
23
+ attr_reader :row_proc
24
+
25
+ # Extends the class with `forward` DSL and Equalizer using `data` attribute
26
+ #
27
+ # @see ClassMethods#forward
28
+ #
29
+ # @api private
30
+ def self.included(klass)
31
+ klass.class_eval do
32
+ extend ClassMethods
33
+
34
+ include Dry::Equalizer(:data)
35
+
36
+ option :row_proc, reader: true, default: proc { |obj| obj.class.row_proc }
37
+ end
38
+ end
39
+
40
+ # Iterate over data using row_proc
41
+ #
42
+ # @return [Enumerator] if block is not given
43
+ #
44
+ # @api private
45
+ def each
46
+ return to_enum unless block_given?
47
+ data.each { |tuple| yield(row_proc[tuple]) }
48
+ end
49
+
50
+ module ClassMethods
51
+ # Default no-op tuple proc
52
+ #
53
+ # @return [Proc]
54
+ #
55
+ # @api private
56
+ def row_proc
57
+ -> tuple { tuple }
58
+ end
59
+
60
+ # Forward provided methods to the underlaying data object
61
+ #
62
+ # @example
63
+ #
64
+ # class MyDataset
65
+ # include DataProxy
66
+ #
67
+ # forward(:find_all, :map)
68
+ # end
69
+ #
70
+ # @return [undefined]
71
+ #
72
+ # @api public
73
+ def forward(*methods)
74
+ # FIXME: we should probably raise if one of the non-forwardable methods
75
+ # was provided
76
+ (methods - NON_FORWARDABLE).each do |method_name|
77
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
78
+ def #{method_name}(*args, &block)
79
+ response = data.public_send(#{method_name.inspect}, *args, &block)
80
+
81
+ if response.equal?(data)
82
+ self
83
+ elsif response.is_a?(data.class)
84
+ self.class.new(response)
85
+ else
86
+ response
87
+ end
88
+ end
89
+ RUBY
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,68 @@
1
+ require 'rom/initializer'
2
+ require 'rom/data_proxy'
3
+
4
+ module ROM
5
+ # A helper module that adds data-proxy behavior to an enumerable object
6
+ #
7
+ # This module is intended to be used by gateways
8
+ #
9
+ # Class that includes this module can define `row_proc` class method which
10
+ # must return a proc-like object which will be used to process each element
11
+ # in the enumerable
12
+ #
13
+ # @example
14
+ # class MyDataset
15
+ # include ROM::EnumerableDataset
16
+ #
17
+ # def self.row_proc
18
+ # -> tuple { tuple.each_with_object({}) { |(k,v), h| h[k.to_sym] = v } }
19
+ # end
20
+ # end
21
+ #
22
+ # ds = MyDataset.new([{ 'name' => 'Jane' }, [:name])
23
+ # ds.to_a # => { :name => 'Jane' }
24
+ #
25
+ # @api public
26
+ module EnumerableDataset
27
+ extend DataProxy::ClassMethods
28
+ include Enumerable
29
+
30
+ # Coerce a dataset to an array
31
+ #
32
+ # @return [Array]
33
+ #
34
+ # @api public
35
+ alias_method :to_ary, :to_a
36
+
37
+ # Included hook which extends a class with DataProxy behavior
38
+ #
39
+ # This module can also be included into other modules so we apply the
40
+ # extension only for classes
41
+ #
42
+ # @api private
43
+ def self.included(klass)
44
+ return unless klass.is_a?(Class)
45
+
46
+ klass.class_eval do
47
+ extend Initializer
48
+ include DataProxy
49
+
50
+ param :data
51
+ end
52
+ end
53
+
54
+ forward :take
55
+
56
+ [
57
+ :chunk, :collect, :collect_concat, :drop_while, :find_all, :flat_map,
58
+ :grep, :map, :reject, :select, :sort, :sort_by, :take_while
59
+ ].each do |method|
60
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
61
+ def #{method}(*args, &block)
62
+ return to_enum unless block
63
+ self.class.new(super(*args, &block), options)
64
+ end
65
+ RUBY
66
+ end
67
+ end
68
+ end
data/lib/rom/gateway.rb CHANGED
@@ -1,9 +1,13 @@
1
+ require 'dry/core/class_attributes'
2
+
3
+ require 'rom/transaction'
4
+
1
5
  module ROM
2
6
  # Abstract gateway class
3
7
  #
4
8
  # @api public
5
9
  class Gateway
6
- extend ClassMacros
10
+ extend Dry::Core::ClassAttributes
7
11
 
8
12
  defines :adapter
9
13
 
@@ -88,11 +92,7 @@ module ROM
88
92
  ROM.adapters.fetch(type)
89
93
  }
90
94
 
91
- if adapter.const_defined?(:Gateway)
92
- adapter.const_get(:Gateway)
93
- else
94
- adapter.const_get(:Repository)
95
- end
95
+ adapter.const_get(:Gateway)
96
96
  end
97
97
 
98
98
  # Returns the adapter, defined for the class
@@ -150,5 +150,22 @@ module ROM
150
150
  def disconnect
151
151
  # noop
152
152
  end
153
+
154
+ # Runs a block inside a transaction. The underlying transaction engine
155
+ # is adapter-specific
156
+ #
157
+ # @param [Hash] Transaction options
158
+ # @return The result of yielding the block or +nil+ if
159
+ # the transaction was rolled back
160
+ #
161
+ # @api public
162
+ def transaction(opts = EMPTY_HASH, &block)
163
+ transaction_runner(opts).run(opts, &block)
164
+ end
165
+
166
+ # @api private
167
+ def transaction_runner(_)
168
+ Transaction::NoOp
169
+ end
153
170
  end
154
171
  end
@@ -1,5 +1,3 @@
1
- require 'rom/support/constants'
2
-
3
1
  module ROM
4
2
  module Global
5
3
  # plugin registration DSL
data/lib/rom/global.rb CHANGED
@@ -1,7 +1,5 @@
1
- # -*- coding: utf-8 -*-
2
1
  require 'rom/plugin_registry'
3
2
  require 'rom/global/plugin_dsl'
4
- require 'rom/support/deprecations'
5
3
 
6
4
  module ROM
7
5
  # Globally accessible public interface exposed via ROM module
@@ -0,0 +1,26 @@
1
+ require 'dry-initializer'
2
+
3
+ module ROM
4
+
5
+ # @api private
6
+ module Initializer
7
+
8
+ # @api private
9
+ def self.extended(base)
10
+ base.extend(Dry::Initializer::Mixin)
11
+ base.include(InstanceMethods)
12
+ end
13
+
14
+ # @api private
15
+ module InstanceMethods
16
+ # Instance options
17
+ #
18
+ # @return [Hash]
19
+ #
20
+ # @api public
21
+ def options
22
+ @__options__
23
+ end
24
+ end
25
+ end
26
+ end
@@ -69,6 +69,23 @@ module ROM
69
69
  complain "#{gateway_instance} must respond to dataset?"
70
70
  end
71
71
 
72
+ # Lint: Ensure +gateway_instance+ supports +transaction+ interface
73
+ #
74
+ # @api public
75
+ def lint_transaction_support
76
+ result = gateway_instance.transaction { 1 }
77
+
78
+ if result != 1
79
+ complain "#{gateway_instance} must return the result of a transaction block"
80
+ end
81
+
82
+ gateway_instance.transaction do |t|
83
+ t.rollback!
84
+
85
+ complain "#{gateway_instance} must interrupt a transaction on rollback"
86
+ end
87
+ end
88
+
72
89
  private
73
90
 
74
91
  # Setup gateway instance
@@ -1,4 +1,4 @@
1
- require 'rom/support/registry'
1
+ require 'rom/registry'
2
2
 
3
3
  module ROM
4
4
  # @private
@@ -17,7 +17,6 @@ module ROM
17
17
  def execute(tuples)
18
18
  Array([tuples]).flatten.map { |tuple|
19
19
  attributes = input[tuple]
20
- validator.call(attributes)
21
20
  relation.insert(attributes.to_h)
22
21
  attributes
23
22
  }.to_a
@@ -34,7 +33,6 @@ module ROM
34
33
  # @see ROM::Commands::Update#execute
35
34
  def execute(params)
36
35
  attributes = input[params]
37
- validator.call(attributes)
38
36
  relation.map { |tuple| tuple.update(attributes.to_h) }
39
37
  end
40
38
  end
@@ -1,5 +1,4 @@
1
- require 'rom/support/options' # FIXME: this shouldn't be required, fix needed in rom-support
2
- require 'rom/support/array_dataset'
1
+ require 'rom/array_dataset'
3
2
 
4
3
  module ROM
5
4
  module Memory
@@ -1,4 +1,5 @@
1
1
  require 'rom/memory/types'
2
+ require 'rom/memory/schema'
2
3
 
3
4
  module ROM
4
5
  module Memory
@@ -14,8 +15,20 @@ module ROM
14
15
  include Memory
15
16
 
16
17
  adapter :memory
18
+ schema_class Memory::Schema
17
19
 
18
- forward :take, :join, :project, :restrict, :order
20
+ forward :take, :join, :restrict, :order
21
+
22
+ # Project a relation with provided attribute names
23
+ #
24
+ # @param [*Array] names A list with attribute names
25
+ #
26
+ # @return [Memory::Relation]
27
+ #
28
+ # @api public
29
+ def project(*names)
30
+ schema.project(*names).(self)
31
+ end
19
32
 
20
33
  # Insert tuples into the relation
21
34
  #
@@ -0,0 +1,13 @@
1
+ require 'rom/schema'
2
+
3
+ module ROM
4
+ module Memory
5
+ class Schema < ROM::Schema
6
+ # @see Schema#call
7
+ # @api public
8
+ def call(relation)
9
+ relation.new(relation.dataset.project(*map(&:name)), schema: self)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- require 'rom/support/registry'
1
+ require 'rom/registry'
2
2
 
3
3
  module ROM
4
4
  # Stores all registered plugins
@@ -1,5 +1,5 @@
1
1
  require 'rom/configuration_dsl'
2
- require 'rom/support/deprecations'
2
+ require 'dry/core/deprecations'
3
3
 
4
4
  module ROM
5
5
  module ConfigurationPlugins
@@ -10,7 +10,11 @@ module ROM
10
10
 
11
11
  # @api private
12
12
  def self.apply(configuration, options = {})
13
- ROM::Deprecations.announce(:macros, "Calling `use(:macros)` is no longer necessary. Macros are enabled by default.")
13
+ Dry::Core::Deprecations.announce(
14
+ :macros,
15
+ "Calling `use(:macros)` is no longer necessary. Macros are enabled by default.",
16
+ tag: :rom
17
+ )
14
18
  end
15
19
  end
16
20
  end
@@ -1,3 +1,5 @@
1
+ require 'dry/core/inflector'
2
+
1
3
  module ROM
2
4
  module Plugins
3
5
  module Relation
@@ -11,7 +13,7 @@ module ROM
11
13
  # @api private
12
14
  def foreign_key(other = nil)
13
15
  if other
14
- if schema
16
+ if schema?
15
17
  rel_name = other.respond_to?(:to_sym) ?
16
18
  ROM::Relation::Name[other.to_sym] : other.base_name
17
19
 
@@ -24,7 +26,7 @@ module ROM
24
26
  relation.foreign_key
25
27
  end
26
28
  else
27
- :"#{Inflector.singularize(name.dataset)}_id"
29
+ :"#{Dry::Core::Inflector.singularize(name.dataset)}_id"
28
30
  end
29
31
  end
30
32
 
@@ -12,7 +12,11 @@ module ROM
12
12
  # @api private
13
13
  def self.included(klass)
14
14
  super
15
- klass.option :__registry__, type: RelationRegistry, default: EMPTY_REGISTRY, reader: true
15
+ return if klass.instance_methods.include?(:__registry__)
16
+ klass.option :__registry__,
17
+ default: proc { EMPTY_REGISTRY },
18
+ reader: true,
19
+ optional: true
16
20
  end
17
21
 
18
22
  # @api private
@@ -0,0 +1,50 @@
1
+ module ROM
2
+ # @api private
3
+ class Registry
4
+ include Enumerable
5
+ include Dry::Equalizer(:elements)
6
+
7
+ class ElementNotFoundError < KeyError
8
+ def initialize(key, name)
9
+ super("#{key.inspect} doesn't exist in #{name} registry")
10
+ end
11
+ end
12
+
13
+ attr_reader :elements, :name
14
+
15
+ def initialize(elements = {}, name = self.class.name)
16
+ @elements = elements
17
+ @name = name
18
+ end
19
+
20
+ def each(&block)
21
+ return to_enum unless block
22
+ elements.each { |element| yield(element) }
23
+ end
24
+
25
+ def key?(name)
26
+ !name.nil? && elements.key?(name.to_sym)
27
+ end
28
+
29
+ def fetch(key)
30
+ raise ArgumentError.new('key cannot be nil') if key.nil?
31
+
32
+ elements.fetch(key.to_sym) do
33
+ return yield if block_given?
34
+
35
+ raise ElementNotFoundError.new(key, name)
36
+ end
37
+ end
38
+ alias_method :[], :fetch
39
+
40
+ def respond_to_missing?(name, include_private = false)
41
+ elements.key?(name) || super
42
+ end
43
+
44
+ private
45
+
46
+ def method_missing(name, *)
47
+ elements.fetch(name) { super }
48
+ end
49
+ end
50
+ end