rom 2.0.2 → 3.0.0

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -7
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile +17 -2
  6. data/Rakefile +7 -2
  7. data/lib/rom/array_dataset.rb +44 -0
  8. data/lib/rom/association_set.rb +11 -5
  9. data/lib/rom/auto_curry.rb +55 -0
  10. data/lib/rom/command.rb +331 -47
  11. data/lib/rom/command_registry.rb +7 -18
  12. data/lib/rom/commands/class_interface.rb +120 -6
  13. data/lib/rom/commands/composite.rb +0 -1
  14. data/lib/rom/commands/graph.rb +7 -15
  15. data/lib/rom/commands/lazy/update.rb +1 -1
  16. data/lib/rom/configuration.rb +2 -0
  17. data/lib/rom/configuration_dsl/command.rb +6 -8
  18. data/lib/rom/configuration_dsl/mapper.rb +2 -3
  19. data/lib/rom/configuration_dsl/mapper_dsl.rb +0 -1
  20. data/lib/rom/configuration_dsl/relation.rb +4 -4
  21. data/lib/rom/configuration_dsl.rb +0 -4
  22. data/lib/rom/constants.rb +7 -1
  23. data/lib/rom/container.rb +11 -17
  24. data/lib/rom/create_container.rb +0 -2
  25. data/lib/rom/data_proxy.rb +94 -0
  26. data/lib/rom/enumerable_dataset.rb +68 -0
  27. data/lib/rom/gateway.rb +74 -32
  28. data/lib/rom/global/plugin_dsl.rb +0 -2
  29. data/lib/rom/global.rb +0 -2
  30. data/lib/rom/initializer.rb +26 -0
  31. data/lib/rom/lint/gateway.rb +17 -0
  32. data/lib/rom/mapper_registry.rb +1 -1
  33. data/lib/rom/memory/commands.rb +0 -2
  34. data/lib/rom/memory/dataset.rb +1 -2
  35. data/lib/rom/memory/relation.rb +14 -1
  36. data/lib/rom/memory/schema.rb +13 -0
  37. data/lib/rom/plugin_registry.rb +1 -1
  38. data/lib/rom/plugins/command/schema.rb +2 -2
  39. data/lib/rom/plugins/configuration/configuration_dsl.rb +6 -2
  40. data/lib/rom/plugins/relation/key_inference.rb +4 -2
  41. data/lib/rom/plugins/relation/registry_reader.rb +5 -1
  42. data/lib/rom/registry.rb +50 -0
  43. data/lib/rom/relation/class_interface.rb +142 -30
  44. data/lib/rom/relation/curried.rb +15 -15
  45. data/lib/rom/relation/view_dsl.rb +31 -0
  46. data/lib/rom/relation.rb +101 -41
  47. data/lib/rom/schema/attribute.rb +367 -0
  48. data/lib/rom/schema/dsl.rb +14 -10
  49. data/lib/rom/schema.rb +337 -19
  50. data/lib/rom/setup/auto_registration.rb +20 -17
  51. data/lib/rom/setup/auto_registration_strategies/base.rb +8 -3
  52. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +4 -3
  53. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +5 -4
  54. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +3 -3
  55. data/lib/rom/setup/finalize/finalize_commands.rb +1 -1
  56. data/lib/rom/setup/finalize/finalize_mappers.rb +1 -1
  57. data/lib/rom/setup/finalize/finalize_relations.rb +4 -2
  58. data/lib/rom/setup/finalize.rb +1 -1
  59. data/lib/rom/transaction.rb +24 -0
  60. data/lib/rom/types.rb +9 -1
  61. data/lib/rom/version.rb +1 -1
  62. data/lib/rom.rb +4 -8
  63. data/rom.gemspec +5 -4
  64. data/spec/integration/command_registry_spec.rb +1 -14
  65. data/spec/integration/commands/create_spec.rb +5 -25
  66. data/spec/integration/commands/delete_spec.rb +1 -1
  67. data/spec/integration/commands/error_handling_spec.rb +1 -1
  68. data/spec/integration/commands/graph_spec.rb +20 -14
  69. data/spec/integration/commands/update_spec.rb +4 -27
  70. data/spec/integration/commands_spec.rb +1 -1
  71. data/spec/integration/{repositories → gateways}/extending_relations_spec.rb +1 -1
  72. data/spec/integration/{repositories → gateways}/setting_logger_spec.rb +2 -2
  73. data/spec/integration/mappers/combine_spec.rb +1 -1
  74. data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
  75. data/spec/integration/mappers/definition_dsl_spec.rb +1 -1
  76. data/spec/integration/mappers/embedded_spec.rb +1 -1
  77. data/spec/integration/mappers/exclude_spec.rb +1 -1
  78. data/spec/integration/mappers/fold_spec.rb +1 -1
  79. data/spec/integration/mappers/group_spec.rb +1 -1
  80. data/spec/integration/mappers/overwrite_attributes_value_spec.rb +1 -1
  81. data/spec/integration/mappers/prefix_separator_spec.rb +1 -1
  82. data/spec/integration/mappers/prefix_spec.rb +1 -1
  83. data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
  84. data/spec/integration/mappers/registering_custom_mappers_spec.rb +1 -1
  85. data/spec/integration/mappers/renaming_attributes_spec.rb +1 -1
  86. data/spec/integration/mappers/reusing_mappers_spec.rb +1 -1
  87. data/spec/integration/mappers/step_spec.rb +1 -1
  88. data/spec/integration/mappers/symbolizing_attributes_spec.rb +1 -1
  89. data/spec/integration/mappers/unfold_spec.rb +1 -1
  90. data/spec/integration/mappers/ungroup_spec.rb +2 -2
  91. data/spec/integration/mappers/unwrap_spec.rb +2 -2
  92. data/spec/integration/mappers/wrap_spec.rb +1 -1
  93. data/spec/integration/memory/commands/create_spec.rb +1 -1
  94. data/spec/integration/memory/commands/delete_spec.rb +1 -1
  95. data/spec/integration/memory/commands/update_spec.rb +1 -1
  96. data/spec/integration/multi_env_spec.rb +1 -1
  97. data/spec/integration/multi_repo_spec.rb +1 -1
  98. data/spec/integration/relations/default_dataset_spec.rb +1 -1
  99. data/spec/integration/relations/reading_spec.rb +1 -1
  100. data/spec/integration/relations/registry_dsl_spec.rb +1 -1
  101. data/spec/integration/setup_spec.rb +10 -4
  102. data/spec/shared/command_graph.rb +8 -4
  103. data/spec/shared/enumerable_dataset.rb +1 -1
  104. data/spec/spec_helper.rb +7 -9
  105. data/spec/support/schema.rb +14 -0
  106. data/spec/unit/rom/array_dataset_spec.rb +59 -0
  107. data/spec/unit/rom/association_set_spec.rb +4 -0
  108. data/spec/unit/rom/auto_curry_spec.rb +63 -0
  109. data/spec/unit/rom/commands/graph_spec.rb +12 -11
  110. data/spec/unit/rom/commands/lazy_spec.rb +8 -5
  111. data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +336 -0
  112. data/spec/unit/rom/commands/result_spec.rb +1 -1
  113. data/spec/unit/rom/commands_spec.rb +26 -3
  114. data/spec/unit/rom/configuration_spec.rb +1 -1
  115. data/spec/unit/rom/container_spec.rb +15 -5
  116. data/spec/unit/rom/create_container_spec.rb +1 -1
  117. data/spec/unit/rom/enumerable_dataset_spec.rb +15 -0
  118. data/spec/unit/rom/gateway_spec.rb +1 -1
  119. data/spec/unit/rom/mapper_registry_spec.rb +1 -1
  120. data/spec/unit/rom/memory/commands_spec.rb +1 -1
  121. data/spec/unit/rom/memory/dataset_spec.rb +1 -1
  122. data/spec/unit/rom/memory/{repository_spec.rb → gateway_spec.rb} +1 -1
  123. data/spec/unit/rom/memory/inheritance_spec.rb +32 -0
  124. data/spec/unit/rom/memory/relation_spec.rb +15 -3
  125. data/spec/unit/rom/memory/storage_spec.rb +1 -1
  126. data/spec/unit/rom/plugin_spec.rb +1 -1
  127. data/spec/unit/rom/plugins/command/schema_spec.rb +5 -5
  128. data/spec/unit/rom/plugins/relation/key_inference_spec.rb +1 -1
  129. data/spec/unit/rom/registry_spec.rb +86 -0
  130. data/spec/unit/rom/relation/attribute_reader_spec.rb +17 -0
  131. data/spec/unit/rom/relation/call_spec.rb +51 -0
  132. data/spec/unit/rom/relation/composite_spec.rb +1 -1
  133. data/spec/unit/rom/relation/graph_spec.rb +1 -1
  134. data/spec/unit/rom/relation/lazy/combine_spec.rb +1 -1
  135. data/spec/unit/rom/relation/lazy_spec.rb +1 -1
  136. data/spec/unit/rom/relation/loaded_spec.rb +1 -1
  137. data/spec/unit/rom/relation/schema_spec.rb +50 -6
  138. data/spec/unit/rom/relation/view_spec.rb +122 -0
  139. data/spec/unit/rom/relation_spec.rb +20 -5
  140. data/spec/unit/rom/schema/accessing_attributes_spec.rb +52 -0
  141. data/spec/unit/rom/schema/append_spec.rb +17 -0
  142. data/spec/unit/rom/schema/exclude_spec.rb +15 -0
  143. data/spec/unit/rom/schema/finalize_spec.rb +59 -0
  144. data/spec/unit/rom/schema/key_predicate_spec.rb +15 -0
  145. data/spec/unit/rom/schema/merge_spec.rb +17 -0
  146. data/spec/unit/rom/schema/prefix_spec.rb +16 -0
  147. data/spec/unit/rom/schema/project_spec.rb +15 -0
  148. data/spec/unit/rom/schema/rename_spec.rb +22 -0
  149. data/spec/unit/rom/schema/type_spec.rb +49 -0
  150. data/spec/unit/rom/schema/uniq_spec.rb +21 -0
  151. data/spec/unit/rom/schema/wrap_spec.rb +17 -0
  152. data/spec/unit/rom/schema_spec.rb +2 -2
  153. metadata +79 -17
  154. data/lib/rom/plugins/relation/view/dsl.rb +0 -32
  155. data/lib/rom/plugins/relation/view.rb +0 -95
  156. data/spec/unit/rom/plugins/relation/view_spec.rb +0 -51
