rom 2.0.2 → 3.0.0

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