rom 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +34 -0
  5. data/CONTRIBUTING.md +1 -0
  6. data/Gemfile +1 -1
  7. data/README.md +12 -7
  8. data/lib/rom.rb +8 -0
  9. data/lib/rom/command.rb +19 -0
  10. data/lib/rom/commands/abstract.rb +6 -1
  11. data/lib/rom/commands/composite.rb +1 -52
  12. data/lib/rom/commands/update.rb +4 -1
  13. data/lib/rom/constants.rb +1 -0
  14. data/lib/rom/env.rb +3 -25
  15. data/lib/rom/global.rb +23 -0
  16. data/lib/rom/global/plugin_dsl.rb +47 -0
  17. data/lib/rom/header.rb +19 -8
  18. data/lib/rom/header/attribute.rb +14 -2
  19. data/lib/rom/lint/enumerable_dataset.rb +3 -1
  20. data/lib/rom/lint/repository.rb +5 -5
  21. data/lib/rom/mapper.rb +2 -1
  22. data/lib/rom/mapper/attribute_dsl.rb +86 -13
  23. data/lib/rom/mapper/dsl.rb +20 -1
  24. data/lib/rom/memory/commands.rb +3 -1
  25. data/lib/rom/memory/dataset.rb +1 -1
  26. data/lib/rom/memory/relation.rb +1 -1
  27. data/lib/rom/pipeline.rb +91 -0
  28. data/lib/rom/plugin.rb +31 -0
  29. data/lib/rom/plugin_registry.rb +134 -0
  30. data/lib/rom/plugins/relation/registry_reader.rb +30 -0
  31. data/lib/rom/processor/transproc.rb +78 -3
  32. data/lib/rom/relation/class_interface.rb +14 -2
  33. data/lib/rom/relation/composite.rb +9 -97
  34. data/lib/rom/relation/graph.rb +76 -0
  35. data/lib/rom/relation/lazy.rb +15 -63
  36. data/lib/rom/relation/materializable.rb +66 -0
  37. data/lib/rom/setup/finalize.rb +16 -5
  38. data/lib/rom/setup_dsl/mapper_dsl.rb +10 -2
  39. data/lib/rom/setup_dsl/setup.rb +1 -1
  40. data/lib/rom/support/array_dataset.rb +7 -4
  41. data/lib/rom/support/data_proxy.rb +7 -7
  42. data/lib/rom/support/deprecations.rb +17 -0
  43. data/lib/rom/support/enumerable_dataset.rb +10 -3
  44. data/lib/rom/support/inflector.rb +1 -1
  45. data/lib/rom/version.rb +1 -1
  46. data/rom.gemspec +1 -1
  47. data/spec/integration/commands/create_spec.rb +3 -3
  48. data/spec/integration/commands/update_spec.rb +24 -4
  49. data/spec/integration/mappers/combine_spec.rb +107 -0
  50. data/spec/integration/mappers/registering_custom_mappers_spec.rb +29 -0
  51. data/spec/integration/mappers/reusing_mappers_spec.rb +22 -0
  52. data/spec/integration/mappers/unwrap_spec.rb +98 -0
  53. data/spec/integration/multi_repo_spec.rb +2 -2
  54. data/spec/integration/repositories/extending_relations_spec.rb +9 -0
  55. data/spec/integration/setup_spec.rb +2 -2
  56. data/spec/shared/enumerable_dataset.rb +4 -1
  57. data/spec/shared/materializable.rb +34 -0
  58. data/spec/shared/proxy.rb +0 -0
  59. data/spec/spec_helper.rb +6 -2
  60. data/spec/support/mutant.rb +9 -6
  61. data/spec/unit/rom/commands_spec.rb +3 -3
  62. data/spec/unit/rom/header_spec.rb +2 -2
  63. data/spec/unit/rom/mapper/dsl_spec.rb +102 -1
  64. data/spec/unit/rom/memory/dataset_spec.rb +10 -33
  65. data/spec/unit/rom/memory/relation_spec.rb +63 -0
  66. data/spec/unit/rom/memory/storage_spec.rb +2 -2
  67. data/spec/unit/rom/plugin_spec.rb +121 -0
  68. data/spec/unit/rom/processor/transproc_spec.rb +47 -6
  69. data/spec/unit/rom/relation/composite_spec.rb +3 -1
  70. data/spec/unit/rom/relation/graph_spec.rb +78 -0
  71. data/spec/unit/rom/relation/lazy/combine_spec.rb +130 -0
  72. data/spec/unit/rom/relation/lazy_spec.rb +3 -1
  73. data/spec/unit/rom/relation/loaded_spec.rb +3 -1
  74. data/spec/unit/rom/setup_spec.rb +8 -8
  75. data/spec/unit/rom/support/array_dataset_spec.rb +3 -1
  76. data/spec/unit/rom/support/class_builder_spec.rb +2 -2
  77. metadata +24 -7
  78. data/lib/rom/relation/registry_reader.rb +0 -23
