rom 2.0.2 → 3.0.0

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -7
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile +17 -2
  6. data/Rakefile +7 -2
  7. data/lib/rom/array_dataset.rb +44 -0
  8. data/lib/rom/association_set.rb +11 -5
  9. data/lib/rom/auto_curry.rb +55 -0
  10. data/lib/rom/command.rb +331 -47
  11. data/lib/rom/command_registry.rb +7 -18
  12. data/lib/rom/commands/class_interface.rb +120 -6
  13. data/lib/rom/commands/composite.rb +0 -1
  14. data/lib/rom/commands/graph.rb +7 -15
  15. data/lib/rom/commands/lazy/update.rb +1 -1
  16. data/lib/rom/configuration.rb +2 -0
  17. data/lib/rom/configuration_dsl/command.rb +6 -8
  18. data/lib/rom/configuration_dsl/mapper.rb +2 -3
  19. data/lib/rom/configuration_dsl/mapper_dsl.rb +0 -1
  20. data/lib/rom/configuration_dsl/relation.rb +4 -4
  21. data/lib/rom/configuration_dsl.rb +0 -4
  22. data/lib/rom/constants.rb +7 -1
  23. data/lib/rom/container.rb +11 -17
  24. data/lib/rom/create_container.rb +0 -2
  25. data/lib/rom/data_proxy.rb +94 -0
  26. data/lib/rom/enumerable_dataset.rb +68 -0
  27. data/lib/rom/gateway.rb +74 -32
  28. data/lib/rom/global/plugin_dsl.rb +0 -2
  29. data/lib/rom/global.rb +0 -2
  30. data/lib/rom/initializer.rb +26 -0
  31. data/lib/rom/lint/gateway.rb +17 -0
  32. data/lib/rom/mapper_registry.rb +1 -1
  33. data/lib/rom/memory/commands.rb +0 -2
  34. data/lib/rom/memory/dataset.rb +1 -2
  35. data/lib/rom/memory/relation.rb +14 -1
  36. data/lib/rom/memory/schema.rb +13 -0
  37. data/lib/rom/plugin_registry.rb +1 -1
  38. data/lib/rom/plugins/command/schema.rb +2 -2
  39. data/lib/rom/plugins/configuration/configuration_dsl.rb +6 -2
  40. data/lib/rom/plugins/relation/key_inference.rb +4 -2
  41. data/lib/rom/plugins/relation/registry_reader.rb +5 -1
  42. data/lib/rom/registry.rb +50 -0
  43. data/lib/rom/relation/class_interface.rb +142 -30
  44. data/lib/rom/relation/curried.rb +15 -15
  45. data/lib/rom/relation/view_dsl.rb +31 -0
  46. data/lib/rom/relation.rb +101 -41
  47. data/lib/rom/schema/attribute.rb +367 -0
  48. data/lib/rom/schema/dsl.rb +14 -10
  49. data/lib/rom/schema.rb +337 -19
  50. data/lib/rom/setup/auto_registration.rb +20 -17
  51. data/lib/rom/setup/auto_registration_strategies/base.rb +8 -3
  52. data/lib/rom/setup/auto_registration_strategies/custom_namespace.rb +4 -3
  53. data/lib/rom/setup/auto_registration_strategies/no_namespace.rb +5 -4
  54. data/lib/rom/setup/auto_registration_strategies/with_namespace.rb +3 -3
  55. data/lib/rom/setup/finalize/finalize_commands.rb +1 -1
  56. data/lib/rom/setup/finalize/finalize_mappers.rb +1 -1
  57. data/lib/rom/setup/finalize/finalize_relations.rb +4 -2
  58. data/lib/rom/setup/finalize.rb +1 -1
  59. data/lib/rom/transaction.rb +24 -0
  60. data/lib/rom/types.rb +9 -1
  61. data/lib/rom/version.rb +1 -1
  62. data/lib/rom.rb +4 -8
  63. data/rom.gemspec +5 -4
  64. data/spec/integration/command_registry_spec.rb +1 -14
  65. data/spec/integration/commands/create_spec.rb +5 -25
  66. data/spec/integration/commands/delete_spec.rb +1 -1
  67. data/spec/integration/commands/error_handling_spec.rb +1 -1
  68. data/spec/integration/commands/graph_spec.rb +20 -14
  69. data/spec/integration/commands/update_spec.rb +4 -27
  70. data/spec/integration/commands_spec.rb +1 -1
  71. data/spec/integration/{repositories → gateways}/extending_relations_spec.rb +1 -1
  72. data/spec/integration/{repositories → gateways}/setting_logger_spec.rb +2 -2
  73. data/spec/integration/mappers/combine_spec.rb +1 -1
  74. data/spec/integration/mappers/deep_embedded_spec.rb +1 -1
  75. data/spec/integration/mappers/definition_dsl_spec.rb +1 -1
  76. data/spec/integration/mappers/embedded_spec.rb +1 -1
  77. data/spec/integration/mappers/exclude_spec.rb +1 -1
  78. data/spec/integration/mappers/fold_spec.rb +1 -1
  79. data/spec/integration/mappers/group_spec.rb +1 -1
  80. data/spec/integration/mappers/overwrite_attributes_value_spec.rb +1 -1
  81. data/spec/integration/mappers/prefix_separator_spec.rb +1 -1
  82. data/spec/integration/mappers/prefix_spec.rb +1 -1
  83. data/spec/integration/mappers/prefixing_attributes_spec.rb +1 -1
  84. data/spec/integration/mappers/registering_custom_mappers_spec.rb +1 -1
  85. data/spec/integration/mappers/renaming_attributes_spec.rb +1 -1
  86. data/spec/integration/mappers/reusing_mappers_spec.rb +1 -1
  87. data/spec/integration/mappers/step_spec.rb +1 -1
  88. data/spec/integration/mappers/symbolizing_attributes_spec.rb +1 -1
  89. data/spec/integration/mappers/unfold_spec.rb +1 -1
  90. data/spec/integration/mappers/ungroup_spec.rb +2 -2
  91. data/spec/integration/mappers/unwrap_spec.rb +2 -2
  92. data/spec/integration/mappers/wrap_spec.rb +1 -1
  93. data/spec/integration/memory/commands/create_spec.rb +1 -1
  94. data/spec/integration/memory/commands/delete_spec.rb +1 -1
  95. data/spec/integration/memory/commands/update_spec.rb +1 -1
  96. data/spec/integration/multi_env_spec.rb +1 -1
  97. data/spec/integration/multi_repo_spec.rb +1 -1
  98. data/spec/integration/relations/default_dataset_spec.rb +1 -1
  99. data/spec/integration/relations/reading_spec.rb +1 -1
  100. data/spec/integration/relations/registry_dsl_spec.rb +1 -1
  101. data/spec/integration/setup_spec.rb +10 -4
  102. data/spec/shared/command_graph.rb +8 -4
  103. data/spec/shared/enumerable_dataset.rb +1 -1
  104. data/spec/spec_helper.rb +7 -9
  105. data/spec/support/schema.rb +14 -0
  106. data/spec/unit/rom/array_dataset_spec.rb +59 -0
  107. data/spec/unit/rom/association_set_spec.rb +4 -0
  108. data/spec/unit/rom/auto_curry_spec.rb +63 -0
  109. data/spec/unit/rom/commands/graph_spec.rb +12 -11
  110. data/spec/unit/rom/commands/lazy_spec.rb +8 -5
  111. data/spec/unit/rom/commands/pre_and_post_processors_spec.rb +336 -0
  112. data/spec/unit/rom/commands/result_spec.rb +1 -1
  113. data/spec/unit/rom/commands_spec.rb +26 -3
  114. data/spec/unit/rom/configuration_spec.rb +1 -1
  115. data/spec/unit/rom/container_spec.rb +15 -5
  116. data/spec/unit/rom/create_container_spec.rb +1 -1
  117. data/spec/unit/rom/enumerable_dataset_spec.rb +15 -0
  118. data/spec/unit/rom/gateway_spec.rb +1 -1
  119. data/spec/unit/rom/mapper_registry_spec.rb +1 -1
  120. data/spec/unit/rom/memory/commands_spec.rb +1 -1
  121. data/spec/unit/rom/memory/dataset_spec.rb +1 -1
  122. data/spec/unit/rom/memory/{repository_spec.rb → gateway_spec.rb} +1 -1
  123. data/spec/unit/rom/memory/inheritance_spec.rb +32 -0
  124. data/spec/unit/rom/memory/relation_spec.rb +15 -3
  125. data/spec/unit/rom/memory/storage_spec.rb +1 -1
  126. data/spec/unit/rom/plugin_spec.rb +1 -1
  127. data/spec/unit/rom/plugins/command/schema_spec.rb +5 -5
  128. data/spec/unit/rom/plugins/relation/key_inference_spec.rb +1 -1
  129. data/spec/unit/rom/registry_spec.rb +86 -0
  130. data/spec/unit/rom/relation/attribute_reader_spec.rb +17 -0
  131. data/spec/unit/rom/relation/call_spec.rb +51 -0
  132. data/spec/unit/rom/relation/composite_spec.rb +1 -1
  133. data/spec/unit/rom/relation/graph_spec.rb +1 -1
  134. data/spec/unit/rom/relation/lazy/combine_spec.rb +1 -1
  135. data/spec/unit/rom/relation/lazy_spec.rb +1 -1
  136. data/spec/unit/rom/relation/loaded_spec.rb +1 -1
  137. data/spec/unit/rom/relation/schema_spec.rb +50 -6
  138. data/spec/unit/rom/relation/view_spec.rb +122 -0
  139. data/spec/unit/rom/relation_spec.rb +20 -5
  140. data/spec/unit/rom/schema/accessing_attributes_spec.rb +52 -0
  141. data/spec/unit/rom/schema/append_spec.rb +17 -0
  142. data/spec/unit/rom/schema/exclude_spec.rb +15 -0
  143. data/spec/unit/rom/schema/finalize_spec.rb +59 -0
  144. data/spec/unit/rom/schema/key_predicate_spec.rb +15 -0
  145. data/spec/unit/rom/schema/merge_spec.rb +17 -0
  146. data/spec/unit/rom/schema/prefix_spec.rb +16 -0
  147. data/spec/unit/rom/schema/project_spec.rb +15 -0
  148. data/spec/unit/rom/schema/rename_spec.rb +22 -0
  149. data/spec/unit/rom/schema/type_spec.rb +49 -0
  150. data/spec/unit/rom/schema/uniq_spec.rb +21 -0
  151. data/spec/unit/rom/schema/wrap_spec.rb +17 -0
  152. data/spec/unit/rom/schema_spec.rb +2 -2
  153. metadata +79 -17
  154. data/lib/rom/plugins/relation/view/dsl.rb +0 -32
  155. data/lib/rom/plugins/relation/view.rb +0 -95
  156. data/spec/unit/rom/plugins/relation/view_spec.rb +0 -51
