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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -65
- data/LICENSE +1 -1
- data/README.md +7 -6
- data/lib/rom/array_dataset.rb +46 -0
- data/lib/rom/associations/abstract.rb +217 -0
- data/lib/rom/associations/definitions/abstract.rb +150 -0
- data/lib/rom/associations/definitions/many_to_many.rb +29 -0
- data/lib/rom/associations/definitions/many_to_one.rb +14 -0
- data/lib/rom/associations/definitions/one_to_many.rb +14 -0
- data/lib/rom/associations/definitions/one_to_one.rb +14 -0
- data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
- data/lib/rom/associations/definitions.rb +7 -0
- data/lib/rom/associations/many_to_many.rb +128 -0
- data/lib/rom/associations/many_to_one.rb +65 -0
- data/lib/rom/associations/one_to_many.rb +65 -0
- data/lib/rom/associations/one_to_one.rb +13 -0
- data/lib/rom/associations/one_to_one_through.rb +13 -0
- data/lib/rom/associations/through_identifier.rb +41 -0
- data/lib/rom/attribute.rb +425 -0
- data/lib/rom/auto_curry.rb +70 -0
- data/lib/rom/cache.rb +87 -0
- data/lib/rom/changeset/associated.rb +110 -0
- data/lib/rom/changeset/create.rb +18 -0
- data/lib/rom/changeset/delete.rb +15 -0
- data/lib/rom/changeset/extensions/relation.rb +26 -0
- data/lib/rom/changeset/pipe.rb +81 -0
- data/lib/rom/changeset/pipe_registry.rb +27 -0
- data/lib/rom/changeset/stateful.rb +285 -0
- data/lib/rom/changeset/update.rb +81 -0
- data/lib/rom/changeset.rb +185 -0
- data/lib/rom/command.rb +351 -0
- data/lib/rom/command_compiler.rb +201 -0
- data/lib/rom/command_proxy.rb +36 -0
- data/lib/rom/commands/class_interface.rb +236 -0
- data/lib/rom/commands/composite.rb +55 -0
- data/lib/rom/commands/create.rb +15 -0
- data/lib/rom/commands/delete.rb +16 -0
- data/lib/rom/commands/graph/class_interface.rb +64 -0
- data/lib/rom/commands/graph/input_evaluator.rb +94 -0
- data/lib/rom/commands/graph.rb +88 -0
- data/lib/rom/commands/lazy/create.rb +35 -0
- data/lib/rom/commands/lazy/delete.rb +39 -0
- data/lib/rom/commands/lazy/update.rb +46 -0
- data/lib/rom/commands/lazy.rb +106 -0
- data/lib/rom/commands/update.rb +16 -0
- data/lib/rom/commands.rb +5 -0
- data/lib/rom/compat/auto_registration.rb +115 -0
- data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
- data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
- data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
- data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
- data/lib/rom/compat/command.rb +74 -0
- data/lib/rom/compat/components/dsl/schema.rb +130 -0
- data/lib/rom/compat/components.rb +91 -0
- data/lib/rom/compat/global.rb +17 -0
- data/lib/rom/compat/mapper.rb +22 -0
- data/lib/rom/compat/registries.rb +47 -0
- data/lib/rom/compat/relation.rb +40 -0
- data/lib/rom/compat/schema/dsl.rb +260 -0
- data/lib/rom/compat/setting_proxy.rb +44 -0
- data/lib/rom/compat/setup.rb +151 -0
- data/lib/rom/compat/transformer.rb +49 -0
- data/lib/rom/compat.rb +22 -0
- data/lib/rom/components/association.rb +26 -0
- data/lib/rom/components/command.rb +24 -0
- data/lib/rom/components/core.rb +148 -0
- data/lib/rom/components/dataset.rb +60 -0
- data/lib/rom/components/dsl/association.rb +47 -0
- data/lib/rom/components/dsl/command.rb +60 -0
- data/lib/rom/components/dsl/core.rb +126 -0
- data/lib/rom/components/dsl/dataset.rb +33 -0
- data/lib/rom/components/dsl/gateway.rb +14 -0
- data/lib/rom/components/dsl/mapper.rb +70 -0
- data/lib/rom/components/dsl/relation.rb +49 -0
- data/lib/rom/components/dsl/schema.rb +150 -0
- data/lib/rom/components/dsl/view.rb +82 -0
- data/lib/rom/components/dsl.rb +255 -0
- data/lib/rom/components/gateway.rb +50 -0
- data/lib/rom/components/mapper.rb +29 -0
- data/lib/rom/components/provider.rb +160 -0
- data/lib/rom/components/registry.rb +154 -0
- data/lib/rom/components/relation.rb +41 -0
- data/lib/rom/components/schema.rb +61 -0
- data/lib/rom/components/view.rb +55 -0
- data/lib/rom/components.rb +55 -0
- data/lib/rom/configuration_dsl.rb +4 -0
- data/lib/rom/constants.rb +135 -0
- data/lib/rom/container.rb +182 -0
- data/lib/rom/core.rb +125 -0
- data/lib/rom/data_proxy.rb +97 -0
- data/lib/rom/enumerable_dataset.rb +70 -0
- data/lib/rom/gateway.rb +232 -0
- data/lib/rom/global.rb +56 -0
- data/lib/rom/header/attribute.rb +190 -0
- data/lib/rom/header.rb +198 -0
- data/lib/rom/inferrer.rb +55 -0
- data/lib/rom/initializer.rb +80 -0
- data/lib/rom/lint/enumerable_dataset.rb +56 -0
- data/lib/rom/lint/gateway.rb +120 -0
- data/lib/rom/lint/linter.rb +79 -0
- data/lib/rom/lint/spec.rb +22 -0
- data/lib/rom/lint/test.rb +98 -0
- data/lib/rom/loader.rb +161 -0
- data/lib/rom/mapper/attribute_dsl.rb +480 -0
- data/lib/rom/mapper/dsl.rb +107 -0
- data/lib/rom/mapper/model_dsl.rb +61 -0
- data/lib/rom/mapper.rb +99 -0
- data/lib/rom/mapper_compiler.rb +84 -0
- data/lib/rom/memory/associations/many_to_many.rb +12 -0
- data/lib/rom/memory/associations/many_to_one.rb +12 -0
- data/lib/rom/memory/associations/one_to_many.rb +12 -0
- data/lib/rom/memory/associations/one_to_one.rb +12 -0
- data/lib/rom/memory/associations.rb +6 -0
- data/lib/rom/memory/commands.rb +60 -0
- data/lib/rom/memory/dataset.rb +127 -0
- data/lib/rom/memory/gateway.rb +66 -0
- data/lib/rom/memory/mapper_compiler.rb +10 -0
- data/lib/rom/memory/relation.rb +91 -0
- data/lib/rom/memory/schema.rb +32 -0
- data/lib/rom/memory/storage.rb +61 -0
- data/lib/rom/memory/types.rb +11 -0
- data/lib/rom/memory.rb +7 -0
- data/lib/rom/model_builder.rb +103 -0
- data/lib/rom/open_struct.rb +112 -0
- data/lib/rom/pipeline.rb +111 -0
- data/lib/rom/plugin.rb +130 -0
- data/lib/rom/plugins/class_methods.rb +37 -0
- data/lib/rom/plugins/command/schema.rb +45 -0
- data/lib/rom/plugins/command/timestamps.rb +149 -0
- data/lib/rom/plugins/dsl.rb +53 -0
- data/lib/rom/plugins/relation/changeset.rb +97 -0
- data/lib/rom/plugins/relation/instrumentation.rb +66 -0
- data/lib/rom/plugins/relation/registry_reader.rb +36 -0
- data/lib/rom/plugins/schema/timestamps.rb +59 -0
- data/lib/rom/plugins.rb +100 -0
- data/lib/rom/processor/composer.rb +37 -0
- data/lib/rom/processor/transformer.rb +415 -0
- data/lib/rom/processor.rb +30 -0
- data/lib/rom/registries/associations.rb +26 -0
- data/lib/rom/registries/commands.rb +11 -0
- data/lib/rom/registries/container.rb +12 -0
- data/lib/rom/registries/datasets.rb +21 -0
- data/lib/rom/registries/gateways.rb +8 -0
- data/lib/rom/registries/mappers.rb +21 -0
- data/lib/rom/registries/nestable.rb +32 -0
- data/lib/rom/registries/relations.rb +8 -0
- data/lib/rom/registries/root.rb +203 -0
- data/lib/rom/registries/schemas.rb +44 -0
- data/lib/rom/registries/views.rb +11 -0
- data/lib/rom/relation/class_interface.rb +61 -0
- data/lib/rom/relation/combined.rb +160 -0
- data/lib/rom/relation/commands.rb +65 -0
- data/lib/rom/relation/composite.rb +53 -0
- data/lib/rom/relation/curried.rb +129 -0
- data/lib/rom/relation/graph.rb +107 -0
- data/lib/rom/relation/loaded.rb +136 -0
- data/lib/rom/relation/materializable.rb +62 -0
- data/lib/rom/relation/name.rb +122 -0
- data/lib/rom/relation/wrap.rb +64 -0
- data/lib/rom/relation.rb +625 -0
- data/lib/rom/repository/class_interface.rb +162 -0
- data/lib/rom/repository/relation_reader.rb +48 -0
- data/lib/rom/repository/root.rb +75 -0
- data/lib/rom/repository/session.rb +60 -0
- data/lib/rom/repository.rb +179 -0
- data/lib/rom/schema/associations_dsl.rb +222 -0
- data/lib/rom/schema/inferrer.rb +106 -0
- data/lib/rom/schema.rb +471 -0
- data/lib/rom/settings.rb +141 -0
- data/lib/rom/setup.rb +297 -0
- data/lib/rom/struct.rb +99 -0
- data/lib/rom/struct_compiler.rb +114 -0
- data/lib/rom/support/configurable.rb +213 -0
- data/lib/rom/support/inflector.rb +31 -0
- data/lib/rom/support/memoizable.rb +61 -0
- data/lib/rom/support/notifications.rb +238 -0
- data/lib/rom/transaction.rb +26 -0
- data/lib/rom/transformer.rb +46 -0
- data/lib/rom/types.rb +74 -0
- data/lib/rom/version.rb +1 -1
- data/lib/rom-changeset.rb +4 -0
- data/lib/rom-core.rb +3 -0
- data/lib/rom-repository.rb +4 -0
- data/lib/rom.rb +3 -3
- metadata +302 -23
data/lib/rom/relation.rb
ADDED
@@ -0,0 +1,625 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/memoizable"
|
4
|
+
require "dry/core/class_attributes"
|
5
|
+
|
6
|
+
require "rom/struct"
|
7
|
+
require "rom/constants"
|
8
|
+
require "rom/initializer"
|
9
|
+
require "rom/support/inflector"
|
10
|
+
|
11
|
+
require "rom/plugins/class_methods"
|
12
|
+
require "rom/relation/class_interface"
|
13
|
+
|
14
|
+
require "rom/auto_curry"
|
15
|
+
require "rom/pipeline"
|
16
|
+
|
17
|
+
require "rom/relation/loaded"
|
18
|
+
require "rom/relation/curried"
|
19
|
+
require "rom/relation/commands"
|
20
|
+
require "rom/relation/composite"
|
21
|
+
require "rom/relation/combined"
|
22
|
+
require "rom/relation/wrap"
|
23
|
+
require "rom/relation/materializable"
|
24
|
+
|
25
|
+
require "rom/types"
|
26
|
+
|
27
|
+
require_relative "registries/root"
|
28
|
+
require_relative "components/provider"
|
29
|
+
|
30
|
+
module ROM
|
31
|
+
# Base relation class
|
32
|
+
#
|
33
|
+
# Relation is a proxy for the dataset object provided by the gateway. It
|
34
|
+
# can forward methods to the dataset, which is why the "native" interface of
|
35
|
+
# the underlying gateway is available in the relation
|
36
|
+
#
|
37
|
+
# Individual adapters sets up their relation classes and provide different APIs
|
38
|
+
# depending on their persistence backend.
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
class Relation
|
42
|
+
extend ROM::Provider(:dataset, :schema, :view, :association, type: :relation)
|
43
|
+
extend Plugins::ClassMethods
|
44
|
+
extend Initializer
|
45
|
+
extend ClassInterface
|
46
|
+
|
47
|
+
include Dry::Core::Memoizable
|
48
|
+
include Relation::Commands
|
49
|
+
|
50
|
+
# Default no-op output schema which is called in `Relation#each`
|
51
|
+
NOOP_OUTPUT_SCHEMA = -> tuple { tuple }.freeze
|
52
|
+
|
53
|
+
setting :auto_map, default: true
|
54
|
+
setting :auto_struct, default: false
|
55
|
+
setting :struct_namespace, default: ROM::Struct
|
56
|
+
setting :wrap_class, default: Relation::Wrap
|
57
|
+
|
58
|
+
# @api private
|
59
|
+
def self.inherited(klass)
|
60
|
+
super
|
61
|
+
|
62
|
+
adapter = config.component.adapter
|
63
|
+
|
64
|
+
klass.configure do |config|
|
65
|
+
# Relations that inherit from an adapter subclass are not considered abstract anymore
|
66
|
+
# You can override it later inside your class' config of course
|
67
|
+
if adapter
|
68
|
+
config.component.abstract = false
|
69
|
+
|
70
|
+
# Use klass' name to set defaults
|
71
|
+
#
|
72
|
+
# ie `Relations::Users` assumes :users id and a corresponding dataset (table in case of SQL)
|
73
|
+
#
|
74
|
+
# TODO: make this behavior configurable?
|
75
|
+
#
|
76
|
+
if klass.name
|
77
|
+
config.component.id = config.component.inflector.component_id(klass.name).to_sym
|
78
|
+
config.component.dataset = config.component.id
|
79
|
+
else
|
80
|
+
config.component.id = :anonymous
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
include Dry::Equalizer(:name, :dataset)
|
87
|
+
include Materializable
|
88
|
+
include Pipeline
|
89
|
+
|
90
|
+
# @!attribute [r] config
|
91
|
+
# @return [Dry::Configurable::Config]
|
92
|
+
# @api private
|
93
|
+
option :config, default: -> { self.class.config }
|
94
|
+
|
95
|
+
# @!attribute [r] name
|
96
|
+
# @return [Name] The relation name
|
97
|
+
# @api public
|
98
|
+
option :name, default: -> { Name[config.component.id, config.component.dataset] }
|
99
|
+
|
100
|
+
# @!attribute [r] registry
|
101
|
+
# @return [registry] Registry::Root with runtime dependency resolving
|
102
|
+
option :registry, default: -> { self.class.registry(config: config) }
|
103
|
+
|
104
|
+
# @!attribute [r] inflector
|
105
|
+
# @return [Dry::Inflector] The default inflector
|
106
|
+
# @api public
|
107
|
+
option :inflector, default: -> { config.component.inflector }
|
108
|
+
|
109
|
+
# @!attribute [r] schemas
|
110
|
+
# @return [Setup::registry] Relation schemas
|
111
|
+
option :schemas, default: -> { registry.schemas.scoped(config.component.id, config: config) }
|
112
|
+
|
113
|
+
# @!attribute [r] schema
|
114
|
+
# @return [Setup::registry] The canonical schema
|
115
|
+
option :schema, default: -> { schemas.infer(config.component.id) }
|
116
|
+
|
117
|
+
# @!attribute [r] datasets
|
118
|
+
# @return [registry] Relation associations
|
119
|
+
option :datasets, default: -> { registry.datasets.scoped(config.component.id, config: config) }
|
120
|
+
|
121
|
+
# @!attribute [r] dataset
|
122
|
+
# @return [Object] dataset used by the relation provided by relation's gateway
|
123
|
+
# @api public
|
124
|
+
option :dataset, default: -> { datasets.infer(config.component.id) }
|
125
|
+
|
126
|
+
# @!attribute [r] associations
|
127
|
+
# @return [Setup::registry] Relation associations
|
128
|
+
option :associations, default: -> { registry.associations.scoped(config.component.id) }
|
129
|
+
|
130
|
+
# @!attribute [r] input_schema
|
131
|
+
# @return [Object#[]] tuple processing function, uses schema or defaults to Hash[]
|
132
|
+
# @api private
|
133
|
+
option :input_schema, default: -> { schema.to_input_hash }
|
134
|
+
|
135
|
+
# @!attribute [r] output_schema
|
136
|
+
# @return [Object#[]] tuple processing function, uses schema or defaults to NOOP_OUTPUT_SCHEMA
|
137
|
+
# @api private
|
138
|
+
option :output_schema, default: lambda {
|
139
|
+
schema.any?(&:read?) ? schema.to_output_hash : NOOP_OUTPUT_SCHEMA
|
140
|
+
}
|
141
|
+
|
142
|
+
# @!attribute [r] auto_map
|
143
|
+
# @return [TrueClass,FalseClass] Whether or not a relation and its compositions should be auto-mapped
|
144
|
+
# @api private
|
145
|
+
option :auto_map, default: -> { config.auto_map }
|
146
|
+
|
147
|
+
# @!attribute [r] auto_struct
|
148
|
+
# @return [TrueClass,FalseClass] Whether or not tuples should be auto-mapped to structs
|
149
|
+
# @api private
|
150
|
+
option :auto_struct, default: -> { config.auto_struct }
|
151
|
+
|
152
|
+
# @!attribute [r] struct_namespace
|
153
|
+
# @return [Module] Custom struct namespace
|
154
|
+
# @api private
|
155
|
+
option :struct_namespace, reader: false, default: -> { config.struct_namespace }
|
156
|
+
|
157
|
+
# @!attribute [r] mappers
|
158
|
+
# @return [registry] an optional mapper registry (empty by default)
|
159
|
+
option :mappers,
|
160
|
+
-> mappers {
|
161
|
+
if mappers.is_a?(Hash)
|
162
|
+
registry.mappers
|
163
|
+
.scoped(config.component.id, opts: {adapter: config.component.adapter})
|
164
|
+
.import(mappers)
|
165
|
+
else
|
166
|
+
mappers
|
167
|
+
end
|
168
|
+
},
|
169
|
+
default: -> {
|
170
|
+
registry.mappers.scoped(config.component.id, opts: {adapter: adapter})
|
171
|
+
}
|
172
|
+
|
173
|
+
# @!attribute [r] commands
|
174
|
+
# @return [Commandregistry] Command registry
|
175
|
+
# @api private
|
176
|
+
option :commands, default: -> do
|
177
|
+
registry.commands.scoped(config.component.id, opts: {adapter: adapter})
|
178
|
+
end
|
179
|
+
|
180
|
+
# @!attribute [r] meta
|
181
|
+
# @return [Hash] Meta data stored in a hash
|
182
|
+
# @api private
|
183
|
+
option :meta, reader: true, default: -> { EMPTY_HASH }
|
184
|
+
|
185
|
+
# @api public
|
186
|
+
def self.new(dataset = nil, **opts)
|
187
|
+
if dataset
|
188
|
+
super(**opts, dataset: dataset)
|
189
|
+
else
|
190
|
+
super(**opts)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Return schema attribute
|
195
|
+
#
|
196
|
+
# @example accessing canonical attribute
|
197
|
+
# users[:id]
|
198
|
+
# # => #<ROM::SQL::Attribute[Integer] primary_key=true name=:id source=ROM::Relation::Name(users)>
|
199
|
+
#
|
200
|
+
# @example accessing joined attribute
|
201
|
+
# tasks_with_users = tasks.join(users).select_append(tasks[:title])
|
202
|
+
# tasks_with_users[:title, :tasks]
|
203
|
+
# # => #<ROM::SQL::Attribute[String] primary_key=false name=:title source=ROM::Relation::Name(tasks)>
|
204
|
+
#
|
205
|
+
# @return [Attribute]
|
206
|
+
#
|
207
|
+
# @api public
|
208
|
+
def [](name)
|
209
|
+
schema[name]
|
210
|
+
end
|
211
|
+
|
212
|
+
# Yields relation tuples
|
213
|
+
#
|
214
|
+
# Every tuple is processed through Relation#output_schema, it's a no-op by default
|
215
|
+
#
|
216
|
+
# @yield [Hash]
|
217
|
+
#
|
218
|
+
# @return [Enumerator] if block is not provided
|
219
|
+
#
|
220
|
+
# @api public
|
221
|
+
def each(&block)
|
222
|
+
return to_enum unless block_given?
|
223
|
+
|
224
|
+
if auto_map?
|
225
|
+
mapper.(dataset.map { |tuple| output_schema[tuple] }).each(&block)
|
226
|
+
else
|
227
|
+
dataset.each { |tuple| yield(output_schema[tuple]) }
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Combine with other relations using configured associations
|
232
|
+
#
|
233
|
+
# @overload combine(*associations)
|
234
|
+
# @example
|
235
|
+
# users.combine(:tasks, :posts)
|
236
|
+
#
|
237
|
+
# @param *associations [Array<Symbol>] A list of association names
|
238
|
+
#
|
239
|
+
# @overload combine(*associations, **nested_associations)
|
240
|
+
# @example
|
241
|
+
# users.combine(:tasks, posts: :authors)
|
242
|
+
#
|
243
|
+
# @param *associations [Array<Symbol>] A list of association names
|
244
|
+
# @param *nested_associations [Hash] A hash with nested association names
|
245
|
+
#
|
246
|
+
# @overload combine(associations)
|
247
|
+
# @example
|
248
|
+
# users.combine(posts: [:authors, reviews: [:tags, comments: :author])
|
249
|
+
#
|
250
|
+
# @param *associations [Hash] A hash with nested association names
|
251
|
+
#
|
252
|
+
# @return [Relation]
|
253
|
+
#
|
254
|
+
# @api public
|
255
|
+
def combine(*args)
|
256
|
+
combine_with(*nodes(*args))
|
257
|
+
end
|
258
|
+
|
259
|
+
# Composes with other relations
|
260
|
+
#
|
261
|
+
# @param [Array<Relation>] others The other relation(s) to compose with
|
262
|
+
#
|
263
|
+
# @return [Relation::Graph]
|
264
|
+
#
|
265
|
+
# @api public
|
266
|
+
def combine_with(*others)
|
267
|
+
Combined.new(self, others)
|
268
|
+
end
|
269
|
+
|
270
|
+
# @api private
|
271
|
+
def nodes(*args)
|
272
|
+
args.reduce([]) do |acc, arg|
|
273
|
+
case arg
|
274
|
+
when Symbol
|
275
|
+
acc << node(arg)
|
276
|
+
when Hash
|
277
|
+
acc.concat(arg.map { |name, opts| node(name).combine(opts) })
|
278
|
+
when Array
|
279
|
+
acc.concat(arg.map { |opts| nodes(opts) }.reduce(:concat))
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
# Create a graph node for a given association identifier
|
285
|
+
#
|
286
|
+
# @param [Symbol, Relation::Name] name
|
287
|
+
#
|
288
|
+
# @return [Relation]
|
289
|
+
#
|
290
|
+
# @api public
|
291
|
+
def node(name)
|
292
|
+
assoc = associations[name]
|
293
|
+
other = assoc.node
|
294
|
+
other.eager_load(assoc)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Return a graph node prepared by the given association
|
298
|
+
#
|
299
|
+
# @param [Association] assoc An association object
|
300
|
+
#
|
301
|
+
# @return [Relation]
|
302
|
+
#
|
303
|
+
# @api public
|
304
|
+
def eager_load(assoc)
|
305
|
+
relation = assoc.prepare(self)
|
306
|
+
|
307
|
+
if assoc.override?
|
308
|
+
relation.(assoc)
|
309
|
+
else
|
310
|
+
relation.preload_assoc(assoc)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Preload other relation via association
|
315
|
+
#
|
316
|
+
# This is used internally when relations are composed
|
317
|
+
#
|
318
|
+
# @return [Relation::Curried]
|
319
|
+
#
|
320
|
+
# @api private
|
321
|
+
def preload_assoc(assoc, other)
|
322
|
+
assoc.preload(self, other)
|
323
|
+
end
|
324
|
+
|
325
|
+
# Wrap other relations using association names
|
326
|
+
#
|
327
|
+
# @example
|
328
|
+
# tasks.wrap(:owner)
|
329
|
+
#
|
330
|
+
# @param [Array<Symbol>] names A list with association identifiers
|
331
|
+
#
|
332
|
+
# @return [Wrap]
|
333
|
+
#
|
334
|
+
# @api public
|
335
|
+
def wrap(*names)
|
336
|
+
wrap_around(*names.map { |n| associations[n].wrap })
|
337
|
+
end
|
338
|
+
|
339
|
+
# Wrap around other relations
|
340
|
+
#
|
341
|
+
# @param [Array<Relation>] others Other relations
|
342
|
+
#
|
343
|
+
# @return [Relation::Wrap]
|
344
|
+
#
|
345
|
+
# @api public
|
346
|
+
def wrap_around(*others)
|
347
|
+
wrap_class.new(self, others)
|
348
|
+
end
|
349
|
+
|
350
|
+
# Loads a relation
|
351
|
+
#
|
352
|
+
# @return [Relation::Loaded]
|
353
|
+
#
|
354
|
+
# @api public
|
355
|
+
def call
|
356
|
+
Loaded.new(self)
|
357
|
+
end
|
358
|
+
|
359
|
+
# Materializes a relation into an array
|
360
|
+
#
|
361
|
+
# @return [Array<Hash>]
|
362
|
+
#
|
363
|
+
# @api public
|
364
|
+
def to_a
|
365
|
+
to_enum.to_a
|
366
|
+
end
|
367
|
+
|
368
|
+
# Returns if this relation is curried
|
369
|
+
#
|
370
|
+
# @return [false]
|
371
|
+
#
|
372
|
+
# @api private
|
373
|
+
def curried?
|
374
|
+
false
|
375
|
+
end
|
376
|
+
|
377
|
+
# Returns if this relation is a graph
|
378
|
+
#
|
379
|
+
# @return [false]
|
380
|
+
#
|
381
|
+
# @api private
|
382
|
+
def graph?
|
383
|
+
false
|
384
|
+
end
|
385
|
+
|
386
|
+
# Return if this is a wrap relation
|
387
|
+
#
|
388
|
+
# @return [false]
|
389
|
+
#
|
390
|
+
# @api private
|
391
|
+
def wrap?
|
392
|
+
false
|
393
|
+
end
|
394
|
+
|
395
|
+
# Returns true if a relation has schema defined
|
396
|
+
#
|
397
|
+
# @return [TrueClass, FalseClass]
|
398
|
+
#
|
399
|
+
# @api private
|
400
|
+
def schema?
|
401
|
+
!schema.empty?
|
402
|
+
end
|
403
|
+
|
404
|
+
# Return a new relation with provided dataset and additional options
|
405
|
+
#
|
406
|
+
# Use this method whenever you need to use dataset API to get a new dataset
|
407
|
+
# and you want to return a relation back. Typically relation API should be
|
408
|
+
# enough though. If you find yourself using this method, it might be worth
|
409
|
+
# to consider reporting an issue that some dataset functionality is not available
|
410
|
+
# through relation API.
|
411
|
+
#
|
412
|
+
# @example with a new dataset
|
413
|
+
# users.new(users.dataset.some_method)
|
414
|
+
#
|
415
|
+
# @example with a new dataset and options
|
416
|
+
# users.new(users.dataset.some_method, other: 'options')
|
417
|
+
#
|
418
|
+
# @param [Object] dataset
|
419
|
+
# @param [Hash] new_opts Additional options
|
420
|
+
#
|
421
|
+
# @api public
|
422
|
+
def new(dataset, **new_opts)
|
423
|
+
opts =
|
424
|
+
if new_opts.empty?
|
425
|
+
options
|
426
|
+
elsif new_opts.key?(:schema)
|
427
|
+
options.merge(new_opts).reject { |k, _| k == :input_schema || k == :output_schema }
|
428
|
+
else
|
429
|
+
options.merge(new_opts)
|
430
|
+
end
|
431
|
+
|
432
|
+
self.class.new(**opts, dataset: dataset)
|
433
|
+
end
|
434
|
+
|
435
|
+
undef_method :with
|
436
|
+
|
437
|
+
# Returns a new instance with the same dataset but new options
|
438
|
+
#
|
439
|
+
# @example
|
440
|
+
# users.with(output_schema: -> tuple { .. })
|
441
|
+
#
|
442
|
+
# @param [Hash] opts New options
|
443
|
+
#
|
444
|
+
# @return [Relation]
|
445
|
+
#
|
446
|
+
# @api public
|
447
|
+
def with(opts)
|
448
|
+
new_options =
|
449
|
+
if opts.key?(:meta)
|
450
|
+
opts.merge(meta: meta.merge(opts[:meta]))
|
451
|
+
else
|
452
|
+
opts
|
453
|
+
end
|
454
|
+
|
455
|
+
new(dataset, **options, **new_options)
|
456
|
+
end
|
457
|
+
|
458
|
+
# Returns AST for the wrapped relation
|
459
|
+
#
|
460
|
+
# @return [Array]
|
461
|
+
#
|
462
|
+
# @api public
|
463
|
+
def to_ast
|
464
|
+
[:relation, [name.relation, attr_ast, meta_ast]]
|
465
|
+
end
|
466
|
+
|
467
|
+
# @api private
|
468
|
+
def attr_ast
|
469
|
+
schema.map(&:to_read_ast)
|
470
|
+
end
|
471
|
+
|
472
|
+
# @api private
|
473
|
+
def meta_ast
|
474
|
+
meta = self.meta.merge(dataset: name.dataset, alias: name.aliaz,
|
475
|
+
struct_namespace: options[:struct_namespace])
|
476
|
+
meta[:model] = false unless auto_struct? || meta[:model]
|
477
|
+
meta
|
478
|
+
end
|
479
|
+
|
480
|
+
# @api private
|
481
|
+
def auto_map?
|
482
|
+
(auto_map || auto_struct) && !meta[:combine_type]
|
483
|
+
end
|
484
|
+
|
485
|
+
# @api private
|
486
|
+
def auto_struct?
|
487
|
+
auto_struct && !meta[:combine_type]
|
488
|
+
end
|
489
|
+
|
490
|
+
# @api private
|
491
|
+
def mapper
|
492
|
+
mappers[to_ast]
|
493
|
+
end
|
494
|
+
|
495
|
+
# Maps relation with custom mappers available via registry
|
496
|
+
#
|
497
|
+
# When `auto_map` is enabled, your mappers will be applied after performing
|
498
|
+
# default auto-mapping. This means that you can compose complex relations
|
499
|
+
# and have them auto-mapped, and use much simpler custom mappers to adjust
|
500
|
+
# resulting data according to your requirements.
|
501
|
+
#
|
502
|
+
# @overload map_with(*mappers)
|
503
|
+
# Map tuples using registered mappers
|
504
|
+
#
|
505
|
+
# @example
|
506
|
+
# users.map_with(:my_mapper, :my_other_mapper)
|
507
|
+
#
|
508
|
+
# @param [Array<Symbol>] mappers A list of mapper identifiers
|
509
|
+
#
|
510
|
+
# @overload map_with(*mappers, auto_map: true)
|
511
|
+
# Map tuples using custom registered mappers and enforce auto-mapping
|
512
|
+
#
|
513
|
+
# @example
|
514
|
+
# users.map_with(:my_mapper, :my_other_mapper, auto_map: true)
|
515
|
+
#
|
516
|
+
# @param [Array<Symbol>] mappers A list of mapper identifiers
|
517
|
+
#
|
518
|
+
# @return [Relation::Composite] Mapped relation
|
519
|
+
#
|
520
|
+
# @api public
|
521
|
+
def map_with(*names, **opts)
|
522
|
+
super(*names).with(opts)
|
523
|
+
end
|
524
|
+
|
525
|
+
# Return a new relation that will map its tuples to instances of the provided class
|
526
|
+
#
|
527
|
+
# @example
|
528
|
+
# users.map_to(MyUserModel)
|
529
|
+
#
|
530
|
+
# @param [Class] klass Your custom model class
|
531
|
+
#
|
532
|
+
# @return [Relation]
|
533
|
+
#
|
534
|
+
# @api public
|
535
|
+
def map_to(klass, **opts)
|
536
|
+
with(opts.merge(auto_map: false, auto_struct: true, meta: {model: klass}))
|
537
|
+
end
|
538
|
+
|
539
|
+
# Return a new relation with an aliased name
|
540
|
+
#
|
541
|
+
# @example
|
542
|
+
# users.as(:people)
|
543
|
+
#
|
544
|
+
# @param [Symbol] aliaz Aliased name
|
545
|
+
#
|
546
|
+
# @return [Relation]
|
547
|
+
#
|
548
|
+
# @api public
|
549
|
+
def as(aliaz)
|
550
|
+
with(name: name.as(aliaz))
|
551
|
+
end
|
552
|
+
|
553
|
+
# @return [Symbol] The wrapped relation's adapter identifier ie :sql or :http
|
554
|
+
#
|
555
|
+
# @api private
|
556
|
+
def adapter
|
557
|
+
config.component.adapter
|
558
|
+
end
|
559
|
+
|
560
|
+
# Return name of the source gateway of this relation
|
561
|
+
#
|
562
|
+
# @return [Symbol]
|
563
|
+
#
|
564
|
+
# @api private
|
565
|
+
def gateway
|
566
|
+
config.component.gateway
|
567
|
+
end
|
568
|
+
|
569
|
+
# Return a foreign key name for the provided relation name
|
570
|
+
#
|
571
|
+
# @param [Name] name The relation name object
|
572
|
+
#
|
573
|
+
# @return [Symbol]
|
574
|
+
#
|
575
|
+
# @api private
|
576
|
+
def foreign_key(name)
|
577
|
+
attr = schema.foreign_key(name.dataset)
|
578
|
+
|
579
|
+
if attr
|
580
|
+
attr.name
|
581
|
+
else
|
582
|
+
:"#{inflector.singularize(name.dataset)}_id"
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
# Return a new relation configured with the provided struct namespace
|
587
|
+
#
|
588
|
+
# @param [Module] ns Custom namespace module for auto-structs
|
589
|
+
#
|
590
|
+
# @return [Relation]
|
591
|
+
#
|
592
|
+
# @api public
|
593
|
+
def struct_namespace(ns)
|
594
|
+
options[:struct_namespace] == ns ? self : with(struct_namespace: ns)
|
595
|
+
end
|
596
|
+
|
597
|
+
memoize :to_ast, :auto_map?, :auto_struct?, :foreign_key, :combine, :wrap, :node
|
598
|
+
|
599
|
+
# we do it here because we want to avoid previous methods to be auto_curried
|
600
|
+
# via method_added hook, which is what AutoCurry uses
|
601
|
+
extend AutoCurry
|
602
|
+
|
603
|
+
auto_curry :preload_assoc
|
604
|
+
|
605
|
+
private
|
606
|
+
|
607
|
+
# Hook used by `Pipeline` to get the class that should be used for composition
|
608
|
+
#
|
609
|
+
# @return [Class]
|
610
|
+
#
|
611
|
+
# @api private
|
612
|
+
def composite_class
|
613
|
+
Relation::Composite
|
614
|
+
end
|
615
|
+
|
616
|
+
# Return configured "wrap" relation class used in Relation#wrap
|
617
|
+
#
|
618
|
+
# @return [Class]
|
619
|
+
#
|
620
|
+
# @api private
|
621
|
+
def wrap_class
|
622
|
+
config.wrap_class
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|