@@ -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
@@ -20,9 +20,9 @@ module ROM
20
20
 
21
21
  input_handler =
22
22
  if default_input != Hash && relation.schema?
23
- -> tuple { relation.schema_hash[input[tuple]] }
23
+ -> tuple { relation.input_schema[input[tuple]] }
24
24
  elsif relation.schema?
25
- relation.schema_hash
25
+ relation.input_schema
26
26
  else
27
27
  default_input
28
28
  end
@@ -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
@@ -1,13 +1,21 @@
1
1
  require 'set'
2
2
 
3
- require 'rom/support/auto_curry'
3
+ require 'dry/core/inflector'
4
+ require 'dry/core/constants'
5
+ require 'dry/core/class_attributes'
6
+
7
+ require 'rom/auto_curry'
4
8
  require 'rom/relation/curried'
5
9
  require 'rom/relation/name'
10
+ require 'rom/relation/view_dsl'
6
11
  require 'rom/schema'
7
12
 
8
13
  module ROM
9
14
  class Relation
15
+ # @api public
10
16
  module ClassInterface
17
+ include Dry::Core::Constants
18
+
11
19
  # Register adapter relation subclasses during setup phase
12
20
  #
13
21
  # In adition those subclasses are extended with an interface for accessing
@@ -17,14 +25,18 @@ module ROM
17
25
  def inherited(klass)
