rom 5.3.2 → 6.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -47
  3. data/LICENSE +1 -1
  4. data/README.md +6 -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 +273 -36
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