rom-sql 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +16 -12
- data/CHANGELOG.md +23 -0
- data/Gemfile +11 -3
- data/README.md +1 -7
- data/lib/rom/sql.rb +4 -7
- data/lib/rom/sql/association.rb +1 -1
- data/lib/rom/sql/association/one_to_many.rb +44 -1
- data/lib/rom/sql/association/one_to_one.rb +1 -38
- data/lib/rom/sql/commands.rb +0 -3
- data/lib/rom/sql/commands/error_wrapper.rb +1 -1
- data/lib/rom/sql/errors.rb +4 -1
- data/lib/rom/sql/extensions.rb +19 -0
- data/lib/rom/sql/{support → extensions}/active_support_notifications.rb +0 -0
- data/lib/rom/sql/extensions/postgres.rb +3 -0
- data/lib/rom/sql/{commands/postgres.rb → extensions/postgres/commands.rb} +38 -0
- data/lib/rom/sql/extensions/postgres/inferrer.rb +64 -0
- data/lib/rom/sql/extensions/postgres/types.rb +65 -0
- data/lib/rom/sql/{support → extensions}/rails_log_subscriber.rb +0 -0
- data/lib/rom/sql/gateway.rb +15 -4
- data/lib/rom/sql/relation.rb +6 -2
- data/lib/rom/sql/relation/reading.rb +18 -0
- data/lib/rom/sql/schema/dsl.rb +7 -4
- data/lib/rom/sql/schema/inferrer.rb +44 -31
- data/lib/rom/sql/types.rb +5 -1
- data/lib/rom/sql/version.rb +1 -1
- data/rom-sql.gemspec +14 -13
- data/spec/extensions/postgres/inferrer_spec.rb +40 -0
- data/spec/extensions/postgres/integration_spec.rb +38 -0
- data/spec/extensions/postgres/types_spec.rb +115 -0
- data/spec/integration/association/many_to_many_spec.rb +2 -1
- data/spec/integration/association/one_to_one_spec.rb +6 -4
- data/spec/integration/combine_spec.rb +1 -1
- data/spec/integration/commands/create_spec.rb +46 -21
- data/spec/integration/commands/delete_spec.rb +13 -38
- data/spec/integration/commands/update_spec.rb +19 -41
- data/spec/integration/commands/upsert_spec.rb +1 -1
- data/spec/integration/gateway_spec.rb +5 -9
- data/spec/integration/migration_spec.rb +6 -7
- data/spec/integration/read_spec.rb +30 -38
- data/spec/integration/schema_inference_spec.rb +211 -49
- data/spec/integration/setup_spec.rb +5 -5
- data/spec/integration/support/active_support_notifications_spec.rb +4 -3
- data/spec/integration/support/rails_log_subscriber_spec.rb +5 -4
- data/spec/shared/database_setup.rb +21 -6
- data/spec/spec_helper.rb +44 -35
- data/spec/unit/association/one_to_many_spec.rb +20 -0
- data/spec/unit/association/one_to_one_spec.rb +23 -2
- data/spec/unit/association_errors_spec.rb +1 -1
- data/spec/unit/gateway_spec.rb +9 -8
- data/spec/unit/logger_spec.rb +1 -1
- data/spec/unit/migration_tasks_spec.rb +3 -3
- data/spec/unit/migrator_spec.rb +3 -2
- data/spec/unit/plugin/assoc_macros/combined_associations_spec.rb +1 -1
- data/spec/unit/plugin/assoc_macros/many_to_many_spec.rb +1 -1
- data/spec/unit/plugin/assoc_macros/many_to_one_spec.rb +1 -1
- data/spec/unit/plugin/assoc_macros/one_to_many_spec.rb +1 -1
- data/spec/unit/relation/associations_spec.rb +27 -0
- data/spec/unit/relation/avg_spec.rb +11 -0
- data/spec/unit/relation/by_pk_spec.rb +15 -0
- data/spec/unit/relation/dataset_spec.rb +48 -0
- data/spec/unit/relation/distinct_spec.rb +14 -0
- data/spec/unit/relation/exclude_spec.rb +13 -0
- data/spec/unit/relation/fetch_spec.rb +21 -0
- data/spec/unit/relation/having_spec.rb +20 -0
- data/spec/unit/relation/inner_join_spec.rb +22 -0
- data/spec/unit/relation/inspect_spec.rb +11 -0
- data/spec/unit/relation/invert_spec.rb +12 -0
- data/spec/unit/relation/left_join_spec.rb +16 -0
- data/spec/unit/relation/map_spec.rb +16 -0
- data/spec/unit/relation/max_spec.rb +11 -0
- data/spec/unit/relation/min_spec.rb +11 -0
- data/spec/unit/relation/pluck_spec.rb +11 -0
- data/spec/unit/relation/prefix_spec.rb +27 -0
- data/spec/unit/relation/primary_key_spec.rb +27 -0
- data/spec/unit/relation/project_spec.rb +22 -0
- data/spec/unit/relation/qualified_columns_spec.rb +27 -0
- data/spec/unit/relation/rename_spec.rb +21 -0
- data/spec/unit/relation/sum_spec.rb +11 -0
- data/spec/unit/relation/union_spec.rb +19 -0
- data/spec/unit/relation/unique_predicate_spec.rb +18 -0
- data/spec/unit/schema_spec.rb +1 -1
- data/spec/unit/types_spec.rb +4 -21
- metadata +79 -11
- data/lib/rom/sql/commands_ext/postgres.rb +0 -45
- data/lib/rom/sql/types/pg.rb +0 -26
- data/spec/unit/relation_spec.rb +0 -272
@@ -8,6 +8,26 @@ RSpec.describe ROM::SQL::Association::OneToMany, helpers: true do
|
|
8
8
|
let(:users) { double(:users, primary_key: :id) }
|
9
9
|
let(:tasks) { double(:tasks) }
|
10
10
|
|
11
|
+
describe '#associate' do
|
12
|
+
let(:source) { :users }
|
13
|
+
let(:target) { :tasks }
|
14
|
+
|
15
|
+
let(:relations) do
|
16
|
+
{ users: users, tasks: tasks }
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns child tuple with FK set' do
|
20
|
+
expect(tasks).to receive(:foreign_key).with(:users).and_return(:user_id)
|
21
|
+
|
22
|
+
task_tuple = { title: 'Task' }
|
23
|
+
user_tuple = { id: 3 }
|
24
|
+
|
25
|
+
expect(assoc.associate(relations, task_tuple, user_tuple)).to eql(
|
26
|
+
user_id: 3, title: 'Task'
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
11
31
|
shared_examples_for 'one-to-many association' do
|
12
32
|
describe '#combine_keys' do
|
13
33
|
it 'returns a hash with combine keys' do
|
@@ -5,8 +5,29 @@ RSpec.describe ROM::SQL::Association::OneToOne, helpers: true do
|
|
5
5
|
|
6
6
|
let(:options) { {} }
|
7
7
|
|
8
|
-
let(:users)
|
9
|
-
let(:tasks)
|
8
|
+
let(:users) { double(:users, primary_key: :id) }
|
9
|
+
let(:tasks) { double(:tasks) }
|
10
|
+
let(:avatars) { double(:avatars) }
|
11
|
+
|
12
|
+
describe '#associate' do
|
13
|
+
let(:source) { :users }
|
14
|
+
let(:target) { :avatar }
|
15
|
+
|
16
|
+
let(:relations) do
|
17
|
+
{ users: users, avatar: avatars }
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns child tuple with FK set' do
|
21
|
+
expect(avatars).to receive(:foreign_key).with(:users).and_return(:user_id)
|
22
|
+
|
23
|
+
avatar_tuple = { url: 'http://rom-rb.org/images/logo.svg' }
|
24
|
+
user_tuple = { id: 3 }
|
25
|
+
|
26
|
+
expect(assoc.associate(relations, avatar_tuple, user_tuple)).to eql(
|
27
|
+
user_id: 3, url: 'http://rom-rb.org/images/logo.svg'
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
10
31
|
|
11
32
|
shared_examples_for 'one-to-one association' do
|
12
33
|
describe '#combine_keys' do
|
data/spec/unit/gateway_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
require 'rom/lint/spec'
|
4
4
|
|
5
|
-
describe ROM::SQL::Gateway do
|
5
|
+
RSpec.describe ROM::SQL::Gateway, :postgres do
|
6
6
|
include_context 'users and tasks'
|
7
7
|
|
8
8
|
let(:gateway) { container.gateways[:default] }
|
@@ -10,13 +10,14 @@ describe ROM::SQL::Gateway do
|
|
10
10
|
it_behaves_like 'a rom gateway' do
|
11
11
|
let(:identifier) { :sql }
|
12
12
|
let(:gateway) { ROM::SQL::Gateway }
|
13
|
-
let(:uri) { POSTGRES_DB_URI }
|
14
13
|
end
|
15
14
|
|
16
|
-
describe 'sqlite with a file db' do
|
15
|
+
describe 'sqlite with a file db', :sqlite, postgres: false do
|
16
|
+
before do
|
17
|
+
Tempfile.new('test.sqlite')
|
18
|
+
end
|
19
|
+
|
17
20
|
it 'establishes an sqlite connection' do
|
18
|
-
db_file = Tempfile.new('test.sqlite')
|
19
|
-
uri = "#{defined?(JRUBY_VERSION) ? 'jdbc:sqlite' : 'sqlite'}://#{db_file.path}"
|
20
21
|
gateway = ROM::SQL::Gateway.new(uri)
|
21
22
|
expect(gateway).to be_instance_of(ROM::SQL::Gateway)
|
22
23
|
end
|
@@ -37,10 +38,10 @@ describe ROM::SQL::Gateway do
|
|
37
38
|
migrator = double('migrator')
|
38
39
|
|
39
40
|
expect(Sequel).to receive(:connect)
|
40
|
-
.with(
|
41
|
+
.with(uri, host: '127.0.0.1')
|
41
42
|
.and_return(conn)
|
42
43
|
|
43
|
-
gateway = ROM::SQL::Gateway.new(
|
44
|
+
gateway = ROM::SQL::Gateway.new(uri, migrator: migrator, host: '127.0.0.1')
|
44
45
|
|
45
46
|
expect(gateway.options).to eql(migrator: migrator)
|
46
47
|
end
|
@@ -49,7 +50,7 @@ describe ROM::SQL::Gateway do
|
|
49
50
|
extensions = [:pg_array, :pg_enum]
|
50
51
|
connection = Sequel.connect uri
|
51
52
|
|
52
|
-
expect(connection).to receive(:extension).with(:pg_array, :pg_enum)
|
53
|
+
expect(connection).to receive(:extension).with(:pg_array, :pg_json, :pg_enum)
|
53
54
|
|
54
55
|
ROM::SQL::Gateway.new(connection, extensions: extensions)
|
55
56
|
end
|
data/spec/unit/logger_spec.rb
CHANGED
@@ -6,9 +6,9 @@ namespace :db do
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
describe 'MigrationTasks' do
|
10
|
-
|
11
|
-
|
9
|
+
RSpec.describe 'MigrationTasks', :postgres, skip_tables: true do
|
10
|
+
include_context 'database setup'
|
11
|
+
|
12
12
|
let(:migrator) { container.gateways[:default].migrator }
|
13
13
|
|
14
14
|
before do
|
data/spec/unit/migrator_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
RSpec.describe ROM::SQL::Migration::Migrator do
|
1
|
+
RSpec.describe ROM::SQL::Migration::Migrator, :postgres, skip_tables: true do
|
2
|
+
include_context 'database setup'
|
3
|
+
|
2
4
|
subject(:migrator) { ROM::SQL::Migration::Migrator.new(conn, options) }
|
3
5
|
|
4
|
-
let(:conn) { Sequel.connect(POSTGRES_DB_URI) }
|
5
6
|
let(:options) { { path: TMP_PATH.join('test/migrations') } }
|
6
7
|
|
7
8
|
describe '#create_file' do
|
@@ -0,0 +1,27 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#associations' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
7
|
+
context 'with schema' do
|
8
|
+
it 'returns configured primary key from the schema' do
|
9
|
+
conf.relation(:users) do
|
10
|
+
schema(infer: true) do
|
11
|
+
associations do
|
12
|
+
has_many :tasks
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
expect(relation.associations[:tasks]).to be(container.relations.users.schema.associations[:tasks])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'without schema' do
|
22
|
+
it 'returns an empty association set' do
|
23
|
+
expect(relation.associations.elements).to be_empty
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#by_pk' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
7
|
+
it 'restricts a relation by its PK' do
|
8
|
+
expect(relation.by_pk(1).to_a).to eql([id: 1, name: 'Jane'])
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is available as a view' do
|
12
|
+
expect(relation.by_pk).to be_curried
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#dataset' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
let(:dataset) { container.gateways[:default].dataset(:users) }
|
7
|
+
|
8
|
+
with_adapters do
|
9
|
+
context 'with schema' do
|
10
|
+
before do
|
11
|
+
conf.relation(:users) do
|
12
|
+
schema do
|
13
|
+
attribute :id, ROM::SQL::Types::Serial
|
14
|
+
attribute :name, ROM::SQL::Types::String
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'uses schema to infer default dataset' do
|
20
|
+
expect(relation.dataset).to eql(dataset.select(:id, :name).order(:users__id))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with cherry-picked attributes in schema' do
|
25
|
+
before do
|
26
|
+
conf.relation(:users) do
|
27
|
+
schema do
|
28
|
+
attribute :id, ROM::SQL::Types::Serial
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'uses schema to infer default dataset' do
|
34
|
+
expect(relation.dataset).to eql(dataset.select(:id).order(:users__id))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'without schema' do
|
39
|
+
before do
|
40
|
+
conf.relation(:users)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'selects all qualified columns and sorts by pk' do
|
44
|
+
expect(relation.dataset).to eql(dataset.select(*relation.columns).order(:users__id))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#distinct' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
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
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#exclude' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
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)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#fetch' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
7
|
+
describe '#fetch' do
|
8
|
+
it 'returns a single tuple identified by the pk' do
|
9
|
+
expect(relation.fetch(1)).to eql(id: 1, name: 'Jane')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'raises when tuple was not found' do
|
13
|
+
expect { relation.fetch(535315412) }.to raise_error(ROM::TupleCountMismatchError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'raises when more tuples were returned' do
|
17
|
+
expect { relation.fetch([1, 2]) }.to raise_error(ROM::TupleCountMismatchError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#having' do
|
2
|
+
subject(:relation) do
|
3
|
+
container.relations.users
|
4
|
+
.inner_join(:tasks, user_id: :id)
|
5
|
+
.select_group(:users__id, :users__name)
|
6
|
+
.select_append { count(:tasks).as(:task_count) }
|
7
|
+
end
|
8
|
+
|
9
|
+
include_context 'users and tasks'
|
10
|
+
|
11
|
+
with_adapters :postgres do
|
12
|
+
before do
|
13
|
+
conn[:tasks].insert(id: 3, user_id: 2, title: "Joe's another task")
|
14
|
+
end
|
15
|
+
|
16
|
+
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
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#inner_join' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
7
|
+
it 'joins relations using inner join' do
|
8
|
+
result = relation.inner_join(:tasks, user_id: :id).select(:name, :title)
|
9
|
+
|
10
|
+
expect(result.to_a).to eql([
|
11
|
+
{ name: 'Jane', title: "Jane's task" },
|
12
|
+
{ name: 'Joe', title: "Joe's task" }
|
13
|
+
])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'raises error when column names are ambiguous' do
|
17
|
+
expect {
|
18
|
+
relation.inner_join(:tasks, user_id: :id).to_a
|
19
|
+
}.to raise_error(Sequel::DatabaseError, /ambiguous/)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#invert' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
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)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#left_join' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
7
|
+
it 'joins relations using left outer join' do
|
8
|
+
result = relation.left_join(:tasks, user_id: :id).select(:name, :title)
|
9
|
+
|
10
|
+
expect(result.to_a).to match_array([
|
11
|
+
{ name: 'Joe', title: "Joe's task" },
|
12
|
+
{ name: 'Jane', title: "Jane's task" }
|
13
|
+
])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
RSpec.describe ROM::Relation, '#map' do
|
2
|
+
subject(:relation) { container.relations.users }
|
3
|
+
|
4
|
+
include_context 'users and tasks'
|
5
|
+
|
6
|
+
with_adapters do
|
7
|
+
it 'yields tuples' do
|
8
|
+
result = relation.map { |tuple| tuple[:name] }
|
9
|
+
expect(result).to eql(%w(Jane Joe))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'plucks value' do
|
13
|
+
expect(relation.map(:name)).to eql(%w(Jane Joe))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|