rom-sql 0.9.1 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +32 -0
  4. data/Gemfile +4 -1
  5. data/lib/rom/plugins/relation/sql/auto_wrap.rb +1 -3
  6. data/lib/rom/sql/association.rb +33 -14
  7. data/lib/rom/sql/association/many_to_many.rb +17 -10
  8. data/lib/rom/sql/association/many_to_one.rb +29 -13
  9. data/lib/rom/sql/association/name.rb +12 -4
  10. data/lib/rom/sql/association/one_to_many.rb +21 -10
  11. data/lib/rom/sql/commands/create.rb +0 -1
  12. data/lib/rom/sql/commands/update.rb +1 -49
  13. data/lib/rom/sql/dsl.rb +29 -0
  14. data/lib/rom/sql/expression.rb +26 -0
  15. data/lib/rom/sql/function.rb +23 -0
  16. data/lib/rom/sql/gateway.rb +24 -9
  17. data/lib/rom/sql/migration.rb +6 -7
  18. data/lib/rom/sql/migration/migrator.rb +7 -8
  19. data/lib/rom/sql/order_dsl.rb +20 -0
  20. data/lib/rom/sql/plugin/associates.rb +58 -45
  21. data/lib/rom/sql/plugin/pagination.rb +8 -11
  22. data/lib/rom/sql/plugins.rb +0 -2
  23. data/lib/rom/sql/projection_dsl.rb +41 -0
  24. data/lib/rom/sql/qualified_attribute.rb +2 -2
  25. data/lib/rom/sql/relation.rb +35 -67
  26. data/lib/rom/sql/relation/reading.rb +77 -25
  27. data/lib/rom/sql/restriction_dsl.rb +24 -0
  28. data/lib/rom/sql/schema.rb +73 -7
  29. data/lib/rom/sql/schema/associations_dsl.rb +4 -3
  30. data/lib/rom/sql/schema/dsl.rb +5 -2
  31. data/lib/rom/sql/schema/inferrer.rb +21 -11
  32. data/lib/rom/sql/transaction.rb +19 -0
  33. data/lib/rom/sql/type.rb +76 -0
  34. data/lib/rom/sql/version.rb +1 -1
  35. data/rom-sql.gemspec +3 -4
  36. data/spec/extensions/postgres/inferrer_spec.rb +19 -9
  37. data/spec/integration/association/many_to_many/custom_fks_spec.rb +73 -0
  38. data/spec/integration/association/many_to_many/from_view_spec.rb +81 -0
  39. data/spec/integration/association/many_to_many_spec.rb +2 -2
  40. data/spec/integration/association/many_to_one/custom_fks_spec.rb +59 -0
  41. data/spec/integration/association/many_to_one/from_view_spec.rb +74 -0
  42. data/spec/integration/association/many_to_one/self_ref_spec.rb +51 -0
  43. data/spec/integration/association/many_to_one_spec.rb +4 -2
  44. data/spec/integration/association/one_to_many/custom_fks_spec.rb +48 -0
  45. data/spec/integration/association/one_to_many/from_view_spec.rb +57 -0
  46. data/spec/integration/association/one_to_many/self_ref_spec.rb +52 -0
  47. data/spec/integration/association/one_to_many_spec.rb +1 -1
  48. data/spec/integration/association/one_to_one_spec.rb +1 -1
  49. data/spec/integration/association/one_to_one_through_spec.rb +2 -2
  50. data/spec/integration/commands/create_spec.rb +11 -27
  51. data/spec/integration/commands/update_spec.rb +54 -109
  52. data/spec/integration/gateway_spec.rb +31 -17
  53. data/spec/integration/plugins/associates_spec.rb +27 -0
  54. data/spec/integration/plugins/auto_wrap_spec.rb +8 -8
  55. data/spec/integration/schema/call_spec.rb +24 -0
  56. data/spec/integration/schema/prefix_spec.rb +18 -0
  57. data/spec/integration/schema/qualified_spec.rb +18 -0
  58. data/spec/integration/schema/rename_spec.rb +23 -0
  59. data/spec/integration/schema/view_spec.rb +29 -0
  60. data/spec/integration/schema_inference_spec.rb +31 -14
  61. data/spec/spec_helper.rb +2 -2
  62. data/spec/support/helpers.rb +7 -0
  63. data/spec/unit/gateway_spec.rb +5 -4
  64. data/spec/unit/projection_dsl_spec.rb +54 -0
  65. data/spec/unit/relation/dataset_spec.rb +3 -3
  66. data/spec/unit/relation/distinct_spec.rb +8 -7
  67. data/spec/unit/relation/exclude_spec.rb +2 -4
  68. data/spec/unit/relation/having_spec.rb +6 -4
  69. data/spec/unit/relation/inner_join_spec.rb +47 -2
  70. data/spec/unit/relation/invert_spec.rb +2 -3
  71. data/spec/unit/relation/left_join_spec.rb +44 -3
  72. data/spec/unit/relation/order_spec.rb +40 -0
  73. data/spec/unit/relation/prefix_spec.rb +2 -0
  74. data/spec/unit/relation/project_spec.rb +3 -1
  75. data/spec/unit/relation/qualified_columns_spec.rb +2 -0
  76. data/spec/unit/relation/rename_spec.rb +2 -0
  77. data/spec/unit/relation/right_join_spec.rb +59 -0
  78. data/spec/unit/relation/select_append_spec.rb +21 -0
  79. data/spec/unit/relation/select_spec.rb +41 -0
  80. data/spec/unit/relation/where_spec.rb +28 -0
  81. data/spec/unit/restriction_dsl_spec.rb +34 -0
  82. metadata +62 -40
  83. data/lib/rom/plugins/relation/sql/base_view.rb +0 -31
  84. data/lib/rom/sql/header.rb +0 -61
  85. data/lib/rom/sql/plugin/assoc_macros.rb +0 -133
  86. data/lib/rom/sql/plugin/assoc_macros/class_interface.rb +0 -128
  87. data/spec/integration/read_spec.rb +0 -111
  88. data/spec/unit/association_errors_spec.rb +0 -19
  89. data/spec/unit/plugin/assoc_macros/combined_associations_spec.rb +0 -73
  90. data/spec/unit/plugin/assoc_macros/many_to_many_spec.rb +0 -53
  91. data/spec/unit/plugin/assoc_macros/many_to_one_spec.rb +0 -61
  92. data/spec/unit/plugin/assoc_macros/one_to_many_spec.rb +0 -78
  93. data/spec/unit/plugin/base_view_spec.rb +0 -18
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ROM::SQL::Schema, '#prefix' do
4
+ include_context 'database setup'
5
+
6
+ with_adapters :postgres do
7
+ before do
8
+ conf.relation(:users) do
9
+ schema(infer: true)
10
+ end
11
+ end
12
+
13
+ it 'auto-projects a relation with renamed columns using provided prefix' do
14
+ expect(relations[:users].schema.prefix(:user).(relations[:users]).dataset.sql)
15
+ .to eql('SELECT "id" AS "user_id", "name" AS "user_name" FROM "users" ORDER BY "users"."id"')
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ROM::SQL::Schema, '#qualified' do
4
+ include_context 'database setup'
5
+
6
+ with_adapters :postgres do
7
+ before do
8
+ conf.relation(:users) do
9
+ schema(infer: true)
10
+ end
11
+ end
12
+
13
+ it 'qualifies column names' do
14
+ expect(relations[:users].schema.qualified.(relations[:users]).dataset.sql)
15
+ .to eql('SELECT "users"."id", "users"."name" FROM "users" ORDER BY "users"."id"')
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ROM::SQL::Schema, '#rename' do
4
+ include_context 'database setup'
5
+
6
+ with_adapters :postgres do
7
+ before do
8
+ conf.relation(:users) do
9
+ schema(infer: true)
10
+ end
11
+ end
12
+
13
+ it 'auto-projects a relation with renamed columns' do
14
+ expect(relations[:users].schema.rename(id: :user_id, name: :user_name).(relations[:users]).dataset.sql)
15
+ .to eql('SELECT "id" AS "user_id", "name" AS "user_name" FROM "users" ORDER BY "users"."id"')
16
+ end
17
+
18
+ it 'auto-projects a relation with renamed and qualified columns' do
19
+ expect(relations[:users].schema.qualified.rename(id: :user_id, name: :user_name).(relations[:users]).dataset.sql)
20
+ .to eql('SELECT "users"."id" AS "user_id", "users"."name" AS "user_name" FROM "users" ORDER BY "users"."id"')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Defining a view using schemas' do
4
+ include_context 'database setup'
5
+
6
+ with_adapters do
7
+ describe 'defining a projected view' do
8
+ before do
9
+ conf.relation(:users) do
10
+ schema(infer: true)
11
+
12
+ view(:names) do
13
+ schema { project(:name) }
14
+ relation { order(:name, :id) }
15
+ end
16
+ end
17
+
18
+ container.relations[:users].insert(name: 'Joe')
19
+ container.relations[:users].insert(name: 'Jane')
20
+ container.relations[:users].insert(name: 'Jade')
21
+ end
22
+
23
+ it 'automatically projects a relation view' do
24
+ expect(relations[:users].names.to_a)
25
+ .to eql([{ name: 'Jade' }, { name: 'Jane' }, { name: 'Joe' }])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -19,25 +19,28 @@ RSpec.describe 'Schema inference for common datatypes' do
19
19
 