data/lib/rom/schema.rb CHANGED
@@ -1,16 +1,43 @@
1
1
  require 'dry-equalizer'
2
2
 
3
- require 'rom/support/constants'
3
+ require 'rom/schema/attribute'
4
4
  require 'rom/schema/dsl'
5
5
  require 'rom/association_set'
6
6
 
7
7
  module ROM
8
8
  # Relation schema
9
9
  #
10
+ # Schemas hold detailed information about relation tuples, including their
11
+ # primitive types (String, Integer, Hash, etc. or custom classes), as well as
12
+ # various meta information like primary/foreign key and literally any other
13
+ # information that a given database adapter may need.
14
+ #
15
+ # Adapters can extend this class and it can be used in adapter-specific relations.
16
+ # In example rom-sql extends schema with Association DSL and many additional
17
+ # SQL-specific APIs in schema types.
18
+ #
19
+ # Schemas are used for projecting canonical relations into other relations and
20
+ # every relation object maintains its schema. This means that we always have
21
+ # all information about relation tuples, even when a relation was projected and
22
+ # diverged from its canonical form.
23
+ #
24
+ # Furthermore schema attributes know their source relations, which makes it
25
+ # possible to merge schemas from multiple relations and maintain information
26
+ # about the source relations. In example when two relations are joined, their
27
+ # schemas are merged, and we know which attributes belong to which relation.
28
+ #
10
29
  # @api public
