rom 5.4.1 → 6.0.0.alpha1

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -65
  3. data/LICENSE +1 -1
  4. data/README.md +7 -6
  5. data/lib/rom/array_dataset.rb +46 -0
  6. data/lib/rom/associations/abstract.rb +217 -0
  7. data/lib/rom/associations/definitions/abstract.rb +150 -0
  8. data/lib/rom/associations/definitions/many_to_many.rb +29 -0
  9. data/lib/rom/associations/definitions/many_to_one.rb +14 -0
  10. data/lib/rom/associations/definitions/one_to_many.rb +14 -0
  11. data/lib/rom/associations/definitions/one_to_one.rb +14 -0
  12. data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
  13. data/lib/rom/associations/definitions.rb +7 -0
  14. data/lib/rom/associations/many_to_many.rb +128 -0
  15. data/lib/rom/associations/many_to_one.rb +65 -0
  16. data/lib/rom/associations/one_to_many.rb +65 -0
  17. data/lib/rom/associations/one_to_one.rb +13 -0
  18. data/lib/rom/associations/one_to_one_through.rb +13 -0
  19. data/lib/rom/associations/through_identifier.rb +41 -0
  20. data/lib/rom/attribute.rb +425 -0
  21. data/lib/rom/auto_curry.rb +70 -0
  22. data/lib/rom/cache.rb +87 -0
  23. data/lib/rom/changeset/associated.rb +110 -0
  24. data/lib/rom/changeset/create.rb +18 -0
  25. data/lib/rom/changeset/delete.rb +15 -0
  26. data/lib/rom/changeset/extensions/relation.rb +26 -0
  27. data/lib/rom/changeset/pipe.rb +81 -0
  28. data/lib/rom/changeset/pipe_registry.rb +27 -0
  29. data/lib/rom/changeset/stateful.rb +285 -0
  30. data/lib/rom/changeset/update.rb +81 -0
  31. data/lib/rom/changeset.rb +185 -0
  32. data/lib/rom/command.rb +351 -0
  33. data/lib/rom/command_compiler.rb +201 -0
  34. data/lib/rom/command_proxy.rb +36 -0
  35. data/lib/rom/commands/class_interface.rb +236 -0
  36. data/lib/rom/commands/composite.rb +55 -0
  37. data/lib/rom/commands/create.rb +15 -0
  38. data/lib/rom/commands/delete.rb +16 -0
  39. data/lib/rom/commands/graph/class_interface.rb +64 -0
  40. data/lib/rom/commands/graph/input_evaluator.rb +94 -0
  41. data/lib/rom/commands/graph.rb +88 -0
  42. data/lib/rom/commands/lazy/create.rb +35 -0
  43. data/lib/rom/commands/lazy/delete.rb +39 -0
  44. data/lib/rom/commands/lazy/update.rb +46 -0
  45. data/lib/rom/commands/lazy.rb +106 -0
  46. data/lib/rom/commands/update.rb +16 -0
  47. data/lib/rom/commands.rb +5 -0
  48. data/lib/rom/compat/auto_registration.rb +115 -0
  49. data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
  50. data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
  51. data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
  52. data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
  53. data/lib/rom/compat/command.rb +74 -0
  54. data/lib/rom/compat/components/dsl/schema.rb +130 -0
  55. data/lib/rom/compat/components.rb +91 -0
  56. data/lib/rom/compat/global.rb +17 -0
  57. data/lib/rom/compat/mapper.rb +22 -0
  58. data/lib/rom/compat/registries.rb +47 -0
  59. data/lib/rom/compat/relation.rb +40 -0
  60. data/lib/rom/compat/schema/dsl.rb +260 -0
  61. data/lib/rom/compat/setting_proxy.rb +44 -0
  62. data/lib/rom/compat/setup.rb +151 -0
  63. data/lib/rom/compat/transformer.rb +49 -0
  64. data/lib/rom/compat.rb +22 -0
  65. data/lib/rom/components/association.rb +26 -0
  66. data/lib/rom/components/command.rb +24 -0
  67. data/lib/rom/components/core.rb +148 -0
  68. data/lib/rom/components/dataset.rb +60 -0
  69. data/lib/rom/components/dsl/association.rb +47 -0
  70. data/lib/rom/components/dsl/command.rb +60 -0
  71. data/lib/rom/components/dsl/core.rb +126 -0
  72. data/lib/rom/components/dsl/dataset.rb +33 -0
  73. data/lib/rom/components/dsl/gateway.rb +14 -0
  74. data/lib/rom/components/dsl/mapper.rb +70 -0
  75. data/lib/rom/components/dsl/relation.rb +49 -0
  76. data/lib/rom/components/dsl/schema.rb +150 -0
  77. data/lib/rom/components/dsl/view.rb +82 -0
  78. data/lib/rom/components/dsl.rb +255 -0
  79. data/lib/rom/components/gateway.rb +50 -0
  80. data/lib/rom/components/mapper.rb +29 -0
  81. data/lib/rom/components/provider.rb +160 -0
  82. data/lib/rom/components/registry.rb +154 -0
  83. data/lib/rom/components/relation.rb +41 -0
  84. data/lib/rom/components/schema.rb +61 -0
  85. data/lib/rom/components/view.rb +55 -0
  86. data/lib/rom/components.rb +55 -0
  87. data/lib/rom/configuration_dsl.rb +4 -0
  88. data/lib/rom/constants.rb +135 -0
  89. data/lib/rom/container.rb +182 -0
  90. data/lib/rom/core.rb +125 -0
  91. data/lib/rom/data_proxy.rb +97 -0
  92. data/lib/rom/enumerable_dataset.rb +70 -0
  93. data/lib/rom/gateway.rb +232 -0
  94. data/lib/rom/global.rb +56 -0
  95. data/lib/rom/header/attribute.rb +190 -0
  96. data/lib/rom/header.rb +198 -0
  97. data/lib/rom/inferrer.rb +55 -0
  98. data/lib/rom/initializer.rb +80 -0
  99. data/lib/rom/lint/enumerable_dataset.rb +56 -0
  100. data/lib/rom/lint/gateway.rb +120 -0
  101. data/lib/rom/lint/linter.rb +79 -0
  102. data/lib/rom/lint/spec.rb +22 -0
  103. data/lib/rom/lint/test.rb +98 -0
  104. data/lib/rom/loader.rb +161 -0
  105. data/lib/rom/mapper/attribute_dsl.rb +480 -0
  106. data/lib/rom/mapper/dsl.rb +107 -0
  107. data/lib/rom/mapper/model_dsl.rb +61 -0
  108. data/lib/rom/mapper.rb +99 -0
  109. data/lib/rom/mapper_compiler.rb +84 -0
  110. data/lib/rom/memory/associations/many_to_many.rb +12 -0
  111. data/lib/rom/memory/associations/many_to_one.rb +12 -0
  112. data/lib/rom/memory/associations/one_to_many.rb +12 -0
  113. data/lib/rom/memory/associations/one_to_one.rb +12 -0
  114. data/lib/rom/memory/associations.rb +6 -0
  115. data/lib/rom/memory/commands.rb +60 -0
  116. data/lib/rom/memory/dataset.rb +127 -0
  117. data/lib/rom/memory/gateway.rb +66 -0
  118. data/lib/rom/memory/mapper_compiler.rb +10 -0
  119. data/lib/rom/memory/relation.rb +91 -0
  120. data/lib/rom/memory/schema.rb +32 -0
  121. data/lib/rom/memory/storage.rb +61 -0
  122. data/lib/rom/memory/types.rb +11 -0
  123. data/lib/rom/memory.rb +7 -0
  124. data/lib/rom/model_builder.rb +103 -0
  125. data/lib/rom/open_struct.rb +112 -0
  126. data/lib/rom/pipeline.rb +111 -0
  127. data/lib/rom/plugin.rb +130 -0
  128. data/lib/rom/plugins/class_methods.rb +37 -0
  129. data/lib/rom/plugins/command/schema.rb +45 -0
  130. data/lib/rom/plugins/command/timestamps.rb +149 -0
  131. data/lib/rom/plugins/dsl.rb +53 -0
  132. data/lib/rom/plugins/relation/changeset.rb +97 -0
  133. data/lib/rom/plugins/relation/instrumentation.rb +66 -0
  134. data/lib/rom/plugins/relation/registry_reader.rb +36 -0
  135. data/lib/rom/plugins/schema/timestamps.rb +59 -0
  136. data/lib/rom/plugins.rb +100 -0
  137. data/lib/rom/processor/composer.rb +37 -0
  138. data/lib/rom/processor/transformer.rb +415 -0
  139. data/lib/rom/processor.rb +30 -0
  140. data/lib/rom/registries/associations.rb +26 -0
  141. data/lib/rom/registries/commands.rb +11 -0
  142. data/lib/rom/registries/container.rb +12 -0
  143. data/lib/rom/registries/datasets.rb +21 -0
  144. data/lib/rom/registries/gateways.rb +8 -0
  145. data/lib/rom/registries/mappers.rb +21 -0
  146. data/lib/rom/registries/nestable.rb +32 -0
  147. data/lib/rom/registries/relations.rb +8 -0
  148. data/lib/rom/registries/root.rb +203 -0
  149. data/lib/rom/registries/schemas.rb +44 -0
  150. data/lib/rom/registries/views.rb +11 -0
  151. data/lib/rom/relation/class_interface.rb +61 -0
  152. data/lib/rom/relation/combined.rb +160 -0
  153. data/lib/rom/relation/commands.rb +65 -0
  154. data/lib/rom/relation/composite.rb +53 -0
  155. data/lib/rom/relation/curried.rb +129 -0
  156. data/lib/rom/relation/graph.rb +107 -0
  157. data/lib/rom/relation/loaded.rb +136 -0
  158. data/lib/rom/relation/materializable.rb +62 -0
  159. data/lib/rom/relation/name.rb +122 -0
  160. data/lib/rom/relation/wrap.rb +64 -0
  161. data/lib/rom/relation.rb +625 -0
  162. data/lib/rom/repository/class_interface.rb +162 -0
  163. data/lib/rom/repository/relation_reader.rb +48 -0
  164. data/lib/rom/repository/root.rb +75 -0
  165. data/lib/rom/repository/session.rb +60 -0
  166. data/lib/rom/repository.rb +179 -0
  167. data/lib/rom/schema/associations_dsl.rb +222 -0
  168. data/lib/rom/schema/inferrer.rb +106 -0
  169. data/lib/rom/schema.rb +471 -0
  170. data/lib/rom/settings.rb +141 -0
  171. data/lib/rom/setup.rb +297 -0
  172. data/lib/rom/struct.rb +99 -0
  173. data/lib/rom/struct_compiler.rb +114 -0
  174. data/lib/rom/support/configurable.rb +213 -0
  175. data/lib/rom/support/inflector.rb +31 -0
  176. data/lib/rom/support/memoizable.rb +61 -0
  177. data/lib/rom/support/notifications.rb +238 -0
  178. data/lib/rom/transaction.rb +26 -0
  179. data/lib/rom/transformer.rb +46 -0
  180. data/lib/rom/types.rb +74 -0
  181. data/lib/rom/version.rb +1 -1
  182. data/lib/rom-changeset.rb +4 -0
  183. data/lib/rom-core.rb +3 -0
  184. data/lib/rom-repository.rb +4 -0
  185. data/lib/rom.rb +3 -3
  186. metadata +302 -23
