rom 0.5.0 → 0.6.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +19 -15
  3. data/.rubocop_todo.yml +28 -0
  4. data/.travis.yml +8 -1
  5. data/CHANGELOG.md +40 -0
  6. data/Gemfile +10 -2
  7. data/Guardfile +12 -10
  8. data/README.md +42 -43
  9. data/Rakefile +13 -23
  10. data/lib/rom.rb +19 -27
  11. data/lib/rom/command.rb +118 -0
  12. data/lib/rom/command_registry.rb +13 -27
  13. data/lib/rom/commands.rb +1 -59
  14. data/lib/rom/commands/abstract.rb +147 -0
  15. data/lib/rom/commands/composite.rb +47 -0
  16. data/lib/rom/commands/create.rb +2 -17
  17. data/lib/rom/commands/delete.rb +5 -25
  18. data/lib/rom/commands/result.rb +5 -5
  19. data/lib/rom/commands/update.rb +3 -27
  20. data/lib/rom/constants.rb +19 -0
  21. data/lib/rom/env.rb +85 -35
  22. data/lib/rom/global.rb +173 -42
  23. data/lib/rom/header.rb +5 -5
  24. data/lib/rom/header/attribute.rb +2 -2
  25. data/lib/rom/lint/enumerable_dataset.rb +52 -0
  26. data/lib/rom/lint/linter.rb +64 -0
  27. data/lib/rom/lint/repository.rb +78 -0
  28. data/lib/rom/lint/spec.rb +20 -0
  29. data/lib/rom/lint/test.rb +98 -0
  30. data/lib/rom/mapper.rb +32 -5
  31. data/lib/rom/mapper/attribute_dsl.rb +240 -0
  32. data/lib/rom/mapper/dsl.rb +100 -0
  33. data/lib/rom/mapper/model_dsl.rb +55 -0
  34. data/lib/rom/mapper_registry.rb +8 -1
  35. data/lib/rom/memory.rb +4 -0
  36. data/lib/rom/memory/commands.rb +46 -0
  37. data/lib/rom/memory/dataset.rb +72 -0
  38. data/lib/rom/memory/relation.rb +44 -0
  39. data/lib/rom/memory/repository.rb +62 -0
  40. data/lib/rom/memory/storage.rb +57 -0
  41. data/lib/rom/model_builder.rb +44 -5
  42. data/lib/rom/processor.rb +1 -1
  43. data/lib/rom/processor/transproc.rb +109 -16
  44. data/lib/rom/reader.rb +91 -39
  45. data/lib/rom/relation.rb +165 -26
  46. data/lib/rom/relation/composite.rb +132 -0
  47. data/lib/rom/relation/curried.rb +48 -0
  48. data/lib/rom/relation/lazy.rb +173 -0
  49. data/lib/rom/relation/loaded.rb +75 -0
  50. data/lib/rom/relation/registry_reader.rb +23 -0
  51. data/lib/rom/repository.rb +93 -34
  52. data/lib/rom/setup.rb +54 -98
  53. data/lib/rom/setup/finalize.rb +85 -76
  54. data/lib/rom/setup_dsl/command.rb +36 -0
  55. data/lib/rom/setup_dsl/command_dsl.rb +34 -0
  56. data/lib/rom/setup_dsl/mapper.rb +32 -0
  57. data/lib/rom/setup_dsl/mapper_dsl.rb +30 -0
  58. data/lib/rom/setup_dsl/relation.rb +21 -0
  59. data/lib/rom/setup_dsl/setup.rb +75 -0
  60. data/lib/rom/support/array_dataset.rb +38 -0
  61. data/lib/rom/support/class_builder.rb +44 -0
  62. data/lib/rom/support/class_macros.rb +56 -0
  63. data/lib/rom/support/data_proxy.rb +102 -0
  64. data/lib/rom/support/enumerable_dataset.rb +58 -0
  65. data/lib/rom/support/inflector.rb +73 -0
  66. data/lib/rom/support/options.rb +188 -0
  67. data/lib/rom/support/registry.rb +4 -8
  68. data/lib/rom/version.rb +1 -1
  69. data/rakelib/benchmark.rake +13 -0
  70. data/rakelib/mutant.rake +16 -0
  71. data/rakelib/rubocop.rake +18 -0
  72. data/rom.gemspec +4 -7
  73. data/spec/integration/commands/create_spec.rb +32 -24
  74. data/spec/integration/commands/delete_spec.rb +15 -7
  75. data/spec/integration/commands/update_spec.rb +13 -11
  76. data/spec/integration/mappers/deep_embedded_spec.rb +4 -11
  77. data/spec/integration/mappers/definition_dsl_spec.rb +31 -44
  78. data/spec/integration/mappers/embedded_spec.rb +9 -24
  79. data/spec/integration/mappers/group_spec.rb +22 -30
  80. data/spec/integration/mappers/prefixing_attributes_spec.rb +18 -23
  81. data/spec/integration/mappers/renaming_attributes_spec.rb +23 -38
  82. data/spec/integration/mappers/symbolizing_attributes_spec.rb +18 -24
  83. data/spec/integration/mappers/wrap_spec.rb +22 -30
  84. data/spec/integration/multi_repo_spec.rb +15 -37
  85. data/spec/integration/relations/reading_spec.rb +82 -14
  86. data/spec/integration/repositories/extending_relations_spec.rb +50 -0
  87. data/spec/integration/{adapters → repositories}/setting_logger_spec.rb +6 -5
  88. data/spec/integration/setup_spec.rb +59 -62
  89. data/spec/shared/enumerable_dataset.rb +49 -0
  90. data/spec/shared/one_behavior.rb +26 -0
  91. data/spec/shared/users_and_tasks.rb +11 -23
  92. data/spec/spec_helper.rb +16 -7
  93. data/spec/support/constant_leak_finder.rb +14 -0
  94. data/spec/test/memory_repository_lint_test.rb +27 -0
  95. data/spec/unit/rom/command_registry_spec.rb +44 -0
  96. data/spec/unit/rom/commands/result_spec.rb +14 -0
  97. data/spec/unit/rom/commands_spec.rb +174 -0
  98. data/spec/unit/rom/env_spec.rb +40 -7
  99. data/spec/unit/rom/global_spec.rb +14 -0
  100. data/spec/unit/rom/{mapper_builder_spec.rb → mapper/dsl_spec.rb} +52 -38
  101. data/spec/unit/rom/mapper_spec.rb +51 -10
  102. data/spec/unit/rom/{adapter/memory → memory}/dataset_spec.rb +6 -4
  103. data/spec/unit/rom/memory/repository_spec.rb +12 -0
  104. data/spec/unit/rom/memory/storage_spec.rb +45 -0
  105. data/spec/unit/rom/model_builder_spec.rb +4 -3
  106. data/spec/unit/rom/processor/transproc_spec.rb +1 -0
  107. data/spec/unit/rom/reader_spec.rb +97 -24
  108. data/spec/unit/rom/relation/composite_spec.rb +65 -0
  109. data/spec/unit/rom/relation/lazy_spec.rb +145 -0
  110. data/spec/unit/rom/relation/loaded_spec.rb +28 -0
  111. data/spec/unit/rom/relation_spec.rb +111 -6
  112. data/spec/unit/rom/repository_spec.rb +59 -9
  113. data/spec/unit/rom/setup_spec.rb +99 -11
  114. data/spec/unit/rom/support/array_dataset_spec.rb +59 -0
  115. data/spec/unit/rom/support/class_builder_spec.rb +42 -0
  116. data/spec/unit/rom/support/enumerable_dataset_spec.rb +17 -0
  117. data/spec/unit/rom/support/inflector_spec.rb +89 -0
  118. data/spec/unit/rom/support/options_spec.rb +119 -0
  119. metadata +74 -112
  120. data/lib/rom/adapter.rb +0 -191
  121. data/lib/rom/adapter/memory.rb +0 -32
  122. data/lib/rom/adapter/memory/commands.rb +0 -31
  123. data/lib/rom/adapter/memory/dataset.rb +0 -67
  124. data/lib/rom/adapter/memory/storage.rb +0 -26
  125. data/lib/rom/commands/with_options.rb +0 -18
  126. data/lib/rom/config.rb +0 -70
  127. data/lib/rom/mapper_builder.rb +0 -52
  128. data/lib/rom/mapper_builder/mapper_dsl.rb +0 -114
  129. data/lib/rom/mapper_builder/model_dsl.rb +0 -29
  130. data/lib/rom/reader_builder.rb +0 -48
  131. data/lib/rom/relation_builder.rb +0 -62
  132. data/lib/rom/setup/base_relation_dsl.rb +0 -46
  133. data/lib/rom/setup/command_dsl.rb +0 -46
  134. data/lib/rom/setup/mapper_dsl.rb +0 -19
  135. data/lib/rom/setup/relation_dsl.rb +0 -20
  136. data/lib/rom/setup/schema_dsl.rb +0 -33
  137. data/spec/integration/adapters/extending_relations_spec.rb +0 -41
  138. data/spec/integration/commands/try_spec.rb +0 -27
  139. data/spec/integration/schema_spec.rb +0 -77
  140. data/spec/unit/config_spec.rb +0 -60
  141. data/spec/unit/rom/adapter_spec.rb +0 -79
  142. data/spec/unit/rom_spec.rb +0 -14
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'virtus'
2
3
 
