rom-sql 2.5.0 → 3.3.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +456 -278
  3. data/LICENSE +20 -0
  4. data/README.md +14 -24
  5. data/lib/rom-sql.rb +2 -0
  6. data/lib/rom/plugins/relation/sql/auto_restrictions.rb +2 -0
  7. data/lib/rom/plugins/relation/sql/instrumentation.rb +2 -0
  8. data/lib/rom/plugins/relation/sql/postgres/explain.rb +6 -7
  9. data/lib/rom/plugins/relation/sql/postgres/full_text_search.rb +53 -0
  10. data/lib/rom/plugins/relation/sql/postgres/streaming.rb +97 -0
  11. data/lib/rom/sql.rb +2 -0
  12. data/lib/rom/sql/associations.rb +2 -0
  13. data/lib/rom/sql/associations/core.rb +10 -0
  14. data/lib/rom/sql/associations/many_to_many.rb +10 -2
  15. data/lib/rom/sql/associations/many_to_one.rb +2 -0
  16. data/lib/rom/sql/associations/one_to_many.rb +2 -0
  17. data/lib/rom/sql/associations/one_to_one.rb +2 -0
  18. data/lib/rom/sql/associations/one_to_one_through.rb +2 -0
  19. data/lib/rom/sql/associations/self_ref.rb +2 -0
  20. data/lib/rom/sql/attribute.rb +87 -29
  21. data/lib/rom/sql/attribute_aliasing.rb +88 -0
  22. data/lib/rom/sql/attribute_wrapping.rb +30 -0
  23. data/lib/rom/sql/commands.rb +2 -0
  24. data/lib/rom/sql/commands/create.rb +2 -0
  25. data/lib/rom/sql/commands/delete.rb +2 -0
  26. data/lib/rom/sql/commands/error_wrapper.rb +2 -0
  27. data/lib/rom/sql/commands/update.rb +2 -0
  28. data/lib/rom/sql/dsl.rb +39 -1
  29. data/lib/rom/sql/error.rb +2 -0
  30. data/lib/rom/sql/errors.rb +2 -0
  31. data/lib/rom/sql/extensions.rb +2 -0
  32. data/lib/rom/sql/extensions/active_support_notifications.rb +2 -0
  33. data/lib/rom/sql/extensions/mysql.rb +2 -0
  34. data/lib/rom/sql/extensions/mysql/type_builder.rb +2 -0
  35. data/lib/rom/sql/extensions/postgres.rb +4 -0
  36. data/lib/rom/sql/extensions/postgres/commands.rb +3 -1
  37. data/lib/rom/sql/extensions/postgres/type_builder.rb +6 -4
  38. data/lib/rom/sql/extensions/postgres/type_serializer.rb +2 -0
  39. data/lib/rom/sql/extensions/postgres/types.rb +2 -0
  40. data/lib/rom/sql/extensions/postgres/types/array.rb +9 -8
  41. data/lib/rom/sql/extensions/postgres/types/array_types.rb +3 -1
  42. data/lib/rom/sql/extensions/postgres/types/geometric.rb +2 -0
  43. data/lib/rom/sql/extensions/postgres/types/json.rb +76 -19
  44. data/lib/rom/sql/extensions/postgres/types/ltree.rb +27 -25
  45. data/lib/rom/sql/extensions/postgres/types/network.rb +2 -0
  46. data/lib/rom/sql/extensions/postgres/types/range.rb +6 -4
  47. data/lib/rom/sql/extensions/rails_log_subscriber.rb +2 -0
  48. data/lib/rom/sql/extensions/sqlite.rb +2 -0
  49. data/lib/rom/sql/extensions/sqlite/type_builder.rb +2 -0
  50. data/lib/rom/sql/extensions/sqlite/types.rb +2 -0
  51. data/lib/rom/sql/foreign_key.rb +3 -1
  52. data/lib/rom/sql/function.rb +84 -6
  53. data/lib/rom/sql/gateway.rb +9 -1
  54. data/lib/rom/sql/group_dsl.rb +2 -0
  55. data/lib/rom/sql/index.rb +2 -0
  56. data/lib/rom/sql/join_dsl.rb +11 -0
  57. data/lib/rom/sql/mapper_compiler.rb +14 -3
  58. data/lib/rom/sql/migration.rb +20 -3
  59. data/lib/rom/sql/migration/inline_runner.rb +2 -0
  60. data/lib/rom/sql/migration/migrator.rb +5 -3
  61. data/lib/rom/sql/migration/recorder.rb +2 -0
  62. data/lib/rom/sql/migration/runner.rb +4 -2
  63. data/lib/rom/sql/migration/schema_diff.rb +4 -2
  64. data/lib/rom/sql/migration/template.rb +2 -0
  65. data/lib/rom/sql/migration/writer.rb +12 -4
  66. data/lib/rom/sql/order_dsl.rb +2 -0
  67. data/lib/rom/sql/plugin/associates.rb +4 -3
  68. data/lib/rom/sql/plugin/nullify.rb +37 -0
  69. data/lib/rom/sql/plugin/pagination.rb +22 -0
  70. data/lib/rom/sql/plugins.rb +4 -0
  71. data/lib/rom/sql/projection_dsl.rb +10 -4
  72. data/lib/rom/sql/rake_task.rb +2 -0
  73. data/lib/rom/sql/relation.rb +3 -1
  74. data/lib/rom/sql/relation/reading.rb +105 -16
  75. data/lib/rom/sql/relation/writing.rb +2 -0
  76. data/lib/rom/sql/restriction_dsl.rb +8 -8
  77. data/lib/rom/sql/schema.rb +16 -2
  78. data/lib/rom/sql/schema/attributes_inferrer.rb +7 -5
  79. data/lib/rom/sql/schema/dsl.rb +3 -1
  80. data/lib/rom/sql/schema/index_dsl.rb +8 -3
  81. data/lib/rom/sql/schema/inferrer.rb +12 -8
  82. data/lib/rom/sql/schema/type_builder.rb +4 -2
  83. data/lib/rom/sql/spec/support.rb +5 -3
  84. data/lib/rom/sql/tasks/migration_tasks.rake +16 -11
  85. data/lib/rom/sql/transaction.rb +2 -0
  86. data/lib/rom/sql/type_dsl.rb +3 -1
  87. data/lib/rom/sql/type_extensions.rb +4 -4
  88. data/lib/rom/sql/type_serializer.rb +3 -1
  89. data/lib/rom/sql/types.rb +6 -4
  90. data/lib/rom/sql/version.rb +3 -1
  91. data/lib/rom/sql/wrap.rb +2 -0
  92. data/lib/rom/types/values.rb +2 -0
  93. metadata +39 -37
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/types/values'
2
4
 