20
20
  context 'for simple table' do
21
21
  let(:dataset) { :users }
22
+ let(:source) { ROM::Relation::Name[dataset] }
22
23
 
23
24
  it 'can infer attributes for dataset' do
24
- expect(schema.attributes).to eql(
25
- id: ROM::SQL::Types::Serial.meta(name: :id),
26
- name: ROM::SQL::Types::String.meta(name: :name)
25
+ expect(schema.to_h).to eql(
26
+ id: ROM::SQL::Types::Serial.meta(name: :id, source: source),
27
+ name: ROM::SQL::Types::String.meta(name: :name, source: source)
27
28
  )
28
29
  end
29
30
  end
30
31
 
31
32
  context 'for a table with FKs' do
32
33
  let(:dataset) { :tasks }
34
+ let(:source) { ROM::Relation::Name[:tasks] }
33
35
 
34
36
  it 'can infer attributes for dataset' do
35
- expect(schema.attributes).to eql(
36
- id: ROM::SQL::Types::Serial.meta(name: :id),
37
- title: ROM::SQL::Types::String.optional.meta(name: :title),
37
+ expect(schema.to_h).to eql(
38
+ id: ROM::SQL::Types::Serial.meta(name: :id, source: source),
39
+ title: ROM::SQL::Types::String.optional.meta(name: :title, source: source),
38
40
  user_id: ROM::SQL::Types::Int.optional.meta(name: :user_id,
39
41
  foreign_key: true,
40
- relation: :users)
42
+ source: source,
43
+ target: :users)
41
44
  )
42
45
  end
43
46
  end
@@ -63,15 +66,16 @@ RSpec.describe 'Schema inference for common datatypes' do
63
66
  end
64
67
 
65
68
  let(:dataset) { :test_inferrence }
69
+ let(:source) { ROM::Relation::Name[dataset] }
66
70
 
67
71
  it 'can infer attributes for dataset' do
68
- expect(schema.attributes).to eql(
69
- id: ROM::SQL::Types::Serial.meta(name: :id),
70
- text: ROM::SQL::Types::String.meta(name: :text),
71
- flag: ROM::SQL::Types::Bool.meta(name: :flag),
72
- date: ROM::SQL::Types::Date.optional.meta(name: :date),
73
- datetime: ROM::SQL::Types::Time.meta(name: :datetime),
74
- data: ROM::SQL::Types::Blob.optional.meta(name: :data)
72
+ expect(schema.to_h).to eql(
73
+ id: ROM::SQL::Types::Serial.meta(name: :id, source: source),
74
+ text: ROM::SQL::Types::String.meta(name: :text, source: source),
75
+ flag: ROM::SQL::Types::Bool.meta(name: :flag, source: source),
76
+ date: ROM::SQL::Types::Date.optional.meta(name: :date, source: source),
77
+ datetime: ROM::SQL::Types::Time.meta(name: :datetime, source: source),
78
+ data: ROM::SQL::Types::Blob.optional.meta(name: :data, source: source)
75
79
  )
76
80
  end
77
81
  end
@@ -226,4 +230,17 @@ RSpec.describe 'Schema inference for common datatypes' do
226
230
  end
227
231
  end
228
232
  end
233
+
234
+ with_adapters(:postgres) do
235
+ context 'with a table without columns' do
236
+ before do
237
+ conn.create_table(:dummy) unless conn.table_exists?(:dummy)
238
+ conf.relation(:dummy) { schema(infer: true) }
239
+ end
240
+
241
+ it 'does not fail with a weird error when a relation does not have attributes' do
242
+ expect(container.relations[:dummy].schema).to be_empty
243
+ end
244
+ end
245
+ end
229
246
  end
data/spec/spec_helper.rb CHANGED
@@ -44,8 +44,8 @@ TMP_PATH = root.join('../tmp')
44
44
  Dir[root.join('shared/**/*')].each { |f| require f }
45
45
  Dir[root.join('support/**/*')].each { |f| require f }
46
46
 
47
- require 'rom/support/deprecations'
48
- ROM::Deprecations.set_logger!(root.join('../log/deprecations.log'))
47
+ require 'dry/core/deprecations'
48
+ Dry::Core::Deprecations.set_logger!(root.join('../log/deprecations.log'))
49
49
 
50
50
  ROM::SQL.load_extensions(:postgres)
51
51
 
@@ -6,4 +6,11 @@ module Helpers
6
6
  def assoc_name(*args)
7
7
  ROM::SQL::Association::Name[*args]
8
8
  end
9
+
10
+ def define_schema(name, attrs)
11
+ ROM::SQL::Schema.define(
12
+ name,
13
+ attributes: attrs.map { |key, value| ROM::SQL::Type.new(value.meta(name: key)) }
14
+ )
15
+ end
9
16
  end
@@ -38,19 +38,20 @@ RSpec.describe ROM::SQL::Gateway, :postgres do
38
38
  migrator = double('migrator')
39
39
 
40
40
  expect(Sequel).to receive(:connect)
41
- .with(uri, host: '127.0.0.1')
41
+ .with(uri, host: '127.0.0.1', migrator: migrator)
42
42
  .and_return(conn)
43
43
 
44
44
  gateway = ROM::SQL::Gateway.new(uri, migrator: migrator, host: '127.0.0.1')
45
45
 
46
- expect(gateway.options).to eql(migrator: migrator)
46
+ expect(gateway.options).to eql(migrator: migrator, host: '127.0.0.1')
47
47
  end
48
48
 
49
49
  it 'allows extensions' do
50
- extensions = [:pg_array, :pg_enum]
50
+ extensions = [:pg_array, :pg_array_ops]
51
51
  connection = Sequel.connect uri
52
52
 
53
- expect(connection).to receive(:extension).with(:pg_array, :pg_json, :pg_enum)
53
+ expect(connection).to receive(:extension).with(:pg_array, :pg_json, :pg_enum, :pg_array_ops)
54
+ expect(connection).to receive(:extension).with(:freeze_datasets) unless RUBY_ENGINE == 'rbx'
54
55
 
55
56
  ROM::SQL::Gateway.new(connection, extensions: extensions)
56
57
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe ROM::SQL::ProjectionDSL, :sqlite, helpers: true do
4
+ include_context 'database setup'
5
+
6
+ subject(:dsl) do
7
+ ROM::SQL::ProjectionDSL.new(schema)
8
+ end
9
+
10
+ let(:schema) do
11
+ define_schema(:users, id: ROM::SQL::Types::Serial, name: ROM::SQL::Types::String)
12
+ end
13
+
14
+ let(:ds) do
15
+ conn[:users]
16
+ end
17
+
18
+ describe '#call' do
19
+ it 'evaluates the block and returns an array with attribute types' do
20
+ literals = dsl
21
+ .call { int::count(id).as(:count) }
22
+ .map { |attr| attr.sql_literal(ds) }
23
+
24
+ expect(literals).to eql(["count(`id`) AS 'count'"])
25
+ end
26
+ end
27
+
28
+ describe '#method_missing' do
29
+ it 'responds to methods matching attribute names' do
30
+ expect(dsl.id).to eql(schema[:id])
31
+ expect(dsl.name).to eql(schema[:name])
32
+ end
33
+
34
+ it 'responds to methods matching type identifiers' do
35
+ expect(dsl.int).to eql(ROM::SQL::Types::Int)
36
+ expect(dsl.string).to eql(ROM::SQL::Types::String)
37
+ expect(dsl.bool).to eql(ROM::SQL::Types::Bool)
38
+ end
39
+
40
+ it 'responds to methods matching type names' do
41
+ expect(dsl.DateTime).to eql(ROM::SQL::Types::DateTime)
42
+ end
43
+
44
+ it 'returns sql functions with return type specified' do
45
+ function = ROM::SQL::Function.new(ROM::SQL::Types::String).upper(schema[:name])
46
+
47
+ expect(dsl.string::upper(schema[:name])).to eql(function)
48
+ end
49
+
50
+ it 'raises NoMethodError when there is no matching attribute or type' do
51
+ expect { dsl.not_here }.to raise_error(NoMethodError, /not_here/)
52
+ end
53
+ end
54
+ end
@@ -17,7 +17,7 @@ RSpec.describe ROM::Relation, '#dataset' do
17
17
  end
18
18
 
19
19
  it 'uses schema to infer default dataset' do
20
- expect(relation.dataset).to eql(dataset.select(:id, :name).order(:users__id))
20
+ expect(relation.dataset.sql).to eql(dataset.select(:id, :name).order(:users__id).sql)
21
21
  end
22
22
  end
23
23
 
@@ -31,7 +31,7 @@ RSpec.describe ROM::Relation, '#dataset' do
31
31
  end
32
32
 
33
33
  it 'uses schema to infer default dataset' do
34
- expect(relation.dataset).to eql(dataset.select(:id).order(:users__id))
34
+ expect(relation.dataset.sql).to eql(dataset.select(:id).order(:users__id).sql)
35
35
  end
36
36
  end
37
37
 
@@ -41,7 +41,7 @@ RSpec.describe ROM::Relation, '#dataset' do
41
41
  end
42
42
 
43
43
  it 'selects all qualified columns and sorts by pk' do
44
- expect(relation.dataset).to eql(dataset.select(*relation.columns).order(:users__id))
44
+ expect(relation.dataset.sql).to eql(dataset.select(*relation.columns).order(:users__id).sql)
45
45
  end
46
46
  end
47
47
  end
@@ -1,14 +1,15 @@
1
1
  RSpec.describe ROM::Relation, '#distinct' do
2
- subject(:relation) { container.relations.users }
2
+ subject(:relation) { relations[:users] }
3
3
 
4
4
  include_context 'users and tasks'
5
5
 
6
- with_adapters do
7
- if !metadata[:sqlite]
8
- it 'delegates to dataset and returns a new relation' do
9
- expect(relation.dataset).to receive(:distinct).with(:name).and_call_original
10
- expect(relation.distinct(:name)).to_not eql(relation)
11
- end
6
+ before do
7
+ relation.insert id: 3, name: 'Jane'
8
+ end
9
+
10
+ with_adapters :postgres do
11
+ it 'delegates to dataset and returns a new relation' do
12
+ expect(relation.distinct(:name).order(:name).group(:name, :id).to_a).to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'Joe' }])
12
13
  end