File without changes
@@ -19,8 +19,12 @@ end
19
19
 
20
20
  root = Pathname(__FILE__).dirname
21
21
 
22
- Dir[root.join('support/*.rb').to_s].each { |f| require f }
23
- Dir[root.join('shared/*.rb').to_s].each { |f| require f }
22
+ Dir[root.join('support/*.rb').to_s].each do |f|
23
+ require f
24
+ end
25
+ Dir[root.join('shared/*.rb').to_s].each do |f|
26
+ require f
27
+ end
24
28
 
25
29
  # Namespace holding all objects created during specs
26
30
  module Test
@@ -1,7 +1,10 @@
1
1
  module Mutant
2
- class Subject
3
- def tests
4
- config.integration.all_tests
5
- end
6
- end
7
- end
2
+ class Selector
3
+ # Expression based test selector
4
+ class Expression < self
5
+ def call(subject)
6
+ integration.all_tests
7
+ end
8
+ end # Expression
9
+ end # Selector
10
+ end # Mutant
@@ -15,11 +15,11 @@ describe 'Commands' do
15
15
 
16
16
  describe '.build_class' do
17
17
  it 'creates a command class constant' do
18
- klass = ROM::Command.build_class(:create, :users, adapter: :memory) do
18
+ klass = ROM::Command.build_class(:create, :users, adapter: :memory) {
19
19
  def super?
20
20
  true
21
21
  end
22
- end
22
+ }
23
23
 
24
24
  expect(klass.name).to eql('ROM::Memory::Commands::Create[Users]')
25
25
  expect(klass.register_as).to eql(:create)
@@ -135,7 +135,7 @@ describe 'Commands' do
135
135
  }.build(users)
136
136
 
137
137
  create_task = Class.new(ROM::Commands::Create) {
138
- def execute(user_tuple, task_input)
138
+ def execute(task_input, user_tuple)
139
139
  relation.insert(task_input.merge(user_id: user_tuple[:user_id]))
140
140
  end
141
141
  }.build(tasks)
@@ -33,7 +33,7 @@ describe ROM::Header do
33
33
 
34
34
  expect(tasks.type).to be(:array)
35
35
  expect(tasks.header.model).to be(model)
36
- expect(tasks.header).to eql(ROM::Header.coerce([[:title]], model))
36
+ expect(tasks.header).to eql(ROM::Header.coerce([[:title]], model: model))
37
37
 
38
38
  expect(input.first[1])
39
39
  .to eql(header: [[:title]], type: :array, model: model)
@@ -55,7 +55,7 @@ describe ROM::Header do
55
55
 
56
56
  expect(tasks.type).to be(:hash)
57
57
  expect(tasks.header.model).to be(model)
58
- expect(tasks.header).to eql(ROM::Header.coerce([[:title]], model))
58
+ expect(tasks.header).to eql(ROM::Header.coerce([[:title]], model: model))
59
59
 
60
60
  expect(input.first[1])
61
61
  .to eql(header: [[:title]], type: :hash, model: model)
@@ -3,7 +3,9 @@ require 'spec_helper'
3
3
  describe ROM::Mapper do
4
4
  subject(:mapper) do
5
5
  klass = Class.new(parent)
6
- options.each { |k, v| klass.send(k, v) }
6
+ options.each do |k, v|
7
+ klass.send(k, v)
8
+ end
7
9
  klass
8
10
  end