18
26
  super
19
27
 
20
- klass.extend ClassMacros
21
- klass.defines :adapter
22
-
23
28
  if respond_to?(:adapter) && adapter.nil?
24
29
  raise MissingAdapterIdentifierError,
25
- "relation class +#{self}+ is missing the adapter identifier"
30
+ "relation class +#{self}+ is missing the adapter identifier"
26
31
  end
27
32
 
33
+ klass.extend Dry::Core::ClassAttributes
34
+ klass.defines :adapter, :schema_class, :schema_inferrer, :schema_dsl
35
+
36
+ klass.schema_dsl Schema::DSL
37
+ klass.schema_class Schema
38
+ klass.schema_inferrer Schema::DEFAULT_INFERRER
39
+
28
40
  # Extend with functionality required by adapters *only* if this is a direct
29
41
  # descendant of an adapter-specific relation subclass
30
42
  return unless respond_to?(:adapter) && klass.superclass == ROM::Relation[adapter]
@@ -32,22 +44,26 @@ module ROM
32
44
  klass.class_eval do
33
45
  use :registry_reader
34
46
 
35
- defines :gateway, :dataset, :dataset_proc, :register_as, :schema_dsl, :schema_inferrer
47
+ defines :gateway, :dataset, :dataset_proc, :register_as
36
48
 
37
49
  gateway :default
38
- schema_dsl Schema::DSL
39
- schema_inferrer nil
40
50
 
41
51
  dataset default_name
42
52
 
