rom-sql 2.0.0.beta1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2 -1
  3. data/lib/rom/plugins/relation/sql/auto_restrictions.rb +19 -6
  4. data/lib/rom/sql/attribute.rb +9 -0
  5. data/lib/rom/sql/commands/create.rb +0 -2
  6. data/lib/rom/sql/commands/delete.rb +0 -2
  7. data/lib/rom/sql/commands/update.rb +0 -2
  8. data/lib/rom/sql/extensions/postgres/attributes_inferrer.rb +1 -1
  9. data/lib/rom/sql/index.rb +16 -0
  10. data/lib/rom/sql/migration/inline_runner.rb +12 -4
  11. data/lib/rom/sql/migration/schema_diff.rb +23 -3
  12. data/lib/rom/sql/plugin/associates.rb +3 -3
  13. data/lib/rom/sql/plugin/timestamps.rb +2 -2
  14. data/lib/rom/sql/relation.rb +7 -0
  15. data/lib/rom/sql/relation/reading.rb +1 -1
  16. data/lib/rom/sql/schema.rb +2 -1
  17. data/lib/rom/sql/schema/dsl.rb +26 -0
  18. data/lib/rom/sql/schema/index_dsl.rb +50 -0
  19. data/lib/rom/sql/schema/inferrer.rb +11 -5
  20. data/lib/rom/sql/version.rb +1 -1
  21. data/spec/integration/auto_migrations/indexes_spec.rb +147 -3
  22. data/spec/integration/{graph_spec.rb → combine_with_spec.rb} +1 -1
  23. data/spec/integration/commands/create_spec.rb +37 -62
  24. data/spec/integration/commands/delete_spec.rb +6 -6
  25. data/spec/integration/commands/update_spec.rb +6 -8
  26. data/spec/integration/plugins/associates/many_to_many_spec.rb +2 -2
  27. data/spec/integration/plugins/associates_spec.rb +7 -7
  28. data/spec/integration/plugins/auto_restrictions_spec.rb +31 -0
  29. data/spec/integration/relation_schema_spec.rb +68 -0
  30. data/spec/integration/schema/inferrer_spec.rb +31 -6
  31. data/spec/support/helpers.rb +1 -1
  32. data/spec/unit/plugin/timestamp_spec.rb +2 -2
  33. data/spec/unit/types_spec.rb +1 -1
  34. metadata +6 -5
  35. data/lib/rom/sql/commands/transaction.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff0be19f60330b9224eaf421b85ad113000c1558
4
- data.tar.gz: 3ccb799110fbc7543f3e9ef9ebd511193c700ab6
3
+ metadata.gz: 8f6a088c5111442c8a6ac85b97315143395afd3a
4
+ data.tar.gz: 909c97090b071f2229b84055686ea34c0a31d9e2
5
5
  SHA512:
6
- metadata.gz: fe36e8c1a886f3231385d3ff9709aa11a4ad13b63b313ec5fd07fc76af0537327184bef102af03c66331750b76d0edfd7838e1ec4e02e8189ac56f91c7749203
7
- data.tar.gz: 3192a10d465305f01d780e4db7af846c14c3c09509afb59f5cdad770383234ab59fc80b49431270aa863adeb7a51f7f3867a05c48cdfde084c7ed1ac1ea83a19
6
+ metadata.gz: ebaf850abe38152fad88f59e91a50ecd2ea882b5e9159a252f30b32da5b383dfd8a8079095454daf7c5cffefd8e72da20f7c7c86094f50a4109b0f5fd00bbd09
7
+ data.tar.gz: 36d28232439734adc2de2f440a28cc57374250efbd001bb4291939ea39c01d7e9cddf1020833bf840baf450ae2f8bee9319b0fd5a8bc93cfcb5eacfb4c26efc7
data/CHANGELOG.md CHANGED
@@ -7,8 +7,9 @@
7
7
 
8
8
  ### Changed
9
9
 
10
- * [BREAKING] based on rom 4.0 now
10
+ * [BREAKING] based on rom 4.0 now (flash-gordon + solnic)
11
11
  * [BREAKING] `Associates` command plugin requires associations now (solnic)
12
+ * [BREAKING] `Command#transaction` is gone in favor of `Relation#transaction` (solnic)
12
13
  * `ManyToOne` no longer uses a join (solnic)
13
14
  * `AutoCombine` and `AutoWrap` plugins were removed as this functionality is provided by core API (solnic)
14
15
 
@@ -39,18 +39,31 @@ module ROM
39
39
  def self.restriction_methods(schema)
40
40
  mod = Module.new
41
41
 