9
11
 
@@ -69,6 +71,18 @@ describe ROM::Mapper do
69
71
  end
70
72
  end
71
73
 
74
+ describe 'reject_keys' do
75
+ let(:attributes) { [[:name, type: :string]] }
76
+ let(:options) { { reject_keys: true } }
77
+
78
+ it 'sets rejected_keys for the header' do
79
+ mapper.reject_keys true
80
+ mapper.attribute :name, type: :string
81
+
82
+ expect(header).to eql(expected_header)
83
+ end
84
+ end
85
+
72
86
  describe 'overriding inherited attributes' do
73
87
  context 'when name matches' do
74
88
  let(:attributes) { [[:name, type: :string]] }
@@ -322,4 +336,91 @@ describe ROM::Mapper do
322
336
  end
323
337
  end
324
338
  end
339
+
340
+ context 'reusing mappers' do
341
+ describe '#group' do
342
+ let(:task_mapper) do
343
+ Class.new(ROM::Mapper) { attribute :title }
344
+ end
345
+
346
+ let(:attributes) do
347
+ [
348
+ [:name],
349
+ [:tasks, type: :array, group: true, header: task_mapper.header]
350
+ ]
351
+ end
352
+
353
+ it 'uses other mapper header' do
354
+ mapper.attribute :name
355
+ mapper.group :tasks, mapper: task_mapper
356
+
357
+ expect(header).to eql(expected_header)
358
+ end
359
+ end
360
+
361
+ describe '#wrap' do
362
+ let(:task_mapper) do
363
+ Class.new(ROM::Mapper) { attribute :title }
364
+ end
365
+
366
+ let(:attributes) do
367
+ [
368
+ [:name],
369
+ [:task, type: :hash, wrap: true, header: task_mapper.header]
370
+ ]
371
+ end
372
+
373
+ it 'uses other mapper header' do
374
+ mapper.attribute :name
375
+ mapper.wrap :task, mapper: task_mapper
376
+
377
+ expect(header).to eql(expected_header)
378
+ end
379
+ end
380
+
381
+ describe '#embedded' do
382
+ let(:task_mapper) do
383
+ Class.new(ROM::Mapper) { attribute :title }
384
+ end
385
+
386
+ let(:attributes) do
387
+ [
388
+ [:name],
389
+ [:task, type: :array, header: task_mapper.header]
390
+ ]
391
+ end
392
+
393
+ it 'uses other mapper header' do
394
+ mapper.attribute :name
395
+ mapper.embedded :task, mapper: task_mapper, type: :hash
396
+
397
+ expect(header).to eql(expected_header)
398
+ end
399
+ end
400
+ end
401
+
402
+ describe '#combine' do
403
+ let(:attributes) do
404
+ [
405
+ [:title],
406
+ [:tasks, combine: true, type: :array, header: [[:title]]]
407
+ ]
408
+ end
409
+
410
+ it 'adds combine attributes' do
411
+ mapper.attribute :title
412
+
413
+ mapper.combine :tasks, on: { title: :title } do
414
+ attribute :title
415
+ end
416
+
417
+ expect(header).to eql(expected_header)
418
+ end
419
+ end
420
+
421
+ describe '#method_missing' do
422
+ it 'responds to DSL methods' do
423
+ expect(mapper).to respond_to(:attribute)
424
+ end
425
+ end
325
426
  end
@@ -1,9 +1,10 @@
1
1
  require 'spec_helper'
2
2
  require 'rom/lint/spec'
3
+
3
4
  require 'rom/memory/dataset'
4
5
 
5
6
  describe ROM::Memory::Dataset do
6
- subject(:dataset) { described_class.new(data) }
7
+ subject(:dataset) { ROM::Memory::Dataset.new(data) }
7
8
 
8
9
  let(:data) do