11
30
  class Schema
12
31
  EMPTY_ASSOCIATION_SET = AssociationSet.new(EMPTY_HASH).freeze
13
32
 
33
+ DEFAULT_INFERRER = proc { [EMPTY_ARRAY, EMPTY_ARRAY].freeze }
34
+
35
+ MissingAttributesError = Class.new(StandardError) do
36
+ def initialize(name, attributes)
37
+ super("missing attributes in #{name.inspect} schema: #{attributes.map(&:inspect).join(', ')}")
38
+ end
39
+ end
40
+
14
41
  include Dry::Equalizer(:name, :attributes, :associations)
15
42
  include Enumerable
16
43
 
@@ -19,7 +46,7 @@ module ROM
19
46
  attr_reader :name
20
47
 
21
48
  # @!attribute [r] attributes
22
- # @return [Hash] The hash with schema attribute types
49
+ # @return [Array] Array with schema attributes
23
50
  attr_reader :attributes
24
51
 
25
52
  # @!attribute [r] associations
@@ -30,43 +57,246 @@ module ROM
30
57
  # @return [#call] An optional inferrer object used in `finalize!`
31
58
  attr_reader :inferrer
32
59
 
33
- # @!attribute [r] primary_key
34
- # @return [Array<Dry::Types::Definition] Primary key array
35
- attr_reader :primary_key
60
+ # @api private
61
+ attr_reader :options
62
+
63
+ # @api private
64
+ attr_reader :relations
36
65
 
