rom 2.0.2 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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