3
5
  module ROM
@@ -222,20 +224,20 @@ module ROM
222
224
  #
223
225
  # end
224
226
  module LTreeMethods
225
- ASCENDANT = ["(".freeze, " @> ".freeze, ")".freeze].freeze
226
- FIND_ASCENDANT = ["(".freeze, " ?@> ".freeze, ")".freeze].freeze
227
- DESCENDANT = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
228
- FIND_DESCENDANT = ["(".freeze, " ?<@ ".freeze, ")".freeze].freeze
229
- MATCH_ANY = ["(".freeze, " ? ".freeze, ")".freeze].freeze
230
- MATCH_ANY_LQUERY = ["(".freeze, " ?~ ".freeze, ")".freeze].freeze
231
- MATCH_LTEXTQUERY = ["(".freeze, " @ ".freeze, ")".freeze].freeze
232
- MATCH_ANY_LTEXTQUERY = ["(".freeze, " ?@ ".freeze, ")".freeze].freeze
227
+ ASCENDANT = ['(', ' @> ', ')'].freeze
228
+ FIND_ASCENDANT = ['(', ' ?@> ', ')'].freeze
229
+ DESCENDANT = ['(', ' <@ ', ')'].freeze
230
+ FIND_DESCENDANT = ['(', ' ?<@ ', ')'].freeze
231
+ MATCH_ANY = ['(', ' ? ', ')'].freeze
232
+ MATCH_ANY_LQUERY = ['(', ' ?~ ', ')'].freeze
233
+ MATCH_LTEXTQUERY = ['(', ' @ ', ')'].freeze
234
+ MATCH_ANY_LTEXTQUERY = ['(', ' ?@ ', ')'].freeze
233
235
 