data/lib/rom/schema.rb ADDED
@@ -0,0 +1,471 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+
5
+ require "rom/components/provider"
6
+ require "rom/constants"
7
+ require "rom/attribute"
8
+ require "rom/schema/inferrer"
9
+ require "rom/support/notifications"
10
+ require "rom/support/memoizable"
11
+
12
+ module ROM
13
+ # Relation schema
14
+ #
15
+ # Schemas hold detailed information about relation tuples, including their
16
+ # primitive types (String, Integer, Hash, etc. or custom classes), as well as
17
+ # various meta information like primary/foreign key and literally any other
18
+ # information that a given database adapter may need.
19
+ #
20
+ # Adapters can extend this class and it can be used in adapter-specific relations.
21
+ # In example rom-sql extends schema with Association DSL and many additional
22
+ # SQL-specific APIs in schema types.
23
+ #
24
+ # Schemas are used for projecting canonical relations into other relations and
25
+ # every relation object maintains its schema. This means that we always have
26
+ # all information about relation tuples, even when a relation was projected and
27
+ # diverged from its canonical form.
28
+ #
29
+ # Furthermore schema attributes know their source relations, which makes it
30
+ # possible to merge schemas from multiple relations and maintain information
31
+ # about the source relations. In example when two relations are joined, their
32
+ # schemas are merged, and we know which attributes belong to which relation.
33
+ #
34
+ # @api public
35
+ class Schema
36
+ extend ROM::Provider(:association, type: :schema)
37
+
38
+ include Memoizable
39
+
40
+ EMPTY_ASSOCIATION_SET = EMPTY_HASH.freeze
41
+
42
+ DEFAULT_INFERRER = Inferrer.new(enabled: false).freeze
43
+
44
+ type_transformation = lambda do |key|
45
+ k = if key.default?
46
+ key.constructor { |value| value.nil? ? Undefined : value }
47
+ else
48
+ key
49
+ end
50
+ k.required(false)
51
+ end
52
+
53
+ HASH_SCHEMA = Types::Coercible::Hash
54
+ .schema(EMPTY_HASH)
55
+ .with_type_transform(type_transformation)
56
+
57
+ extend Initializer
58
+
59
+ include Dry::Equalizer(:name, :attributes, :associations)
60
+ include Enumerable
61
+
62
+ configure(:component) do |config|
63
+ config.constant = self
64
+ config.attr_class = Attribute
65
+ config.inferrer = DEFAULT_INFERRER
66
+ end
67
+
68
+ # @!attribute [r] name
69
+ # @return [Symbol] The name of this schema
70
+ param :name
71
+
72
+ # @!attribute [r] registry
73
+ # @return [registry] Registry::Root with runtime dependency resolving
74
+ option :registry, default: -> { self.class.registry }
75
+
76
+ # @!attribute [r] relations
77
+ # @return [registry] Setup relation registry
78
+ option :relations, default: -> { registry.relations }
79
+
80
+ # @!attribute [r] attributes
81
+ # @return [Array] Array with schema attributes
82
+ option :attributes, default: -> { EMPTY_ARRAY }
83
+
84
+ # @!attribute [r] associations
85
+ # @return [Setup::Registry::Root] Optional association set (this is adapter-specific)
86
+ option :associations, default: -> { EMPTY_ASSOCIATION_SET }
87
+
88
+ # @!attribute [r] inferrer
89
+ # @return [#call] An optional inferrer object used in `finalize!`
90
+ option :inferrer, default: -> { DEFAULT_INFERRER }
91
+
92
+ # @!attribute [r] canonical
93
+ # @return [Symbol] The canonical schema which is carried in all schema instances
94
+ option :canonical, default: -> { self }
95
+
96
+ # @api private
97
+ option :attr_class, default: -> { Attribute }
98
+
99
+ # @!attribute [r] primary_key_name
100
+ # @return [Symbol] The name of the primary key. This is set because in
101
+ # most of the cases relations don't have composite pks
102
+ option :primary_key_name, optional: true
103
+
104
+ # @!attribute [r] primary_key_names
105
+ # @return [Array<Symbol>] A list of all pk names
106
+ option :primary_key_names, optional: true
107
+
108
+ alias_method :to_ary, :attributes
109
+
110
+ # Define a relation schema from plain rom types and optional options
111
+ #
112
+ # Resulting schema will decorate plain rom types with adapter-specific types
113
+ # By default `Attribute` will be used
114
+ #
115
+ # @param [Relation::Name, Symbol] name The schema name, typically ROM::Relation::Name
116
+ #
117
+ # @return [Schema]
118
+ #
119
+ # @api public
120
+ def self.define(name, attributes: EMPTY_ARRAY, attr_class: Attribute, **options)
121
+ new(
122
+ name,
123
+ attr_class: attr_class,
124
+ attributes: attributes(attributes, attr_class),
125
+ **options
126
+ ) { |schema| yield(schema) if block_given? }
127
+ end
128
+
129
+ # Builds a representation of the information needed to create an
130
+ # attribute.
131
+ #
132
+ # This representation is consumed by `Schema.define` in order to create
133
+ # the actual attributes.
134
+ #
135
+ # @return [Hash] A hash with `:type` and `:options` keys.
136
+ #
137
+ # @api private
138
+ def self.build_attribute_info(type, **options)
139
+ {
140
+ type: type,
141
+ options: options
142
+ }
143
+ end
144
+
145
+ # @api private
146
+ def self.attributes(attributes, attr_class)
147
+ attributes.map do |attr|
148
+ attr_class.new(attr[:type], **attr.fetch(:options))
149
+ end
150
+ end
151
+
152
+ # @api private
153
+ def initialize(*)
154
+ super
155
+
156
+ yield(self) if block_given?
157
+ end
158
+ ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
159
+
160
+ # Abstract method for creating a new relation based on schema definition
161
+ #
162
+ # This can be used by views to generate a new relation automatically.
163
+ # In example a schema can project a relation, join any additional relations
164
+ # if it includes attributes from other relations etc.
165
+ #
166
+ # Default implementation is a no-op and it simply returns back untouched relation
167
+ #
168
+ # @param [Relation] relation
169
+ #
170
+ # @return [Relation]
171
+ #
172
+ # @api public
173
+ def call(relation)
174
+ relation
175
+ end
176
+
177
+ # Iterate over schema's attributes
178
+ #
179
+ # @yield [Attribute]
180
+ #
181
+ # @api public
182
+ def each(&block)
183
+ attributes.each(&block)
184
+ end
185
+
186
+ # Check if schema has any attributes
187
+ #
188
+ # @return [TrueClass, FalseClass]
189
+ #
190
+ # @api public
191
+ def empty?
192
+ attributes.empty?
193
+ end
194
+
195
+ # Coerce schema into a <AttributeName=>Attribute> Hash
196
+ #
197
+ # @return [Hash]
198
+ #
199
+ # @api public
200
+ def to_h
201
+ each_with_object({}) { |attr, h| h[attr.name] = attr }
202
+ end
203
+
204
+ # Return attribute
205
+ #
206
+ # @param [Symbol] key The attribute name
207
+ # @param [Symbol, Relation::Name] src The source relation (for merged schemas)
208
+ #
209
+ # @raise KeyError
210
+ #
211
+ # @api public
212
+ def [](key, src = nil)
213
+ if count_index[key].zero?
214
+ raise(KeyError, "#{key.inspect} attribute doesn't exist in #{src} schema")
215
+ elsif count_index[key] > 1 && src.nil?
216
+ raise(KeyError, "#{key.inspect} attribute is not unique") if count_index[key] > 1
217
+ elsif src
218
+ source_index[src][key]
219
+ else
220
+ name_index[key]
221
+ end
222
+ end
223
+
224
+ # Project a schema to include only specified attributes
225
+ #
226
+ # @param [*Array<Symbol, Attribute>] names Attribute names
227
+ #
228
+ # @return [Schema]
229
+ #
230
+ # @api public
231
+ def project(*names)
232
+ new(names.map { |name| name.is_a?(Symbol) ? self[name] : name })
233
+ end
234
+
235
+ # Exclude provided attributes from a schema
236
+ #
237
+ # @param [*Array] names Attribute names
238
+ #
239
+ # @return [Schema]
240
+ #
241
+ # @api public
242
+ def exclude(*names)
243
+ project(*(map(&:name) - names))
244
+ end
245
+
246
+ # Project a schema with renamed attributes
247
+ #
248
+ # @param [Hash] mapping The attribute mappings
249
+ #
250
+ # @return [Schema]
251
+ #
252
+ # @api public
253
+ def rename(mapping)
254
+ new_attributes = map do |attr|
255
+ alias_name = mapping[attr.name]
256
+ alias_name ? attr.aliased(alias_name) : attr
257
+ end
258
+
259
+ new(new_attributes)
260
+ end
261
+
262
+ # Project a schema with renamed attributes using provided prefix
263
+ #
264
+ # @param [Symbol] prefix The name of the prefix
265
+ #
266
+ # @return [Schema]
267
+ #
268
+ # @api public
269
+ def prefix(prefix)
270
+ new(map { |attr| attr.prefixed(prefix) })
271
+ end
272
+
273
+ # Return new schema with all attributes marked as prefixed and wrapped
274
+ #
275
+ # This is useful when relations are joined and the right side should be marked
276
+ # as wrapped
277
+ #
278
+ # @param [Symbol] prefix The prefix used for aliasing wrapped attributes
279
+ #
280
+ # @return [Schema]
281
+ #
282
+ # @api public
283
+ def wrap(prefix = name.dataset)
284
+ new(map { |attr| attr.wrapped? ? attr : attr.wrapped(prefix) })
285
+ end
286
+
287
+ # Return FK attribute for a given relation name
288
+ #
289
+ # @return [Attribute]
290
+ #
291
+ # @api public
292
+ def foreign_key(relation)
293
+ detect { |attr| attr.foreign_key? && attr.target == relation }
294
+ end
295
+
296
+ # Return primary key attributes
297
+ #
298
+ # @return [Array<Attribute>]
299
+ #
300
+ # @api public
301
+ def primary_key
302
+ select(&:primary_key?)
303
+ end
304
+
305
+ # Merge with another schema
306
+ #
307
+ # @param [Schema] other Other schema
308
+ #
309
+ # @return [Schema]
310
+ #
311
+ # @api public
312
+ def merge(other)
313
+ append(*other)
314
+ end
315
+ alias_method :+, :merge
316
+
317
+ # Append more attributes to the schema
318
+ #
319
+ # This returns a new schema instance
320
+ #
321
+ # @param [Array<Attribute>] new_attributes
322
+ #
323
+ # @return [Schema]
324
+ #
325
+ # @api public
326
+ def append(*new_attributes)
327
+ new(attributes + new_attributes)
328
+ end
329
+
330
+ # Return a new schema with uniq attributes
331
+ #
332
+ # @return [Schema]
333
+ #
334
+ # @api public
335
+ def uniq(&block)
336
+ if block
337
+ new(attributes.uniq(&block))
338
+ else
339
+ new(attributes.uniq(&:name))
340
+ end
341
+ end
342
+
343
+ # Return if a schema includes an attribute with the given name
344
+ #
345
+ # @param [Symbol] name The name of the attribute
346
+ #
347
+ # @return [Boolean]
348
+ #
349
+ # @api public
350
+ def key?(name)
351
+ !attributes.detect { |attr| attr.name == name }.nil?
352
+ end
353
+
354
+ # Return if a schema is canonical
355
+ #
356
+ # @return [Boolean]
357
+ #
358
+ # @api public
359
+ def canonical?
360
+ equal?(canonical)
361
+ end
362
+
363
+ # Finalize a schema
364
+ #
365
+ # @return [self]
366
+ #
367
+ # @api private
368
+ def finalize!(*)
369
+ return self if frozen?
370
+
371
+ freeze
372
+ end
373
+
374
+ # This hook is called when relation is being build during container finalization
375
+ #
376
+ # When block is provided it'll be called just before freezing the instance
377
+ # so that additional ivars can be set
378
+ #
379
+ # @return [self]
380
+ #
381
+ # @api private
382
+ def finalize_attributes!(gateway: nil)
383
+ inferrer.(self, gateway).each { |key, value| set!(key, value) }
384
+
385
+ yield if block_given?
386
+
387
+ initialize_primary_key_names
388
+
389
+ self
390
+ end
391
+
392
+ # Return coercion function using attribute read types
393
+ #
394
+ # This is used for `output_schema` in relations
395
+ #
396
+ # @return [Dry::Types::Hash]
397
+ #
398
+ # @api private
399
+ def to_output_hash
400
+ HASH_SCHEMA.schema(
401
+ map { |attr| [attr.key, attr.to_read_type] }.to_h
402
+ )
403
+ end
404
+
405
+ # Return coercion function using attribute types
406
+ #
407
+ # This is used for `input_schema` in relations, typically commands use it
408
+ # for processing input
409
+ #
410
+ # @return [Dry::Types::Hash]
411
+ #
412
+ # @api private
413
+ def to_input_hash
414
+ HASH_SCHEMA.schema(
415
+ map { |attr| [attr.name, attr.to_write_type] }.to_h
416
+ )
417
+ end
418
+
419
+ # Return AST for the schema
420
+ #
421
+ # @return [Array]
422
+ #
423
+ # @api public
424
+ def to_ast
425
+ [:schema, [name, attributes.map(&:to_ast)]]
426
+ end
427
+
428
+ # @api private
429
+ def set!(key, value)
430
+ instance_variable_set("@#{key}", value)
431
+ options[key] = value
432
+ end
433
+
434
+ private
435
+
436
+ # @api private
437
+ def count_index
438
+ reduce(Hash.new(0)) do |index, attr|
439
+ index.merge(attr.name => index[attr.name] + 1)
440
+ end
441
+ end
442
+
443
+ # @api private
444
+ def name_index
445
+ map { |attr| [attr.name, attr] }.to_h
446
+ end
447
+
448
+ # @api private
449
+ def source_index
450
+ select(&:source)
451
+ .group_by(&:source)
452
+ .map { |src, grp| [src.to_sym, grp.map { |attr| [attr.name, attr] }.to_h] }
453
+ .to_h
454
+ end
455
+
456
+ # @api private
457
+ def new(attributes)
458
+ self.class.new(name, **options, attributes: attributes)
459
+ end
460
+
461
+ # @api private
462
+ def initialize_primary_key_names
463
+ return if primary_key.empty?
464
+
465
+ set!(:primary_key_name, primary_key[0].name)
466
+ set!(:primary_key_names, primary_key.map(&:name))
467
+ end
468
+
469
+ memoize :count_index, :name_index, :source_index, :to_ast, :to_input_hash, :to_output_hash
470
+ end
471
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "support/inflector"
4
+ require_relative "support/configurable"
5
+
6
+ # rubocop:disable Metrics/ModuleLength
7
+ module ROM
8
+ extend Configurable
9
+
10
+ # Defaults for all component types
11
+ setting :component do
12
+ setting :type
13
+ setting :abstract, default: false
14
+ setting :adapter
15
+ setting :gateway, default: :default
16
+ setting :inflector, default: Inflector
17
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
18
+ end
19
+
20
+ # Gateway defaults
21
+ setting :gateway do
22
+ setting :type, default: :gateway
23
+ setting :abstract
24
+ setting :id, default: :default
25
+ setting :namespace, default: "gateways"
26
+ setting :adapter
27
+ setting :logger
28
+ setting :args, default: EMPTY_ARRAY, constructor: :dup.to_proc
29
+ setting :opts, default: EMPTY_HASH, constructor: :dup.to_proc
30
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
31
+ end
32
+
33
+ # Dataset defaults
34
+ setting :dataset do
35
+ setting :type, default: :dataset
36
+ setting :abstract
37
+ setting :id
38
+ setting :relation_id
39
+ setting :namespace, default: "datasets"
40
+ setting :adapter
41
+ setting :gateway
42
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
43
+ end
44
+
45
+ # Schema defaults
46
+ setting :schema do
47
+ setting :type, default: :schema
48
+ setting :id
49
+ setting :abstract
50
+ setting :namespace, default: "schemas", join: true
51
+ setting :dataset
52
+ setting :as # TODO: move to rom/compat
53
+ setting :relation # TODO: move to rom/compat
54
+ setting :adapter
55
+ setting :gateway
56
+ setting :view, default: false
57
+ setting :infer, default: false
58
+ setting :constant
59
+ setting :dsl_class # TODO: move to rom/compat
60
+ setting :attr_class
61
+ setting :inferrer
62
+ setting :attributes, default: EMPTY_ARRAY, constructor: :dup.to_proc
63
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
64
+ setting :options, default: EMPTY_HASH, constructor: :dup.to_proc
65
+ end
66
+
67
+ # Relation defaults
68
+ setting :relation do
69
+ setting :type, default: :relation
70
+ setting :abstract
71
+ setting :id, default: :anonymous
72
+ setting :infer_id_from_class, inherit: true
73
+ setting :namespace, default: "relations"
74
+ setting :dataset
75
+ setting :adapter
76
+ setting :inflector
77
+ setting :gateway
78
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
79
+ end
80
+
81
+ # Relation view defaults
82
+ setting :view do
83
+ setting :type, default: :view
84
+ setting :abstract
85
+ setting :id
86
+ setting :namespace, default: "views", join: true
87
+ setting :args, default: [].freeze
88
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
89
+ end
90
+
91
+ # Association defaults
92
+ setting :association do
93
+ setting :type, default: :association
94
+ setting :id
95
+ setting :namespace, default: "associations", join: true
96
+ setting :inflector
97
+ setting :adapter
98
+ setting :as
99
+ setting :name
100
+ setting :abstract
101
+ setting :relation
102
+ setting :source
103
+ setting :target
104
+ setting :through
105
+ setting :foreign_key
106
+ setting :result
107
+ setting :view
108
+ setting :override
109
+ setting :combine_keys, default: {}
110
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
111
+ end
112
+
113
+ # Command defaults
114
+ setting :command do
115
+ setting :type, default: :command
116
+ setting :abstract
117
+ setting :id
118
+ setting :namespace, default: "commands", join: true
119
+ setting :relation
120
+ setting :adapter
121
+ setting :gateway
122
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
123
+ end
124
+
125
+ # Command defaults
126
+ setting :mapper do
127
+ setting :type, default: :mapper
128
+ setting :abstract
129
+ setting :id
130
+ setting :namespace, default: "mappers", join: true
131
+ setting :relation
132
+ setting :adapter
133
+ setting :plugins, default: EMPTY_ARRAY, inherit: true
134
+ end
135
+
136
+ # @api private
137
+ def self.settings
138
+ _settings
139
+ end
140
+ end
141
+ # rubocop:enable Metrics/ModuleLength