rom-repository 1.4.0 → 2.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.
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