234
- def match(type, expr, query)
236
+ def match(_type, expr, query)
235
237
  Attribute[SQL::Types::Bool].meta(sql_expr: Sequel::SQL::BooleanExpression.new(:'~', expr, query))
236
238
  end
237
239
 
238
- def match_any(type, expr, query)
240
+ def match_any(_type, expr, query)
239
241
  array = build_array_query(query)
240
242
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(MATCH_ANY, expr, array))
241
243
  end
@@ -248,9 +250,9 @@ module ROM
248
250
 
249
251
  def build_array_query(query, array_type = 'lquery')
250
252
  case query
251
- when Array
253
+ when ::Array
252
254
  ROM::SQL::Types::PG::Array(array_type)[query]
253
- when String
255
+ when ::String
254
256
  ROM::SQL::Types::PG::Array(array_type)[query.split(',')]
255
257
  end
256
258
  end
@@ -259,31 +261,31 @@ module ROM
259
261
  TypeExtensions.register(ROM::SQL::Types::PG::Array('ltree', LTree)) do
260
262
  include LTreeMethods
261
263
 
262
- def contain_any_ltextquery(type, expr, query)
264
+ def contain_any_ltextquery(_type, expr, query)
263
265
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::MATCH_LTEXTQUERY, expr, query))
264
266
  end
265
267
 
266
- def contain_ancestor(type, expr, query)
268
+ def contain_ancestor(_type, expr, query)
267
269
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::ASCENDANT, expr, query))
268
270
  end
269
271
 
270
- def contain_descendant(type, expr, query)
272
+ def contain_descendant(_type, expr, query)
271
273
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::DESCENDANT, expr, query))
272
274
  end
273
275
 
274
- def find_ancestor(type, expr, query)
276
+ def find_ancestor(_type, expr, query)
275
277
  Attribute[LTree].meta(sql_expr: custom_operator_expr(LTreeMethods::FIND_ASCENDANT, expr, query))
276
278
  end
277
279
 
278
- def find_descendant(type, expr, query)
280
+ def find_descendant(_type, expr, query)
279
281
  Attribute[LTree].meta(sql_expr: custom_operator_expr(LTreeMethods::FIND_DESCENDANT, expr, query))
280
282
  end
281
283
 
282
- def match_any_lquery(type, expr, query)
284
+ def match_any_lquery(_type, expr, query)
283
285
  Attribute[LTree].meta(sql_expr: custom_operator_expr(LTreeMethods::MATCH_ANY_LQUERY, expr, query))
284
286
  end
285
287
 
286
- def match_any_ltextquery(type, expr, query)
288
+ def match_any_ltextquery(_type, expr, query)
287
289
  Attribute[LTree].meta(sql_expr: custom_operator_expr(LTreeMethods::MATCH_ANY_LTEXTQUERY, expr, query))
288
290
  end
289
291
  end
@@ -291,29 +293,29 @@ module ROM
291
293
  TypeExtensions.register(LTree) do
292
294
  include LTreeMethods
293
295
 
294
- def match_ltextquery(type, expr, query)
296
+ def match_ltextquery(_type, expr, query)
295
297
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::MATCH_LTEXTQUERY, expr, query))
296
298
  end
297
299
 
298
- def contain_descendant(type, expr, query)
300
+ def contain_descendant(_type, expr, query)
299
301
  array = build_array_query(query, 'ltree')
300
302
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::DESCENDANT, expr, array))
301
303
  end
302
304
 
303
- def descendant(type, expr, query)
305
+ def descendant(_type, expr, query)
304
306
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::DESCENDANT, expr, query))
305
307
  end
306
308
 
307
- def contain_ascendant(type, expr, query)
309
+ def contain_ascendant(_type, expr, query)
308
310
  array = build_array_query(query, 'ltree')
309
311
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::ASCENDANT, expr, array))
310
312
  end
311
313
 
312
- def ascendant(type, expr, query)
314
+ def ascendant(_type, expr, query)
313
315
  Attribute[SQL::Types::Bool].meta(sql_expr: custom_operator_expr(LTreeMethods::ASCENDANT, expr, query))
314
316
  end
315
317
 