3
4
  describe 'Setting up ROM' do
4
5
  context 'with existing schema' do
@@ -7,8 +8,13 @@ describe 'Setting up ROM' do
7
8
  let(:jane) { { name: 'Jane', email: 'jane@doe.org' } }
8
9
  let(:joe) { { name: 'Joe', email: 'joe@doe.org' } }
9
10
 
11
+ before do
12
+ setup.relation(:users)
13
+ setup.relation(:tasks)
14
+ end
15
+
10
16
  it 'configures schema relations' do
11
- expect(rom.memory.users).to match_array([joe, jane])
17
+ expect(rom.repositories[:default][:users]).to match_array([joe, jane])
12
18
  end
13
19
 
14
20
  it 'configures rom relations' do
@@ -33,32 +39,58 @@ describe 'Setting up ROM' do
33
39
 
34
40
  context 'without schema' do
35
41
  it 'builds empty registries if there is no schema' do
36
- setup = ROM.setup(memory: 'memory://test')
37
-
38
- setup.relation(:users)
42
+ setup = ROM.setup(:memory)
39
43
 
40
44
  rom = setup.finalize
41
45
 
42
46
  expect(rom.relations).to eql(ROM::RelationRegistry.new)
43
- expect(rom.mappers).to eql(ROM::ReaderRegistry.new)
47
+ expect(rom.readers).to eql(ROM::ReaderRegistry.new)
44
48
  end
