rom-core 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +603 -0
  3. data/LICENSE +20 -0
  4. data/README.md +18 -0
  5. data/lib/rom-core.rb +1 -0
  6. data/lib/rom/array_dataset.rb +44 -0
  7. data/lib/rom/association_set.rb +16 -0
  8. data/lib/rom/associations/abstract.rb +135 -0
  9. data/lib/rom/associations/definitions.rb +5 -0
  10. data/lib/rom/associations/definitions/abstract.rb +116 -0
  11. data/lib/rom/associations/definitions/many_to_many.rb +24 -0
  12. data/lib/rom/associations/definitions/many_to_one.rb +11 -0
  13. data/lib/rom/associations/definitions/one_to_many.rb +11 -0
  14. data/lib/rom/associations/definitions/one_to_one.rb +11 -0
  15. data/lib/rom/associations/definitions/one_to_one_through.rb +11 -0
  16. data/lib/rom/associations/many_to_many.rb +81 -0
  17. data/lib/rom/associations/many_to_one.rb +37 -0
  18. data/lib/rom/associations/one_to_many.rb +37 -0
  19. data/lib/rom/associations/one_to_one.rb +8 -0
  20. data/lib/rom/associations/one_to_one_through.rb +8 -0
  21. data/lib/rom/associations/through_identifier.rb +39 -0
  22. data/lib/rom/auto_curry.rb +55 -0
  23. data/lib/rom/cache.rb +46 -0
  24. data/lib/rom/command.rb +488 -0
  25. data/lib/rom/command_compiler.rb +239 -0
  26. data/lib/rom/command_proxy.rb +24 -0
  27. data/lib/rom/command_registry.rb +141 -0
  28. data/lib/rom/commands.rb +3 -0
  29. data/lib/rom/commands/class_interface.rb +270 -0
  30. data/lib/rom/commands/composite.rb +53 -0
  31. data/lib/rom/commands/create.rb +13 -0
  32. data/lib/rom/commands/delete.rb +14 -0
  33. data/lib/rom/commands/graph.rb +88 -0
  34. data/lib/rom/commands/graph/class_interface.rb +62 -0
  35. data/lib/rom/commands/graph/input_evaluator.rb +62 -0
  36. data/lib/rom/commands/lazy.rb +99 -0
  37. data/lib/rom/commands/lazy/create.rb +23 -0
  38. data/lib/rom/commands/lazy/delete.rb +27 -0
  39. data/lib/rom/commands/lazy/update.rb +34 -0
  40. data/lib/rom/commands/result.rb +96 -0
  41. data/lib/rom/commands/update.rb +14 -0
  42. data/lib/rom/configuration.rb +114 -0
  43. data/lib/rom/configuration_dsl.rb +87 -0
  44. data/lib/rom/configuration_dsl/command.rb +41 -0
  45. data/lib/rom/configuration_dsl/command_dsl.rb +35 -0
  46. data/lib/rom/configuration_dsl/relation.rb +26 -0
  47. data/lib/rom/configuration_plugin.rb +17 -0
  48. data/lib/rom/constants.rb +64 -0
  49. data/lib/rom/container.rb +147 -0
  50. data/lib/rom/core.rb +46 -0
  51. data/lib/rom/create_container.rb +60 -0
  52. data/lib/rom/data_proxy.rb +94 -0
  53. data/lib/rom/enumerable_dataset.rb +68 -0
  54. data/lib/rom/environment.rb +70 -0
  55. data/lib/rom/gateway.rb +184 -0
  56. data/lib/rom/global.rb +58 -0
  57. data/lib/rom/global/plugin_dsl.rb +47 -0
  58. data/lib/rom/initializer.rb +64 -0
  59. data/lib/rom/lint/enumerable_dataset.rb +54 -0
  60. data/lib/rom/lint/gateway.rb +120 -0
  61. data/lib/rom/lint/linter.rb +78 -0
  62. data/lib/rom/lint/spec.rb +20 -0
  63. data/lib/rom/lint/test.rb +98 -0
  64. data/lib/rom/mapper_registry.rb +24 -0
  65. data/lib/rom/memory.rb +4 -0
  66. data/lib/rom/memory/associations.rb +4 -0
  67. data/lib/rom/memory/associations/many_to_many.rb +10 -0
  68. data/lib/rom/memory/associations/many_to_one.rb +10 -0
  69. data/lib/rom/memory/associations/one_to_many.rb +10 -0
  70. data/lib/rom/memory/associations/one_to_one.rb +10 -0
  71. data/lib/rom/memory/commands.rb +56 -0
  72. data/lib/rom/memory/dataset.rb +97 -0
  73. data/lib/rom/memory/gateway.rb +64 -0
  74. data/lib/rom/memory/relation.rb +62 -0
  75. data/lib/rom/memory/schema.rb +23 -0
  76. data/lib/rom/memory/storage.rb +59 -0
  77. data/lib/rom/memory/types.rb +9 -0
  78. data/lib/rom/pipeline.rb +105 -0
  79. data/lib/rom/plugin.rb +25 -0
  80. data/lib/rom/plugin_base.rb +45 -0
  81. data/lib/rom/plugin_registry.rb +197 -0
  82. data/lib/rom/plugins/command/schema.rb +37 -0
  83. data/lib/rom/plugins/configuration/configuration_dsl.rb +21 -0
  84. data/lib/rom/plugins/relation/instrumentation.rb +51 -0
  85. data/lib/rom/plugins/relation/registry_reader.rb +44 -0
  86. data/lib/rom/plugins/schema/timestamps.rb +58 -0
  87. data/lib/rom/registry.rb +71 -0
  88. data/lib/rom/relation.rb +548 -0
  89. data/lib/rom/relation/class_interface.rb +282 -0
  90. data/lib/rom/relation/commands.rb +23 -0
  91. data/lib/rom/relation/composite.rb +46 -0
  92. data/lib/rom/relation/curried.rb +103 -0
  93. data/lib/rom/relation/graph.rb +197 -0
  94. data/lib/rom/relation/loaded.rb +127 -0
  95. data/lib/rom/relation/materializable.rb +66 -0
  96. data/lib/rom/relation/name.rb +111 -0
  97. data/lib/rom/relation/view_dsl.rb +64 -0
  98. data/lib/rom/relation/wrap.rb +83 -0
  99. data/lib/rom/relation_registry.rb +10 -0
  100. data/lib/rom/schema.rb +437 -0
  101. data/lib/rom/schema/associations_dsl.rb +195 -0
  102. data/lib/rom/schema/attribute.rb +419 -0
  103. data/lib/rom/schema/dsl.rb +164 -0
  104. data/lib/rom/schema/inferrer.rb +66 -0
  105. data/lib/rom/schema_plugin.rb +27 -0
  106. data/lib/rom/setup.rb +68 -0
  107. data/lib/rom/setup/auto_registration.rb +74 -0
  108. data/lib/rom/setup/auto_registration_strategies/base.rb +16 -0
  109. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +63 -0
  110. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +20 -0
  111. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +18 -0
  112. data/lib/rom/setup/finalize.rb +103 -0
  113. data/lib/rom/setup/finalize/finalize_commands.rb +60 -0
  114. data/lib/rom/setup/finalize/finalize_mappers.rb +56 -0
  115. data/lib/rom/setup/finalize/finalize_relations.rb +135 -0
  116. data/lib/rom/support/configurable.rb +85 -0
  117. data/lib/rom/support/memoizable.rb +58 -0
  118. data/lib/rom/support/notifications.rb +103 -0
  119. data/lib/rom/transaction.rb +24 -0
  120. data/lib/rom/types.rb +26 -0
  121. data/lib/rom/version.rb +5 -0
  122. metadata +289 -0