43
- # Relation's dataset name
44
- #
45
- # In example a table name in an SQL database
46
- #
47
- # @return [Symbol]
48
- #
49
- # @api public
50
- attr_reader :name
53
+ # skip defining :name option if it's a class that already has this method
54
+ # which can happen when an adapter's relation class inherits from another
55
+ # adapter's relation class (ie YAML::Relation < Memory::Relation)
56
+ unless instance_methods.include?(:name)
57
+ # Relation's dataset name
58
+ #
59
+ # In example a table name in an SQL database
60
+ #
61
+ # @return [Symbol]
62
+ #
63
+ # @api public
64
+ option :name, reader: true, optional: true,
65
+ default: -> r { Name.new(r.class.register_as, r.class.dataset) }
66
+ end
51
67
 
52
68
  # Set dataset name
53
69
  #
@@ -95,12 +111,6 @@ module ROM
95
111
  end
96
112
  end
97
113
 
98
- # @api private
99
- def initialize(dataset, options = EMPTY_HASH)
100
- @name = Name.new(self.class.register_as, self.class.dataset)
101
- super
102
- end
103
-
104
114
  # Return name of the source gateway of this relation
105
115
  #
106
116
  # @return [Symbol]
@@ -151,21 +161,108 @@ module ROM
151
161
  # @param [Boolean] infer Whether to do an automatic schema inferring
152
162
  #
153
163
  # @api public
154
- def schema(dataset = nil, infer: false, &block)
164
+ def schema(dataset = nil, as: nil, infer: false, &block)
155
165
  if defined?(@schema)
156
166
  @schema
157
167
  elsif block || infer
158
168
  self.dataset(dataset) if dataset
159
- self.register_as(self.dataset) unless register_as
169
+
170
+ if as
171
+ self.register_as(as)
172
+ else
173
+ self.register_as(self.dataset) unless register_as
174
+ end
160
175
 
161
176
  name = Name[register_as, self.dataset]
162
177
  inferrer = infer ? schema_inferrer : nil
163
- dsl = schema_dsl.new(name, inferrer, &block)
178
+
179
+ unless schema_class
180
+ raise MissingSchemaClassError.new(self)
181
+ end
182
+
183
+ dsl = schema_dsl.new(name, schema_class: schema_class, inferrer: inferrer, &block)
164
184
 
165
185
  @schema = dsl.call
166
186
  end
167
187
  end
168
188
 
189
+ # Define a relation view with a specific schema
190
+ #
191
+ # Explicit relation views allow relation composition with auto-mapping
192
+ # in repositories. It's useful for cases like defining custom views
193
+ # for associations where relations (even from different databases) can
194
+ # be composed together and automatically mapped in memory to structs.
195
+ #
196
+ # @overload view(name, schema, &block)
197
+ # @example View with the canonical schema
198
+ # class Users < ROM::Relation[:sql]
199
+ # view(:listing, schema) do
200
+ # order(:name)
201
+ # end
202
+ # end
203
+ #
204
+ # @example View with a projected schema
205
+ # class Users < ROM::Relation[:sql]
206
+ # view(:listing, schema.project(:id, :name)) do
207
+ # order(:name)
208
+ # end
209
+ # end
210
+ #
211
+ # @overload view(name, &block)
212
+ # @example View with the canonical schema and arguments
213
+ # class Users < ROM::Relation[:sql]
214
+ # view(:by_name) do |name|
215
+ # where(name: name)
216
+ # end
217
+ # end
218
+ #
219
+ # @example View with projected schema and arguments
220
+ # class Users < ROM::Relation[:sql]
221
+ # view(:by_name) do
222
+ # schema { project(:id, :name) }
223
+ # relation { |name| where(name: name) }
224
+ # end
225
+ # end
226
+ #
227
+ # @return [Symbol] view method name
228
+ #
229
+ # @api public
230
+ def view(*args, &block)
231
+ if args.size == 1 && block.arity > 0
232
+ raise ArgumentError, "header must be set as second argument"
233
+ end
234
+
235
+ name, new_schema_fn, relation_block =
236
+ if args.size == 1
237
+ ViewDSL.new(*args, schema, &block).call
238
+ else
239
+ [*args, block]
240
+ end
241
+
242
+ schemas[name] =
243
+ if args.size == 2
244
+ schema.project(*args[1])
245
+ else
246
+ new_schema_fn
247
+ end
248
+
249
+ if relation_block.arity > 0
250
+ auto_curry_guard do
251
+ define_method(name, &relation_block)
252
+
253
+ auto_curry(name) do
254
+ schemas[name].(self)
255
+ end
256
+ end
257
+ else
258
+ define_method(name) do
259
+ schemas[name].(instance_exec(&relation_block))
260
+ end
261
+ end
262
+
263
+ name
264
+ end
265
+
169
266
  # Dynamically define a method that will forward to the dataset and wrap
170
267
  # response in the relation itself
171
268
  #
@@ -179,7 +276,7 @@ module ROM
179
276
  methods.each do |method|
