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
@@ -0,0 +1,236 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/class_builder"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# Base command class with factory class-level interface and setup-related logic
|
7
|
+
#
|
8
|
+
# @private
|
9
|
+
class Command
|
10
|
+
module ClassInterface
|
11
|
+
# This hook sets up default class state
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def inherited(klass)
|
15
|
+
super
|
16
|
+
klass.instance_variable_set(:@before, before.dup)
|
17
|
+
klass.instance_variable_set(:@after, after.dup)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets up the base class
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def self.extended(klass)
|
24
|
+
super
|
25
|
+
klass.set_hooks(:before, [])
|
26
|
+
klass.set_hooks(:after, [])
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return adapter specific sub-class based on the adapter identifier
|
30
|
+
#
|
31
|
+
# This is a syntax sugar to make things consistent
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# ROM::Commands::Create[:memory]
|
35
|
+
# # => ROM::Memory::Commands::Create
|
36
|
+
#
|
37
|
+
# @param [Symbol] adapter identifier
|
38
|
+
#
|
39
|
+
# @return [Class]
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
def [](adapter)
|
43
|
+
adapter_namespace(adapter).const_get(Inflector.demodulize(name))
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return namespaces that contains command subclasses of a specific adapter
|
47
|
+
#
|
48
|
+
# @param [Symbol] adapter identifier
|
49
|
+
#
|
50
|
+
# @return [Module]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
def adapter_namespace(adapter)
|
54
|
+
ROM.adapters.fetch(adapter).const_get(:Commands)
|
55
|
+
rescue KeyError
|
56
|
+
raise AdapterNotPresentError.new(adapter, :relation)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Build a command class for a specific relation with options
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# class CreateUser < ROM::Commands::Create[:memory]
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# command = CreateUser.build(rom.relations[:users])
|
66
|
+
#
|
67
|
+
# @param [Relation] relation
|
68
|
+
# @param [Hash] options
|
69
|
+
#
|
70
|
+
# @return [Command]
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
def build(relation, **options)
|
74
|
+
new(relation, **options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Create a command class with a specific type
|
78
|
+
#
|
79
|
+
# @param [Symbol] name Command name
|
80
|
+
# @param [Class] type Command class
|
81
|
+
#
|
82
|
+
# @yield [Class]
|
83
|
+
#
|
84
|
+
# @return [Class, Object]
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def create_class(type: self, meta: {}, rel_meta: {}, plugins: {}, **, &block)
|
88
|
+
klass = Dry::Core::ClassBuilder.new(name: type.name, parent: type).call
|
89
|
+
|
90
|
+
result = meta.fetch(:result, :one)
|
91
|
+
klass.config.result = rel_meta.fetch(:combine_type, result)
|
92
|
+
|
93
|
+
meta.each do |name, value|
|
94
|
+
if klass.respond_to?(name)
|
95
|
+
klass.public_send(name, value)
|
96
|
+
else
|
97
|
+
klass.config[name] = value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
plugins.each do |plugin, options|
|
102
|
+
klass.use(plugin, **options)
|
103
|
+
end
|
104
|
+
|
105
|
+
if block
|
106
|
+
yield(klass)
|
107
|
+
else
|
108
|
+
klass
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return configured adapter identifier
|
113
|
+
#
|
114
|
+
# @return [Symbol]
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
def adapter
|
118
|
+
config.component.adapter
|
119
|
+
end
|
120
|
+
|
121
|
+
# Set before-execute hooks
|
122
|
+
#
|
123
|
+
# @overload before(hook)
|
124
|
+
# Set an before hook as a method name
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# class CreateUser < ROM::Commands::Create[:sql]
|
128
|
+
# relation :users
|
129
|
+
# register_as :create
|
130
|
+
#
|
131
|
+
# before :my_hook
|
132
|
+
#
|
133
|
+
# def my_hook(tuple, *)
|
134
|
+
# puts "hook called#
|
135
|
+
# end
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# @overload before(hook_opts)
|
139
|
+
# Set an before hook as a method name with arguments
|
140
|
+
#
|
141
|
+
# @example
|
142
|
+
# class CreateUser < ROM::Commands::Create[:sql]
|
143
|
+
# relation :users
|
144
|
+
# register_as :create
|
145
|
+
#
|
146
|
+
# before my_hook: { arg1: 1, arg2: 2 }
|
147
|
+
#
|
148
|
+
# def my_hook(tuple, arg1:, arg2:)
|
149
|
+
# puts "hook called with args: #{arg1} and #{arg2}"
|
150
|
+
# end
|
151
|
+
# end
|
152
|
+
#
|
153
|
+
# @param [Hash<Symbol=>Hash>] hook Options with method name and pre-set args
|
154
|
+
#
|
155
|
+
# @return [Array<Hash, Symbol>] A list of all configured before hooks
|
156
|
+
#
|
157
|
+
# @api public
|
158
|
+
def before(*hooks)
|
159
|
+
if hooks.empty?
|
160
|
+
@before
|
161
|
+
else
|
162
|
+
set_hooks(:before, hooks)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Set after-execute hooks
|
167
|
+
#
|
168
|
+
# @overload after(hook)
|
169
|
+
# Set an after hook as a method name
|
170
|
+
#
|
171
|
+
# @example
|
172
|
+
# class CreateUser < ROM::Commands::Create[:sql]
|
173
|
+
# relation :users
|
174
|
+
# register_as :create
|
175
|
+
#
|
176
|
+
# after :my_hook
|
177
|
+
#
|
178
|
+
# def my_hook(tuple, *)
|
179
|
+
# puts "hook called#
|
180
|
+
# end
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
# @overload after(hook_opts)
|
184
|
+
# Set an after hook as a method name with arguments
|
185
|
+
#
|
186
|
+
# @example
|
187
|
+
# class CreateUser < ROM::Commands::Create[:sql]
|
188
|
+
# relation :users
|
189
|
+
# register_as :create
|
190
|
+
#
|
191
|
+
# after my_hook: { arg1: 1, arg1: 2 }
|
192
|
+
#
|
193
|
+
# def my_hook(tuple, arg1:, arg2:)
|
194
|
+
# puts "hook called with args: #{arg1} and #{arg2}"
|
195
|
+
# end
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# @param [Hash<Symbol=>Hash>] hook Options with method name and pre-set args
|
199
|
+
#
|
200
|
+
# @return [Array<Hash, Symbol>] A list of all configured after hooks
|
201
|
+
#
|
202
|
+
# @api public
|
203
|
+
def after(*hooks)
|
204
|
+
if hooks.empty?
|
205
|
+
@after
|
206
|
+
else
|
207
|
+
set_hooks(:after, hooks)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Set new or more hooks
|
212
|
+
#
|
213
|
+
# @api private
|
214
|
+
def set_hooks(type, hooks)
|
215
|
+
ivar = :"@#{type}"
|
216
|
+
|
217
|
+
if instance_variable_defined?(ivar)
|
218
|
+
instance_variable_get(ivar).concat(hooks)
|
219
|
+
else
|
220
|
+
instance_variable_set(ivar, hooks)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Return default name of the command class based on its name
|
225
|
+
#
|
226
|
+
# During setup phase this is used by defalut as `register_as` option
|
227
|
+
#
|
228
|
+
# @return [Symbol]
|
229
|
+
#
|
230
|
+
# @api private
|
231
|
+
def default_name
|
232
|
+
Inflector.underscore(Inflector.demodulize(name)).to_sym
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/pipeline"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module Commands
|
7
|
+
# Composite command that consists of left and right commands
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Composite < Pipeline::Composite
|
11
|
+
# Calls the composite command
|
12
|
+
#
|
13
|
+
# Right command is called with a result from the left one
|
14
|
+
#
|
15
|
+
# @return [Object]
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def call(*args)
|
19
|
+
response = left.call(*args)
|
20
|
+
|
21
|
+
if response.nil? || (many? && response.empty?)
|
22
|
+
return one? ? nil : EMPTY_ARRAY
|
23
|
+
end
|
24
|
+
|
25
|
+
if one? && !graph?
|
26
|
+
if right.is_a?(Command) || right.is_a?(Commands::Composite)
|
27
|
+
right.call([response].first)
|
28
|
+
else
|
29
|
+
right.call([response]).first
|
30
|
+
end
|
31
|
+
elsif one? && graph?
|
32
|
+
right.call(response).first
|
33
|
+
else
|
34
|
+
right.call(response)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
alias_method :[], :call
|
38
|
+
|
39
|
+
# @api private
|
40
|
+
def graph?
|
41
|
+
left.is_a?(Graph)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @api private
|
45
|
+
def result
|
46
|
+
left.result
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def decorate?(response)
|
51
|
+
super || response.is_a?(Graph)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/command"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module Commands
|
7
|
+
# Delete command
|
8
|
+
#
|
9
|
+
# This command removes tuples from its target relation
|
10
|
+
#
|
11
|
+
# @abstract
|
12
|
+
class Delete < Command
|
13
|
+
config.restrictable = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/commands/graph/input_evaluator"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module Commands
|
7
|
+
class Graph
|
8
|
+
# Class methods for command Graph
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
module ClassInterface
|
12
|
+
# Build a command graph recursively
|
13
|
+
#
|
14
|
+
# This is used by `Container#command` when array with options is passed in
|
15
|
+
#
|
16
|
+
# @param [Registry] registry The command registry from container
|
17
|
+
# @param [Array] options The options array
|
18
|
+
# @param [Array] path The path for input evaluator proc
|
19
|
+
#
|
20
|
+
# @return [Graph]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def build(registry, options, path = EMPTY_ARRAY)
|
24
|
+
options.reduce { |spec, other| build_command(registry, spec, other, path) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def build_command(registry, spec, other, path)
|
29
|
+
cmd_opts, nodes = other
|
30
|
+
|
31
|
+
key, relation =
|
32
|
+
if spec.is_a?(Hash)
|
33
|
+
spec.to_a.first
|
34
|
+
else
|
35
|
+
[spec, spec]
|
36
|
+
end
|
37
|
+
|
38
|
+
name, opts =
|
39
|
+
if cmd_opts.is_a?(Hash)
|
40
|
+
cmd_opts.to_a.first
|
41
|
+
else
|
42
|
+
[cmd_opts]
|
43
|
+
end
|
44
|
+
|
45
|
+
command = registry[relation][name]
|
46
|
+
tuple_path = Array[*path] << key
|
47
|
+
input_proc = InputEvaluator.build(tuple_path, nodes)
|
48
|
+
|
49
|
+
command = command.curry(input_proc, opts)
|
50
|
+
|
51
|
+
if nodes
|
52
|
+
if nodes.all? { |node| node.is_a?(Array) }
|
53
|
+
command.combine(*nodes.map { |node| build(registry, node, tuple_path) })
|
54
|
+
else
|
55
|
+
command.combine(build(registry, nodes, tuple_path))
|
56
|
+
end
|
57
|
+
else
|
58
|
+
command
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Commands
|
5
|
+
class Graph
|
6
|
+
# Evaluator for lazy commands which extracts values for commands from nested hashes
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class InputEvaluator
|
10
|
+
include Dry::Equalizer(:tuple_path, :excluded_keys)
|
11
|
+
|
12
|
+
# @!attribute [r] tuple_path
|
13
|
+
# @return [Array<Symbol>] A list of keys pointing to a value inside a hash
|
14
|
+
attr_reader :tuple_path
|
15
|
+
|
16
|
+
# @!attribute [r] excluded_keys
|
17
|
+
# @return [Array<Symbol>] A list of keys that should be excluded
|
18
|
+
attr_reader :excluded_keys
|
19
|
+
|
20
|
+
# @!attribute [r] exclude_proc
|
21
|
+
# @return [Array<Symbol>] A function that should determine which keys should be excluded
|
22
|
+
attr_reader :exclude_proc
|
23
|
+
|
24
|
+
# Build an input evaluator
|
25
|
+
#
|
26
|
+
# @param [Array<Symbol>] tuple_path The tuple path
|
27
|
+
# @param [Array] nodes
|
28
|
+
#
|
29
|
+
# @return [InputEvaluator]
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def self.build(tuple_path, nodes)
|
33
|
+
new(tuple_path, extract_excluded_keys(nodes))
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
def self.extract_excluded_keys(nodes)
|
38
|
+
return unless nodes
|
39
|
+
|
40
|
+
nodes
|
41
|
+
.map { |item| item.is_a?(Array) && item.size > 1 ? item.first : item }
|
42
|
+
.compact
|
43
|
+
.map { |item| item.is_a?(Hash) ? item.keys.first : item }
|
44
|
+
.reject { |item| item.is_a?(Array) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return default exclude_proc
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def self.exclude_proc(excluded_keys)
|
51
|
+
-> input { input.reject { |k, _| excluded_keys.include?(k) } }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Initialize a new input evaluator
|
55
|
+
#
|
56
|
+
# @return [InputEvaluator]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
def initialize(tuple_path, excluded_keys)
|
60
|
+
@tuple_path = tuple_path
|
61
|
+
@excluded_keys = excluded_keys
|
62
|
+
@exclude_proc = self.class.exclude_proc(excluded_keys)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Evaluate input hash
|
66
|
+
#
|
67
|
+
# @param [Hash] input The input hash
|
68
|
+
# @param [Integer] index Optional index
|
69
|
+
#
|
70
|
+
# @return [Hash]
|
71
|
+
def call(input, index = nil)
|
72
|
+
value =
|
73
|
+
begin
|
74
|
+
if index
|
75
|
+
tuple_path[0..tuple_path.size - 2]
|
76
|
+
.reduce(input) { |a, e| a.fetch(e) }
|
77
|
+
.at(index)[tuple_path.last]
|
78
|
+
else
|
79
|
+
tuple_path.reduce(input) { |a, e| a.fetch(e) }
|
80
|
+
end
|
81
|
+
rescue KeyError => e
|
82
|
+
raise KeyMissing, e.message
|
83
|
+
end
|
84
|
+
|
85
|
+
if excluded_keys
|
86
|
+
value.is_a?(Array) ? value.map(&exclude_proc) : exclude_proc[value]
|
87
|
+
else
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/initializer"
|
4
|
+
require "rom/pipeline"
|
5
|
+
require "rom/commands/graph/class_interface"
|
6
|
+
|
7
|
+
module ROM
|
8
|
+
module Commands
|
9
|
+
# Command graph
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class Graph
|
13
|
+
extend Initializer
|
14
|
+
include Dry::Equalizer(:root, :nodes)
|
15
|
+
|
16
|
+
extend ClassInterface
|
17
|
+
|
18
|
+
include Pipeline
|
19
|
+
include Pipeline::Proxy
|
20
|
+
|
21
|
+
# @attr_reader [Command] root The root command
|
22
|
+
param :root
|
23
|
+
|
24
|
+
# @attr_reader [Array<Command>] nodes The child commands
|
25
|
+
param :nodes
|
26
|
+
|
27
|
+
alias_method :left, :root
|
28
|
+
alias_method :right, :nodes
|
29
|
+
|
30
|
+
# @attr_reader [Symbol] root's relation name
|
31
|
+
option :name, default: -> { root.name }
|
32
|
+
|
33
|
+
# Calls root and all nodes with the result from root
|
34
|
+
#
|
35
|
+
# Graph results are mappable through `combine` operation in mapper DSL
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# create_user = rom.commands[:users][:create]
|
39
|
+
# create_task = rom.commands[:tasks][:create]
|
40
|
+
#
|
41
|
+
# command = create_user
|
42
|
+
# .curry(name: 'Jane')
|
43
|
+
# .combine(create_task.curry(title: 'Task'))
|
44
|
+
#
|
45
|
+
# command.call
|
46
|
+
#
|
47
|
+
# @return [Array] nested array with command results
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def call(*args)
|
51
|
+
left = root.call(*args)
|
52
|
+
|
53
|
+
right = nodes.map { |node|
|
54
|
+
response =
|
55
|
+
if node.lazy?
|
56
|
+
node.call(args.first, left)
|
57
|
+
else
|
58
|
+
node.call(left)
|
59
|
+
end
|
60
|
+
|
61
|
+
if node.one? && !node.graph?
|
62
|
+
[response]
|
63
|
+
else
|
64
|
+
response
|
65
|
+
end
|
66
|
+
}
|
67
|
+
|
68
|
+
if one?
|
69
|
+
[[left], right]
|
70
|
+
else
|
71
|
+
[left, right]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @api private
|
76
|
+
def graph?
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# @api public
|
83
|
+
def composite_class
|
84
|
+
Command::Composite
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Commands
|
5
|
+
class Lazy
|
6
|
+
# Lazy command wrapper for create commands
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Create < Lazy
|
10
|
+
# Execute a command
|
11
|
+
#
|
12
|
+
# @see Command::Create#call
|
13
|
+
#
|
14
|
+
# @return [Hash,Array<Hash>]
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def call(*args)
|
18
|
+
first = args.first
|
19
|
+
last = args.last
|
20
|
+
size = args.size
|
21
|
+
|
22
|
+
if size > 1 && last.is_a?(Array)
|
23
|
+
last.map.with_index do |parent, index|
|
24
|
+
children = evaluator.call(first, index)
|
25
|
+
command_proc[command, parent, children].call(children, parent)
|
26
|
+
end.reduce(:concat)
|
27
|
+
else
|
28
|
+
input = evaluator.call(first)
|
29
|
+
command.call(input, *args[1..size - 1])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Commands
|
5
|
+
class Lazy
|
6
|
+
# Lazy command wrapper for delete commands
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Delete < Lazy
|
10
|
+
# Execute a lazy delete command
|
11
|
+
#
|
12
|
+
# @see Commands::Delete#call
|
13
|
+
#
|
14
|
+
# @return [Hash, Array<Hash>]
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def call(*args)
|
18
|
+
first = args.first
|
19
|
+
last = args.last
|
20
|
+
size = args.size
|
21
|
+
|
22
|
+
if size > 1 && last.is_a?(Array)
|
23
|
+
raise NotImplementedError
|
24
|
+
else
|
25
|
+
input = evaluator.call(first)
|
26
|
+
|
27
|
+
if input.is_a?(Array)
|
28
|
+
input.map do |item|
|
29
|
+
command_proc[command, *(size > 1 ? [last, item] : [input])].call
|
30
|
+
end
|
31
|
+
else
|
32
|
+
command_proc[command, input].call
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ROM
|
4
|
+
module Commands
|
5
|
+
class Lazy
|
6
|
+
# Lazy command wrapper for update commands
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Update < Lazy
|
10
|
+
# Execute a lazy update command
|
11
|
+
#
|
12
|
+
# @see Commands::Update#call
|
13
|
+
#
|
14
|
+
# @return [Hash, Array<Hash>]
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def call(*args)
|
18
|
+
first = args.first
|
19
|
+
last = args.last
|
20
|
+
size = args.size
|
21
|
+
|
22
|
+
if size > 1 && last.is_a?(Array)
|
23
|
+
last.map.with_index do |parent, index|
|
24
|
+
children = evaluator.call(first, index)
|
25
|
+
|
26
|
+
children.map do |child|
|
27
|
+
command_proc[command, parent, child].call(child, parent)
|
28
|
+
end
|
29
|
+
end.reduce(:concat)
|
30
|
+
else
|
31
|
+
input = evaluator.call(first)
|
32
|
+
|
33
|
+
if input.is_a?(Array)
|
34
|
+
input.map.with_index do |item, _index|
|
35
|
+
command_proc[command, last, item].call(item, *args[1..size - 1])
|
36
|
+
end
|
37
|
+
else
|
38
|
+
command_proc[command, *(size > 1 ? [last, input] : [input])]
|
39
|
+
.call(input, *args[1..size - 1])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|