45
49
  end
46
50
 
47
- describe 'quick setup' do
48
- it 'exposes boot DSL inside the setup block' do
49
- User = Class.new do
50
- include Virtus.value_object
51
- values { attribute :name, String }
51
+ describe 'defining classes' do
52
+ it 'sets up registries based on class definitions' do
53
+ ROM.setup(:memory)
54
+
55
+ class Test::UserRelation < ROM::Relation[:memory]
56
+ dataset :users
57
+
58
+ def by_name(name)
59
+ restrict(name: name)
60
+ end
52
61
  end
53
62
 
54
- rom = ROM.setup(memory: 'memory://test') do
55
- schema do
56
- base_relation(:users) do
57
- repository :memory
58
- attribute :name
59
- end
63
+ class Test::TaskRelation < ROM::Relation[:memory]
64
+ dataset :tasks
65
+ end
66
+
67
+ class Test::CreateUser < ROM::Commands::Update[:memory]
68
+ relation :users
69
+ register_as :create
70
+ end
71
+
72
+ rom = ROM.finalize.env
73
+
74
+ expect(rom.relations.users).to be_kind_of(Test::UserRelation)
75
+ expect(rom.relations.users.tasks).to be(rom.relations.tasks)
76
+
77
+ expect(rom.commands.users[:create]).to be_kind_of(Test::CreateUser)
78
+
79
+ expect(rom.relations.tasks).to be_kind_of(Test::TaskRelation)
80
+ expect(rom.relations.tasks.users).to be(rom.relations.users)
81
+ end
82
+ end
83
+
84
+ describe 'quick setup' do
85
+ it 'exposes boot DSL inside the setup block' do
86
+ module Test
87
+ User = Class.new do
88
+ include Virtus.value_object
89
+ values { attribute :name, String }
60
90
  end