37
- alias_method :to_h, :attributes
66
+ alias_method :to_ary, :attributes
67
+
68
+ # Define a relation schema from plain rom types
69
+ #
70
+ # Resulting schema will decorate plain rom types with adapter-specific types
71
+ # By default `Schema::Attribute` will be used
72
+ #
73
+ # @param [Relation::Name, Symbol] name The schema name, typically ROM::Relation::Name
74
+ #
75
+ # @return [Schema]
76
+ #
77
+ # @api public
78
+ def self.define(name, attr_class: Attribute, attributes: EMPTY_ARRAY, associations: EMPTY_ASSOCIATION_SET, inferrer: DEFAULT_INFERRER)
79
+ new(
80
+ name,
81
+ attributes: attributes(attributes, attr_class),
82
+ associations: associations,
83
+ inferrer: inferrer,
84
+ attr_class: attr_class
85
+ )
86
+ end
38
87
 
39
88
  # @api private
40
- def initialize(name, attributes, inferrer: nil, associations: EMPTY_ASSOCIATION_SET)
89
+ def self.attributes(attributes, attr_class)
90
+ attributes.map { |type| attr_class.new(type) }
91
+ end
92
+
93
+ # @api private
94
+ def initialize(name, options)
41
95
  @name = name
42
- @attributes = attributes
43
- @associations = associations
44
- @inferrer = inferrer
96
+ @options = options
97
+ @attributes = options[:attributes] || EMPTY_ARRAY
98
+ @associations = options[:associations]
99
+ @inferrer = options[:inferrer] || DEFAULT_INFERRER
100
+ @relations = options[:relations] || EMPTY_HASH
101
+ end
102
+
103
+ # Abstract method for creating a new relation based on schema definition
104
+ #
105
+ # This can be used by views to generate a new relation automatically.
106
+ # In example a schema can project a relation, join any additional relations
107
+ # if it uncludes attributes from other relations etc.
108
+ #
109
+ # Default implementation is a no-op and it simply returns back untouched relation
110
+ #
111
+ # @param [Relation]
112
+ #
113
+ # @return [Relation]
114
+ #
115
+ # @api public
116
+ def call(relation)
117
+ relation
45
118
  end
46
119
 
47
120
  # Iterate over schema's attributes
48
121
  #
49
- # @yield [Dry::Data::Type]
122
+ # @yield [Schema::Attribute]
50
123
  #
51
124
  # @api public
52
125
  def each(&block)
53
- attributes.each_value(&block)
126
+ attributes.each(&block)
127
+ end
128
+
129
+ # Check if schema has any attributes
130
+ #
131
+ # @return [TrueClass, FalseClass]
132
+ #
133
+ # @api public
134
+ def empty?
135
+ attributes.size == 0
136
+ end
137
+
138
+ # Coerce schema into a <AttributeName=>Attribute> Hash
139
+ #
140
+ # @return [Hash]
141
+ #
142
+ # @api public
143
+ def to_h
144
+ each_with_object({}) { |attr, h| h[attr.name] = attr }
54
145
  end
55
146
 
56
147
  # Return attribute
57
148
  #