13
14
  end
14
15
  end
@@ -1,13 +1,11 @@
1
1
  RSpec.describe ROM::Relation, '#exclude' do
2
- subject(:relation) { container.relations.users }
2
+ subject(:relation) { relations[:users] }
3
3
 
4
4
  include_context 'users and tasks'
5
5
 
6
6
  with_adapters do
7
7
  it 'delegates to dataset and returns a new relation' do
8
- expect(relation.dataset)
9
- .to receive(:exclude).with(name: 'Jane').and_call_original
10
- expect(relation.exclude(name: 'Jane')).to_not eq(relation)
8
+ expect(relation.exclude(name: 'Jane').to_a).to eql([{ id: 2, name: 'Joe' }])
11
9
  end
12
10
  end
13
11
  end
@@ -1,9 +1,10 @@
1
1
  RSpec.describe ROM::Relation, '#having' do
2
2
  subject(:relation) do
3
- container.relations.users
3
+ relations[:users]
4
4
  .inner_join(:tasks, user_id: :id)
5
- .select_group(:users__id, :users__name)
6
- .select_append { count(:tasks).as(:task_count) }
5
+ .qualified
6
+ .select_group(:id, :name)
7
+ .select_append { int::count(:tasks).as(:task_count) }
7
8
  end
8
9
 