91
+ end
61
92
 
93
+ rom = ROM.setup(:memory) do
62
94
  relation(:users) do
63
95
  def by_name(name)
64
96
  restrict(name: name)
@@ -71,34 +103,29 @@ describe 'Setting up ROM' do
71
103
 
72
104
  mappers do
73
105
  define(:users) do
74
- model User
106
+ model Test::User
75
107
  end
76
108
  end
77
109
  end
78
110
 
79
- rom.command(:users).try { create(name: 'Jane') }
111
+ rom.commands.users.create.call(name: 'Jane')
80
112
 
81
113
  expect(rom.read(:users).by_name('Jane').to_a)
82
- .to eql([User.new(name: 'Jane')])
114
+ .to eql([Test::User.new(name: 'Jane')])
83
115
  end
84
116
  end
85
117
 
86
118
  describe 'multi-step setup' do
87
119
  it 'exposes boot DSL that can be invoked multiple times' do
88
- User = Class.new do
89
- include Virtus.value_object
90
- values { attribute :name, String }
91
- end
92
-
93
- ROM.setup(memory: 'memory://test')
94
-
95
- ROM.schema do
96
- base_relation(:users) do
97
- repository :memory
98
- attribute :name
120
+ module Test
121
+ User = Class.new do
122
+ include Virtus.value_object
123
+ values { attribute :name, String }
99
124
  end
100
125
  end
101
126
 
127
+ ROM.setup(:memory)
128
+
102
129
  ROM.relation(:users) do
103
130
  def by_name(name)
104
131
  restrict(name: name)
@@ -111,7 +138,7 @@ describe 'Setting up ROM' do
111
138
 
112
139
  ROM.mappers do
113
140
  define(:users) do
114
- model User
141
+ model Test::User
115
142
  end
116
143
  end
117
144
 
@@ -120,37 +147,7 @@ describe 'Setting up ROM' do
120
147
  rom.command(:users).create.call(name: 'Jane')
121
148
 
122
149
  expect(rom.read(:users).by_name('Jane').to_a)
123
- .to eql([User.new(name: 'Jane')])
124
- end
125
- end
126
-
127
- describe 'setup with extra options' do
128
- shared_examples 'adapter with extra options' do
129
- subject(:adapter) { setup.default.adapter }
130
-
131
- it 'has connection uri' do
132
- expect(adapter.uri).to eql(
133
- Addressable::URI.parse('memory://localhost/test')
134
- )
135
- end
136
-
137
- it 'has extra options' do
138
- expect(adapter.options).to eql(super: 'option')
139
- end
140
- end
141
-
142
- context 'with a connection uri and options passed separately' do
143
- let(:setup) { ROM.setup('memory://localhost/test', super: 'option') }
144
-
145
- it_behaves_like 'adapter with extra options'
146
- end
147
-
148
- context 'with option hash' do
149
- let(:setup) do
150
- ROM.setup(adapter: 'memory', database: 'test', super: 'option')
151
- end
152
-
153
- it_behaves_like 'adapter with extra options'
150
+ .to eql([Test::User.new(name: 'Jane')])
154
151
  end
155
152
  end
156
153
  end