42
- indexed_attrs = schema.indexes.map { |index| index.attributes[0] }.uniq
42
+ methods = schema.indexes.each_with_object([]) do |index, generated|
43
+ next if index.partial?
43
44
 
44
- methods = indexed_attrs.map do |attr|
45
- meth_name = :"by_#{attr.name}"
45
+ attributes = index.to_a
46
+ meth_name = :"by_#{ attributes.map(&:name).join('_and_') }"
47
+
48
+ next if generated.include?(meth_name)
46
49
 
47
50
  mod.module_eval do
48
- define_method(meth_name) do |value|
49
- where(attr.is(value))
51
+ if attributes.size == 1
52
+ attribute = attributes[0]
53
+
54
+ define_method(meth_name) do |value|
55
+ where(attribute.is(value))
56
+ end
57
+ else
58
+ indexed_attributes = attributes.map.with_index.to_a
59
+
60
+ define_method(meth_name) do |*values|
61
+ where(indexed_attributes.map { |attr, idx| attr.is(values[idx]) }.reduce(:&))
62
+ end
50
63
  end
51
64
  end
52
65
 
53
- meth_name
66
+ generated << meth_name
54
67
  end
55
68
 
56
69
  [methods, mod]
@@ -306,6 +306,15 @@ module ROM
306
306
  meta
307
307
  end
308
308
 
309
+ # @api private
310
+ def unwrap
311
+ if optional?
312
+ self.class.new(right, options).meta(meta)
313
+ else
314
+ self
315
+ end
316
+ end
317
+
309
318
  private
310
319
 
311
320
  # Return Sequel Expression object for an attribute
@@ -1,5 +1,4 @@
1
1
  require 'rom/sql/commands/error_wrapper'
2
- require 'rom/sql/commands/transaction'
3
2
 
4
3
  module ROM
5
4
  module SQL
@@ -10,7 +9,6 @@ module ROM
10
9
  class Create < ROM::Commands::Create
11
10
  adapter :sql
12
11
 
13
- include Transaction
14
12
  include ErrorWrapper
15
13
 
16
14
  use :associates
@@ -1,5 +1,4 @@
1
1
  require 'rom/sql/commands/error_wrapper'
2
- require 'rom/sql/commands/transaction'
3
2
 
4
3
  module ROM
5
4
  module SQL
@@ -10,7 +9,6 @@ module ROM
10
9
  class Delete < ROM::Commands::Delete
11
10
  adapter :sql
12
11
 
13
- include Transaction
14
12
  include ErrorWrapper
15
13
 
16
14
  # Deletes tuples from a relation
@@ -1,5 +1,4 @@
1
1
  require 'rom/sql/commands/error_wrapper'
2
- require 'rom/sql/commands/transaction'
3
2
 
4
3
  module ROM
5
4
  module SQL
@@ -10,7 +9,6 @@ module ROM
10
9
  class Update < ROM::Commands::Update
11
10
  adapter :sql
12
11
 
13
- include Transaction
14
12
  include ErrorWrapper
15
13
 
16
14
  use :schema
@@ -34,7 +34,7 @@ module ROM
34
34
  'path' => Types::PG::PathT
35
35
  ).freeze
36
36
 
37
- db_array_type_matcher Sequel::Postgres::PGArray::EMPTY_BRACKET
37
+ db_array_type_matcher '[]'.freeze
38
38
 
39
39
  private
40
40
 
data/lib/rom/sql/index.rb CHANGED
@@ -8,6 +8,22 @@ module ROM
8
8
  param :attributes
9
9
 
10
10
  option :name, optional: true
11
+
12
+ option :unique, default: -> { false }
13
+
14
+ alias_method :unique?, :unique
15
+
16
+ option :type, optional: true
17
+
18
+ option :predicate, optional: true
19
+
20
+ def to_a
21
+ attributes
22
+ end
23
+
24
+ def partial?
25
+ !predicate.nil?
26
+ end
11
27
  end
12
28
  end
13
29
  end
@@ -37,8 +37,12 @@ module ROM
37
37
  end
38
38
  end
39
39
 
40
- diff.indexes.each do |idx|
41
- index idx.attribute
40
+ diff.indexes.each do |index|
41
+ index index.attributes,
42
+ name: index.name,
43
+ unique: index.unique?,
44
+ type: index.type,
45
+ where: index.predicate
42
46
  end
43
47
  end
44
48
  end
@@ -72,9 +76,13 @@ module ROM
72
76
  diff.index_changes.each do |index|
73
77
  case index
74
78
  when SchemaDiff::IndexAdded
