rom 0.8.1 → 0.9.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +5 -1
  4. data/lib/rom.rb +35 -16
  5. data/lib/rom/command.rb +1 -9
  6. data/lib/rom/commands/graph/class_interface.rb +2 -2
  7. data/lib/rom/constants.rb +0 -6
  8. data/lib/rom/{env.rb → container.rb} +3 -3
  9. data/lib/rom/environment.rb +238 -0
  10. data/lib/rom/environment_plugin.rb +17 -0
  11. data/lib/rom/environment_plugins/auto_registration.rb +17 -0
  12. data/lib/rom/global.rb +0 -203
  13. data/lib/rom/mapper_registry.rb +2 -0
  14. data/lib/rom/pipeline.rb +2 -0
  15. data/lib/rom/plugin.rb +4 -18
  16. data/lib/rom/plugin_base.rb +31 -0
  17. data/lib/rom/plugin_registry.rb +54 -17
  18. data/lib/rom/relation.rb +54 -11
  19. data/lib/rom/relation/class_interface.rb +14 -21
  20. data/lib/rom/relation/curried.rb +36 -2
  21. data/lib/rom/relation/graph.rb +7 -0
  22. data/lib/rom/relation_registry.rb +4 -0
  23. data/lib/rom/setup.rb +9 -8
  24. data/lib/rom/setup/finalize.rb +5 -5
  25. data/lib/rom/version.rb +1 -1
  26. data/rom.gemspec +2 -0
  27. data/spec/integration/commands/create_spec.rb +1 -1
  28. data/spec/integration/commands/update_spec.rb +1 -1
  29. data/spec/integration/mappers/unwrap_spec.rb +1 -1
  30. data/spec/spec_helper.rb +2 -0
  31. data/spec/unit/rom/{env_spec.rb → container_spec.rb} +5 -5
  32. data/spec/unit/rom/plugin_spec.rb +0 -8
  33. data/spec/unit/rom/relation/composite_spec.rb +2 -2
  34. data/spec/unit/rom/relation/curried_spec.rb +53 -0
  35. data/spec/unit/rom/relation/graph_spec.rb +4 -0
  36. data/spec/unit/rom/relation/lazy/combine_spec.rb +6 -6
  37. data/spec/unit/rom/relation/lazy_spec.rb +4 -8
  38. data/spec/unit/rom/relation_spec.rb +0 -14
  39. data/spec/unit/rom/setup_spec.rb +1 -1
  40. metadata +52 -35
  41. data/lib/rom/header.rb +0 -193
  42. data/lib/rom/header/attribute.rb +0 -184
  43. data/lib/rom/mapper.rb +0 -103
  44. data/lib/rom/mapper/attribute_dsl.rb +0 -477
  45. data/lib/rom/mapper/dsl.rb +0 -119
  46. data/lib/rom/mapper/model_dsl.rb +0 -55
  47. data/lib/rom/model_builder.rb +0 -101
  48. data/lib/rom/processor.rb +0 -28
  49. data/lib/rom/processor/transproc.rb +0 -388
  50. data/lib/rom/relation/lazy.rb +0 -145
  51. data/lib/rom/support/array_dataset.rb +0 -41
  52. data/lib/rom/support/class_builder.rb +0 -44
  53. data/lib/rom/support/class_macros.rb +0 -56
  54. data/lib/rom/support/data_proxy.rb +0 -102
  55. data/lib/rom/support/deprecations.rb +0 -36
  56. data/lib/rom/support/enumerable_dataset.rb +0 -65
  57. data/lib/rom/support/inflector.rb +0 -73
  58. data/lib/rom/support/options.rb +0 -195
  59. data/lib/rom/support/registry.rb +0 -43
  60. data/spec/unit/rom/header_spec.rb +0 -102
  61. data/spec/unit/rom/mapper/dsl_spec.rb +0 -467
  62. data/spec/unit/rom/mapper_spec.rb +0 -84
  63. data/spec/unit/rom/model_builder_spec.rb +0 -46
  64. data/spec/unit/rom/processor/transproc_spec.rb +0 -448
  65. data/spec/unit/rom/support/array_dataset_spec.rb +0 -61
  66. data/spec/unit/rom/support/class_builder_spec.rb +0 -42
  67. data/spec/unit/rom/support/enumerable_dataset_spec.rb +0 -17
  68. data/spec/unit/rom/support/inflector_spec.rb +0 -89
  69. data/spec/unit/rom/support/options_spec.rb +0 -119