316
- def +(type, expr, other)
318
+ def +(_type, expr, other)
317
319
  other_value = case other
318
320
  when ROM::Types::Values::TreePath
319
321
  other
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ipaddr'
2
4
 
3
5
  module ROM
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sequel/core'
2
4
 
3
5
  Sequel.extension(:pg_range, :pg_range_ops)
@@ -31,13 +33,13 @@ module ROM
31
33
 
32
34
  @range_parsers = {
33
35
  int4range: Sequel::Postgres::PGRange::Parser.new(
34
- 'int4range', SQL::Types::Coercible::Int
36
+ 'int4range', SQL::Types::Coercible::Integer
35
37
  ),
36
38
  int8range: Sequel::Postgres::PGRange::Parser.new(
37
- 'int8range', SQL::Types::Coercible::Int
39
+ 'int8range', SQL::Types::Coercible::Integer
38
40
  ),
39
41
  numrange: Sequel::Postgres::PGRange::Parser.new(
40
- 'numrange', SQL::Types::Coercible::Int
42
+ 'numrange', SQL::Types::Coercible::Integer
41
43
  ),
42
44
  tsrange: Sequel::Postgres::PGRange::Parser.new(
43
45
  'tsrange', ::Time.method(:parse)
@@ -75,7 +77,7 @@ module ROM
75
77
  # @api private
76
78
  def self.range(name, read_type)
77
79
  Type(name) do
78
- type = SQL::Types.Definition(Values::Range).constructor do |range|
80
+ type = SQL::Types.Nominal(Values::Range).constructor do |range|
79
81
  format('%s%s,%s%s',
80
82
  range.exclude_begin? ? :'(' : :'[',
81
83
  range.lower,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/log_subscriber'
2
4
 
3
5
  module ROM
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/sql/extensions/sqlite/types'
2
4
  require 'rom/sql/extensions/sqlite/type_builder'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  module SQLite
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry-types'
2
4
 
3
5
  module ROM
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  # @api private
@@ -5,7 +7,7 @@ module ROM
5
7
  extend Initializer
6
8
  include Dry::Equalizer(:attributes, :parent_table, :options)
7
9
 
8
- DEFAULT_PARENT_KEYS = %i(id).freeze
10
+ DEFAULT_PARENT_KEYS = %i[id].freeze
9
11
 
10
12
  param :attributes
11
13
 
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/attribute'
4
+ require 'rom/sql/attribute_wrapping'
2
5
 
3
6
  module ROM
4
7
  module SQL
@@ -6,6 +9,8 @@ module ROM
6
9
  #
7
10
  # @api public
8
11
  class Function < ROM::Attribute
12
+ include AttributeWrapping
13
+
9
14
  class << self
10
15
  # @api private
11
16
  def frame_limit(value)
@@ -37,6 +42,19 @@ module ROM
37
42
  WINDOW_FRAMES[:rows] = WINDOW_FRAMES[rows: [:start, :current]]
38
43
  WINDOW_FRAMES[range: :current] = WINDOW_FRAMES[range: [:current, :current]]
39
44
 
45
+ # Return a new attribute with an alias
46
+ #
47
+ # @example
48
+ # string::coalesce(users[:name], users[:id]).aliased(:display_name)
49
+ #
50
+ # @return [SQL::Function]
51
+ #
52
+ # @api public
53
+ def aliased(alias_name)
54
+ super.with(name: name || alias_name)
55
+ end
56
+ alias_method :as, :aliased
57
+
40
58
  # @api private
41
59
  def sql_literal(ds)
42
60
  if name
@@ -48,7 +66,7 @@ module ROM
48
66
 
49
67
  # @api private
50
68
  def name
51
- meta[:alias] || super
69
+ self.alias || super
52
70
  end
53
71
 
54
72
  # @see Attribute#qualified
@@ -60,6 +78,22 @@ module ROM
60
78
  )
61
79
  end
62
80
 
81
+ # @see Attribute#qualified_projection
82
+ #
83
+ # @api private
84
+ def qualified_projection(table_alias = nil)
85
+ meta(
86
+ func: ::Sequel::SQL::Function.new(func.name, *func.args.map { |arg| arg.respond_to?(:qualified_projection) ? arg.qualified_projection(table_alias) : arg })
87
+ )
88
+ end
89
+
90
+ # @see Attribute#qualified?
91
+ #
92
+ # @api private
93
+ def qualified?(_table_alias = nil)
94
+ meta[:func].args.all?(&:qualified?)
95
+ end
96
+
63
97
  # @see ROM::SQL::Attribute#is
64
98
  #
65
99
  # @api public
@@ -80,8 +114,8 @@ module ROM
80
114
  # @see https://www.postgresql.org/docs/9.6/static/tutorial-window.html
81
115
  #
82
116
  # @example
83
- # users.select { [id, int::row_number().over(partition: name, order: id).as(:row_no)] }
84
- # users.select { [id, int::row_number().over(partition: [first_name, last_name], order: id).as(:row_no)] }
117
+ # users.select { [id, integer::row_number().over(partition: name, order: id).as(:row_no)] }
118
+ # users.select { [id, integer::row_number().over(partition: [first_name, last_name], order: id).as(:row_no)] }
85
119
  #
86
120
  # @example frame variants
87
121
  # # ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
@@ -132,16 +166,36 @@ module ROM
132
166
  Attribute[type].meta(sql_expr: ::Sequel.cast(expr, db_type))
133
167
  end
134
168
 
169
+ # Add a CASE clause for handling if/then logic. This version of CASE search for the first
170
+ # branch which evaluates to `true`. See SQL::Attriubte#case if you're looking for the
171
+ # version that matches an expression result
172
+ #
173
+ # @example
174
+ # users.select { bool::case(status.is("active") => true, else: false).as(:activated) }
175
+ #
176
+ # @param [Hash] mapping mapping between boolean SQL expressions to arbitrary SQL expressions
177
+ # @return [ROM::SQL::Attribute]
178
+ #
179
+ # @api public
180
+ def case(mapping)
181
+ mapping = mapping.dup
182
+ otherwise = mapping.delete(:else) do
183
+ raise ArgumentError, 'provide the default case using the :else keyword'
184
+ end
185
+
186
+ Attribute[type].meta(sql_expr: ::Sequel.case(mapping, otherwise))
187
+ end
188
+
135
189
  # Add a FILTER clause to aggregate function (supported by PostgreSQL 9.4+)
136
190
  # @see https://www.postgresql.org/docs/current/static/sql-expressions.html
137
191
  #
138
192
  # Filter aggregate using the specified conditions
139
193
  #
140
194
  # @example
141
- # users.project { int::count(:id).filter(name.is("Jack")).as(:jacks) }.order(nil)
142
- # users.project { int::count(:id).filter { name.is("John") }).as(:johns) }.order(nil)
195
+ # users.project { integer::count(:id).filter(name.is("Jack")).as(:jacks) }.order(nil)
196
+ # users.project { integer::count(:id).filter { name.is("John") }).as(:johns) }.order(nil)
143
197
  #
144
- # @param [Hash,SQL::Attribute] Conditions
198
+ # @param condition [Hash,SQL::Attribute] Conditions
145
199
  # @yield [block] A block with restrictions
146
200
  #
147
201
  # @return [SQL::Function]
@@ -158,6 +212,30 @@ module ROM
158
212
  super(conditions)
159
213
  end
160
214
 
215
+ # Add a WITHIN GROUP clause to aggregate function (supported by PostgreSQL)
216
+ # @see https://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES
217
+ #
218
+ # Establishes an order for an ordered-set aggregate, see the docs for more details
219
+ #
220
+ # @example
221
+ # households.project { fload::percentile_cont(0.5).within_group(income).as(:percentile) }
222
+ #
223
+ # @param args [Array] A list of expressions for sorting within a group
224
+ # @yield [block] A block for getting the expressions using the Order DSL
225
+ #
226
+ # @return [SQL::Function]
227
+ #
228
+ # @api public
229
+ def within_group(*args, &block)
230
+ if block
231
+ group = args + ::ROM::SQL::OrderDSL.new(schema).(&block)
232
+ else
233
+ group = args
234
+ end
235
+
236
+ super(*group)
237
+ end
238
+
161
239
  private
162
240
 
163
241
  # @api private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'sequel/core'
3
5
 
@@ -21,7 +23,7 @@ module ROM
21
23
  adapter :sql
22
24
 
23
25
  CONNECTION_EXTENSIONS = {
24
- postgres: %i(pg_array pg_json pg_enum)
26
+ postgres: %i[pg_array pg_json pg_enum]
25
27
  }.freeze
26
28
 
27
29
  # @!attribute [r] logger
@@ -79,6 +81,7 @@ module ROM
79
81
  def initialize(uri, options = EMPTY_HASH)
80
82
  @connection = connect(uri, options)
81
83
  load_extensions(Array(options[:extensions]))
84
+ Notifications.trigger("configuration.gateway.connected", connection: @connection)
82
85
 
83
86
  @options = options
84
87
 
@@ -234,6 +237,9 @@ module ROM
234
237
  # this will be default in Sequel 5.0.0 and since we don't rely
235
238
  # on dataset mutation it is safe to enable it already
236
239
  connection.extension(:freeze_datasets) unless RUBY_ENGINE == 'rbx'
240
+
241
+ # for ROM::SQL::Relation#nullify
242
+ connection.extension(:null_dataset)
237
243
  end
238
244
 
239
245
  # @api private
@@ -242,4 +248,6 @@ module ROM
242
248
  end
243
249
  end
244
250
  end
251
+
252
+ Configuration.register_event("configuration.gateway.connected")
245
253
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/sql/dsl'
2
4
 
3
5
  module ROM
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module SQL
3
5
  # @api private
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/sql/restriction_dsl'
4
+
5
+ module ROM
6
+ module SQL
7
+ # @api private
8
+ class JoinDSL < RestrictionDSL
9
+ end
10
+ end
11
+ end
@@ -1,17 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/mapper_compiler'
2
4
 
3
5
  module ROM
4
6
  module SQL
5
7
  class MapperCompiler < ROM::MapperCompiler
6
8
  def visit_attribute(node)
7
- name, _, meta = node
9
+ name, _, meta_options = node
8
10
 
9
- if meta[:wrapped]
10
- [name, from: meta[:alias]]
11
+ if meta_options[:wrapped]
12
+ [extract_wrapped_name(node), from: meta_options[:alias]]
11
13
  else
12
14
  [name]
13
15
  end
14
16
  end
17
+
18
+ private
19
+
20
+ def extract_wrapped_name(node)
21
+ _, _, meta_options = node
22
+ unwrapped_name = meta_options[:alias].to_s.dup
23
+ unwrapped_name.slice!("#{meta_options[:wrapped]}_")
24
+ unwrapped_name.to_sym
25
+ end
15
26
  end
16
27
  end
17
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/sql/migration/migrator'
2
4
  require 'rom/sql/migration/schema_diff'
3
5
 
@@ -97,8 +99,8 @@ module ROM
97
99
  attr_reader :migrator
98
100
 
99
101
  # @api private
100
- def initialize(uri, options = EMPTY_HASH)
101
- @migrator = options.fetch(:migrator) { Migrator.new(connection) }
102
+ def initialize(_uri, options = EMPTY_HASH)
103
+ @migrator = create_migrator(options[:migrator])
102
104
 
103
105
  self.class.instance ||= self
104
106
  end
@@ -139,13 +141,28 @@ module ROM
139
141
  end
140
142
 
141
143
  # @api public
142
- def auto_migrate!(conf, options = EMPTY_HASH, &block)
144
+ def auto_migrate!(conf, options = EMPTY_HASH)
143
145
  schemas = conf.relation_classes(self).map do |klass|
144
146
  klass.schema_proc.call.finalize_attributes!(gateway: self)
145
147
  end
146
148
 
147
149
  migrator.auto_migrate!(self, schemas, options)
148
150
  end
151
+
152
+ private
153
+
154
+ # Create a `Migrator`. If `migrator_option` is a `Hash`, use it as options to `Migrator.new`.
155
+ #
156
+ # @api private
157
+ def create_migrator(migrator_option)
158
+ return Migrator.new(connection) unless migrator_option
159
+
160
+ if migrator_option.is_a?(Hash)
161
+ Migrator.new(connection, **migrator_option)
162
+ else
163
+ migrator_option
164
+ end
165
+ end
149
166
  end
150
167
  end
151
168
  end