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/global.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/components"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# Globally accessible public interface exposed via ROM module
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
module Global
|
10
|
+
# Set base global registries in ROM constant
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def self.extended(rom)
|
14
|
+
super
|
15
|
+
|
16
|
+
rom.instance_variable_set("@adapters", {})
|
17
|
+
end
|
18
|
+
|
19
|
+
# An internal adapter identifier => adapter module map used by setup
|
20
|
+
#
|
21
|
+
# @return [Hash<Symbol=>Module>]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
attr_reader :adapters
|
25
|
+
|
26
|
+
# An internal component handler registry
|
27
|
+
#
|
28
|
+
# @return [Plugins]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
attr_reader :handlers
|
32
|
+
|
33
|
+
# @api public
|
34
|
+
def setup(*args, &block)
|
35
|
+
case args.first
|
36
|
+
when Setup
|
37
|
+
args.first
|
38
|
+
else
|
39
|
+
Setup.new(*args, &block)
|
40
|
+
end.finalize
|
41
|
+
end
|
42
|
+
|
43
|
+
# Register adapter namespace under a specified identifier
|
44
|
+
#
|
45
|
+
# @param [Symbol] identifier
|
46
|
+
# @param [Class,Module] adapter
|
47
|
+
#
|
48
|
+
# @return [self]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
def register_adapter(identifier, adapter)
|
52
|
+
adapters[identifier] = adapter
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
class Header
|
7
|
+
# An attribute provides information about a specific attribute in a tuple
|
8
|
+
#
|
9
|
+
# This may include information about how an attribute should be renamed,
|
10
|
+
# or how its value should coerced.
|
11
|
+
#
|
12
|
+
# More complex attributes describe how an attribute should be transformed.
|
13
|
+
#
|
14
|
+
# @private
|
15
|
+
class Attribute
|
16
|
+
include Dry::Equalizer(:name, :key, :type)
|
17
|
+
|
18
|
+
# @return [Symbol] name of an attribute
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
attr_reader :name
|
22
|
+
|
23
|
+
# @return [Symbol] key of an attribute that corresponds to tuple attribute
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
attr_reader :key
|
27
|
+
|
28
|
+
# @return [Symbol] type identifier (defaults to :object)
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
attr_reader :type
|
32
|
+
|
33
|
+
# @return [Hash] additional meta information
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
attr_reader :meta
|
37
|
+
|
38
|
+
# Return attribute class for a given meta hash
|
39
|
+
#
|
40
|
+
# @param [Hash] meta hash with type information and optional transformation info
|
41
|
+
#
|
42
|
+
# @return [Class]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def self.[](meta)
|
46
|
+
key = (meta.keys & TYPE_MAP.keys).first
|
47
|
+
TYPE_MAP.fetch(key || meta[:type], self)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Coerce an array with attribute meta-data into an attribute object
|
51
|
+
#
|
52
|
+
# @param [Array<Symbol,Hash>] input attribute name/options pair
|
53
|
+
#
|
54
|
+
# @return [Attribute]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def self.coerce(input)
|
58
|
+
name = input[0]
|
59
|
+
meta = (input[1] || {}).dup
|
60
|
+
|
61
|
+
meta[:type] ||= :object
|
62
|
+
|
63
|
+
meta[:header] = Header.coerce(meta[:header], model: meta[:model]) if meta.key?(:header)
|
64
|
+
|
65
|
+
self[meta].new(name, meta)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @api private
|
69
|
+
def initialize(name, meta)
|
70
|
+
@name = name
|
71
|
+
@meta = meta
|
72
|
+
@key = meta.fetch(:from) { name }
|
73
|
+
@type = meta.fetch(:type)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return if an attribute has a specific type identifier
|
77
|
+
#
|
78
|
+
# @api private
|
79
|
+
def typed?
|
80
|
+
type != :object
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return if an attribute should be aliased
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def aliased?
|
87
|
+
key != name
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return :key-to-:name mapping hash
|
91
|
+
#
|
92
|
+
# @return [Hash]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def mapping
|
96
|
+
{key => name}
|
97
|
+
end
|
98
|
+
|
99
|
+
def union?
|
100
|
+
key.is_a? ::Array
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Embedded attribute is a special attribute type that has a header
|
105
|
+
#
|
106
|
+
# This is the base of complex attributes like Hash or Group
|
107
|
+
#
|
108
|
+
# @private
|
109
|
+
class Embedded < Attribute
|
110
|
+
include Dry::Equalizer(:name, :key, :type, :header)
|
111
|
+
|
112
|
+
# return [Header] header of an attribute
|
113
|
+
#
|
114
|
+
# @api private
|
115
|
+
attr_reader :header
|
116
|
+
|
117
|
+
# @api private
|
118
|
+
def initialize(*)
|
119
|
+
super
|
120
|
+
@header = meta.fetch(:header)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Return tuple keys from the header
|
124
|
+
#
|
125
|
+
# @return [Array<Symbol>]
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
def tuple_keys
|
129
|
+
header.tuple_keys
|
130
|
+
end
|
131
|
+
|
132
|
+
def pop_keys
|
133
|
+
header.pop_keys
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Array is an embedded attribute type
|
138
|
+
Array = Class.new(Embedded)
|
139
|
+
|
140
|
+
# Hash is an embedded attribute type
|
141
|
+
Hash = Class.new(Embedded)
|
142
|
+
|
143
|
+
# Combined is an embedded attribute type describing combination of multiple
|
144
|
+
# relations
|
145
|
+
Combined = Class.new(Embedded)
|
146
|
+
|
147
|
+
# Wrap is a special type of Hash attribute that requires wrapping
|
148
|
+
# transformation
|
149
|
+
Wrap = Class.new(Hash)
|
150
|
+
|
151
|
+
# Unwrap is a special type of Hash attribute that requires unwrapping
|
152
|
+
# transformation
|
153
|
+
Unwrap = Class.new(Hash)
|
154
|
+
|
155
|
+
# Group is a special type of Array attribute that requires grouping
|
156
|
+
# transformation
|
157
|
+
Group = Class.new(Array)
|
158
|
+
|
159
|
+
# Ungroup is a special type of Array attribute that requires ungrouping
|
160
|
+
# transformation
|
161
|
+
Ungroup = Class.new(Array)
|
162
|
+
|
163
|
+
# Fold is a special type of Array attribute that requires folding
|
164
|
+
# transformation
|
165
|
+
Fold = Class.new(Array)
|
166
|
+
|
167
|
+
# Unfold is a special type of Array attribute that requires unfolding
|
168
|
+
# transformation
|
169
|
+
Unfold = Class.new(Array)
|
170
|
+
|
171
|
+
# Exclude is a special type of Attribute to be removed
|
172
|
+
Exclude = Class.new(Attribute)
|
173
|
+
|
174
|
+
# TYPE_MAP is a (hash) map of ROM::Header identifiers to ROM::Header types
|
175
|
+
#
|
176
|
+
# @private
|
177
|
+
TYPE_MAP = {
|
178
|
+
combine: Combined,
|
179
|
+
wrap: Wrap,
|
180
|
+
unwrap: Unwrap,
|
181
|
+
group: Group,
|
182
|
+
ungroup: Ungroup,
|
183
|
+
fold: Fold,
|
184
|
+
unfold: Unfold,
|
185
|
+
hash: Hash,
|
186
|
+
array: Array,
|
187
|
+
exclude: Exclude
|
188
|
+
}.freeze
|
189
|
+
end
|
190
|
+
end
|
data/lib/rom/header.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/header/attribute"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# Header provides information about data mapping of a specific relation
|
7
|
+
#
|
8
|
+
# Processors use headers to build objects that process raw relations that go
|
9
|
+
# through mappers.
|
10
|
+
#
|
11
|
+
# @private
|
12
|
+
class Header
|
13
|
+
include Enumerable
|
14
|
+
include Dry::Equalizer(:attributes, :model)
|
15
|
+
|
16
|
+
# @return [Class] optional model associated with a header
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
attr_reader :model
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
attr_reader :reject_keys
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
attr_reader :copy_keys
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
attr_reader :attributes
|
29
|
+
|
30
|
+
# @return [Hash] attribute key/name mapping for all primitive attributes
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
attr_reader :mapping
|
34
|
+
|
35
|
+
# @return [Array] all attribute keys that are in a tuple
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
attr_reader :tuple_keys
|
39
|
+
|
40
|
+
# @return [Array] all attribute names that are popping from a tuple
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
attr_reader :pop_keys
|
44
|
+
|
45
|
+
# Coerce array with attribute definitions into a header object
|
46
|
+
#
|
47
|
+
# @param [Array<Array>] input attribute name/option pairs
|
48
|
+
# @param [Hash] options
|
49
|
+
#
|
50
|
+
# @return [Header]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
def self.coerce(input, options = {})
|
54
|
+
if input.instance_of?(self)
|
55
|
+
input
|
56
|
+
else
|
57
|
+
attributes = input.each_with_object({}) { |pair, h|
|
58
|
+
h[pair.first] = Attribute.coerce(pair)
|
59
|
+
}
|
60
|
+
|
61
|
+
new(attributes, options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api private
|
66
|
+
def initialize(attributes, options = {})
|
67
|
+
@options = options
|
68
|
+
@model = options[:model]
|
69
|
+
@copy_keys = options.fetch(:copy_keys, false)
|
70
|
+
@reject_keys = options.fetch(:reject_keys, false)
|
71
|
+
|
72
|
+
@attributes = attributes
|
73
|
+
initialize_mapping
|
74
|
+
initialize_tuple_keys
|
75
|
+
initialize_pop_keys
|
76
|
+
end
|
77
|
+
|
78
|
+
# Iterate over attributes
|
79
|
+
#
|
80
|
+
# @yield [Attribute]
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
def each(&block)
|
84
|
+
attributes.each_value(&block)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return if there are any aliased attributes
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def aliased?
|
91
|
+
any?(&:aliased?)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return attribute keys
|
95
|
+
#
|
96
|
+
# An attribute key corresponds to tuple attribute names
|
97
|
+
#
|
98
|
+
# @api private
|
99
|
+
def keys
|
100
|
+
attributes.keys
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return attribute identified by its name
|
104
|
+
#
|
105
|
+
# @return [Attribute]
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
def [](name)
|
109
|
+
attributes.fetch(name)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return all Combined attributes
|
113
|
+
#
|
114
|
+
# @return [Array<Combined>]
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def combined
|
118
|
+
by_type(Combined)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns all attributes that require preprocessing
|
122
|
+
#
|
123
|
+
# @return [Array<Group,Fold>]
|
124
|
+
#
|
125
|
+
# @api private
|
126
|
+
def preprocessed
|
127
|
+
by_type(Group, Fold)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns all attributes that require postprocessing
|
131
|
+
#
|
132
|
+
# @return [Array<Ungroup,Unfold>]
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
def postprocessed
|
136
|
+
by_type(Ungroup, Unfold)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return all Wrap attributes
|
140
|
+
#
|
141
|
+
# @return [Array<Wrap>]
|
142
|
+
#
|
143
|
+
# @api private
|
144
|
+
def wraps
|
145
|
+
by_type(Wrap)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Return all non-primitive attributes that don't require mapping
|
149
|
+
#
|
150
|
+
# @return [Array<Group,Fold,Ungroup,Unfold,Wrap,Unwrap>]
|
151
|
+
#
|
152
|
+
# @api private
|
153
|
+
def non_primitives
|
154
|
+
preprocessed + wraps
|
155
|
+
end
|
156
|
+
|
157
|
+
# Return all primitive attributes that require mapping
|
158
|
+
#
|
159
|
+
# @return [Array<Attribute>]
|
160
|
+
#
|
161
|
+
# @api private
|
162
|
+
def primitives
|
163
|
+
to_a - non_primitives
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
# Find all attribute matching specific attribute class (not kind)
|
169
|
+
#
|
170
|
+
# @return [Array<Attribute>]
|
171
|
+
#
|
172
|
+
# @api private
|
173
|
+
def by_type(*types)
|
174
|
+
select { |attribute| types.include?(attribute.class) }
|
175
|
+
end
|
176
|
+
|
177
|
+
# Set mapping hash from primitive attributes
|
178
|
+
#
|
179
|
+
# @api private
|
180
|
+
def initialize_mapping
|
181
|
+
@mapping = primitives.map(&:mapping).reduce(:merge) || {}
|
182
|
+
end
|
183
|
+
|
184
|
+
# Set all tuple keys from all attributes going deep into Wrap and Group too
|
185
|
+
#
|
186
|
+
# @api private
|
187
|
+
def initialize_tuple_keys
|
188
|
+
@tuple_keys = mapping.keys.flatten + non_primitives.flat_map(&:tuple_keys)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Set all tuple keys from all attributes popping from Unwrap and Ungroup
|
192
|
+
#
|
193
|
+
# @api private
|
194
|
+
def initialize_pop_keys
|
195
|
+
@pop_keys = mapping.values + non_primitives.flat_map(&:tuple_keys)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
data/lib/rom/inferrer.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/memoizable"
|
4
|
+
require "dry/effects"
|
5
|
+
|
6
|
+
require "rom/constants"
|
7
|
+
require "rom/cache"
|
8
|
+
require "rom/command_compiler"
|
9
|
+
require "rom/mapper_compiler"
|
10
|
+
|
11
|
+
module ROM
|
12
|
+
# @api private
|
13
|
+
class Inferrer
|
14
|
+
include Dry::Core::Memoizable
|
15
|
+
include Dry::Effects::Reader(:registry)
|
16
|
+
|
17
|
+
attr_reader :cache, :config
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def initialize
|
21
|
+
@cache = Cache.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def call(ast, type, **opts)
|
26
|
+
compiler(type, **opts).then do |compiler|
|
27
|
+
case type
|
28
|
+
when :mappers
|
29
|
+
compiler.(ast)
|
30
|
+
when :commands
|
31
|
+
compiler.(*ast) # TODO: unify this
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
memoize def compiler(type, adapter:)
|
38
|
+
case type
|
39
|
+
when :mappers
|
40
|
+
# TODO: move this to config
|
41
|
+
adapter_ns = ROM.adapters[adapter] || ROM
|
42
|
+
|
43
|
+
klass = if adapter_ns.const_defined?(:MapperCompiler)
|
44
|
+
adapter_ns::MapperCompiler
|
45
|
+
else
|
46
|
+
MapperCompiler
|
47
|
+
end
|
48
|
+
|
49
|
+
klass.new(cache: cache)
|
50
|
+
when :commands
|
51
|
+
CommandCompiler.new(cache: cache.namespaced(:commands), registry: registry)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-initializer"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
# @api private
|
7
|
+
module Initializer
|
8
|
+
# @api private
|
9
|
+
module DefineWithHook
|
10
|
+
# @api private
|
11
|
+
def param(*)
|
12
|
+
super.tap { __define_with__ }
|
13
|
+
end
|
14
|
+
ruby2_keywords(:param) if respond_to?(:ruby2_keywords, true)
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
def option(*)
|
18
|
+
super.tap do
|
19
|
+
__define_with__ unless method_defined?(:with)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
ruby2_keywords(:option) if respond_to?(:ruby2_keywords, true)
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def __define_with__
|
26
|
+
seq_names = dry_initializer
|
27
|
+
.definitions
|
28
|
+
.reject { |_, d| d.option }
|
29
|
+
.keys
|
30
|
+
.join(", ")
|
31
|
+
|
32
|
+
seq_names << ", " unless seq_names.empty?
|
33
|
+
|
34
|
+
undef_method(:with) if method_defined?(:with)
|
35
|
+
|
36
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
37
|
+
def with(**new_options)
|
38
|
+
if new_options.empty?
|
39
|
+
self
|
40
|
+
else
|
41
|
+
self.class.new(#{seq_names}**options, **new_options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
def self.extended(base)
|
50
|
+
base.extend(Dry::Initializer[undefined: false])
|
51
|
+
base.extend(DefineWithHook)
|
52
|
+
base.include(InstanceMethods)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
module InstanceMethods
|
57
|
+
# Instance options
|
58
|
+
#
|
59
|
+
# @return [Hash]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def options
|
63
|
+
@__options__ ||= self.class.dry_initializer.definitions.values.each_with_object({}) do |item, obj|
|
64
|
+
obj[item.target] = instance_variable_get(item.ivar)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
define_method(:class, Kernel.instance_method(:class))
|
69
|
+
define_method(:instance_variable_get, Kernel.instance_method(:instance_variable_get))
|
70
|
+
|
71
|
+
# This makes sure we memoize options before an object becomes frozen
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
def freeze
|
75
|
+
options
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rom/lint/linter"
|
4
|
+
|
5
|
+
module ROM
|
6
|
+
module Lint
|
7
|
+
# Ensures that a [ROM::EnumerableDataset] extension correctly yields
|
8
|
+
# arrays and tuples
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class EnumerableDataset < ROM::Lint::Linter
|
12
|
+
# The linted subject
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
attr_reader :dataset
|
16
|
+
|
17
|
+
# The expected data
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
attr_reader :data
|
21
|
+
|
22
|
+
# Create a linter for EnumerableDataset
|
23
|
+
#
|
24
|
+
# @param [EnumerableDataset] dataset the linted subject
|
25
|
+
# @param [Object] data the expected data
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def initialize(dataset, data)
|
29
|
+
@dataset = dataset
|
30
|
+
@data = data
|
31
|
+
end
|
32
|
+
|
33
|
+
# Lint: Ensure that +dataset+ yield tuples via +each+
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def lint_each
|
37
|
+
result = []
|
38
|
+
dataset.each do |tuple|
|
39
|
+
result << tuple
|
40
|
+
end
|
41
|
+
return if result == data
|
42
|
+
|
43
|
+
complain "#{dataset.class}#each must yield tuples"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Lint: Ensure that +dataset+'s array equals to expected +data+
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
def lint_to_a
|
50
|
+
return if dataset.to_a == data
|
51
|
+
|
52
|
+
complain "#{dataset.class}#to_a must cast dataset to an array"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|