@@ -0,0 +1,49 @@
1
+ shared_examples_for "an enumerable dataset" do
2
+ subject(:dataset) { klass.new(data) }
3
+
4
+ let(:data) do
5
+ [{ 'name' => 'Jane' }, { 'name' => 'Joe' }]
6
+ end
7
+
8
+ describe '#each' do
9
+ it 'yields tuples through row_proc' do
10
+ result = []
11
+
12
+ dataset.each { |tuple| result << tuple }
13
+ expect(result).to match_array([{ name: 'Jane' }, { name: 'Joe' }])
14
+ end
15
+ end
16
+
17
+ describe '#to_a' do
18
+ it 'casts dataset to an array' do
19
+ expect(dataset.to_a).to eql([{ name: 'Jane' }, { name: 'Joe' }])
20
+ end
21
+ end
22
+
23
+ describe '#find_all' do
24
+ it 'yields tuples through row_proc' do
25
+ result = dataset.find_all { |tuple| tuple[:name] == 'Jane' }
26
+
27
+ expect(result).to be_instance_of(klass)
28
+ expect(result).to match_array([{ name: 'Jane' }])
29
+ end
30
+ end
31
+
32
+ describe '#kind_of?' do
33
+ it 'does not forward to data object' do
34
+ expect(dataset).to be_kind_of(klass)
35
+ end
36
+ end
37
+
38
+ describe '#instance_of?' do
39
+ it 'does not forward to data object' do
40
+ expect(dataset).to be_instance_of(klass)
41
+ end
42
+ end
43
+
44
+ describe '#is_a?' do
45
+ it 'does not forward to data object' do
46
+ expect(dataset.is_a?(klass)).to be(true)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ shared_examples_for 'a relation that returns one tuple' do
2
+ describe '#one' do
3
+ it 'returns first tuple' do
4
+ rom.relations.users.delete(name: 'Joe', email: 'joe@doe.org')
5
+ expect(relation.one).to eql(name: 'Jane', email: 'jane@doe.org')
6
+ end
7
+
8
+ it 'raises error when there is more than one tuple' do
9
+ expect { relation.one }.to raise_error(ROM::TupleCountMismatchError)
10
+ end
11
+ end
12
+
13
+ describe '#one!' do
14
+ it 'returns first tuple' do
15
+ rom.relations.users.delete(name: 'Joe', email: 'joe@doe.org')
16
+ expect(relation.one!).to eql(name: 'Jane', email: 'jane@doe.org')
17
+ end
18
+
19
+ it 'raises error when there is no tuples' do
20
+ rom.relations.users.delete(name: 'Jane', email: 'jane@doe.org')
21
+ rom.relations.users.delete(name: 'Joe', email: 'joe@doe.org')
22
+
23
+ expect { relation.one! }.to raise_error(ROM::TupleCountMismatchError)
24
+ end
25
+ end
26
+ end
@@ -1,34 +1,22 @@
1
1
  RSpec.shared_context 'users and tasks' do
2
+ require 'rom/memory'
3
+
2
4
  subject(:rom) { setup.finalize }
3
5
 
4
- let(:setup) { ROM.setup(memory: "memory://localhost") }
6
+ let(:setup) { ROM.setup(:memory) }
5
7
 
6
8
  before do
7
- setup.schema do
8
- base_relation(:users) do
9
- repository :memory
10
-
11
- attribute :name
12
- attribute :email
13
- end
14
-
15
- base_relation(:tasks) do
16
- repository :memory
17
-
18
- attribute :name
19
- attribute :title
20
- attribute :priority
21
- end
22
- end
9
+ repository = setup.default
23
10
 
24
- db = setup.memory
11
+ users = repository.dataset(:users)
12
+ tasks = repository.dataset(:tasks)
25
13
 
26
- db[:users].insert(name: "Joe", email: "joe@doe.org")
27
- db[:users].insert(name: "Jane", email: "jane@doe.org")
14
+ users.insert(name: "Joe", email: "joe@doe.org")
15
+ users.insert(name: "Jane", email: "jane@doe.org")
28
16
 
29
- db[:tasks].insert(name: "Joe", title: "be nice", priority: 1)
30
- db[:tasks].insert(name: "Joe", title: "sleep well", priority: 2)
17
+ tasks.insert(name: "Joe", title: "be nice", priority: 1)
18
+ tasks.insert(name: "Joe", title: "sleep well", priority: 2)
31
19
 
32
- db[:tasks].insert(name: "Jane", title: "be cool", priority: 2)
20
+ tasks.insert(name: "Jane", title: "be cool", priority: 2)
33
21
  end
34
22
  end
data/spec/spec_helper.rb CHANGED
@@ -9,22 +9,31 @@ if RUBY_ENGINE == "rbx"
9
9
  CodeClimate::TestReporter.start
10
10
  end
11
11
 