149
+ # @param [Symbol] key The attribute name
150
+ # @param [Symbol, Relation::Name] src The source relation (for merged schemas)
151
+ #
152
+ # @raise KeyError
153
+ #
154
+ # @api public
155
+ def [](key, src = name.to_sym)
156
+ attr =
157
+ if count_index[key].equal?(1)
158
+ name_index[key]
159
+ else
160
+ source_index[src][key]
161
+ end
162
+
163
+ unless attr
164
+ raise(KeyError, "#{key.inspect} attribute doesn't exist in #{src} schema")
165
+ end
166
+
167
+ attr
168
+ end
169
+
170
+ # Project a schema to include only specified attributes
171
+ #
172
+ # @param [*Array<Symbol, Schema::Attribute>] names Attribute names
173
+ #
174
+ # @return [Schema]
175
+ #
176
+ # @api public
177
+ def project(*names)
178
+ new(names.map { |name| name.is_a?(Symbol) ? self[name] : name })
179
+ end
180
+
181
+ # Exclude provided attributes from a schema
182
+ #
183
+ # @param [*Array] names Attribute names
184
+ #
185
+ # @return [Schema]
186
+ #
187
+ # @api public
188
+ def exclude(*names)
189
+ project(*(map(&:name) - names))
190
+ end
191
+
192
+ # Project a schema with renamed attributes
193
+ #
194
+ # @param [Hash] mapping The attribute mappings
195
+ #
196
+ # @return [Schema]
197
+ #
198
+ # @api public
199
+ def rename(mapping)
200
+ new_attributes = map do |attr|
201
+ alias_name = mapping[attr.name]
202
+ alias_name ? attr.aliased(alias_name) : attr
203
+ end
204
+
205
+ new(new_attributes)
206
+ end
207
+
208
+ # Project a schema with renamed attributes using provided prefix
209
+ #
210
+ # @param [Symbol] prefix The name of the prefix
211
+ #
212
+ # @return [Schema]
213
+ #
214
+ # @api public
215
+ def prefix(prefix)
216
+ new(map { |attr| attr.prefixed(prefix) })
217
+ end
218
+
219
+ # Return new schema with all attributes marked as prefixed and wrapped
220
+ #
221
+ # This is useful when relations are joined and the right side should be marked
222
+ # as wrapped
223
+ #
224
+ # @param [Symbol] prefix The prefix used for aliasing wrapped attributes
225
+ #
226
+ # @return [Schema]
227
+ #
58
228
  # @api public
59
- def [](name)
60
- attributes.fetch(name)
229
+ def wrap(prefix = name.dataset)
230
+ new(map { |attr| attr.wrapped(prefix) })
61
231
  end
62
232
 
63
233
  # Return FK attribute for a given relation name
64
234
  #
65
- # @return [Dry::Types::Definition]
235
+ # @return [Schema::Attribute]
66
236
  #
67
237
  # @api public
68
238
  def foreign_key(relation)
69
- detect { |attr| attr.meta[:foreign_key] && attr.meta[:relation] == relation }
239
+ detect { |attr| attr.foreign_key? && attr.target == relation }
240
+ end
241
+
242
+ # Return primary key attributes
243
+ #
244
+ # @return [Array<Schema::Attribute>]
245
+ #
246
+ # @api public
247
+ def primary_key
248
+ select(&:primary_key?)
249
+ end
250
+
251
+ # Merge with another schema
252
+ #
253
+ # @param [Schema] other Other schema
254
+ #
255
+ # @return [Schema]
256
+ #
257
+ # @api public
258
+ def merge(other)
259
+ append(*other)
260
+ end
261
+ alias_method :+, :merge
262
+
263
+ # Append more attributes to the schema
264
+ #
265
+ # This returns a new schema instance
266
+ #
267
+ # @param [*Array<Schema::Attribute>]
268
+ #
269
+ # @return [Schema]
270
+ #
271
+ # @api public
272
+ def append(*new_attributes)
273
+ new(attributes + new_attributes)
274
+ end
275
+
276
+ # Return a new schema with uniq attributes
277
+ #
278
+ # @param [*Array<Schema::Attribute>]
279
+ #
280
+ # @return [Schema]
281
+ #
282
+ # @api public
283
+ def uniq(&block)
284
+ if block
285
+ new(attributes.uniq(&block))
286
+ else
287
+ new(attributes.uniq(&:name))
288
+ end
289
+ end
290
+
291
+ # Return if a schema includes an attribute with the given name
292
+ #
293
+ # @param [Symbol] name The name of the attribute
294
+ #
295
+ # @return [Boolean]
296
+ #
297
+ # @api public
298
+ def key?(name)
299
+ ! attributes.detect { |attr| attr.name == name }.nil?
70
300
  end
71
301
 
72
302
  # This hook is called when relation is being build during container finalization
@@ -77,13 +307,101 @@ module ROM
77
307
  # @return [self]
78
308
  #
79
309
  # @api private
