rom-repository 0.3.1 → 1.0.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/.travis.yml +11 -13
- data/CHANGELOG.md +25 -0
- data/Gemfile +13 -3
- data/lib/rom/repository.rb +57 -19
- data/lib/rom/repository/changeset.rb +89 -26
- data/lib/rom/repository/changeset/create.rb +34 -0
- data/lib/rom/repository/changeset/delete.rb +15 -0
- data/lib/rom/repository/changeset/pipe.rb +11 -4
- data/lib/rom/repository/changeset/update.rb +11 -1
- data/lib/rom/repository/command_compiler.rb +51 -30
- data/lib/rom/repository/command_proxy.rb +3 -1
- data/lib/rom/repository/header_builder.rb +3 -3
- data/lib/rom/repository/mapper_builder.rb +2 -2
- data/lib/rom/repository/relation_proxy.rb +26 -35
- data/lib/rom/repository/relation_proxy/combine.rb +59 -27
- data/lib/rom/repository/root.rb +4 -6
- data/lib/rom/repository/session.rb +55 -0
- data/lib/rom/repository/struct_builder.rb +29 -17
- data/lib/rom/repository/version.rb +1 -1
- data/lib/rom/struct.rb +11 -20
- data/rom-repository.gemspec +4 -3
- data/spec/integration/command_macros_spec.rb +5 -2
- data/spec/integration/command_spec.rb +0 -6
- data/spec/integration/multi_adapter_spec.rb +8 -5
- data/spec/integration/repository_spec.rb +58 -2
- data/spec/integration/root_repository_spec.rb +9 -2
- data/spec/integration/typed_structs_spec.rb +31 -0
- data/spec/shared/database.rb +5 -1
- data/spec/shared/relations.rb +3 -1
- data/spec/shared/repo.rb +13 -1
- data/spec/shared/structs.rb +39 -0
- data/spec/spec_helper.rb +7 -5
- data/spec/support/mutant.rb +10 -0
- data/spec/unit/changeset/map_spec.rb +42 -0
- data/spec/unit/changeset_spec.rb +32 -6
- data/spec/unit/relation_proxy_spec.rb +27 -9
- data/spec/unit/repository/changeset_spec.rb +125 -0
- data/spec/unit/repository/inspect_spec.rb +18 -0
- data/spec/unit/repository/session_spec.rb +251 -0
- data/spec/unit/session_spec.rb +54 -0
- data/spec/unit/struct_builder_spec.rb +45 -1
- metadata +41 -17
- data/lib/rom/repository/struct_attributes.rb +0 -46
- data/spec/unit/header_builder_spec.rb +0 -73
- data/spec/unit/plugins/view_spec.rb +0 -29
- data/spec/unit/sql/relation_spec.rb +0 -54
- data/spec/unit/struct_spec.rb +0 -22
data/spec/shared/relations.rb
CHANGED
data/spec/shared/repo.rb
CHANGED
@@ -6,7 +6,7 @@ RSpec.shared_context('repo') do
|
|
6
6
|
|
7
7
|
let(:repo_class) do
|
8
8
|
Class.new(ROM::Repository[:users]) do
|
9
|
-
relations :tasks, :tags, :posts, :labels
|
9
|
+
relations :tasks, :tags, :posts, :labels, :posts_labels
|
10
10
|
|
11
11
|
def find_users(criteria)
|
12
12
|
users.find(criteria)
|
@@ -36,6 +36,18 @@ RSpec.shared_context('repo') do
|
|
36
36
|
aggregate(one: tasks.find(title: title))
|
37
37
|
end
|
38
38
|
|
39
|
+
def users_with_posts_and_their_labels
|
40
|
+
users.combine(posts: [:labels])
|
41
|
+
end
|
42
|
+
|
43
|
+
def posts_with_labels
|
44
|
+
posts.combine_children(many: labels)
|
45
|
+
end
|
46
|
+
|
47
|
+
def label_with_posts
|
48
|
+
labels.combine_children(one: posts)
|
49
|
+
end
|
50
|
+
|
39
51
|
def tasks_for_users(users)
|
40
52
|
tasks.for_users(users)
|
41
53
|
end
|
data/spec/shared/structs.rb
CHANGED
@@ -13,6 +13,22 @@ RSpec.shared_context 'structs' do
|
|
13
13
|
repo.tags.mapper.model
|
14
14
|
end
|
15
15
|
|
16
|
+
let(:post_struct) do
|
17
|
+
repo.posts.mapper.model
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:label_struct) do
|
21
|
+
repo.labels.mapper.model
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:post_with_labels_struct) do
|
25
|
+
mapper_for(repo.posts_with_labels).model
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:label_with_posts_struct) do
|
29
|
+
mapper_for(repo.label_with_posts).model
|
30
|
+
end
|
31
|
+
|
16
32
|
let(:tag_with_task_struct) do
|
17
33
|
mapper_for(repo.tag_with_wrapped_task).model
|
18
34
|
end
|
@@ -25,6 +41,10 @@ RSpec.shared_context 'structs' do
|
|
25
41
|
mapper_for(repo.users_with_task).model
|
26
42
|
end
|
27
43
|
|
44
|
+
let(:user_with_posts_struct) do
|
45
|
+
mapper_for(repo.users_with_posts_and_their_labels).model
|
46
|
+
end
|
47
|
+
|
28
48
|
let(:task_with_tags_struct) do
|
29
49
|
mapper_for(repo.tasks_with_tags).model
|
30
50
|
end
|
@@ -100,4 +120,23 @@ RSpec.shared_context 'structs' do
|
|
100
120
|
let(:joe_task) do
|
101
121
|
task_struct.new(id: 1, user_id: 2, title: 'Joe Task')
|
102
122
|
end
|
123
|
+
|
124
|
+
let(:jane_with_posts) do
|
125
|
+
user_with_posts_struct.new(id: 1, name: 'Jane', posts: [post_with_label])
|
126
|
+
end
|
127
|
+
|
128
|
+
let(:label_red) do
|
129
|
+
label_with_posts_struct.new(id: 1, name: 'red', post: 1)
|
130
|
+
end
|
131
|
+
|
132
|
+
let(:label_blue) do
|
133
|
+
label_with_posts_struct.new(id: 3, name: 'blue', post: 1)
|
134
|
+
end
|
135
|
+
|
136
|
+
let(:post_with_label) do
|
137
|
+
post_with_labels_struct.new(id: 2, title: 'Hello From Jane',
|
138
|
+
body: 'Jane Post',
|
139
|
+
author_id: 1,
|
140
|
+
labels: [label_red, label_blue])
|
141
|
+
end
|
103
142
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,9 +4,11 @@
|
|
4
4
|
require "bundler"
|
5
5
|
Bundler.setup
|
6
6
|
|
7
|
-
if RUBY_ENGINE ==
|
8
|
-
require
|
9
|
-
|
7
|
+
if ENV['COVERAGE'] == 'true' && RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '2.4.0' && ENV['CI'] == 'true'
|
8
|
+
require 'simplecov'
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter '/spec/'
|
11
|
+
end
|
10
12
|
end
|
11
13
|
|
12
14
|
require 'rom-sql'
|
@@ -28,8 +30,8 @@ Dir[root.join('shared/*.rb').to_s].each do |f|
|
|
28
30
|
require f
|
29
31
|
end
|
30
32
|
|
31
|
-
require '
|
32
|
-
|
33
|
+
require 'dry/core/deprecations'
|
34
|
+
Dry::Core::Deprecations.set_logger!(root.join('../log/deprecations.log'))
|
33
35
|
|
34
36
|
# Namespace holding all objects created during specs
|
35
37
|
module Test
|
@@ -0,0 +1,42 @@
|
|
1
|
+
RSpec.describe ROM::Changeset, '.map' do
|
2
|
+
subject(:changeset) do
|
3
|
+
Class.new(ROM::Changeset) do
|
4
|
+
map do
|
5
|
+
unwrap :address
|
6
|
+
rename_keys street: :address_street, city: :address_city, country: :address_country
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_command_type
|
10
|
+
:test
|
11
|
+
end
|
12
|
+
end.new(relation, __data__: user_data)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:relation) { double(:relation) }
|
16
|
+
|
17
|
+
context 'with a hash' do
|
18
|
+
let(:user_data) do
|
19
|
+
{ name: 'Jane', address: { street: 'Street 1', city: 'NYC', country: 'US' } }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets up custom data pipe' do
|
23
|
+
expect(changeset.to_h)
|
24
|
+
.to eql(name: 'Jane', address_street: 'Street 1', address_city: 'NYC', address_country: 'US' )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with an array' do
|
29
|
+
let(:user_data) do
|
30
|
+
[{ name: 'Jane', address: { street: 'Street 1', city: 'NYC', country: 'US' } },
|
31
|
+
{ name: 'Joe', address: { street: 'Street 2', city: 'KRK', country: 'PL' } }]
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets up custom data pipe' do
|
35
|
+
expect(changeset.to_a)
|
36
|
+
.to eql([
|
37
|
+
{ name: 'Jane', address_street: 'Street 1', address_city: 'NYC', address_country: 'US' },
|
38
|
+
{ name: 'Joe', address_street: 'Street 2', address_city: 'KRK', address_country: 'PL' }
|
39
|
+
])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/unit/changeset_spec.rb
CHANGED
@@ -6,7 +6,7 @@ RSpec.describe ROM::Changeset do
|
|
6
6
|
it 'returns a hash with changes' do
|
7
7
|
expect(relation).to receive(:fetch).with(2).and_return(jane)
|
8
8
|
|
9
|
-
changeset = ROM::Changeset::Update.new(relation, { name: "Jane Doe" }, primary_key: 2)
|
9
|
+
changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" }, primary_key: 2)
|
10
10
|
|
11
11
|
expect(changeset.diff).to eql(name: "Jane Doe")
|
12
12
|
end
|
@@ -16,7 +16,7 @@ RSpec.describe ROM::Changeset do
|
|
16
16
|
it 'returns true when data differs from the original tuple' do
|
17
17
|
expect(relation).to receive(:fetch).with(2).and_return(jane)
|
18
18
|
|
19
|
-
changeset = ROM::Changeset::Update.new(relation, { name: "Jane Doe" }, primary_key: 2)
|
19
|
+
changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" }, primary_key: 2)
|
20
20
|
|
21
21
|
expect(changeset).to be_diff
|
22
22
|
end
|
@@ -24,16 +24,16 @@ RSpec.describe ROM::Changeset do
|
|
24
24
|
it 'returns false when data are equal to the original tuple' do
|
25
25
|
expect(relation).to receive(:fetch).with(2).and_return(jane)
|
26
26
|
|
27
|
-
changeset = ROM::Changeset::Update.new(relation, { name: "Jane" }, primary_key: 2)
|
27
|
+
changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane" }, primary_key: 2)
|
28
28
|
|
29
29
|
expect(changeset).to_not be_diff
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
describe 'quacks like a hash' do
|
34
|
-
subject(:changeset) { ROM::Changeset::Create.new(relation, data) }
|
34
|
+
subject(:changeset) { ROM::Changeset::Create.new(relation, __data__: data) }
|
35
35
|
|
36
|
-
let(:data) { instance_double(Hash) }
|
36
|
+
let(:data) { instance_double(Hash, class: Hash) }
|
37
37
|
|
38
38
|
it 'delegates to its data hash' do
|
39
39
|
expect(data).to receive(:[]).with(:name).and_return('Jane')
|
@@ -47,7 +47,7 @@ RSpec.describe ROM::Changeset do
|
|
47
47
|
new_changeset = changeset.merge(foo: 'bar')
|
48
48
|
|
49
49
|
expect(new_changeset).to be_instance_of(ROM::Changeset::Create)
|
50
|
-
expect(new_changeset.options).to eql(changeset.options)
|
50
|
+
expect(new_changeset.options).to eql(changeset.options.merge(__data__: { foo: 'bar' }))
|
51
51
|
expect(new_changeset.to_h).to eql(foo: 'bar')
|
52
52
|
end
|
53
53
|
|
@@ -55,4 +55,30 @@ RSpec.describe ROM::Changeset do
|
|
55
55
|
expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
describe 'quacks like an array' do
|
60
|
+
subject(:changeset) { ROM::Changeset::Create.new(relation, __data__: data) }
|
61
|
+
|
62
|
+
let(:data) { instance_double(Array, class: Array) }
|
63
|
+
|
64
|
+
it 'delegates to its data hash' do
|
65
|
+
expect(data).to receive(:[]).with(1).and_return('Jane')
|
66
|
+
|
67
|
+
expect(changeset[1]).to eql('Jane')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'maintains its own type' do
|
71
|
+
expect(data).to receive(:+).with([1, 2]).and_return([1, 2])
|
72
|
+
|
73
|
+
new_changeset = changeset + [1, 2]
|
74
|
+
|
75
|
+
expect(new_changeset).to be_instance_of(ROM::Changeset::Create)
|
76
|
+
expect(new_changeset.options).to eql(changeset.options.merge(__data__: [1, 2]))
|
77
|
+
expect(new_changeset.to_a).to eql([1, 2])
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'raises NoMethodError when an unknown message was sent' do
|
81
|
+
expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
|
82
|
+
end
|
83
|
+
end
|
58
84
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'dry-struct'
|
2
|
+
|
1
3
|
RSpec.describe 'loading proxy' do
|
2
4
|
include_context 'database'
|
3
5
|
include_context 'relations'
|
@@ -17,6 +19,13 @@ RSpec.describe 'loading proxy' do
|
|
17
19
|
ROM::Repository::RelationProxy.new(rom.relation(:tags), name: :tags)
|
18
20
|
end
|
19
21
|
|
22
|
+
describe '#inspect' do
|
23
|
+
specify do
|
24
|
+
expect(users.inspect).
|
25
|
+
to eql("#<ROM::Relation[Users] name=users dataset=#{users.dataset.inspect}>")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
20
29
|
describe '#each' do
|
21
30
|
it 'yields loaded structs' do
|
22
31
|
result = []
|
@@ -56,7 +65,7 @@ RSpec.describe 'loading proxy' do
|
|
56
65
|
|
57
66
|
context 'setting custom model type' do
|
58
67
|
let(:user_type) do
|
59
|
-
Class.new(Dry::
|
68
|
+
Class.new(Dry::Struct) do
|
60
69
|
attribute :id, Dry::Types['strict.int']
|
61
70
|
attribute :name, Dry::Types['strict.string']
|
62
71
|
end
|
@@ -103,7 +112,7 @@ RSpec.describe 'loading proxy' do
|
|
103
112
|
[:relation, [
|
104
113
|
:users,
|
105
114
|
{ dataset: :users },
|
106
|
-
[:header, [[:attribute, :id], [:attribute, :name]]]]
|
115
|
+
[:header, [[:attribute, users.schema[:id]], [:attribute, users.schema[:name]]]]]
|
107
116
|
]
|
108
117
|
)
|
109
118
|
end
|
@@ -116,12 +125,15 @@ RSpec.describe 'loading proxy' do
|
|
116
125
|
:users,
|
117
126
|
{ dataset: :users },
|
118
127
|
[:header, [
|
119
|
-
[:attribute, :id],
|
120
|
-
[:attribute, :name],
|
128
|
+
[:attribute, users.schema[:id]],
|
129
|
+
[:attribute, users.schema[:name]],
|
121
130
|
[:relation, [
|
122
131
|
:tasks,
|
123
132
|
{ dataset: :tasks, keys: { id: :user_id }, combine_type: :many, combine_name: :user_tasks },
|
124
|
-
[:header, [
|
133
|
+
[:header, [
|
134
|
+
[:attribute, tasks.schema[:id]],
|
135
|
+
[:attribute, tasks.schema[:user_id]],
|
136
|
+
[:attribute, tasks.schema[:title]]]]
|
125
137
|
]]
|
126
138
|
]
|
127
139
|
]]]
|
@@ -131,18 +143,24 @@ RSpec.describe 'loading proxy' do
|
|
131
143
|
it 'returns valid ast for a wrapped relation' do
|
132
144
|
relation = tags.wrap_parent(task: tasks)
|
133
145
|
|
146
|
+
tags_schema = tags.schema.qualified
|
147
|
+
tasks_schema = tasks.schema.wrap.qualified
|
148
|
+
|
134
149
|
expect(relation.to_ast).to eql(
|
135
150
|
[:relation, [
|
136
151
|
:tags,
|
137
152
|
{ dataset: :tags },
|
138
153
|
[:header, [
|
139
|
-
[:attribute, :id],
|
140
|
-
[:attribute, :task_id],
|
141
|
-
[:attribute, :name],
|
154
|
+
[:attribute, tags_schema[:id]],
|
155
|
+
[:attribute, tags_schema[:task_id]],
|
156
|
+
[:attribute, tags_schema[:name]],
|
142
157
|
[:relation, [
|
143
158
|
:tasks,
|
144
159
|
{ dataset: :tasks, keys: { id: :task_id }, wrap: true, combine_name: :task },
|
145
|
-
[:header, [
|
160
|
+
[:header, [
|
161
|
+
[:attribute, tasks_schema[:id]],
|
162
|
+
[:attribute, tasks_schema[:user_id]],
|
163
|
+
[:attribute, tasks_schema[:title]]]]
|
146
164
|
]]
|
147
165
|
]]
|
148
166
|
]]
|
@@ -0,0 +1,125 @@
|
|
1
|
+
RSpec.describe ROM::Repository, '#changeset' do
|
2
|
+
subject(:repo) do
|
3
|
+
Class.new(ROM::Repository) { relations :users }.new(rom)
|
4
|
+
end
|
5
|
+
|
6
|
+
include_context 'database'
|
7
|
+
include_context 'relations'
|
8
|
+
|
9
|
+
describe ROM::Changeset::Create do
|
10
|
+
context 'with a hash' do
|
11
|
+
let(:changeset) do
|
12
|
+
repo.changeset(:users, name: 'Jane')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has data' do
|
16
|
+
expect(changeset.to_h).to eql(name: 'Jane')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has relation' do
|
20
|
+
expect(changeset.relation).to be(repo.users)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can return a dedicated command' do
|
24
|
+
expect(changeset.command.call).to eql(id: 1, name: 'Jane')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with an array' do
|
29
|
+
let(:changeset) do
|
30
|
+
repo.changeset(:users, data)
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:data) do
|
34
|
+
[{ name: 'Jane' }, { name: 'Joe' }]
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has data' do
|
38
|
+
expect(changeset.to_a).to eql(data)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'has relation' do
|
42
|
+
expect(changeset.relation).to be(repo.users)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'can return a dedicated command' do
|
46
|
+
expect(changeset.command.call).to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'Joe' }])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe ROM::Changeset::Update do
|
52
|
+
let(:changeset) do
|
53
|
+
repo.changeset(:users, user[:id], name: 'Jane Doe')
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:user) do
|
57
|
+
repo.command(:create, repo.users).call(name: 'Jane')
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'has data' do
|
61
|
+
expect(changeset.to_h).to eql(name: 'Jane Doe')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'has diff' do
|
65
|
+
expect(changeset.diff).to eql(name: 'Jane Doe')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'has relation' do
|
69
|
+
expect(changeset.relation).to be(repo.users)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'can return a dedicated command' do
|
73
|
+
expect(changeset.command.call).to eql(id: 1, name: 'Jane Doe')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe ROM::Changeset::Delete do
|
78
|
+
let(:changeset) do
|
79
|
+
repo.changeset(delete: relation)
|
80
|
+
end
|
81
|
+
|
82
|
+
let(:relation) do
|
83
|
+
repo.users.by_pk(user[:id])
|
84
|
+
end
|
85
|
+
|
86
|
+
let(:user) do
|
87
|
+
repo.command(:create, repo.users).call(name: 'Jane')
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'has relation' do
|
91
|
+
expect(changeset.relation).to eql(relation)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'can return a dedicated command' do
|
95
|
+
expect(changeset.command.call.to_h).to eql(id: 1, name: 'Jane')
|
96
|
+
expect(relation.one).to be(nil)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'custom changeset class' do
|
101
|
+
let(:changeset) do
|
102
|
+
repo.changeset(changeset_class[:users]).data({})
|
103
|
+
end
|
104
|
+
|
105
|
+
let(:changeset_class) do
|
106
|
+
Class.new(ROM::Changeset::Create) do
|
107
|
+
def to_h
|
108
|
+
__data__.merge(name: 'Jane')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'has data' do
|
114
|
+
expect(changeset.to_h).to eql(name: 'Jane')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'has relation' do
|
118
|
+
expect(changeset.relation).to be(repo.users)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'can return a dedicated command' do
|
122
|
+
expect(changeset.command.call).to eql(id: 1, name: 'Jane')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|