9
10
  include_context 'users and tasks'
@@ -14,7 +15,8 @@ RSpec.describe ROM::Relation, '#having' do
14
15
  end
15
16
 
16
17
  it 'restricts a relation using HAVING clause' do
17
- expect(relation.having { count(:tasks__id) >= 2 }.to_a).to eq([{ id: 2, name: 'Joe', task_count: 2 }])
18
+ expect(relation.having { count(id.qualified) >= 2 }.to_a).
19
+ to eq([{ id: 2, name: 'Joe', task_count: 2 }])
18
20
  end
19
21
  end
20
22
  end
@@ -1,11 +1,19 @@
1
1
  RSpec.describe ROM::Relation, '#inner_join' do
2
- subject(:relation) { container.relations.users }
2
+ subject(:relation) { relations[:users] }
3
+
4
+ let(:tasks) { relations[:tasks] }
3
5
 
4
6
  include_context 'users and tasks'
5
7
 
6
8
  with_adapters do
7
9
  it 'joins relations using inner join' do
8
- result = relation.inner_join(:tasks, user_id: :id).select(:name, :title)
10
+ relation.insert id: 3, name: 'Jade'
11
+
12
+ result = relation.
13
+ inner_join(:tasks, user_id: :id).
14
+ select(:name, tasks[:title])
15
+
16
+ expect(result.schema.map(&:name)).to eql(%i[name title])
9
17
 