80
- def finalize!(gateway = nil, &block)
310
+ def finalize!(gateway: nil, relations: nil, &block)
81
311
  return self if frozen?
82
312
 
83
- @attributes = inferrer.call(name.dataset, gateway) if inferrer
84
- @primary_key = select { |attr| attr.meta[:primary_key] == true }
313
+ inferred, missing = inferrer.call(name, gateway)
314
+
315
+ attr_names = map(&:name)
316
+ inferred_attrs = self.class.attributes(inferred, attr_class).
317
+ reject { |attr| attr_names.include?(attr.name) }
318
+
319
+ attributes.concat(inferred_attrs)
320
+
321
+ missing_attributes = missing - map(&:name)
322
+
323
+ if missing_attributes.size > 0
324
+ raise MissingAttributesError.new(name, missing_attributes)
325
+ end
326
+
85
327
  block.call if block
328
+
329
+ count_index
330
+ name_index
331
+ source_index
332
+
86
333
  freeze
87
334
  end
335
+
336
+ # Return coercion function using attribute read types
337
+ #
338
+ # This is used for `output_schema` in relations
339
+ #
340
+ # @return [Dry::Types::Hash]
341
+ #
342
+ # @api private
343
+ def to_output_hash
344
+ Types::Coercible::Hash.schema(
345
+ map { |attr| [attr.name, attr.to_read_type] }.to_h
346
+ )
347
+ end
348
+
349
+ # Return coercion function using attribute types
350
+ #
351
+ # This is used for `input_schema` in relations, typically commands use it
352
+ # for processing input
353
+ #
354
+ # @return [Dry::Types::Hash]
355
+ #
356
+ # @api private
357
+ def to_input_hash
358
+ Types::Coercible::Hash.schema(
359
+ map { |attr| [attr.name, attr] }.to_h
360
+ )
361
+ end
362
+
363
+ # Return a new schema with new options
364
+ #
365
+ # @example
366
+ # schema.with(inferrer: my_inferrer)
367
+ #
368
+ # @param [Hash] new_options
369
+ #
370
+ # @return [Schema]
371
+ #
372
+ # @api public
373
+ def with(new_options)
374
+ self.class.new(name, options.merge(new_options))
375
+ end
376
+
377
+ private
378
+
379
+ # @api private
380
+ def count_index
381
+ @count_index ||= map(&:name).map { |name| [name, count { |attr| attr.name == name }] }.to_h
382
+ end
383
+
384
+ # @api private
385
+ def name_index
386
+ @name_index ||= map { |attr| [attr.name, attr] }.to_h
387
+ end
388
+
389
+ # @api private
390
+ def source_index
391
+ @source_index ||= select(&:source).
392
+ group_by(&:source).
393
+ map { |src, grp| [src.to_sym, grp.map { |attr| [attr.name, attr] }.to_h] }.
394
+ to_h
395
+ end
396
+
397
+ # @api private
398
+ def attr_class
399
+ options.fetch(:attr_class)
400
+ end
401
+
402
+ # @api private
403
+ def new(attributes)
404
+ self.class.new(name, options.merge(attributes: attributes))
405
+ end
88
406
  end
89
407
  end
@@ -1,34 +1,37 @@
1
1
  require 'pathname'
2
2
 
3
- require 'rom/support/constants'
4
- require 'rom/support/inflector'
5
- require 'rom/support/options'
3
+ require 'dry/core/inflector'
6
4
 
5
+ require 'rom/types'
6
+ require 'rom/initializer'
7
7
  require 'rom/setup/auto_registration_strategies/no_namespace'
8
8
  require 'rom/setup/auto_registration_strategies/with_namespace'
9
9
  require 'rom/setup/auto_registration_strategies/custom_namespace'
10
10
 
11
11
  module ROM
12
12
  class AutoRegistration
13
- include Options
13
+ extend Initializer
14
14
 
15
- option :namespace, reader: true, type: [TrueClass, FalseClass, String], default: true
15
+ NamespaceType = Types::Strict::Bool | Types::Strict::String
16
+ PathnameType = Types.Constructor(Pathname, &Kernel.method(:Pathname))
16
17
 
17
- option :component_dirs, reader: true, type: ::Hash, default: {
18
+ param :directory, type: PathnameType
19
+
20
+ option :namespace, reader: true, type: NamespaceType, default: proc { true }
21
+
22
+ option :component_dirs, reader: true, type: Types::Strict::Hash, default: proc { {
18
23
  relations: :relations,
19
24
  mappers: :mappers,
20
25
  commands: :commands
21
- }
26
+ } }
22
27
 