@@ -0,0 +1,71 @@
1
+ require 'dry/equalizer'
2
+
3
+ require 'rom/initializer'
4
+ require 'rom/cache'
5
+ require 'rom/constants'
6
+
7
+ module ROM
8
+ # @api private
9
+ class Registry
10
+ extend Initializer
11
+
12
+ include Enumerable
13
+ include Dry::Equalizer(:elements)
14
+
15
+ param :elements
16
+
17
+ option :cache, reader: true, default: -> { Cache.new }
18
+
19
+ def self.new(*args)
20
+ case args.size
21
+ when 0
22
+ super({}, {})
23
+ when 1
24
+ super(*args, {})
25
+ else
26
+ super(*args)
27
+ end
28
+ end
29
+
30
+ def self.element_not_found_error
31
+ ElementNotFoundError
32
+ end
33
+
34
+ def map(&block)
35
+ new_elements = elements.each_with_object({}) do |(name, element), h|
36
+ h[name] = yield(element)
37
+ end
38
+ self.class.new(new_elements, options)
39
+ end
40
+
41
+ def each(&block)
42
+ return to_enum unless block
43
+ elements.each { |element| yield(element) }
44
+ end
45
+
46
+ def key?(name)
47
+ !name.nil? && elements.key?(name.to_sym)
48
+ end
49
+
50
+ def fetch(key)
51
+ raise ArgumentError.new('key cannot be nil') if key.nil?
52
+
53
+ elements.fetch(key.to_sym) do
54
+ return yield if block_given?
55
+
56
+ raise self.class.element_not_found_error.new(key, self)
57
+ end
58
+ end
59
+ alias_method :[], :fetch
60
+
61
+ def respond_to_missing?(name, include_private = false)
62
+ elements.key?(name) || super
63
+ end
64
+
65
+ private
66
+
67
+ def method_missing(name, *)
68
+ elements.fetch(name) { super }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,548 @@
1
+ require 'dry/core/class_attributes'
2
+
3
+ require 'rom/struct'
4
+ require 'rom/constants'
5
+ require 'rom/initializer'
6
+ require 'rom/support/memoizable'
7
+
8
+ require 'rom/relation/class_interface'
9
+
10
+ require 'rom/auto_curry'
11
+ require 'rom/pipeline'
12
+ require 'rom/mapper_registry'
13
+ require 'rom/command_registry'
14
+
15
+ require 'rom/relation/loaded'
16
+ require 'rom/relation/curried'
17
+ require 'rom/relation/composite'
18
+ require 'rom/relation/graph'
19
+ require 'rom/relation/wrap'
20
+ require 'rom/relation/materializable'
21
+ require 'rom/relation/commands'
22
+ require 'rom/association_set'
23
+
24
+ require 'rom/types'
25
+ require 'rom/schema'
26
+
27
+ module ROM
28
+ # Base relation class
29
+ #
30
+ # Relation is a proxy for the dataset object provided by the gateway. It
31
+ # can forward methods to the dataset, which is why the "native" interface of
32
+ # the underlying gateway is available in the relation. This interface,
33
+ # however, is considered private and should not be used outside of the
34
+ # relation instance.
35
+ #
36
+ # Individual adapters sets up their relation classes and provide different APIs
37
+ # depending on their persistence backend.
38
+ #
39
+ # Vanilla Relation class doesn't have APIs that are specific to ROM container setup.
40
+ # When adapter Relation class inherits from this class, these APIs are added automatically,
41
+ # so that they can be registered within a container.
42
+ #
43
+ # @see ROM::Relation::ClassInterface
44
+ #
45
+ # @api public
46
+ class Relation
47
+ # Default no-op output schema which is called in `Relation#each`
48
+ NOOP_OUTPUT_SCHEMA = -> tuple { tuple }.freeze
49
+
50
+ extend Initializer
51
+ extend ClassInterface
52
+
53
+ include Relation::Commands
54
+ include Memoizable
55
+
56
+ extend Dry::Core::ClassAttributes
57
+
58
+ defines :adapter, :gateway, :schema_opts, :schema_class,
59
+ :schema_attr_class, :schema_inferrer, :schema_dsl,
60
+ :wrap_class, :auto_map, :auto_struct, :struct_namespace
61
+
62
+ gateway :default
63
+
64
+ auto_map true
65
+ auto_struct false
66
+ struct_namespace ROM::Struct
67
+
68
+ schema_opts EMPTY_HASH
69
+ schema_dsl Schema::DSL
70
+ schema_attr_class Schema::Attribute
71
+ schema_class Schema
72
+ schema_inferrer Schema::DEFAULT_INFERRER
73
+
74
+ wrap_class Relation::Wrap
75
+
76
+ include Dry::Equalizer(:name, :dataset)
77
+ include Materializable
78
+ include Pipeline
79
+
80
+ # @!attribute [r] dataset
81
+ # @return [Object] dataset used by the relation provided by relation's gateway
82
+ # @api public
83
+ param :dataset
84
+
85
+ # @!attribute [r] schema
86
+ # @return [Schema] relation schema, defaults to class-level canonical
87
+ # schema (if it was defined) and sets an empty one as
88
+ # the fallback
89
+ # @api public
90
+ option :schema, default: -> { self.class.schema || self.class.default_schema }
91
+
92
+ # @!attribute [r] name
93
+ # @return [Object] The relation name
94
+ # @api public
95
+ option :name, default: -> { self.class.schema ? self.class.schema.name : self.class.default_name }
96
+
97
+ # @!attribute [r] input_schema
98
+ # @return [Object#[]] tuple processing function, uses schema or defaults to Hash[]
99
+ # @api private
100
+ option :input_schema, default: -> { schema.to_input_hash }
101
+
102
+ # @!attribute [r] output_schema
103
+ # @return [Object#[]] tuple processing function, uses schema or defaults to NOOP_OUTPUT_SCHEMA
104
+ # @api private
105
+ option :output_schema, default: -> {
106
+ schema.any?(&:read?) ? schema.to_output_hash : NOOP_OUTPUT_SCHEMA
107
+ }
108
+
109
+ # @!attribute [r] auto_map
110
+ # @return [TrueClass,FalseClass] Whether or not a relation and its compositions should be auto-mapped
111
+ # @api private
112
+ option :auto_map, reader: true, default: -> { self.class.auto_map }
113
+
114
+ # @!attribute [r] auto_struct
115
+ # @return [TrueClass,FalseClass] Whether or not tuples should be auto-mapped to structs
116
+ # @api private
117
+ option :auto_struct, reader: true, default: -> { self.class.auto_struct }
118
+
119
+ # @!attribute [r] struct_namespace
120
+ # @return [Module] Custom struct namespace
121
+ # @api private
122
+ option :struct_namespace, reader: false, default: -> { self.class.struct_namespace }
123
+
124
+ # @!attribute [r] mappers
125
+ # @return [MapperRegistry] an optional mapper registry (empty by default)
126
+ option :mappers, default: -> { MapperRegistry.new }
127
+
128
+ # @!attribute [r] commands
129
+ # @return [CommandRegistry] Command registry
130
+ # @api private
131
+ option :commands, default: -> { CommandRegistry.new({}, relation_name: name.relation) }
132
+
133
+ # @!attribute [r] meta
134
+ # @return [Hash] Meta data stored in a hash
135
+ # @api private
136
+ option :meta, reader: true, default: -> { EMPTY_HASH }
137
+
138
+ # Return schema attribute
139
+ #
140
+ # @example accessing canonical attribute
141
+ # users[:id]
142
+ # # => #<ROM::SQL::Attribute[Integer] primary_key=true name=:id source=ROM::Relation::Name(users)>
143
+ #
144
+ # @example accessing joined attribute
145
+ # tasks_with_users = tasks.join(users).select_append(tasks[:title])
146
+ # tasks_with_users[:title, :tasks]
147
+ # # => #<ROM::SQL::Attribute[String] primary_key=false name=:title source=ROM::Relation::Name(tasks)>
148
+ #
149
+ # @return [Schema::Attribute]
150
+ #
151
+ # @api public
152
+ def [](name)
153
+ schema[name]
154
+ end
155
+
156
+ # Yields relation tuples
157
+ #
158
+ # Every tuple is processed through Relation#output_schema, it's a no-op by default
159
+ #
160
+ # @yield [Hash]
161
+ #
162
+ # @return [Enumerator] if block is not provided
163
+ #
164
+ # @api public
165
+ def each(&block)
166
+ return to_enum unless block
167
+
168
+ if auto_struct?
169
+ mapper.(dataset.map { |tuple| output_schema[tuple] }).each { |struct| yield(struct) }
170
+ else
171
+ dataset.each { |tuple| yield(output_schema[tuple]) }
172
+ end
173
+ end
174
+
175
+ # Composes with other relations
176
+ #
177
+ # @param [Array<Relation>] others The other relation(s) to compose with
178
+ #
179
+ # @return [Relation::Graph]
180
+ #
181
+ # @api public
182
+ def graph(*others)
183
+ Graph.build(self, others)
184
+ end
185
+
186
+ # Combine with other relations
187
+ #
188
+ # @overload combine(*associations)
189
+ # Composes relations using configured associations
190
+
191
+ # @example
192
+ # users.combine(:tasks, :posts)
193
+ # @param *associations [Array<Symbol>] A list of association names
194
+ #
195
+ # @return [Relation]
196
+ #
197
+ # @api public
198
+ def combine(*args)
199
+ graph(*nodes(*args))
200
+ end
201
+
202
+ # @api private
203
+ def nodes(*args)
204
+ args.map do |arg|
205
+ case arg
206
+ when Symbol
207
+ node(arg)
208
+ when Hash
209
+ arg.reduce(self) { |r, (k, v)| r.node(k).combine(*v) }
210
+ when Array
211
+ arg.map { |opts| nodes(opts) }
212
+ end
213
+ end.flatten(0)
214
+ end
215
+
216
+ # @api public
217
+ def node(name)
218
+ assoc = associations[name]
219
+ other = assoc.node
220
+ other.eager_load(assoc)
221
+ end
222
+
223
+ # @api public
224
+ def eager_load(assoc)
225
+ relation = assoc.prepare(self)
226
+
227
+ if assoc.override?
228
+ relation.(assoc)
229
+ else
230
+ relation.preload_assoc(assoc)
231
+ end
232
+ end
233
+
234
+ # @api private
235
+ def preload_assoc(assoc, other)
236
+ assoc.preload(self, other)
237
+ end
238
+
239
+ # Wrap other relations
240
+ #
241
+ # @example
242
+ # tasks.wrap(:owner)
243
+ #
244
+ # @param [Hash] options
245
+ #
246
+ # @return [RelationProxy]
247
+ #
248
+ # @api public
249
+ def wrap(*names)
250
+ wrap_class.new(self, names.map { |n| associations[n].wrap })
251
+ end
252
+
253
+ # Loads relation
254
+ #
255
+ # @return [Relation::Loaded]
256
+ #
257
+ # @api public
258
+ def call
259
+ Loaded.new(self)
260
+ end
261
+
262
+ # Materializes a relation into an array
263
+ #
264
+ # @return [Array<Hash>]
265
+ #
266
+ # @api public
267
+ def to_a
268
+ to_enum.to_a
269
+ end
270
+
271
+ # Returns if this relation is curried
272
+ #
273
+ # @return [false]
274
+ #
275
+ # @api private
276
+ def curried?
277
+ false
278
+ end
279
+
280
+ # Returns if this relation is a graph
281
+ #
282
+ # @return [false]
283
+ #
284
+ # @api private
285
+ def graph?
286
+ false
287
+ end
288
+
289
+ # Return if this is a wrap relation
290
+ #
291
+ # @return [false]
292
+ #
293
+ # @api private
294
+ def wrap?
295
+ false
296
+ end
297
+
298
+ # Returns true if a relation has schema defined
299
+ #
300
+ # @return [TrueClass, FalseClass]
301
+ #
302
+ # @api private
303
+ def schema?
304
+ ! schema.empty?
305
+ end
306
+
307
+ # Return a new relation with provided dataset and additional options
308
+ #
309
+ # Use this method whenever you need to use dataset API to get a new dataset
310
+ # and you want to return a relation back. Typically relation API should be
311
+ # enough though. If you find yourself using this method, it might be worth
312
+ # to consider reporting an issue that some dataset functionality is not available
313
+ # through relation API.
314
+ #
315
+ # @example with a new dataset
316
+ # users.new(users.dataset.some_method)
317
+ #
318
+ # @example with a new dataset and options
319
+ # users.new(users.dataset.some_method, other: 'options')
320
+ #
321
+ # @param [Object] dataset
322
+ # @param [Hash] new_opts Additional options
323
+ #
324
+ # @api public
325
+ def new(dataset, new_opts = EMPTY_HASH)
326
+ if new_opts.empty?
327
+ opts = options
328
+ elsif new_opts.key?(:schema)
329
+ opts = options.reject { |k, _| k == :input_schema || k == :output_schema }.merge(new_opts)
330
+ else
331
+ opts = options.merge(new_opts)
332
+ end
333
+
334
+ self.class.new(dataset, opts)
335
+ end
336
+
337
+ # Returns a new instance with the same dataset but new options
338
+ #
339
+ # @example
340
+ # users.with(output_schema: -> tuple { .. })
341
+ #
342
+ # @param new_options [Hash]
343
+ #
344
+ # @return [Relation]
345
+ #
346
+ # @api private
347
+ def with(opts)
348
+ new_options =
349
+ if opts.key?(:meta)
350
+ opts.merge(meta: meta.merge(opts[:meta]))
351
+ else
352
+ opts
353
+ end
354
+
355
+ new(dataset, options.merge(new_options))
356
+ end
357
+
358
+ # Return schema's association set (empty by default)
359
+ #
360
+ # @return [AssociationSet] Schema's association set (empty by default)
361
+ #
362
+ # @api public
363
+ def associations
364
+ schema.associations
365
+ end
366
+
367
+ # Returns AST for the wrapped relation
368
+ #
369
+ # @return [Array]
370
+ #
371
+ # @api public
372
+ def to_ast
373
+ [:relation, [name.relation, attr_ast, meta_ast]]
374
+ end
375
+
376
+ # @api private
377
+ def attr_ast
378
+ schema.map { |t| t.to_read_ast }
379
+ end
380
+
381
+ # @api private
382
+ def meta_ast
383
+ meta = self.meta.merge(dataset: name.dataset, alias: name.aliaz, struct_namespace: options[:struct_namespace])
384
+ meta[:model] = false unless auto_struct? || meta[:model]
385
+ meta
386
+ end
387
+
388
+ # @api private
389
+ def auto_map?
390
+ (auto_map || auto_struct) && !meta[:combine_type]
391
+ end
392
+
393
+ # @api private
394
+ def auto_struct?
395
+ auto_struct && !meta[:combine_type]
396
+ end
397
+
398
+ # @api private
399
+ def mapper
400
+ mappers[to_ast]
401
+ end
402
+
403
+ # Maps the wrapped relation with other mappers available in the registry
404
+ #
405
+ # @overload map_with(model)
406
+ # Map tuples to the provided custom model class
407
+ #
408
+ # @example
409
+ # users.as(MyUserModel)
410
+ #
411
+ # @param [Class>] model Your custom model class
412
+ #
413
+ # @overload map_with(*mappers)
414
+ # Map tuples using registered mappers
415
+ #
416
+ # @example
417
+ # users.map_with(:my_mapper, :my_other_mapper)
418
+ #
419
+ # @param [Array<Symbol>] mappers A list of mapper identifiers
420
+ #
421
+ # @overload map_with(*mappers, auto_map: true)
422
+ # Map tuples using auto-mapping and custom registered mappers
423
+ #
424
+ # If `auto_map` is enabled, your mappers will be applied after performing
425
+ # default auto-mapping. This means that you can compose complex relations
426
+ # and have them auto-mapped, and use much simpler custom mappers to adjust
427
+ # resulting data according to your requirements.
428
+ #
429
+ # @example
430
+ # users.map_with(:my_mapper, :my_other_mapper, auto_map: true)
431
+ #
432
+ # @param [Array<Symbol>] mappers A list of mapper identifiers
433
+ #
434
+ # @return [RelationProxy] A new relation proxy with pipelined relation
435
+ #
436
+ # @api public
437
+ def map_with(*names, **opts)
438
+ super(*names).with(opts)
439
+ end
440
+
441
+ # Return a new relation that will map its tuples to instance of the provided class
442
+ #
443
+ # @example
444
+ # users.map_to(MyUserModel)
445
+ #
446
+ # @param [Class] klass Your custom model class
447
+ #
448
+ # @return [Relation::Composite]
449
+ #
450
+ # @api public
451
+ def map_to(klass, **opts)
452
+ with(opts.merge(meta: { model: klass }))
453
+ end
454
+
455
+ # Return a new relation with an aliased name
456
+ #
457
+ # @example
458
+ # users.as(:people)
459
+ #
460
+ # @param [Class] klass Your custom model class
461
+ #
462
+ # @return [Relation::Composite]
463
+ #
464
+ # @api public
465
+ def as(aliaz)
466
+ with(name: name.as(aliaz))
467
+ end
468
+
469
+ # @return [Symbol] The wrapped relation's adapter identifier ie :sql or :http
470
+ #
471
+ # @api private
472
+ def adapter
473
+ self.class.adapter
474
+ end
475
+
476
+ # Return name of the source gateway of this relation
477
+ #
478
+ # @return [Symbol]
479
+ #
480
+ # @api private
481
+ def gateway
482
+ self.class.gateway
483
+ end
484
+
485
+ # Return all registered relation schemas
486
+ #
487
+ # This holds all schemas defined via `view` DSL
488
+ #
489
+ # @return [Hash<Symbol=>Schema>]
490
+ #
491
+ # @api public
492
+ def schemas
493
+ self.class.schemas
494
+ end
495
+
496
+ # Return a foreign key name for the provided relation name
497
+ #
498
+ # @param [Name] name The relation name object
499
+ #
500
+ # @return [Symbol]
501
+ #
502
+ # @api private
503
+ def foreign_key(name)
504
+ attr = schema.foreign_key(name.dataset)
505
+
506
+ if attr
507
+ attr.name
508
+ else
509
+ :"#{Dry::Core::Inflector.singularize(name.dataset)}_id"
510
+ end
511
+ end
512
+
513
+ # Return a new relation configured with the provided struct namespace
514
+ #
515
+ # @param [Module] namespace
516
+ #
517
+ # @return [Relation]
518
+ #
519
+ # @api public
520
+ def struct_namespace(ns)
521
+ options[:struct_namespace] == ns ? self : with(struct_namespace: ns)
522
+ end
523
+
524
+ memoize :to_ast, :auto_map?, :auto_struct?, :foreign_key, :combine, :wrap, :node
525
+
526
+ # we do it here because we want to avoid previous methods to be auto_curried
527
+ # via method_added hook, which is what AutoCurry uses
528
+ extend AutoCurry
529
+
530
+ auto_curry :preload_assoc
531
+
532
+ private
533
+
534
+ # Hook used by `Pipeline` to get the class that should be used for composition
535
+ #
536
+ # @return [Class]
537
+ #
538
+ # @api private
539
+ def composite_class
540
+ Relation::Composite
541
+ end
542
+
543
+ # @api private
544
+ def wrap_class
545
+ self.class.wrap_class
546
+ end
547
+ end
548
+ end