75
- add_index index.attribute
79
+ add_index index.attributes,
80
+ name: index.name,
81
+ unique: index.unique?,
82
+ type: index.type,
83
+ where: index.predicate
76
84
  when SchemaDiff::IndexRemoved
77
- drop_index index.attribute
85
+ drop_index index.attributes, name: index.name
78
86
  end
79
87
  end
80
88
  end
@@ -111,8 +111,28 @@ module ROM
111
111
  @index = index
112
112
  end
113
113
 
114
- def attribute
115
- index.attributes[0].name
114
+ def attributes
115
+ index.attributes.map(&:name)
116
+ end
117
+
118
+ def name
119
+ index.name
120
+ end
121
+
122
+ def unique?
123
+ index.unique?
124
+ end
125
+
126
+ def type
127
+ index.type
128
+ end
129
+
130
+ def predicate
131
+ index.predicate
132
+ end
133
+
134
+ def partial?
135
+ !predicate.nil?
116
136
  end
117
137
  end
118
138
 
@@ -165,7 +185,7 @@ module ROM
165
185
  current.indexes.any? { |curr_idx| curr_idx.attributes == idx.attributes }
166
186
  }
167
187
  removed_indexes = current.indexes.select { |idx|
168
- target.indexes.none? { |tgt_idx| idx.attributes == tgt_idx }
188
+ target.indexes.none? { |tgt_idx| idx.attributes == tgt_idx.attributes }
169
189
  }
170
190
 
171
191
  removed_indexes.map { |idx| IndexRemoved.new(idx) } +
@@ -60,7 +60,7 @@ module ROM
60
60
  after_hooks = associate_options.select(&:after?).map(&:to_hash)
61
61
 
62
62
  command.
63
- with_opts(configured_associations: configured_assocs + associate_options.map(&:name)).
63
+ with(configured_associations: configured_assocs + associate_options.map(&:name)).
64
64
  before(*before_hooks).
65
65
  after(*after_hooks)
66
66
  end
@@ -73,10 +73,10 @@ module ROM
73
73
  # associates :user, key: [:user_id, :id]
74
74
  # end
75
75
  #
76
- # create_user = rom.command(:user).create.with(name: 'Jane')
76
+ # create_user = rom.command(:user).create.curry(name: 'Jane')
77
77
  #
78
78
  # create_tasks = rom.command(:tasks).create
79
- # .with [{ title: 'One' }, { title: 'Two' } ]
79
+ # .curry [{ title: 'One' }, { title: 'Two' } ]
80
80
  #
81
81
  # command = create_user >> create_tasks
82
82
  # command.call
@@ -33,7 +33,7 @@ module ROM
33
33
  # timestamps :created_at, :updated_at
34
34
  # end
35
35
  #
36
- # create_user = rom.command(:user).create.with(name: 'Jane')
36
+ # create_user = rom.command(:user).create.curry(name: 'Jane')
37
37
  #
38
38
  # result = create_user.call
39
39
  # result[:created_at] #=> Time.now.utc
@@ -57,7 +57,7 @@ module ROM
57
57
  # datestamps :created_on, :updated_on
58
58
  # end
59
59
  #
60
- # create_user = rom.command(:user).create.with(name: 'Jane')
60
+ # create_user = rom.command(:user).create.curry(name: 'Jane')
61
61
  #
62
62
  # result = create_user.call
63
63
  # result[:created_at] #=> Date.today
@@ -2,6 +2,7 @@ require 'rom/sql/types'
2
2
  require 'rom/sql/schema'
3
3
  require 'rom/sql/attribute'
4
4
  require 'rom/sql/wrap'
5
+ require 'rom/sql/transaction'
5
6
 
6
7
  require 'rom/sql/relation/reading'
7
8
  require 'rom/sql/relation/writing'
@@ -24,6 +25,7 @@ module ROM
24
25
  schema_class SQL::Schema
25
26
  schema_attr_class SQL::Attribute
26
27
  schema_inferrer ROM::SQL::Schema::Inferrer.new.freeze
28
+ schema_dsl SQL::Schema::DSL
27
29
  wrap_class SQL::Wrap
28
30
 
29
31
  subscribe('configuration.relations.schema.set', adapter: :sql) do |event|
@@ -113,6 +115,11 @@ module ROM
113
115
  associations[name].()
114
116
  end
115
117
 
118
+ # @api public
119
+ def transaction(opts = EMPTY_HASH, &block)
120
+ Transaction.new(dataset.db).run(opts, &block)
121
+ end
122
+
116
123
  # Return raw column names
117
124
  #