@@ -1,84 +0,0 @@
1
- require 'spec_helper'
2
- require 'rom/memory/dataset'
3
-
4
- require 'ostruct'
5
-
6
- describe ROM::Mapper do
7
- subject(:mapper) { mapper_class.build }
8
-
9
- let(:mapper_class) do
10
- user_model = self.user_model
11
-
12
- Class.new(ROM::Mapper) do
13
- attribute :id
14
- attribute :name
15
- model user_model
16
- end
17
- end
18
-
19
- let(:relation) do
20
- [{ id: 1, name: 'Jane' }, { id: 2, name: 'Joe' }]
21
- end
22
-
23
- let(:user_model) do
24
- Class.new(OpenStruct) { include Equalizer.new(:id, :name) }
25
- end
26
-
27
- let(:jane) { user_model.new(id: 1, name: 'Jane') }
28
- let(:joe) { user_model.new(id: 2, name: 'Joe') }
29
-
30
- describe '.registry' do
31
- it 'builds mapper class registry for base and virtual relations' do
32
- users = Class.new(ROM::Mapper) { relation(:users) }
33
- entity = Class.new(ROM::Mapper) do
34
- relation(:users)
35
- register_as(:entity)
36
- end
37
- active = Class.new(users) { relation(:active) }
38
- admins = Class.new(users) { relation(:admins) }
39
- custom = Class.new(users) { register_as(:custom) }
40
-
41
- registry = ROM::Mapper.registry([users, entity, active, admins, custom])
42
-
43
- expect(registry).to eql(
44
- users: {
45
- users: users.build,
46
- entity: entity.build,
47
- active: active.build,
48
- admins: admins.build,
49
- custom: custom.build
50
- }
51
- )
52
- end
53
- end
54
-
55
- describe '.relation' do
56
- it 'inherits from parent' do
57
- base = Class.new(ROM::Mapper) { relation(:users) }
58
- virt = Class.new(base)
59
-
60
- expect(virt.relation).to be(:users)
61
- expect(virt.base_relation).to be(:users)
62
- end
63
-
64
- it 'allows overriding' do
65
- base = Class.new(ROM::Mapper) { relation(:users) }
66
- virt = Class.new(base) { relation(:active) }
67
-
68
- expect(virt.relation).to be(:active)
69
- expect(virt.base_relation).to be(:users)
70
- end
71
- end
72
-
73
- describe "#each" do
74
- it "yields all mapped objects" do
75
- result = []
76
-
77
- mapper.call(relation).each do |tuple|
78
- result << tuple
79
- end
80
-
81
- expect(result).to eql([jane, joe])
82
- end
83
- end
84
- end
@@ -1,46 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ROM::ModelBuilder do
4
- describe '#call' do
5
- it 'builds a class with a constructor accepting attributes' do
6
- builder = ROM::ModelBuilder::PORO.new
7
-
8
- klass = builder.call([:name])
9
-
10
- object = klass.new(name: 'Jane')
11
-
12
- expect(object.name).to eql('Jane')
13
-
14
- expect { object.name = 'Jane' }.to raise_error(NoMethodError)
15
-
16
- klass = builder.call([:name, :email])
17
-
18
- object = klass.new(name: 'Jane', email: 'jane@doe.org')
19
-
20
- expect(object.name).to eql('Jane')
21
- expect(object.email).to eql('jane@doe.org')
22
- end
23
-
24
- context 'when :name option is present' do
25
- it 'defines a constant for the model' do
26
- builder = ROM::ModelBuilder::PORO.new(name: 'User')
27
-
28
- builder.call([:name, :email])
29
-
30
- expect(Object.const_defined?(:User)).to be(true)
31
- Object.send(:remove_const, :User)
32
- end
33
-
34
- it 'defines a constant within a namespace for the model' do
35
- module Test::MyApp; module Entities; end; end
36
-
37
- builder = ROM::ModelBuilder::PORO.new(name: 'Test::MyApp::Entities::User')
38
-
39
- builder.call([:name, :email])
40
-
41
- expect(Test::MyApp::Entities.const_defined?(:User)).to be(true)
42
- expect(Object.const_defined?(:User)).to be(false)
43
- end
44
- end
45
- end
46
- end
@@ -1,448 +0,0 @@
1
- require 'spec_helper'
2
- require 'virtus'
3
-
4
- describe ROM::Processor::Transproc do
5
- subject(:transproc) { ROM::Processor::Transproc.build(header) }
6
-
7
- let(:header) { ROM::Header.coerce(attributes, options) }
8
- let(:options) { {} }
9
-
10
- context 'no mapping' do
11
- let(:attributes) { [[:name]] }
12
- let(:relation) { [{ name: 'Jane' }, { name: 'Joe' }] }
13
-
14
- it 'returns tuples' do
15
- expect(transproc[relation]).to eql(relation)
16
- end
17
- end
18
-
19
- context 'coercing values' do
20
- let(:attributes) { [[:name, type: :string], [:age, type: :integer]] }
21
- let(:relation) { [{ name: :Jane, age: '1' }, { name: :Joe, age: '2' }] }
22
-
23
- it 'returns tuples' do
24
- expect(transproc[relation]).to eql([
25
- { name: 'Jane', age: 1 }, { name: 'Joe', age: 2 }
26
- ])
27
- end
28
- end
29
-
30
- context 'mapping to object' do
31
- let(:options) { { model: model } }
32
-
33
- let(:model) do
34
- Class.new do
35
- include Virtus.value_object
36
- values { attribute :name }
37
- end
38
- end
39
-
40
- let(:attributes) { [[:name]] }
41
- let(:relation) { [{ name: 'Jane' }, { name: 'Joe' }] }
42
-
43
- it 'returns tuples' do
44
- expect(transproc[relation]).to eql([
45
- model.new(name: 'Jane'), model.new(name: 'Joe')
46
- ])
47
- end
48
- end
49
-
50
- context 'renaming keys' do
51
- let(:attributes) do
52
- [[:name, from: 'name']]
53
- end
54
-
55
- let(:options) do
56
- { reject_keys: true }
57
- end
58
-
59
- let(:relation) do
60
- [
61
- { 'name' => 'Jane', 'age' => 21 }, { 'name' => 'Joe', age: 22 }
62
- ]
63
- end
64
-
65
- it 'returns tuples with rejected keys' do
66
- expect(transproc[relation]).to eql([{ name: 'Jane' }, { name: 'Joe' }])
67
- end
68
- end
69
-
70
- describe 'rejecting keys' do
71
- let(:options) { { reject_keys: true } }
72
-
73
- let(:attributes) do
74
- [
75
- ['name'],
76
- ['tasks', type: :array, group: true, header: [['title']]]
77
- ]
78
- end
79
-
80
- let(:relation) do
81
- [
82
- { 'name' => 'Jane', 'age' => 21, 'title' => 'Task One' },
83
- { 'name' => 'Jane', 'age' => 21, 'title' => 'Task Two' },
84
- { 'name' => 'Joe', 'age' => 22, 'title' => 'Task One' }
85
- ]
86
- end
87
-
88
- it 'returns tuples with unknown keys rejected' do
89
- expect(transproc[relation]).to eql([
90
- { 'name' => 'Jane',
91
- 'tasks' => [{ 'title' => 'Task One' }, { 'title' => 'Task Two' }] },
92
- { 'name' => 'Joe',
93
- 'tasks' => [{ 'title' => 'Task One' }] }
94
- ])
95
- end
96
- end
97
-
98
- context 'mapping nested hash' do
99
- let(:relation) do
100
- [
101
- { 'name' => 'Jane', 'task' => { 'title' => 'Task One' } },
102
- { 'name' => 'Joe', 'task' => { 'title' => 'Task Two' } }
103
- ]
104
- end
105
-
106
- context 'when no mapping is needed' do
107
- let(:attributes) { [['name'], ['task', type: :hash, header: [[:title]]]] }
108
-
109
- it 'returns tuples' do
110
- expect(transproc[relation]).to eql(relation)
111
- end
112
- end
113
-
114
- context 'with deeply nested hashes' do
115
- context 'when no renaming is required' do
116
- let(:relation) do
117
- [
118
- { 'user' => { 'name' => 'Jane', 'task' => { 'title' => 'Task One' } } },
119
- { 'user' => { 'name' => 'Joe', 'task' => { 'title' => 'Task Two' } } }
120
- ]
121
- end
122
-
123
- let(:attributes) do
124
- [[
125
- 'user', type: :hash, header: [
126
- ['name'],
127
- ['task', type: :hash, header: [['title']]]
128
- ]
129
- ]]
130
- end
131
-
132
- it 'returns tuples' do
133
- expect(transproc[relation]).to eql(relation)
134
- end
135
- end
136
-
137
- context 'when renaming is required' do
138
- let(:relation) do
139
- [
140
- { user: { name: 'Jane', task: { title: 'Task One' } } },
141
- { user: { name: 'Joe', task: { title: 'Task Two' } } }
142
- ]
143
- end
144
-
145
- let(:attributes) do
146
- [[
147
- 'user', type: :hash, header: [
148
- ['name'],
149
- ['task', type: :hash, header: [['title']]]
150
- ]
151
- ]]
152
- end
153
-
154
- it 'returns tuples' do
155
- expect(transproc[relation]).to eql(relation)
156
- end
157
- end
158
- end
159
-
160
- context 'renaming keys' do
161
- context 'when only hash needs renaming' do
162
- let(:attributes) do
163
- [
164
- ['name'],
165
- [:task, from: 'task', type: :hash, header: [[:title, from: 'title']]]
166
- ]
167
- end
168
-
169
- it 'returns tuples with key renamed in the nested hash' do
170
- expect(transproc[relation]).to eql([
171
- { 'name' => 'Jane', :task => { title: 'Task One' } },
172
- { 'name' => 'Joe', :task => { title: 'Task Two' } }
173
- ])
174
- end
175
- end
176
-
177
- context 'when all attributes need renaming' do
178
- let(:attributes) do
179
- [
180
- [:name, from: 'name'],
181
- [:task, from: 'task', type: :hash, header: [[:title, from: 'title']]]
182
- ]
183
- end
184
-
185
- it 'returns tuples with key renamed in the nested hash' do
186
- expect(transproc[relation]).to eql([
187
- { name: 'Jane', task: { title: 'Task One' } },
188
- { name: 'Joe', task: { title: 'Task Two' } }
189
- ])
190
- end
191
- end
192
- end
193
- end
194
-
195
- context 'wrapping tuples' do
196
- let(:relation) do
197
- [
198
- { 'name' => 'Jane', 'title' => 'Task One' },
199
- { 'name' => 'Joe', 'title' => 'Task Two' }
200
- ]
201
- end
202
-
203
- context 'when no mapping is needed' do
204
- let(:attributes) do
205
- [
206
- ['name'],
207
- ['task', type: :hash, wrap: true, header: [['title']]]
208
- ]
209
- end
210
-
211
- it 'returns wrapped tuples' do
212
- expect(transproc[relation]).to eql([
213
- { 'name' => 'Jane', 'task' => { 'title' => 'Task One' } },
214
- { 'name' => 'Joe', 'task' => { 'title' => 'Task Two' } }
215
- ])
216
- end
217
- end
218
-
219
- context 'with deeply wrapped tuples' do
220
- let(:attributes) do
221
- [
222
- ['user', type: :hash, wrap: true, header: [
223
- ['name'],
224
- ['task', type: :hash, wrap: true, header: [['title']]]
225
- ]]
226
- ]
227
- end
228
-
229
- it 'returns wrapped tuples' do
230
- expect(transproc[relation]).to eql([
231
- { 'user' => { 'name' => 'Jane', 'task' => { 'title' => 'Task One' } } },
232
- { 'user' => { 'name' => 'Joe', 'task' => { 'title' => 'Task Two' } } }
233
- ])
234
- end
235
- end
236
-
237
- context 'renaming keys' do
238
- context 'when only wrapped tuple requires renaming' do
239
- let(:attributes) do
240
- [
241
- ['name'],
242
- ['task', type: :hash, wrap: true, header: [[:title, from: 'title']]]
243
- ]
244
- end
245
-
246
- it 'returns wrapped tuples with renamed keys' do
247
- expect(transproc[relation]).to eql([
248
- { 'name' => 'Jane', 'task' => { title: 'Task One' } },
249
- { 'name' => 'Joe', 'task' => { title: 'Task Two' } }
250
- ])
251
- end
252
- end
253
-
254
- context 'when all attributes require renaming' do
255
- let(:attributes) do
256
- [
257
- [:name, from: 'name'],
258
- [:task, type: :hash, wrap: true, header: [[:title, from: 'title']]]
259
- ]
260
- end
261
-
262
- it 'returns wrapped tuples with all keys renamed' do
263
- expect(transproc[relation]).to eql([
264
- { name: 'Jane', task: { title: 'Task One' } },
265
- { name: 'Joe', task: { title: 'Task Two' } }
266
- ])
267
- end
268
- end
269
- end
270
- end
271
-
272
- context 'unwrapping tuples' do
273
- let(:relation) do
274
- [
275
- { 'user' => { 'name' => 'Leo', 'task' => { 'title' => 'Task 1' } } },
276
- { 'user' => { 'name' => 'Joe', 'task' => { 'title' => 'Task 2' } } }
277
- ]
278
- end
279
-
280
- context 'when no mapping is needed' do
281
- let(:attributes) do
282
- [
283
- ['user', type: :hash, unwrap: true, header: [['name'], ['task']]]
284
- ]
285
- end
286
-
287
- it 'returns unwrapped tuples' do
288
- expect(transproc[relation]).to eql([
289
- { 'name' => 'Leo', 'task' => { 'title' => 'Task 1' } },
290
- { 'name' => 'Joe', 'task' => { 'title' => 'Task 2' } }
291
- ])
292
- end
293
- end
294
-
295
- context 'partially' do
296
- context 'without renaming the rest of the wrap' do
297
- let(:attributes) do
298
- [
299
- ['user', type: :hash, unwrap: true, header: [['task']]]
300
- ]
301
- end
302
-
303
- it 'returns unwrapped tuples' do
304
- expect(transproc[relation]).to eql([
305
- { 'user' => { 'name' => 'Leo' }, 'task' => { 'title' => 'Task 1' } },
306
- { 'user' => { 'name' => 'Joe' }, 'task' => { 'title' => 'Task 2' } }
307
- ])
308
- end
309
- end
310
-
311
- context 'with renaming the rest of the wrap' do
312
- let(:attributes) do
313
- [
314
- ['man', from: 'user', type: :hash, unwrap: true, header: [['task']]]
315
- ]
316
- end
317
-
318
- it 'returns unwrapped tuples' do
319
- expect(transproc[relation]).to eql([
320
- { 'man' => { 'name' => 'Leo' }, 'task' => { 'title' => 'Task 1' } },
321
- { 'man' => { 'name' => 'Joe' }, 'task' => { 'title' => 'Task 2' } }
322
- ])
323
- end
324
- end
325
- end
326
-
327
- context 'deeply' do
328
- let(:attributes) do
329
- [
330
- ['user', type: :hash, unwrap: true, header: [
331
- ['name'],
332
- ['title'],
333
- ['task', type: :hash, unwrap: true, header: [['title']]]
334
- ]]
335
- ]
336
- end
337
-
338
- it 'returns unwrapped tuples' do
339
- expect(transproc[relation]).to eql([
340
- { 'name' => 'Leo', 'title' => 'Task 1' },
341
- { 'name' => 'Joe', 'title' => 'Task 2' }
342
- ])
343
- end
344
- end
345
- end
346
-
347
- context 'grouping tuples' do
348
- let(:relation) do
349
- [
350
- { 'name' => 'Jane', 'title' => 'Task One' },
351
- { 'name' => 'Jane', 'title' => 'Task Two' },
352
- { 'name' => 'Joe', 'title' => 'Task One' },
353
- { 'name' => 'Joe', 'title' => nil }
354
- ]
355
- end
356
-
357
- context 'when no mapping is needed' do
358
- let(:attributes) do
359
- [
360
- ['name'],
361
- ['tasks', type: :array, group: true, header: [['title']]]
362
- ]
363
- end
364
-
365
- it 'returns wrapped tuples with all keys renamed' do
366
- expect(transproc[relation]).to eql([
367
- { 'name' => 'Jane',
368
- 'tasks' => [{ 'title' => 'Task One' }, { 'title' => 'Task Two' }] },
369
- { 'name' => 'Joe',
370
- 'tasks' => [{ 'title' => 'Task One' }] }
371
- ])
372
- end
373
- end
374
-
375
- context 'renaming keys' do
376
- context 'when only grouped tuple requires renaming' do
377
- let(:attributes) do
378
- [
379
- ['name'],
380
- ['tasks', type: :array, group: true, header: [[:title, from: 'title']]]
381
- ]
382
- end
383
-
384
- it 'returns grouped tuples with renamed keys' do
385
- expect(transproc[relation]).to eql([
386
- { 'name' => 'Jane',
387
- 'tasks' => [{ title: 'Task One' }, { title: 'Task Two' }] },
388
- { 'name' => 'Joe',
389
- 'tasks' => [{ title: 'Task One' }] }
390
- ])
391
- end
392
- end
393
-
394
- context 'when all attributes require renaming' do
395
- let(:attributes) do
396
- [
397
- [:name, from: 'name'],
398
- [:tasks, type: :array, group: true, header: [[:title, from: 'title']]]
399
- ]
400
- end
401
-
402
- it 'returns grouped tuples with all keys renamed' do
403
- expect(transproc[relation]).to eql([
404
- { name: 'Jane',
405
- tasks: [{ title: 'Task One' }, { title: 'Task Two' }] },
406
- { name: 'Joe',
407
- tasks: [{ title: 'Task One' }] }
408
- ])
409
- end
410
- end
411
- end
412
-
413
- context 'nested grouping' do
414
- let(:relation) do
415
- [
416
- { name: 'Jane', title: 'Task One', tag: 'red' },
417
- { name: 'Jane', title: 'Task One', tag: 'green' },
418
- { name: 'Joe', title: 'Task One', tag: 'blue' }
419
- ]
420
- end
421
-
422
- let(:attributes) do
423
- [
424
- [:name],
425
- [:tasks, type: :array, group: true, header: [
426
- [:title],
427
- [:tags, type: :array, group: true, header: [[:tag]]]
428
- ]]
429
- ]
430
- end
431
-
432
- it 'returns deeply grouped tuples' do
433
- expect(transproc[relation]).to eql([
434
- { name: 'Jane',
435
- tasks: [
436
- { title: 'Task One', tags: [{ tag: 'red' }, { tag: 'green' }] }
437
- ]
438
- },
439
- { name: 'Joe',
440
- tasks: [
441
- { title: 'Task One', tags: [{ tag: 'blue' }] }
442
- ]
443
- }
444
- ])
445
- end
446
- end
447
- end
448
- end