rom 5.4.2 → 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.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -71
  3. data/LICENSE +1 -1
  4. data/README.md +7 -6
  5. data/lib/rom/array_dataset.rb +46 -0
  6. data/lib/rom/associations/abstract.rb +217 -0
  7. data/lib/rom/associations/definitions/abstract.rb +150 -0
  8. data/lib/rom/associations/definitions/many_to_many.rb +29 -0
  9. data/lib/rom/associations/definitions/many_to_one.rb +14 -0
  10. data/lib/rom/associations/definitions/one_to_many.rb +14 -0
  11. data/lib/rom/associations/definitions/one_to_one.rb +14 -0
  12. data/lib/rom/associations/definitions/one_to_one_through.rb +14 -0
  13. data/lib/rom/associations/definitions.rb +7 -0
  14. data/lib/rom/associations/many_to_many.rb +128 -0
  15. data/lib/rom/associations/many_to_one.rb +65 -0
  16. data/lib/rom/associations/one_to_many.rb +65 -0
  17. data/lib/rom/associations/one_to_one.rb +13 -0
  18. data/lib/rom/associations/one_to_one_through.rb +13 -0
  19. data/lib/rom/associations/through_identifier.rb +41 -0
  20. data/lib/rom/attribute.rb +425 -0
  21. data/lib/rom/auto_curry.rb +70 -0
  22. data/lib/rom/cache.rb +87 -0
  23. data/lib/rom/changeset/associated.rb +110 -0
  24. data/lib/rom/changeset/create.rb +18 -0
  25. data/lib/rom/changeset/delete.rb +15 -0
  26. data/lib/rom/changeset/extensions/relation.rb +26 -0
  27. data/lib/rom/changeset/pipe.rb +81 -0
  28. data/lib/rom/changeset/pipe_registry.rb +27 -0
  29. data/lib/rom/changeset/stateful.rb +285 -0
  30. data/lib/rom/changeset/update.rb +81 -0
  31. data/lib/rom/changeset.rb +185 -0
  32. data/lib/rom/command.rb +351 -0
  33. data/lib/rom/command_compiler.rb +201 -0
  34. data/lib/rom/command_proxy.rb +36 -0
  35. data/lib/rom/commands/class_interface.rb +236 -0
  36. data/lib/rom/commands/composite.rb +55 -0
  37. data/lib/rom/commands/create.rb +15 -0
  38. data/lib/rom/commands/delete.rb +16 -0
  39. data/lib/rom/commands/graph/class_interface.rb +64 -0
  40. data/lib/rom/commands/graph/input_evaluator.rb +94 -0
  41. data/lib/rom/commands/graph.rb +88 -0
  42. data/lib/rom/commands/lazy/create.rb +35 -0
  43. data/lib/rom/commands/lazy/delete.rb +39 -0
  44. data/lib/rom/commands/lazy/update.rb +46 -0
  45. data/lib/rom/commands/lazy.rb +106 -0
  46. data/lib/rom/commands/update.rb +16 -0
  47. data/lib/rom/commands.rb +5 -0
  48. data/lib/rom/compat/auto_registration.rb +115 -0
  49. data/lib/rom/compat/auto_registration_strategies/base.rb +29 -0
  50. data/lib/rom/compat/auto_registration_strategies/custom_namespace.rb +84 -0
  51. data/lib/rom/compat/auto_registration_strategies/no_namespace.rb +33 -0
  52. data/lib/rom/compat/auto_registration_strategies/with_namespace.rb +29 -0
  53. data/lib/rom/compat/command.rb +74 -0
  54. data/lib/rom/compat/components/dsl/schema.rb +130 -0
  55. data/lib/rom/compat/components.rb +91 -0
  56. data/lib/rom/compat/global.rb +17 -0
  57. data/lib/rom/compat/mapper.rb +22 -0
  58. data/lib/rom/compat/registries.rb +47 -0
  59. data/lib/rom/compat/relation.rb +40 -0
  60. data/lib/rom/compat/schema/dsl.rb +260 -0
  61. data/lib/rom/compat/setting_proxy.rb +44 -0
  62. data/lib/rom/compat/setup.rb +151 -0
  63. data/lib/rom/compat/transformer.rb +49 -0
  64. data/lib/rom/compat.rb +22 -0
  65. data/lib/rom/components/association.rb +26 -0
  66. data/lib/rom/components/command.rb +24 -0
  67. data/lib/rom/components/core.rb +148 -0
  68. data/lib/rom/components/dataset.rb +60 -0
  69. data/lib/rom/components/dsl/association.rb +47 -0
  70. data/lib/rom/components/dsl/command.rb +60 -0
  71. data/lib/rom/components/dsl/core.rb +126 -0
  72. data/lib/rom/components/dsl/dataset.rb +33 -0
  73. data/lib/rom/components/dsl/gateway.rb +14 -0
  74. data/lib/rom/components/dsl/mapper.rb +70 -0
  75. data/lib/rom/components/dsl/relation.rb +49 -0
  76. data/lib/rom/components/dsl/schema.rb +150 -0
  77. data/lib/rom/components/dsl/view.rb +82 -0
  78. data/lib/rom/components/dsl.rb +255 -0
  79. data/lib/rom/components/gateway.rb +50 -0
  80. data/lib/rom/components/mapper.rb +29 -0
  81. data/lib/rom/components/provider.rb +160 -0
  82. data/lib/rom/components/registry.rb +154 -0
  83. data/lib/rom/components/relation.rb +41 -0
  84. data/lib/rom/components/schema.rb +61 -0
  85. data/lib/rom/components/view.rb +55 -0
  86. data/lib/rom/components.rb +55 -0
  87. data/lib/rom/configuration_dsl.rb +4 -0
  88. data/lib/rom/constants.rb +135 -0
  89. data/lib/rom/container.rb +182 -0
  90. data/lib/rom/core.rb +125 -0
  91. data/lib/rom/data_proxy.rb +97 -0
  92. data/lib/rom/enumerable_dataset.rb +70 -0
  93. data/lib/rom/gateway.rb +232 -0
  94. data/lib/rom/global.rb +56 -0
  95. data/lib/rom/header/attribute.rb +190 -0
  96. data/lib/rom/header.rb +198 -0
  97. data/lib/rom/inferrer.rb +55 -0
  98. data/lib/rom/initializer.rb +80 -0
  99. data/lib/rom/lint/enumerable_dataset.rb +56 -0
  100. data/lib/rom/lint/gateway.rb +120 -0
  101. data/lib/rom/lint/linter.rb +79 -0
  102. data/lib/rom/lint/spec.rb +22 -0
  103. data/lib/rom/lint/test.rb +98 -0
  104. data/lib/rom/loader.rb +161 -0
  105. data/lib/rom/mapper/attribute_dsl.rb +480 -0
  106. data/lib/rom/mapper/dsl.rb +107 -0
  107. data/lib/rom/mapper/model_dsl.rb +61 -0
  108. data/lib/rom/mapper.rb +99 -0
  109. data/lib/rom/mapper_compiler.rb +84 -0
  110. data/lib/rom/memory/associations/many_to_many.rb +12 -0
  111. data/lib/rom/memory/associations/many_to_one.rb +12 -0
  112. data/lib/rom/memory/associations/one_to_many.rb +12 -0
  113. data/lib/rom/memory/associations/one_to_one.rb +12 -0
  114. data/lib/rom/memory/associations.rb +6 -0
  115. data/lib/rom/memory/commands.rb +60 -0
  116. data/lib/rom/memory/dataset.rb +127 -0
  117. data/lib/rom/memory/gateway.rb +66 -0
  118. data/lib/rom/memory/mapper_compiler.rb +10 -0
  119. data/lib/rom/memory/relation.rb +91 -0
  120. data/lib/rom/memory/schema.rb +32 -0
  121. data/lib/rom/memory/storage.rb +61 -0
  122. data/lib/rom/memory/types.rb +11 -0
  123. data/lib/rom/memory.rb +7 -0
  124. data/lib/rom/model_builder.rb +103 -0
  125. data/lib/rom/open_struct.rb +112 -0
  126. data/lib/rom/pipeline.rb +111 -0
  127. data/lib/rom/plugin.rb +130 -0
  128. data/lib/rom/plugins/class_methods.rb +37 -0
  129. data/lib/rom/plugins/command/schema.rb +45 -0
  130. data/lib/rom/plugins/command/timestamps.rb +149 -0
  131. data/lib/rom/plugins/dsl.rb +53 -0
  132. data/lib/rom/plugins/relation/changeset.rb +97 -0
  133. data/lib/rom/plugins/relation/instrumentation.rb +66 -0
  134. data/lib/rom/plugins/relation/registry_reader.rb +36 -0
  135. data/lib/rom/plugins/schema/timestamps.rb +59 -0
  136. data/lib/rom/plugins.rb +100 -0
  137. data/lib/rom/processor/composer.rb +37 -0
  138. data/lib/rom/processor/transformer.rb +415 -0
  139. data/lib/rom/processor.rb +30 -0
  140. data/lib/rom/registries/associations.rb +26 -0
  141. data/lib/rom/registries/commands.rb +11 -0
  142. data/lib/rom/registries/container.rb +12 -0
  143. data/lib/rom/registries/datasets.rb +21 -0
  144. data/lib/rom/registries/gateways.rb +8 -0
  145. data/lib/rom/registries/mappers.rb +21 -0
  146. data/lib/rom/registries/nestable.rb +32 -0
  147. data/lib/rom/registries/relations.rb +8 -0
  148. data/lib/rom/registries/root.rb +203 -0
  149. data/lib/rom/registries/schemas.rb +44 -0
  150. data/lib/rom/registries/views.rb +11 -0
  151. data/lib/rom/relation/class_interface.rb +61 -0
  152. data/lib/rom/relation/combined.rb +160 -0
  153. data/lib/rom/relation/commands.rb +65 -0
  154. data/lib/rom/relation/composite.rb +53 -0
  155. data/lib/rom/relation/curried.rb +129 -0
  156. data/lib/rom/relation/graph.rb +107 -0
  157. data/lib/rom/relation/loaded.rb +136 -0
  158. data/lib/rom/relation/materializable.rb +62 -0
  159. data/lib/rom/relation/name.rb +122 -0
  160. data/lib/rom/relation/wrap.rb +64 -0
  161. data/lib/rom/relation.rb +625 -0
  162. data/lib/rom/repository/class_interface.rb +162 -0
  163. data/lib/rom/repository/relation_reader.rb +48 -0
  164. data/lib/rom/repository/root.rb +75 -0
  165. data/lib/rom/repository/session.rb +60 -0
  166. data/lib/rom/repository.rb +179 -0
  167. data/lib/rom/schema/associations_dsl.rb +222 -0
  168. data/lib/rom/schema/inferrer.rb +106 -0
  169. data/lib/rom/schema.rb +471 -0
  170. data/lib/rom/settings.rb +141 -0
  171. data/lib/rom/setup.rb +297 -0
  172. data/lib/rom/struct.rb +99 -0
  173. data/lib/rom/struct_compiler.rb +114 -0
  174. data/lib/rom/support/configurable.rb +213 -0
  175. data/lib/rom/support/inflector.rb +31 -0
  176. data/lib/rom/support/memoizable.rb +61 -0
  177. data/lib/rom/support/notifications.rb +238 -0
  178. data/lib/rom/transaction.rb +26 -0
  179. data/lib/rom/transformer.rb +46 -0
  180. data/lib/rom/types.rb +74 -0
  181. data/lib/rom/version.rb +1 -1
  182. data/lib/rom-changeset.rb +4 -0
  183. data/lib/rom-core.rb +3 -0
  184. data/lib/rom-repository.rb +4 -0
  185. data/lib/rom.rb +3 -3
  186. metadata +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
@@ -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