rom-sql 0.3.2 → 0.4.0.beta1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +55 -18
- data/.rubocop_todo.yml +15 -0
- data/.travis.yml +10 -5
- data/CHANGELOG.md +18 -0
- data/Gemfile +8 -1
- data/Guardfile +24 -0
- data/README.md +14 -22
- data/Rakefile +13 -5
- data/lib/rom/sql.rb +5 -5
- data/lib/rom/sql/commands.rb +7 -49
- data/lib/rom/sql/commands/create.rb +29 -0
- data/lib/rom/sql/commands/delete.rb +18 -0
- data/lib/rom/sql/commands/transaction.rb +17 -0
- data/lib/rom/sql/commands/update.rb +54 -0
- data/lib/rom/sql/commands_ext/postgres.rb +24 -0
- data/lib/rom/sql/header.rb +8 -9
- data/lib/rom/sql/migration.rb +26 -0
- data/lib/rom/sql/plugin/pagination.rb +93 -0
- data/lib/rom/sql/rake_task.rb +2 -0
- data/lib/rom/sql/relation.rb +320 -0
- data/lib/rom/sql/relation/associations.rb +104 -0
- data/lib/rom/sql/relation/class_methods.rb +47 -0
- data/lib/rom/sql/relation/inspection.rb +16 -0
- data/lib/rom/sql/repository.rb +59 -0
- data/lib/rom/sql/support/rails_log_subscriber.rb +1 -1
- data/lib/rom/sql/tasks/migration_tasks.rake +56 -0
- data/lib/rom/sql/version.rb +1 -1
- data/rom-sql.gemspec +2 -3
- data/spec/integration/commands/create_spec.rb +66 -8
- data/spec/integration/commands/delete_spec.rb +22 -3
- data/spec/integration/commands/update_spec.rb +57 -6
- data/spec/integration/read_spec.rb +42 -1
- data/spec/shared/database_setup.rb +10 -5
- data/spec/spec_helper.rb +17 -0
- data/spec/support/active_support_notifications_spec.rb +5 -4
- data/spec/support/rails_log_subscriber_spec.rb +2 -2
- data/spec/unit/logger_spec.rb +5 -3
- data/spec/unit/many_to_many_spec.rb +2 -2
- data/spec/unit/migration_spec.rb +34 -0
- data/spec/unit/migration_tasks_spec.rb +99 -0
- data/spec/unit/one_to_many_spec.rb +0 -2
- data/spec/unit/plugin/pagination_spec.rb +73 -0
- data/spec/unit/relation_spec.rb +49 -3
- data/spec/unit/repository_spec.rb +33 -0
- data/spec/unit/schema_spec.rb +5 -17
- metadata +32 -35
- data/lib/rom/sql/adapter.rb +0 -100
- data/lib/rom/sql/relation_inclusion.rb +0 -149
- data/lib/rom/sql/support/sequel_dataset_ext.rb +0 -33
- data/spec/unit/adapter_spec.rb +0 -48
- data/spec/unit/config_spec.rb +0 -54
@@ -1,26 +1,77 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Commands / Update' do
|
4
|
-
include_context '
|
4
|
+
include_context 'database setup'
|
5
5
|
|
6
6
|
subject(:users) { rom.commands.users }
|
7
7
|
|
8
|
-
|
8
|
+
let(:relation) { rom.relations.users }
|
9
|
+
let(:piotr) { relation.by_name('Piotr').first }
|
10
|
+
let(:peter) { { name: 'Peter' } }
|
11
|
+
|
12
|
+
before do
|
9
13
|
setup.relation(:users) do
|
14
|
+
def by_id(id)
|
15
|
+
where(id: id).limit(1)
|
16
|
+
end
|
17
|
+
|
10
18
|
def by_name(name)
|
11
19
|
where(name: name)
|
12
20
|
end
|
13
21
|
end
|
14
22
|
|
15
23
|
setup.commands(:users) do
|
16
|
-
define(:update)
|
17
|
-
|
18
|
-
|
24
|
+
define(:update)
|
25
|
+
end
|
26
|
+
|
27
|
+
relation.insert(name: 'Piotr')
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#transaction' do
|
31
|
+
it 'update record if there was no errors' do
|
32
|
+
result = users.update.transaction do
|
33
|
+
users.update.by_id(piotr[:id]).set(peter)
|
19
34
|
end
|
35
|
+
|
36
|
+
expect(result.value).to eq([{ id: 1, name: 'Peter' }])
|
20
37
|
end
|
21
38
|
|
22
|
-
|
39
|
+
it 'updates nothing if error was raised' do
|
40
|
+
users.update.transaction do
|
41
|
+
users.update.by_id(piotr[:id]).set(peter)
|
42
|
+
raise ROM::SQL::Rollback
|
43
|
+
end
|
44
|
+
|
45
|
+
expect(relation.first[:name]).to eq('Piotr')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'updates everything when there is no original tuple' do
|
50
|
+
result = users.try do
|
51
|
+
users.update.by_id(piotr[:id]).set(peter)
|
52
|
+
end
|
53
|
+
|
54
|
+
expect(result.value.to_a).to match_array([{ id: 1, name: 'Peter' }])
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'updates when attributes changed' do
|
58
|
+
result = users.try do
|
59
|
+
users.update.by_id(piotr[:id]).change(piotr).to(peter)
|
60
|
+
end
|
23
61
|
|
24
62
|
expect(result.value.to_a).to match_array([{ id: 1, name: 'Peter' }])
|
25
63
|
end
|
64
|
+
|
65
|
+
it 'does not update when attributes did not change' do
|
66
|
+
piotr_rel = double('piotr_rel').as_null_object
|
67
|
+
|
68
|
+
expect(relation).to receive(:by_id).with(piotr[:id]).and_return(piotr_rel)
|
69
|
+
expect(piotr_rel).not_to receive(:update)
|
70
|
+
|
71
|
+
result = users.try do
|
72
|
+
users.update.by_id(piotr[:id]).change(piotr).to(name: piotr[:name])
|
73
|
+
end
|
74
|
+
|
75
|
+
expect(result.value.to_a).to be_empty
|
76
|
+
end
|
26
77
|
end
|
@@ -4,7 +4,7 @@ require 'virtus'
|
|
4
4
|
describe 'Reading relations' do
|
5
5
|
include_context 'users and tasks'
|
6
6
|
|
7
|
-
|
7
|
+
before :each do
|
8
8
|
class Task
|
9
9
|
include Virtus.value_object(coerce: true)
|
10
10
|
|
@@ -24,6 +24,16 @@ describe 'Reading relations' do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
class UserTaskCount
|
28
|
+
include Virtus.value_object(coerce: true)
|
29
|
+
|
30
|
+
values do
|
31
|
+
attribute :id, Integer
|
32
|
+
attribute :name, String
|
33
|
+
attribute :task_count, Integer
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
27
37
|
setup.relation(:tasks)
|
28
38
|
|
29
39
|
setup.relation(:users) do
|
@@ -42,6 +52,22 @@ describe 'Reading relations' do
|
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
55
|
+
setup.relation(:user_task_counts) do
|
56
|
+
dataset :users
|
57
|
+
register_as :user_task_counts
|
58
|
+
one_to_many :tasks, key: :user_id
|
59
|
+
|
60
|
+
def all
|
61
|
+
with_tasks.select_group(:users__id, :users__name).select_append {
|
62
|
+
count(:tasks).as(:task_count)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def with_tasks
|
67
|
+
association_left_join(:tasks, select: [:id, :title])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
45
71
|
setup.mappers do
|
46
72
|
define(:users) do
|
47
73
|
model User
|
@@ -53,8 +79,14 @@ describe 'Reading relations' do
|
|
53
79
|
attribute :title
|
54
80
|
end
|
55
81
|
end
|
82
|
+
|
83
|
+
define(:user_task_counts) do
|
84
|
+
model UserTaskCount
|
85
|
+
end
|
56
86
|
end
|
87
|
+
end
|
57
88
|
|
89
|
+
it 'loads domain objects' do
|
58
90
|
user = rom.read(:users).with_tasks.by_name('Piotr').to_a.first
|
59
91
|
|
60
92
|
expect(user).to eql(
|
@@ -62,4 +94,13 @@ describe 'Reading relations' do
|
|
62
94
|
id: 1, name: 'Piotr', tasks: [Task.new(id: 1, title: 'Finish ROM')]
|
63
95
|
))
|
64
96
|
end
|
97
|
+
|
98
|
+
it 'works with grouping and aggregates' do
|
99
|
+
rom.relations[:tasks].insert(id: 2, user_id: 1, title: 'Get Milk')
|
100
|
+
|
101
|
+
users_with_task_count = rom.read(:user_task_counts).all
|
102
|
+
expect(users_with_task_count.to_a).to eq([
|
103
|
+
UserTaskCount.new(id: 1, name: "Piotr", task_count: 2)
|
104
|
+
])
|
105
|
+
end
|
65
106
|
end
|
@@ -1,17 +1,22 @@
|
|
1
1
|
shared_context 'database setup' do
|
2
2
|
subject(:rom) { setup.finalize }
|
3
3
|
|
4
|
-
let(:
|
5
|
-
let(:conn) {
|
4
|
+
let(:uri) { 'postgres://localhost/rom' }
|
5
|
+
let(:conn) { Sequel.connect(uri) }
|
6
|
+
let(:setup) { ROM.setup(:sql, uri) }
|
7
|
+
|
8
|
+
def drop_tables
|
9
|
+
[:users, :tasks, :tags, :task_tags].each { |name| conn.drop_table?(name) }
|
10
|
+
end
|
6
11
|
|
7
12
|
before do
|
8
|
-
|
13
|
+
conn.loggers << LOGGER
|
9
14
|
|
10
|
-
|
15
|
+
drop_tables
|
11
16
|
|
12
17
|
conn.create_table :users do
|
13
18
|
primary_key :id
|
14
|
-
String :name
|
19
|
+
String :name, null: false
|
15
20
|
index :name, unique: true
|
16
21
|
end
|
17
22
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
|
3
6
|
if RUBY_ENGINE == 'rbx'
|
4
7
|
require "codeclimate-test-reporter"
|
5
8
|
CodeClimate::TestReporter.start
|
6
9
|
end
|
7
10
|
|
8
11
|
require 'rom-sql'
|
12
|
+
require 'rom/sql/rake_task'
|
13
|
+
# FIXME: why do we need to require it manually??
|
14
|
+
require 'sequel/adapters/postgres'
|
9
15
|
require 'logger'
|
10
16
|
|
11
17
|
LOGGER = Logger.new(File.open('./log/test.log', 'a'))
|
@@ -13,3 +19,14 @@ LOGGER = Logger.new(File.open('./log/test.log', 'a'))
|
|
13
19
|
root = Pathname(__FILE__).dirname
|
14
20
|
|
15
21
|
Dir[root.join('shared/*.rb').to_s].each { |f| require f }
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.before do
|
25
|
+
@constants = Object.constants
|
26
|
+
end
|
27
|
+
|
28
|
+
config.after do
|
29
|
+
added_constants = Object.constants - @constants
|
30
|
+
added_constants.each { |name| Object.send(:remove_const, name) }
|
31
|
+
end
|
32
|
+
end
|
@@ -7,16 +7,17 @@ describe 'ActiveSupport::Notifications support' do
|
|
7
7
|
include_context 'database setup'
|
8
8
|
|
9
9
|
it 'works' do
|
10
|
-
rom.
|
10
|
+
rom.repositories[:default].use_logger(LOGGER)
|
11
11
|
|
12
12
|
sql = nil
|
13
13
|
|
14
|
-
ActiveSupport::Notifications.subscribe('sql.rom') do
|
14
|
+
ActiveSupport::Notifications.subscribe('sql.rom') do |*, payload|
|
15
15
|
sql = payload[:sql]
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
query = %(SELECT * FROM "users" WHERE name = 'notification test')
|
19
|
+
conn.run(query)
|
19
20
|
|
20
|
-
expect(sql).to eql(
|
21
|
+
expect(sql).to eql(query)
|
21
22
|
end
|
22
23
|
end
|
@@ -18,11 +18,11 @@ describe 'Rails log subscriber' do
|
|
18
18
|
|
19
19
|
before do
|
20
20
|
set_logger(logger)
|
21
|
-
rom.
|
21
|
+
rom.repositories[:default].use_logger(logger)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'works' do
|
25
|
-
|
25
|
+
conn.run(test_query)
|
26
26
|
|
27
27
|
expect(logger.logged(:debug).last).to include(test_query)
|
28
28
|
end
|
data/spec/unit/logger_spec.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Logger' do
|
4
|
-
include_context '
|
4
|
+
include_context 'database setup'
|
5
5
|
|
6
6
|
it 'sets up a logger for sequel' do
|
7
|
-
repository = rom.
|
7
|
+
repository = rom.repositories[:default]
|
8
|
+
|
9
|
+
repository.use_logger(LOGGER)
|
8
10
|
|
9
11
|
expect(repository.logger).to be(LOGGER)
|
10
|
-
expect(
|
12
|
+
expect(conn.loggers).to include(LOGGER)
|
11
13
|
end
|
12
14
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ROM::SQL::Migration do
|
4
|
+
let(:migration) { ROM::SQL::Migration }
|
5
|
+
|
6
|
+
context '#run' do
|
7
|
+
it 'calls Sequel migration code' do
|
8
|
+
migration.path = 'foo/bar'
|
9
|
+
migration.connection = double
|
10
|
+
opts = { foo: 'bar' }
|
11
|
+
|
12
|
+
expect(Sequel::Migrator).to receive(:run)
|
13
|
+
.with(migration.connection, migration.path, opts)
|
14
|
+
|
15
|
+
migration.run(opts)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context '#path' do
|
20
|
+
it 'returns default path if non provided' do
|
21
|
+
migration.path = nil
|
22
|
+
|
23
|
+
expect(migration.path).to eq ROM::SQL::Migration::DEFAULT_PATH
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context '.create' do
|
28
|
+
it 'calls Sequel migration block' do
|
29
|
+
expect(Sequel).to receive(:migration)
|
30
|
+
|
31
|
+
ROM::SQL::Migration.create {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
namespace :db do
|
4
|
+
task :load_setup do
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'MigrationTasks' do
|
9
|
+
context 'db:reset' do
|
10
|
+
it 'calls proper commands' do
|
11
|
+
expect(ROM::SQL::Migration).to receive(:run).with(target: 0)
|
12
|
+
expect(ROM::SQL::Migration).to receive(:run)
|
13
|
+
|
14
|
+
expect {
|
15
|
+
Rake::Task["db:reset"].invoke
|
16
|
+
}.to output("<= db:reset executed\n").to_stdout
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'db:migrate' do
|
21
|
+
context 'with VERSION' do
|
22
|
+
it 'calls proper commands' do
|
23
|
+
expect(ROM::SQL::Migration).to receive(:run).with(target: 1)
|
24
|
+
|
25
|
+
expect {
|
26
|
+
Rake::Task["db:migrate"].invoke(1)
|
27
|
+
}.to output("<= db:migrate version=[1] executed\n").to_stdout
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'without VERSION' do
|
32
|
+
it 'calls proper commands' do
|
33
|
+
expect(ROM::SQL::Migration).to receive(:run)
|
34
|
+
|
35
|
+
expect {
|
36
|
+
Rake::Task["db:migrate"].execute
|
37
|
+
}.to output("<= db:migrate executed\n").to_stdout
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'db:clean' do
|
43
|
+
it 'calls proper commands' do
|
44
|
+
expect(ROM::SQL::Migration).to receive(:run).with(target: 0)
|
45
|
+
|
46
|
+
expect {
|
47
|
+
Rake::Task["db:clean"].invoke
|
48
|
+
}.to output("<= db:clean executed\n").to_stdout
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'db:create_migration' do
|
53
|
+
context 'without NAME' do
|
54
|
+
it 'exit without creating any file' do
|
55
|
+
expect(File).to_not receive(:write)
|
56
|
+
|
57
|
+
expect {
|
58
|
+
expect {
|
59
|
+
Rake::Task["db:create_migration"].execute
|
60
|
+
}.to output(/No NAME specified/).to_stdout
|
61
|
+
}.to raise_error(SystemExit)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with NAME' do
|
66
|
+
let(:dirname) { 'db/migration' }
|
67
|
+
let(:name) { 'foo_bar' }
|
68
|
+
let(:version) { '001' }
|
69
|
+
let(:filename) { "#{version}_#{name}.rb" }
|
70
|
+
let(:path) { File.join(dirname, filename) }
|
71
|
+
|
72
|
+
before do
|
73
|
+
expect(ROM::SQL::Migration).to receive(:path).and_return(dirname)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'calls proper commands with default VERSION' do
|
77
|
+
time = double(utc: double(strftime: '001'))
|
78
|
+
expect(Time).to receive(:now).and_return(time)
|
79
|
+
expect(FileUtils).to receive(:mkdir_p).with(dirname)
|
80
|
+
expect(File).to receive(:write).with(path, /ROM::SQL::Migration/)
|
81
|
+
|
82
|
+
expect {
|
83
|
+
Rake::Task["db:create_migration"].execute(
|
84
|
+
Rake::TaskArguments.new([:name], [name]))
|
85
|
+
}.to output(path+"\n").to_stdout
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'calls proper commands with manualy set VERSION' do
|
89
|
+
expect(FileUtils).to receive(:mkdir_p).with(dirname)
|
90
|
+
expect(File).to receive(:write).with(path, /ROM::SQL::Migration/)
|
91
|
+
|
92
|
+
expect {
|
93
|
+
Rake::Task["db:create_migration"].execute(
|
94
|
+
Rake::TaskArguments.new([:name, :version], [name, version]))
|
95
|
+
}.to output(path+"\n").to_stdout
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'rom/sql/plugin/pagination'
|
4
|
+
|
5
|
+
describe 'Plugin / Pagination' do
|
6
|
+
include_context 'database setup'
|
7
|
+
|
8
|
+
before do
|
9
|
+
9.times { |i| conn[:users].insert(name: "User #{i}") }
|
10
|
+
|
11
|
+
setup.relation(:users) do
|
12
|
+
include ROM::SQL::Plugin::Pagination # meh such constants not wow
|
13
|
+
|
14
|
+
per_page 4
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#page' do
|
19
|
+
it 'allow to call with stringify number' do
|
20
|
+
expect {
|
21
|
+
rom.relation(:users).page('5')
|
22
|
+
}.to_not raise_error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#per_page' do
|
27
|
+
it 'allow to call with stringify number' do
|
28
|
+
expect {
|
29
|
+
rom.relation(:users).per_page('5')
|
30
|
+
}.to_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns paginated relation with provided limit' do
|
34
|
+
users = rom.relation(:users).page(2).per_page(5)
|
35
|
+
|
36
|
+
expect(users.relation.dataset.opts[:offset]).to eql(5)
|
37
|
+
expect(users.relation.dataset.opts[:limit]).to eql(5)
|
38
|
+
|
39
|
+
expect(users.pager.current_page).to eql(2)
|
40
|
+
|
41
|
+
expect(users.pager.total).to be(9)
|
42
|
+
expect(users.pager.total_pages).to be(2)
|
43
|
+
|
44
|
+
expect(users.pager.next_page).to be(nil)
|
45
|
+
expect(users.pager.prev_page).to be(1)
|
46
|
+
expect(users.pager.limit_value).to eql(5)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#pager' do
|
51
|
+
it 'returns a pager with pagination meta-info' do
|
52
|
+
users = rom.relation(:users).page(1)
|
53
|
+
|
54
|
+
expect(users.pager.total).to be(9)
|
55
|
+
expect(users.pager.total_pages).to be(3)
|
56
|
+
|
57
|
+
expect(users.pager.current_page).to be(1)
|
58
|
+
expect(users.pager.next_page).to be(2)
|
59
|
+
expect(users.pager.prev_page).to be(nil)
|
60
|
+
|
61
|
+
users = rom.relation(:users).page(2)
|
62
|
+
|
63
|
+
expect(users.pager.current_page).to be(2)
|
64
|
+
expect(users.pager.next_page).to be(3)
|
65
|
+
expect(users.pager.prev_page).to be(1)
|
66
|
+
|
67
|
+
users = rom.relation(:users).page(3)
|
68
|
+
|
69
|
+
expect(users.pager.next_page).to be(nil)
|
70
|
+
expect(users.pager.prev_page).to be(2)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|