rom 0.6.2 → 0.7.0

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 (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