10
18
  expect(result.to_a).to eql([
11
19
  { name: 'Jane', title: "Jane's task" },
@@ -13,10 +21,47 @@ RSpec.describe ROM::Relation, '#inner_join' do
13
21
  ])
14
22
  end
15
23
 
24
+ context 'with associations' do
25
+ before do
26
+ conf.relation(:users) do
27
+ schema(infer: true) do
28
+ associations { has_many :tasks }
29
+ end
30
+ end
31
+
32
+ conf.relation(:tasks) do
33
+ schema(infer: true) do
34
+ associations { belongs_to :user }
35
+ end
36
+ end
37
+
38
+ relation.insert id: 3, name: 'Jade'
39
+ end
40
+
41
+ it 'joins relation with join keys inferred' do
42
+ result = relation.
43
+ inner_join(tasks).
44
+ select(:name, tasks[:title])
45
+
46
+ expect(result.schema.map(&:name)).to eql(%i[name title])
47
+
48
+ expect(result.to_a).to eql([
49
+ { name: 'Jane', title: "Jane's task" },
50
+ { name: 'Joe', title: "Joe's task" }
51
+ ])
52
+ end
53
+ end
54
+
16
55
  it 'raises error when column names are ambiguous' do
17
56
  expect {
18
57
  relation.inner_join(:tasks, user_id: :id).to_a
19
58
  }.to raise_error(Sequel::DatabaseError, /ambiguous/)
20
59
  end
60
+
61
+ it 'raises error when join arg is unsupported' do
62
+ expect {
63
+ relation.inner_join(421)
64
+ }.to raise_error(ArgumentError, /other/)
65
+ end
21
66
  end
22
67
  end
@@ -1,12 +1,11 @@
1
1
  RSpec.describe ROM::Relation, '#invert' do
2
- subject(:relation) { container.relations.users }
2
+ subject(:relation) { relations[:users] }
3
3
 
4
4
  include_context 'users and tasks'
5
5
 
6
6
  with_adapters do
7
7
  it 'delegates to dataset and returns a new relation' do
8
- expect(relation.dataset).to receive(:invert).and_call_original
9
- expect(relation.invert).to_not eq(relation)
8
+ expect(relation.where(name: 'Jane').invert.to_a).to eql([{ id: 2, name: 'Joe' }])
10
9
  end
11
10
  end
12
11
  end