23
- attr_reader :globs, :directory
24
-
25
- def initialize(directory, options = EMPTY_HASH)
26
- super
27
- @directory = Pathname(directory)
28
- @globs = Hash[component_dirs.map { |component, directory|
29
- [component, @directory.join("#{directory}/**/*.rb")]
30
- }]
31
- end
28
+ option :globs, reader: true, default: -> r {
29
+ Hash[
30
+ component_dirs.map { |component, directory|
31
+ [component, r.directory.join("#{directory}/**/*.rb")]
32
+ }
33
+ ]
34
+ }
32
35
 
33
36
  def relations
34
37
  load_entities(:relations)
@@ -62,7 +65,7 @@ module ROM
62
65
  file: file, directory: directory, entity: component_dirs.fetch(entity)
63
66
  ).call
64
67
  end
65
- Inflector.constantize(klass_name)
68
+ Dry::Core::Inflector.constantize(klass_name)
66
69
  end
67
70
  end
68
71
  end
@@ -1,11 +1,16 @@
1
+ require 'rom/types'
2
+ require 'rom/initializer'
3
+
1
4
  module ROM
2
5
  module AutoRegistrationStrategies
3
6
  class Base
4
- include Options
7
+ extend Initializer
8
+
9
+ PathnameType = Types.Definition(Pathname).constrained(type: Pathname)
5
10
 
6
- option :file, reader: true, type: String
11
+ option :file, reader: true, type: Types::Strict::String
7
12
 
8
- EXTENSION_REGEX = /\.rb$/.freeze
13
+ EXTENSION_REGEX = /\.rb\z/.freeze
9
14
  end
10
15
  end
11
16
  end
@@ -1,15 +1,16 @@
1
1
  require 'pathname'
2
2
 
3
- require 'rom/support/inflector'
3
+ require 'dry/core/inflector'
4
+ require 'rom/types'
4
5
  require 'rom/setup/auto_registration_strategies/base'
5
6
 
6
7
  module ROM
7
8
  module AutoRegistrationStrategies
8
9
  class CustomNamespace < Base
9
- option :namespace, reader: true, type: String
10
+ option :namespace, reader: true, type: Types::Strict::String
10
11
 
11
12
  def call
12
- "#{namespace}::#{Inflector.camelize(filename)}"
13
+ "#{namespace}::#{Dry::Core::Inflector.camelize(filename)}"
13
14
  end
14
15
 
15
16
  private
@@ -1,16 +1,17 @@
1
1
  require 'pathname'
2
2
 
3
- require 'rom/support/inflector'
3
+ require 'dry/core/inflector'
4
+ require 'rom/types'
4
5
  require 'rom/setup/auto_registration_strategies/base'
5
6
 
6
7
  module ROM
7
8
  module AutoRegistrationStrategies
8
9
  class NoNamespace < Base
9
- option :directory, reader: true, type: Pathname
10
- option :entity, reader: true, type: Symbol
10
+ option :directory, reader: true, type: PathnameType
11
+ option :entity, reader: true, type: Types::Strict::Symbol
11
12
 
12
13
  def call
