rom-repository 1.4.0 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -6
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +18 -1
  5. data/lib/rom-repository.rb +1 -2
  6. data/lib/rom/repository.rb +9 -216
  7. data/lib/rom/repository/class_interface.rb +16 -33
  8. data/lib/rom/repository/relation_reader.rb +46 -0
  9. data/lib/rom/repository/root.rb +3 -59
  10. data/lib/rom/repository/version.rb +1 -1
  11. metadata +9 -98
  12. data/.gitignore +0 -3
  13. data/.rspec +0 -3
  14. data/.travis.yml +0 -27
  15. data/.yardopts +0 -2
  16. data/Gemfile +0 -38
  17. data/Rakefile +0 -19
  18. data/lib/rom/open_struct.rb +0 -35
  19. data/lib/rom/repository/changeset.rb +0 -155
  20. data/lib/rom/repository/changeset/associated.rb +0 -100
  21. data/lib/rom/repository/changeset/create.rb +0 -16
  22. data/lib/rom/repository/changeset/delete.rb +0 -17
  23. data/lib/rom/repository/changeset/pipe.rb +0 -97
  24. data/lib/rom/repository/changeset/restricted.rb +0 -28
  25. data/lib/rom/repository/changeset/stateful.rb +0 -282
  26. data/lib/rom/repository/changeset/update.rb +0 -82
  27. data/lib/rom/repository/command_compiler.rb +0 -257
  28. data/lib/rom/repository/command_proxy.rb +0 -26
  29. data/lib/rom/repository/header_builder.rb +0 -65
  30. data/lib/rom/repository/mapper_builder.rb +0 -23
  31. data/lib/rom/repository/relation_proxy.rb +0 -337
  32. data/lib/rom/repository/relation_proxy/combine.rb +0 -320
  33. data/lib/rom/repository/relation_proxy/wrap.rb +0 -78
  34. data/lib/rom/repository/struct_builder.rb +0 -83
  35. data/lib/rom/struct.rb +0 -113
  36. data/log/.gitkeep +0 -0
  37. data/rom-repository.gemspec +0 -23
  38. data/spec/integration/changeset_spec.rb +0 -193
  39. data/spec/integration/command_macros_spec.rb +0 -191
  40. data/spec/integration/command_spec.rb +0 -228
  41. data/spec/integration/multi_adapter_spec.rb +0 -73
  42. data/spec/integration/repository/aggregate_spec.rb +0 -58
  43. data/spec/integration/repository_spec.rb +0 -406
  44. data/spec/integration/root_repository_spec.rb +0 -106
  45. data/spec/integration/typed_structs_spec.rb +0 -64
  46. data/spec/shared/database.rb +0 -79
  47. data/spec/shared/mappers.rb +0 -35
  48. data/spec/shared/models.rb +0 -41
  49. data/spec/shared/plugins.rb +0 -66
  50. data/spec/shared/relations.rb +0 -115
  51. data/spec/shared/repo.rb +0 -86
  52. data/spec/shared/seeds.rb +0 -30
  53. data/spec/shared/structs.rb +0 -140
  54. data/spec/spec_helper.rb +0 -83
  55. data/spec/support/mapper_registry.rb +0 -9
  56. data/spec/support/mutant.rb +0 -10
  57. data/spec/unit/changeset/associate_spec.rb +0 -120
  58. data/spec/unit/changeset/map_spec.rb +0 -111
  59. data/spec/unit/changeset_spec.rb +0 -186
  60. data/spec/unit/relation_proxy_spec.rb +0 -202
  61. data/spec/unit/repository/changeset_spec.rb +0 -197
  62. data/spec/unit/repository/inspect_spec.rb +0 -18
  63. data/spec/unit/repository/session_spec.rb +0 -251
  64. data/spec/unit/repository/transaction_spec.rb +0 -42
  65. data/spec/unit/session_spec.rb +0 -46
  66. data/spec/unit/struct_builder_spec.rb +0 -128