180
277
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
181
278
  def #{method}(*args, &block)
182
- __new__(dataset.__send__(:#{method}, *args, &block))
279
+ new(dataset.__send__(:#{method}, *args, &block))
183
280
  end
184
281
  RUBY
185
282
  end
@@ -203,7 +300,7 @@ module ROM
203
300
  # @api private
204
301
  def default_name
205
302
  return unless name
206
- Inflector.underscore(name).tr('/', '_').to_sym
303
+ Dry::Core::Inflector.underscore(name).tr('/', '_').to_sym
207
304
  end
208
305
 
209
306
  # @api private
@@ -214,16 +311,31 @@ module ROM
214
311
  # @api private
215
312
  def view_methods
216
313
  ancestor_methods = ancestors.reject { |klass| klass == self }
217
- .map(&:instance_methods).flatten
314
+ .map(&:instance_methods).flatten(1)
218
315
 
219
316
  instance_methods - ancestor_methods + auto_curried_methods
220
317
  end
221
318
 
319
+ # @api private
320
+ def schemas
321
+ @schemas ||= {}
322
+ end
323
+
324
+ # @api private
325
+ def default_schema(relation)
326
+ relation_class = relation.class
327
+ relation_class.schema || relation_class.schema_class.define(relation_class.default_name)
328
+ end
329
+
222
330
  # Hook to finalize a relation after its instance was created
223
331
  #
224
332
  # @api private
225
- def finalize(_container, _relation)
226
- # noop
333
+ def finalize(_container, relation)
334
+ schemas = relation.schemas.reduce({}) do |h, (a, e)|
335
+ h.update(a => e.is_a?(Proc) ? instance_exec(&e) : e)
336
+ end
337
+ relation.schemas.update(schemas)
338
+ relation
227
339
  end
228
340
  end
229
341
  end
@@ -1,5 +1,5 @@
1
- require 'rom/support/options'
2
-
1
+ require 'rom/types'
2
+ require 'rom/initializer'
3
3
  require 'rom/pipeline'
4
4
  require 'rom/relation/name'
5
5
  require 'rom/relation/materializable'
@@ -7,23 +7,23 @@ require 'rom/relation/materializable'
7
7
  module ROM
8
8
  class Relation
9
9
  class Curried
10
- include Options
10
+ extend Initializer
11
11
  include Materializable
12
12
  include Pipeline
13
13
 
14
- option :name, type: Symbol
15
- option :arity, type: Integer, reader: true, default: -1
16
- option :curry_args, type: Array, reader: true, default: EMPTY_ARRAY
17
-
18
- attr_reader :relation
14
+ param :relation
19
15
 
20
- attr_reader :name
16
+ option :name, optional: true, type: Types::Strict::Symbol
17
+ option :arity, type: Types::Strict::Int, reader: true, default: proc { -1 }
18
+ option :curry_args, reader: true, default: proc { EMPTY_ARRAY }
21
19
 
22
- # @api private
23
- def initialize(relation, options = EMPTY_HASH)
24
- @relation = relation
25
- @name = relation.name.with(options[:name])
26
- super
20
+ # Relation name
21
+ #
22
+ # @return [ROM::Relation::Name]
23
+ #
24
+ # @api public
25
+ def name
26
+ @name == Dry::Initializer::UNDEFINED ? relation.name : relation.name.with(@name)
27
27
  end
28
28
 
29
29
  # Load relation if args match the arity
@@ -75,7 +75,7 @@ module ROM
75
75
 
76
76
  # @api private
77
77
  def __new__(relation, new_opts = EMPTY_HASH)
78
- Curried.new(relation, options.merge(new_opts))
78
+ self.class.new(relation, new_opts.empty? ? options : options.merge(new_opts))
79
79
  end
80
80
 
81
81
  # @api private
@@ -0,0 +1,31 @@
1
+ module ROM
2
+ class Relation
3
+ class ViewDSL
4
+ attr_reader :name
5
+
6
+ attr_reader :relation_block
7
+
8
+ attr_reader :new_schema
9
+
10
+ def initialize(name, schema, &block)
11
+ @name = name
12
+ @schema = schema
13
+ @new_schema = nil
14
+ @relation_block = nil
15
+ instance_eval(&block)
16
+ end
17
+
18
+ def schema(&block)
19
+ @new_schema = -> { @schema.instance_exec(&block) }
20
+ end
21
+
22
+ def relation(&block)
23
+ @relation_block = lambda(&block)
24
+ end
25
+
26
+ def call
27
+ [name, new_schema, relation_block]
28
+ end
29
+ end
30
+ end
31
+ end