12
- require 'virtus'
13
12
  require 'rom'
14
- require 'rom/adapter/memory'
13
+
14
+ begin
15
+ require 'byebug'
16
+ rescue LoadError
17
+ end
15
18
 
16
19
  root = Pathname(__FILE__).dirname
17
20
 
18
21
  Dir[root.join('support/*.rb').to_s].each { |f| require f }
19
22
  Dir[root.join('shared/*.rb').to_s].each { |f| require f }
20
23
 
21
- RSpec.configure do |config|
22
- config.before do
23
- @constants = Object.constants
24
+ # Namespace holding all objects created during specs
25
+ module Test
26
+ def self.remove_constants
27
+ constants.each(&method(:remove_const))
24
28
  end
29
+ end
25
30
 
31
+ RSpec.configure do |config|
26
32
  config.after do
27
- added_constants = Object.constants - @constants
28
- added_constants.each { |name| Object.send(:remove_const, name) }
33
+ Test.remove_constants
34
+ end
35
+
36
+ config.around do |example|
37
+ ConstantLeakFinder.find(example)
29
38
  end
30
39
  end
@@ -0,0 +1,14 @@
1
+ # Finds leaking constants created during ROM specs
2
+ module ConstantLeakFinder
3
+ def self.find(example)
4
+ constants = Object.constants
5
+
6
+ example.run
7
+
8
+ added_constants = (Object.constants - constants)
9
+ added = added_constants.map(&Object.method(:const_get))
10
+ if added.any? { |mod| mod.ancestors.map(&:name).grep(/\AROM/).any? }
11
+ raise "Leaking constants: #{added_constants.inspect}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ require 'rom'
2
+ require 'rom/memory'
3
+ require 'rom/lint/test'
4
+
5
+ require 'minitest/autorun'
6
+
7
+ class MemoryRepositoryLintTest < Minitest::Test
8
+ include ROM::Lint::TestRepository
9
+
10
+ def setup
11
+ @repository = ROM::Memory::Repository
12
+ @identifier = :memory
13
+ end
14
+
15
+ def repository_instance
16
+ ROM::Memory::Repository.new
17
+ end
18
+ end
19
+
20
+ class MemoryDatasetLintTest < Minitest::Test
21
+ include ROM::Lint::TestEnumerableDataset
22
+
23
+ def setup
24
+ @data = [{ name: 'Jane', age: 24 }, { name: 'Joe', age: 25 }]
25
+ @dataset = ROM::Memory::Dataset.new(@data)
26
+ end
27
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ROM::CommandRegistry' do
4
+ subject(:env) { setup.finalize }
5
+
6
+ let(:setup) { ROM.setup(:memory) }
7
+ let(:users) { env.command(:users) }
8
+
9
+ before do
10
+ setup.relation(:users)
11
+
12
+ setup.commands(:users) do
13
+ define(:create) do
14
+ validator proc { |input| raise(ROM::CommandError) unless input[:name] }
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#try' do
20
+ it 'returns a success result object on successful execution' do
21
+ result = users.try { users.create.call(name: 'Jane') }
22
+
23
+ expect(result).to match_array([{ name: 'Jane' }])
24
+ end
25
+
26
+ it 'returns a success result on successful curried-command execution' do
27
+ result = users.try { users.create.curry(name: 'Jane') }
28
+
29
+ expect(result).to match_array([{ name: 'Jane' }])
30
+ end
31
+
32
+ it 'returns a failure result object on failed execution' do
33
+ result = users.try { users.create.call({}) }
34
+
35
+ expect(result.value).to be(nil)
36
+ end
37
+
38
+ it 'returns a failure result on unsuccessful curried-command execution' do
39
+ result = users.try { users.create.curry({}) }
40
+
41
+ expect(result.value).to be(nil)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe ROM::Commands::Result do
4
+ describe '#value' do
5
+ subject(:result) { ROM::Commands::Result::Success }
6
+
7
+ it 'bubble up nested values' do
8
+ data = double(to_ary: ['foo'])
9
+ r = result.new(result.new(result.new(data)))
10
+
11
+ expect(r.value).to eq(data)
12
+ end
13
+ end
14
+ end