9
10
  [
@@ -15,40 +16,16 @@ describe ROM::Memory::Dataset do
15
16
 
16
17
  it_behaves_like "a rom enumerable dataset"
17
18
 
18
- describe '#project' do
19
- it 'projects tuples with the provided keys' do
20
- expect(dataset.project(:name, :age)).to match_array([
21
- { name: 'Jane', age: 10 },
22
- { name: 'Jade', age: 11 },
23
- { name: 'Joe', age: 12 }
24
- ])
25
- end
26
- end
27
-
28
- describe '#restrict' do
29
- it 'restricts data using criteria hash' do
30
- expect(dataset.restrict(age: 10)).to match_array([
31
- { name: 'Jane', email: 'jane@doe.org', age: 10 }
32
- ])
19
+ describe 'subclassing' do
20
+ it 'supports options' do
21
+ descendant = Class.new(ROM::Memory::Dataset) do
22
+ option :path, reader: true
23
+ end
33
24
 
34
- expect(dataset.restrict(age: 10.0)).to match_array([])
35
- end
36
-
37
- it 'restricts data using block' do
38
- expect(dataset.restrict { |tuple| tuple[:age] > 10 }).to match_array([
39
- { name: 'Jade', email: 'jade@doe.org', age: 11 },
40
- { name: 'Joe', email: 'joe@doe.org', age: 12 }
41
- ])
42
- end
43
- end
25
+ dataset = descendant.new([1, 2, 3], path: '/data')
44
26
 
45
- describe '#order' do
46
- it 'sorts data using provided attribute names' do
47
- expect(dataset.order(:name)).to match_array([
48
- { name: 'Jade', email: 'jade@doe.org', age: 11 },
49
- { name: 'Jane', email: 'jane@doe.org', age: 10 },
50
- { name: 'Joe', email: 'joe@doe.org', age: 12 }
51
- ])
27
+ expect(dataset.to_a).to eql([1, 2, 3])
28
+ expect(dataset.path).to eql('/data')
52
29
  end
53
30
  end
54
31
  end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ require 'rom/lint/spec'
3
+
4
+ require 'rom/memory/dataset'
5
+ require 'rom/memory/relation'
6
+
7
+ describe ROM::Memory::Relation do
8
+ subject(:relation) { ROM::Memory::Relation.new(dataset) }
9
+
10
+ let(:dataset) do
11
+ ROM::Memory::Dataset.new([
12
+ { name: 'Jane', email: 'jane@doe.org', age: 10 },
13
+ { name: 'Jade', email: 'jade@doe.org', age: 11 },
14
+ { name: 'Joe', email: 'joe@doe.org', age: 12 }
15
+ ])
16
+ end
17
+
18
+ describe '#take' do
19
+ it 'takes given number of tuples' do
20
+ expect(relation.take(2)).to match_array([
21
+ { name: 'Jane', email: 'jane@doe.org', age: 10 },
22
+ { name: 'Jade', email: 'jade@doe.org', age: 11 }
23
+ ])
24
+ end
25
+ end
26
+
27
+ describe '#project' do
28
+ it 'projects tuples with the provided keys' do
29
+ expect(relation.project(:name, :age)).to match_array([
30
+ { name: 'Jane', age: 10 },
31
+ { name: 'Jade', age: 11 },
32
+ { name: 'Joe', age: 12 }
33
+ ])
34
+ end
35
+ end
36
+
37
+ describe '#restrict' do
38
+ it 'restricts data using criteria hash' do
39
+ expect(relation.restrict(age: 10)).to match_array([
40
+ { name: 'Jane', email: 'jane@doe.org', age: 10 }
41
+ ])
42
+
43
+ expect(relation.restrict(age: 10.0)).to match_array([])
44
+ end
45
+
46
+ it 'restricts data using block' do
47
+ expect(relation.restrict { |tuple| tuple[:age] > 10 }).to match_array([
48
+ { name: 'Jade', email: 'jade@doe.org', age: 11 },
49
+ { name: 'Joe', email: 'joe@doe.org', age: 12 }
50
+ ])
51
+ end
52
+ end
53
+
54
+ describe '#order' do
55
+ it 'sorts data using provided attribute names' do
56
+ expect(relation.order(:name).to_a).to eq([
57
+ { name: 'Jade', email: 'jade@doe.org', age: 11 },
58
+ { name: 'Jane', email: 'jane@doe.org', age: 10 },
59
+ { name: 'Joe', email: 'joe@doe.org', age: 12 }
60
+ ])
61
+ end
62
+ end
63
+ end
@@ -33,13 +33,13 @@ describe ROM::Memory::Storage do
33
33
  end
34
34
 
35
35
  def threaded_operations
36
- threads.times.map do |thread|
36
+ threads.times.map { |thread|
37
37
  Thread.new do
38
38
  operations.times do |operation|
39
39
  yield thread, operation
40
40
  end
41
41
  end
42
- end.each(&:join)
42
+ }.each(&:join)
43
43
  end
44
44
  end
45
45
  end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ROM::PluginRegistry" do
4
+ subject(:env) { setup.finalize }
5
+
6
+ let(:setup) { ROM.setup(:memory) }
7
+
8
+ before do
9
+ Test::CommandPlugin = Module.new
10
+ Test::MapperPlugin = Module.new
11
+ Test::RelationPlugin = Module.new do
12
+ def self.included(mod)
13
+ mod.exposed_relations << :plugged_in
14
+ end
15
+
16
+ def plugged_in
17
+ "a relation"
18
+ end
19
+ end
20
+
21
+ ROM.plugins do
22
+ register :publisher, Test::CommandPlugin, type: :command
23
+ register :pager, Test::RelationPlugin, type: :relation
24
+ register :translater, Test::MapperPlugin, type: :mapper
25
+ end
26
+ end
27
+
28
+ around do |example|
29
+ orig_plugins = ROM.plugin_registry
30
+ example.run
31
+ ROM.instance_variable_set('@plugin_registry', orig_plugins)
32
+ end
33
+
34
+ it "includes relation plugins" do
35
+ setup.relation(:users) do
36
+ use :pager
37
+ end
38
+
39
+ expect(env.relation(:users).plugged_in).to eq "a relation"
40
+ end
41
+
42
+ it "makes command plugins available" do
43
+ setup.relation(:users)
44
+
45
+ Class.new(ROM::Commands::Create[:memory]) do
46
+ relation :users
47
+ register_as :create
48
+ use :publisher
49
+ end
50
+
51
+ expect(env.command(:users).create).to be_kind_of Test::CommandPlugin
52
+ end
53
+
54
+ it "inclues plugins in mappers" do
55
+ setup.relation(:users)
56
+
57
+ Class.new(ROM::Mapper) do
58
+ relation :users
59
+ register_as :translator
60
+ use :translater
61
+ end
62
+
63
+ expect(env.mappers[:users][:translator]).to be_kind_of Test::MapperPlugin
64
+ end
65
+
66
+ it "restricts plugins to defined type" do
67
+ expect {
68
+ setup.relation(:users) do
69
+ use :publisher
70
+ end
71
+ }.to raise_error ROM::UnknownPluginError
72
+ end
73
+
74
+ it "allows definition of adapter restricted plugins" do
75
+ Test::LazyPlugin = Module.new do
76
+ def self.included(mod)
77
+ mod.exposed_relations << :lazy?
78
+ end
79
+
80
+ def lazy?
81
+ true
82
+ end
83
+ end
84
+
85
+ ROM.plugins do
86
+ adapter :memory do
87
+ register :lazy, Test::LazyPlugin, type: :relation
88
+ end
89
+ end
90
+
91
+ setup.relation(:users) do
92
+ use :lazy, adapter: :memory
93
+ end
94
+
95
+ expect(env.relation(:users)).to be_lazy
96
+ end
97
+
98
+ it "respects adapter restrictions" do
99
+ Test::LazyPlugin = Module.new
100
+ Test::LazySQLPlugin = Module.new
101
+
102
+ ROM.plugins do
103
+ register :lazy, Test::LazyPlugin, type: :command
104
+
105
+ adapter :sql do
106
+ register :lazy, Test::LazySQLPlugin, type: :command
107
+ end
108
+ end
109
+
110
+ setup.relation(:users)
111
+
112
+ Class.new(ROM::Commands::Create[:memory]) do
113
+ relation :users
114
+ register_as :create
115
+ use :lazy, adapter: :memory
116
+ end
117
+
118
+ expect(env.command(:users).create).not_to be_kind_of Test::LazySQLPlugin
119
+ expect(env.command(:users).create).to be_kind_of Test::LazyPlugin
120
+ end
121
+ end