@@ -1,111 +0,0 @@
1
- RSpec.describe ROM::Changeset, '.map' do
2
- context 'single mapping with transaction DSL' do
3
- subject(:changeset) do
4
- Class.new(ROM::Changeset::Create[:users]) do
5
- map do
6
- unwrap :address
7
- rename_keys street: :address_street, city: :address_city, country: :address_country
8
- end
9
-
10
- def default_command_type
11
- :test
12
- end
13
- end.new(relation, __data__: user_data)
14
- end
15
-
16
- let(:relation) { double(:relation) }
17
-
18
- context 'with a hash' do
19
- let(:user_data) do
20
- { name: 'Jane', address: { street: 'Street 1', city: 'NYC', country: 'US' } }
21
- end
22
-
23
- it 'sets up custom data pipe' do
24
- expect(changeset.to_h)
25
- .to eql(name: 'Jane', address_street: 'Street 1', address_city: 'NYC', address_country: 'US' )
26
- end
27
- end
28
-
29
- context 'with an array' do
30
- let(:user_data) do
31
- [{ name: 'Jane', address: { street: 'Street 1', city: 'NYC', country: 'US' } },
32
- { name: 'Joe', address: { street: 'Street 2', city: 'KRK', country: 'PL' } }]
33
- end
34
-
35
- it 'sets up custom data pipe' do
36
- expect(changeset.to_a)
37
- .to eql([
38
- { name: 'Jane', address_street: 'Street 1', address_city: 'NYC', address_country: 'US' },
39
- { name: 'Joe', address_street: 'Street 2', address_city: 'KRK', address_country: 'PL' }
40
- ])
41
- end
42
- end
43
- end
44
-
45
- context 'accessing data in a map block' do
46
- subject(:changeset) do
47
- Class.new(ROM::Changeset::Create[:users]) do
48
- map do |tuple|
49
- extend_data(tuple)
50
- end
51
-
52
- private
53
-
54
- def extend_data(tuple)
55
- tuple.merge(email: "#{self[:name].downcase}@test.com")
56
- end
57
- end.new(relation).data(user_data)
58
- end
59
-
60
- let(:relation) { double(:relation) }
61
- let(:user_data) { { name: 'Jane' } }
62
-
63
- it 'extends data in a map block' do
64
- expect(changeset.to_h).to eql(name: 'Jane', email: 'jane@test.com')
65
- end
66
- end
67
-
68
- context 'multi mapping with custom blocks' do
69
- subject(:changeset) do
70
- Class.new(ROM::Changeset::Create[:users]) do
71
- map do |tuple|
72
- tuple.merge(one: next_value)
73
- end
74
-
75
- map do |tuple|
76
- tuple.merge(two: next_value)
77
- end
78
-
79
- map do |three: next_value, **other|
80
- { three: three, **other }
81
- end
82
-
83
- def initialize(*args)
84
- super
85
- @counter = 0
86
- end
87
-
88
- def default_command_type
89
- :test
90
- end
91
-
92
- def next_value
93
- @counter += 1
94
- end
95
- end.new(relation).data(user_data)
96
- end
97
-
98
- let(:relation) { double(:relation) }
99
- let(:user_data) { { name: 'Jane' } }
100
-
101
- it 'applies mappings in order of definition' do
102
- expect(changeset.to_h).to eql(name: 'Jane', one: 1, two: 2, three: 3)
103
- end
104
-
105
- it 'inherits pipes' do
106
- klass = Class.new(changeset.class)
107
-
108
- expect(klass.pipes).to eql(changeset.class.pipes)
109
- end
110
- end
111
- end
@@ -1,186 +0,0 @@
1
- require 'ostruct'
2
-
3
- RSpec.describe ROM::Changeset do
4
- let(:jane) { { id: 2, name: "Jane" } }
5
- let(:relation) { double(ROM::Relation, name: :users) }
6
-
7
- describe '.[]' do
8
- it 'returns a changeset preconfigured for a specific relation' do
9
- klass = ROM::Changeset::Create[:users]
10
-
11
- expect(klass.relation).to be(:users)
12
- expect(klass < ROM::Changeset::Create).to be(true)
13
- end
14
-
15
- it 'caches results' do
16
- create = ROM::Changeset::Create[:users]
17
- update = ROM::Changeset::Update[:users]
18
-
19
- expect(create).to be(ROM::Changeset::Create[:users])
20
- expect(create < ROM::Changeset::Create).to be(true)
21
-
22
- expect(update).to be(ROM::Changeset::Update[:users])
23
- expect(update < ROM::Changeset::Update).to be(true)
24
- end
25
- end
26
-
27
- describe '#diff' do
28
- it 'returns a hash with changes' do
29
- expect(relation).to receive(:one).and_return(jane)
30
-
31
- changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" })
32
-
33
- expect(changeset.diff).to eql(name: "Jane Doe")
34
- end
35
-
36
- it 'does not consider keys that are not present on the original tuple' do
37
- expect(relation).to receive(:one).and_return(jane)
38
-
39
- changeset = ROM::Changeset::Update.new(relation).data(foo: 'bar')
40
-
41
- expect(changeset.diff).to eql({})
42
- end
43
- end
44
-
45
- describe '#diff?' do
46
- it 'returns true when data differs from the original tuple' do
47
- expect(relation).to receive(:one).and_return(jane)
48
-
49
- changeset = ROM::Changeset::Update.new(relation).data(name: "Jane Doe")
50
-
51
- expect(changeset).to be_diff
52
- end
53
-
54
- it 'returns false when data are equal to the original tuple' do
55
- expect(relation).to receive(:one).and_return(jane)
56
-
57
- changeset = ROM::Changeset::Update.new(relation).data(name: 'Jane')
58
-
59
- expect(changeset).to_not be_diff
60
- end
61
-
62
- it 'returns false when data contains keys that are not available on the original tuple' do
63
- expect(relation).to receive(:one).and_return(jane)
64
-
65
- changeset = ROM::Changeset::Update.new(relation).data(foo: 'bar')
66
-
67
- expect(changeset).to_not be_diff
68
- end
69
-
70
- it 'uses piped data for diff' do
71
- expect(relation).to receive(:one).and_return(jane)
72
-
73
- changeset = ROM::Changeset::Update.new(relation).data(name: "Jane").map { |name: | { name: name.upcase } }
74
-
75
- expect(changeset).to be_diff
76
- end
77
- end
78
-
79
- describe '#clean?' do
80
- it 'returns true when data are equal to the original tuple' do
81
- expect(relation).to receive(:one).and_return(jane)
82
-
83
- changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane" })
84
-
85
- expect(changeset).to be_clean
86
- end
87
-
88
- it 'returns false when data differs from the original tuple' do
89
- expect(relation).to receive(:one).and_return(jane)
90
-
91
- changeset = ROM::Changeset::Update.new(relation, __data__: { name: "Jane Doe" })
92
-
93
- expect(changeset).to_not be_clean
94
- end
95
- end
96
-
97
- describe 'quacks like a hash' do
98
- subject(:changeset) { ROM::Changeset::Create.new(relation, __data__: data) }
99
-
100
- let(:data) { instance_double(Hash, class: Hash) }
101
-
102
- it 'delegates to its data hash' do
103
- expect(data).to receive(:[]).with(:name).and_return('Jane')
104
-
105
- expect(changeset[:name]).to eql('Jane')
106
- end
107
-
108
- it 'maintains its own type' do
109
- expect(data).to receive(:merge).with(foo: 'bar').and_return(foo: 'bar')
110
-
111
- new_changeset = changeset.merge(foo: 'bar')
112
-
113
- expect(new_changeset).to be_instance_of(ROM::Changeset::Create)
114
- expect(new_changeset.options).to eql(changeset.options.merge(__data__: { foo: 'bar' }))
115
- expect(new_changeset.to_h).to eql(foo: 'bar')
116
- end
117
-
118
- it 'raises NoMethodError when an unknown message was sent' do
119
- expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
120
- end
121
- end
122
-
123
- describe 'quacks like an array' do
124
- subject(:changeset) { ROM::Changeset::Create.new(relation, __data__: data) }
125
-
126
- let(:data) { instance_double(Array, class: Array) }
127
-
128
- it 'delegates to its data hash' do
129
- expect(data).to receive(:[]).with(1).and_return('Jane')
130
-
131
- expect(changeset[1]).to eql('Jane')
132
- end
133
-
134
- it 'maintains its own type' do
135
- expect(data).to receive(:+).with([1, 2]).and_return([1, 2])
136
-
137
- new_changeset = changeset + [1, 2]
138
-
139
- expect(new_changeset).to be_instance_of(ROM::Changeset::Create)
140
- expect(new_changeset.options).to eql(changeset.options.merge(__data__: [1, 2]))
141
- expect(new_changeset.to_a).to eql([1, 2])
142
- end
143
-
144
- it 'raises NoMethodError when an unknown message was sent' do
145
- expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
146
- end
147
- end
148
-
149
- describe 'quacks like a custom object' do
150
- subject(:changeset) { ROM::Changeset::Create.new(relation, __data__: data) }
151
-
152
- let(:data) { OpenStruct.new(name: 'Jane') }
153
-
154
- it 'delegates to its data hash' do
155
- expect(changeset[:name]).to eql('Jane')
156
- end
157
-
158
- it 'raises NoMethodError when an unknown message was sent' do
159
- expect { changeset.not_here }.to raise_error(NoMethodError, /not_here/)
160
- end
161
-
162
- it 'has correct result type' do
163
- expect(changeset.result).to be(:one)
164
- end
165
- end
166
-
167
- describe '#inspect' do
168
- context 'with a stateful changeset' do
169
- subject(:changeset) { ROM::Changeset::Create.new(relation).data(name: 'Jane') }
170
-
171
- specify do
172
- expect(changeset.inspect).
173
- to eql('#<ROM::Changeset::Create relation=:users data={:name=>"Jane"}>')
174
- end
175
- end
176
-
177
- context 'with a data-less changeset' do
178
- subject(:changeset) { ROM::Changeset::Delete.new(relation) }
179
-
180
- specify do
181
- expect(changeset.inspect).
182
- to eql('#<ROM::Changeset::Delete relation=:users>')
183
- end
184
- end
185
- end
186
- end
@@ -1,202 +0,0 @@
1
- require 'dry-struct'
2
-
3
- RSpec.describe 'loading proxy' do
4
- include_context 'database'
5
- include_context 'relations'
6
- include_context 'repo'
7
- include_context 'structs'
8
- include_context 'seeds'
9
-
10
- let(:users_proxy) do
11
- repo.users
12
- end
13
-
14
- let(:tasks_proxy) do
15
- repo.tasks
16
- end
17
-
18
- let(:tags_proxy) do
19
- repo.tags
20
- end
21
-
22
- describe '#inspect' do
23
- specify do
24
- expect(users_proxy.inspect).
25
- to eql("#<ROM::Relation[Users] name=users dataset=#{users.dataset.inspect}>")
26
- end
27
- end
28
-
29
- describe '#each' do
30
- it 'yields loaded structs' do
31
- result = []
32
-
33
- users_proxy.each { |user| result << user }
34
-
35
- expect(result).to eql([jane, joe])
36
- end
37
-
38
- it 'returns an enumerator when block is not given' do
39
- expect(users_proxy.each.to_a).to eql([jane, joe])
40
- end
41
- end
42
-
43
- describe '#map_with/#as' do
44
- context 'with custom mappers' do
45
- before do
46
- configuration.mappers do
47
- register :users, {
48
- name_list: -> users { users.map { |u| u[:name] } },
49
- upcase_names: -> names { names.map(&:upcase) },
50
- identity: -> users { users }
51
- }
52
- end
53
- end
54
-
55
- it 'sends the relation through custom mappers' do
56
- expect(users_proxy.map_with(:name_list, :upcase_names).to_a).to match_array(%w(JANE JOE))
57
- end
58
-
59
- it 'does not use the default(ROM::Struct) mapper' do
60
- expect(users_proxy.map_with(:identity).to_a).to match_array(
61
- [{ id: 1, name: 'Jane' }, {id: 2, name: 'Joe' }]
62
- )
63
- end
64
-
65
- it 'raises error when custom mapper is used with a model class' do
66
- expect { users_proxy.map_with(:name_list, Class.new) }.
67
- to raise_error(ArgumentError, 'using custom mappers and a model is not supported')
68
- end
69
- end
70
-
71
- context 'setting custom model type' do
72
- let(:user_type) do
73
- Class.new(Dry::Struct) do
74
- attribute :id, Dry::Types['strict.int']
75
- attribute :name, Dry::Types['strict.string']
76
- end
77
- end
78
-
79
- let(:custom_users) { users_proxy.as(user_type) }
80
-
81
- it 'instantiates custom model' do
82
- expect(custom_users.where(name: 'Jane').one).to be_instance_of(user_type)
83
- end
84
- end
85
- end
86
-
87
- describe 'retrieving a single struct' do
88
- describe '#first' do
89
- it 'returns exactly one struct' do
90
- expect(users_proxy.first).to eql(jane)
91
- end
92
- end
93
-
94
- describe '#one' do
95
- it 'returns exactly one struct' do
96
- expect(users_proxy.find(id: 1).one).to eql(jane)
97
-
98
- expect(users_proxy.find(id: 3).one).to be(nil)
99
-
100
- expect { users_proxy.find(id: [1,2]).one }.to raise_error(ROM::TupleCountMismatchError)
101
- end
102
- end
103
-
104
- describe '#one!' do
105
- it 'returns exactly one struct' do
106
- expect(users_proxy.find(id: 1).one!).to eql(jane)
107
-
108
- expect { users_proxy.find(id: [1, 2]).one! }.to raise_error(ROM::TupleCountMismatchError)
109
- expect { users_proxy.find(id: [3]).one! }.to raise_error(ROM::TupleCountMismatchError)
110
- end
111
- end
112
- end
113
-
114
- describe '#to_ast' do
115
- it 'returns valid ast for a single relation' do
116
- expect(users_proxy.to_ast).to eql(
117
- [:relation, [
118
- :users,
119
- { dataset: :users },
120
- [:header, [[:attribute, users_proxy.schema[:id]], [:attribute, users_proxy.schema[:name]]]]]
121
- ]
122
- )
123
- end
124
-
125
- it 'returns valid ast for a combined relation' do
126
- relation = users_proxy.combine(many: { user_tasks: [tasks_proxy, id: :user_id] })
127
-
128
- expect(relation.to_ast).to eql(
129
- [:relation, [
130
- :users,
131
- { dataset: :users },
132
- [:header, [
133
- [:attribute, users_proxy.schema[:id]],
134
- [:attribute, users_proxy.schema[:name]],
135
- [:relation, [
136
- :tasks,
137
- { dataset: :tasks, keys: { id: :user_id },
138
- combine_type: :many, combine_name: :user_tasks },
139
- [:header, [
140
- [:attribute, tasks_proxy.schema[:id]],
141
- [:attribute, tasks_proxy.schema[:user_id]],
142
- [:attribute, tasks_proxy.schema[:title]]]]
143
- ]]
144
- ]
145
- ]]]
146
- )
147
- end
148
-
149
- it 'returns valid ast for a wrapped relation' do
150
- relation = tags_proxy.wrap_parent(task: tasks_proxy)
151
-
152
- tags_schema = tags_proxy.schema.qualified
153
- tasks_schema = tasks_proxy.schema.wrap
154
-
155
- expect(relation.to_ast).to eql(
156
- [:relation, [
157
- :tags,
158
- { dataset: :tags },
159
- [:header, [
160
- [:attribute, tags_schema[:id]],
161
- [:attribute, tags_schema[:task_id]],
162
- [:attribute, tags_schema[:name]],
163
- [:relation, [
164
- :tasks,
165
- { dataset: :tasks, keys: { id: :task_id },
166
- wrap_from_assoc: false, wrap: true, combine_name: :task },
167
- [:header, [
168
- [:attribute, tasks_schema[:id]],
169
- [:attribute, tasks_schema[:user_id]],
170
- [:attribute, tasks_schema[:title]]]]
171
- ]]
172
- ]]
173
- ]]
174
- )
175
- end
176
- end
177
-
178
- describe '#method_missing' do
179
- it 'proxies to the underlying relation' do
180
- expect(users_proxy.gateway).to be(:default)
181
- end
182
-
183
- it 'returns proxy when response was not materialized' do
184
- expect(users_proxy.by_pk(1)).to be_instance_of(ROM::Repository::RelationProxy)
185
- end
186
-
187
- it 'returns curried proxy when response was curried' do
188
- expect(users_proxy.by_pk).to be_instance_of(ROM::Repository::RelationProxy)
189
- end
190
-
191
- it 'raises when method is missing' do
192
- expect { users_proxy.not_here }.to raise_error(NoMethodError, "undefined method `not_here' for ROM::Relation[Users]")
193
- end
194
-
195
- it 'proxies Kernel methods when using with SimpleDelegator' do
196
- proxy = Class.new(SimpleDelegator).new(users_proxy)
197
-
198
- expect(users_proxy.select(:name)).to be_instance_of(ROM::Repository::RelationProxy)
199
- expect(proxy.select(:name)).to be_instance_of(ROM::Repository::RelationProxy)
200
- end
201
- end
202
- end