118
125
  # @return [Array<Symbol>]
@@ -851,7 +851,7 @@ module ROM
851
851
  clause = lock_clause(options)
852
852
 
853
853
  if block
854
- dataset.db.transaction do
854
+ transaction do
855
855
  block.call(dataset.lock_style(clause).to_a)
856
856
  end
857
857
  else
@@ -1,5 +1,6 @@
1
1
  require 'rom/schema'
2
2
 
3
+ require 'rom/sql/schema/dsl'
3
4
  require 'rom/sql/order_dsl'
4
5
  require 'rom/sql/group_dsl'
5
6
  require 'rom/sql/projection_dsl'
@@ -12,7 +13,7 @@ module ROM
12
13
  class Schema < ROM::Schema
13
14
  # @!attribute [r] attributes
14
15
  # @return [Array<Index>] Array with schema indexes
15
- option :indexes, default: -> { EMPTY_ARRAY }
16
+ option :indexes, default: -> { EMPTY_SET }
16
17
 
17
18
  # @api public
18
19
  def restriction(&block)
@@ -0,0 +1,26 @@
1
+ require 'rom/sql/schema/index_dsl'
2
+
3
+ module ROM
4
+ module SQL
5
+ class Schema < ROM::Schema
6
+ # @api public
7
+ class DSL < ROM::Schema::DSL
8
+ attr_reader :index_dsl
9
+
10
+ def indexes(&block)
11
+ @index_dsl = IndexDSL.new(options, &block)
12
+ end
13
+
14
+ def opts
15
+ if index_dsl
16
+ opts = super
17
+
18
+ { **opts, indexes: index_dsl.(relation, opts[:attributes]) }
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ require 'set'
2
+
3
+ module ROM
4
+ module SQL
5
+ class Schema < ROM::Schema
6
+ # @api public
7
+ class IndexDSL # < BasicObject
8
+ extend Initializer
9
+
10
+ option :attr_class
11
+
12
+ attr_reader :registry
13
+
14
+ # @api private
15
+ def initialize(*, &block)
16
+ super
17
+
18
+ @registry = []
19
+
20
+ instance_exec(&block)
21
+ end
22
+
23
+ # @api public
24
+ def index(*attributes, **options)
25
+ registry << [attributes, options]
26
+ end
27
+
28
+ # @api private
29
+ def call(schema_name, types)
30
+ attributes = types.map { |type| attr_class.new(type).meta(source: schema_name) }
31
+
32
+ registry.map { |attr_names, options|
33
+ build_index(attributes, attr_names, options)
34
+ }.to_set
35
+ end
36
+
37
+ private
38
+
39
+ # @api private
40
+ def build_index(attributes, attr_names, options)
41
+ index_attributes = attr_names.map do |name|
42
+ attributes.find { |a| a.name == name }.unwrap
43
+ end
44
+
45
+ Index.new(index_attributes, options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -24,7 +24,7 @@ module ROM
24
24
  def call(schema, gateway)
25
25
  inferred = super
26
26
 
27
- indexes = get_indexes(gateway, schema.name.dataset, inferred[:attributes])
27
+ indexes = get_indexes(gateway, schema, inferred[:attributes])
28
28
 
29
29
  { **inferred, indexes: indexes }
30
30
  rescue Sequel::Error => error
@@ -33,20 +33,26 @@ module ROM
33
33
  end
34
34
 
35
35
  # @api private
36
- def get_indexes(gateway, dataset, attributes)
36
+ def get_indexes(gateway, schema, attributes)
37
+ dataset = schema.name.dataset
38
+
37
39
  if enabled? && gateway.connection.respond_to?(:indexes)
38
40
  gateway.connection.indexes(dataset).map { |name, body|
39
41
  columns = body[:columns].map { |name|
40
- attributes.find { |attr| attr.name == name }
42
+ attributes.find { |attr| attr.name == name }.unwrap
41
43
  }
42
44
 
43
- SQL::Index.new(columns, name: name)
45
+ SQL::Index.new(columns, name: name, unique: body[:unique])
44
46
  }.to_set
45
47
  else
46
- attributes.select(&:indexed?).map { |attr| SQL::Index.new([attr]) }.to_set
48
+ schema.indexes | indexes_from_attributes(attributes)
47
49
  end
48
50
  end
49
51
 
52
+ def indexes_from_attributes(attributes)
53
+ attributes.select(&:indexed?).map { |attr| SQL::Index.new([attr.unwrap]) }.to_set
54
+ end
55
+
50
56
  # @api private
51
57
  def suppress_errors
52
58
  with(raise_on_error: false, silent: true)