13
- Inflector.camelize(
14
+ Dry::Core::Inflector.camelize(
14
15
  file.sub(/^#{directory}\/#{entity}\//, '').sub(EXTENSION_REGEX, '')
15
16
  )
16
17
  end
@@ -1,15 +1,15 @@
1
1
  require 'pathname'
2
2
 
3
- require 'rom/support/inflector'
3
+ require 'dry/core/inflector'
4
4
  require 'rom/setup/auto_registration_strategies/base'
5
5
 
6
6
  module ROM
7
7
  module AutoRegistrationStrategies
8
8
  class WithNamespace < Base
9
- option :directory, reader: true, type: Pathname
9
+ option :directory, reader: true, type: PathnameType
10
10
 
11
11
  def call
12
- Inflector.camelize(
12
+ Dry::Core::Inflector.camelize(
13
13
  file.sub(/^#{directory.dirname}\//, '').sub(EXTENSION_REGEX, '')
14
14
  )
15
15
  end
@@ -1,4 +1,4 @@
1
- require 'rom/support/registry'
1
+ require 'rom/registry'
2
2
  require 'rom/command_registry'
3
3
 
4
4
  module ROM
@@ -1,4 +1,4 @@
1
- require 'rom/support/registry'
1
+ require 'rom/registry'
2
2
 
3
3
  module ROM
4
4
  class Finalize
@@ -50,10 +50,12 @@ module ROM
50
50
  gateway = @gateways.fetch(klass.gateway)
51
51
  ds_proc = klass.dataset_proc || -> _ { self }
52
52
 
53
- klass.schema.finalize!(gateway) if klass.schema
53
+ klass.schema(infer: true) unless klass.schema
54
+ schema = klass.schema.finalize!(gateway: gateway, relations: registry)
55
+
54
56
  dataset = gateway.dataset(klass.dataset).instance_exec(klass, &ds_proc)
55
57
 
56
- klass.new(dataset, __registry__: registry)
58
+ klass.new(dataset, __registry__: registry, schema: schema.with(relations: registry))
57
59
  end
58
60
  end
59
61
  end
@@ -1,7 +1,7 @@
1
1
  require 'rom/relation'
2
2
  require 'rom/command'
3
3
 
4
- require 'rom/support/registry'
4
+ require 'rom/registry'
5
5
  require 'rom/command_registry'
6
6
  require 'rom/mapper_registry'
7
7
 
@@ -0,0 +1,24 @@
1
+ module ROM
2
+ # @api private
3
+ class Transaction
4
+ # @api private
5
+ Rollback = Class.new(StandardError)
6
+
7
+ # @api private
8
+ def run(opts = EMPTY_HASH)
9
+ yield(self)
10
+ rescue Rollback
11
+ # noop
12
+ end
13
+
14
+ # Unconditionally roll back the current transaction
15
+ #
16
+ # @api public
17
+ def rollback!
18
+ raise Rollback
19
+ end
20
+
21
+ # @api private
22
+ NoOp = Transaction.new.freeze
23
+ end
24
+ end
data/lib/rom/types.rb CHANGED
@@ -9,9 +9,17 @@ module ROM
9
9
  super
10
10
  end
11
11
 
12
+ def self.Definition(primitive)
13
+ Dry::Types::Definition.new(primitive)
14
+ end
15
+
16
+ def self.Constructor(primitive, &block)
17
+ Types.Definition(primitive).constructor(&block)
18
+ end
19
+
12
20
  module Methods
13
21
  def ForeignKey(relation, type = Types::Int)
14
- type.meta(foreign_key: true, relation: relation)
22
+ type.meta(foreign_key: true, target: relation)
15
23
  end
16
24
  end
17
25
  end
data/lib/rom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ROM
2
- VERSION = '2.0.2'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
data/lib/rom.rb CHANGED
@@ -1,16 +1,12 @@
1
1
  require 'dry-equalizer'
2
+ require 'dry/core/constants'
2
3
 
3
- require 'rom-support'
4
4
  require 'rom/version'
5
5
  require 'rom/constants'
6
6
 
7
- # internal ROM support lib
8
- require 'rom/support/inflector'
9
- require 'rom/support/registry'
10
- require 'rom/support/options'
11
- require 'rom/support/class_macros'
12
- require 'rom/support/class_builder'
13
- require 'rom/support/inheritance_hook'
7
+ module ROM
8
+ include Dry::Core::Constants
9
+ end
14
10
 
15
11
  # core parts
16
12
  require 'rom/configuration_plugin'
data/rom.gemspec CHANGED
@@ -17,10 +17,11 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
19
19
  gem.add_runtime_dependency 'dry-equalizer', '~> 0.2'
20
- gem.add_runtime_dependency 'dry-types', '~> 0.8'
21
- gem.add_runtime_dependency 'rom-support', '~> 2.0'
22
- gem.add_runtime_dependency 'rom-mapper', '~> 0.4.0'
20
+ gem.add_runtime_dependency 'dry-types', '~> 0.9', '>= 0.9.4'
21
+ gem.add_runtime_dependency 'dry-core', '~> 0.2', '>= 0.2.3'
22
+ gem.add_runtime_dependency 'dry-initializer', '~> 0.10', '>= 0.10.2'
23
+ gem.add_runtime_dependency 'rom-mapper', '~> 0.5'
23
24
 
24
25
  gem.add_development_dependency 'rake', '~> 10.3'
25
- gem.add_development_dependency 'rspec', '~> 3.3'
26
+ gem.add_development_dependency 'rspec', '~> 3.5'
26
27
  end