vorpal 0.0.7 → 0.1.0.rc1
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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/README.md +2 -2
- data/lib/vorpal/config_builder.rb +6 -12
- data/lib/vorpal/configuration.rb +2 -1
- data/lib/vorpal/db_driver.rb +18 -1
- data/lib/vorpal/engine.rb +1 -5
- data/lib/vorpal/exceptions.rb +5 -0
- data/lib/vorpal/identity_map.rb +1 -0
- data/lib/vorpal/version.rb +1 -1
- data/spec/helpers/db_helpers.rb +35 -2
- data/spec/helpers/profile_helpers.rb +26 -0
- data/spec/integration_spec_helper.rb +3 -11
- data/spec/vorpal/acceptance/aggregate_mapper_spec.rb +81 -77
- data/spec/vorpal/performance/performance_spec.rb +18 -23
- data/spec/vorpal/unit/config_builder_spec.rb +0 -25
- data/spec/vorpal/unit/identity_map_spec.rb +13 -2
- data/vorpal.gemspec +1 -1
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17ab11df546bfd8e01294e22adea87aca049266c
|
4
|
+
data.tar.gz: 1e25a0a08f20f54b277c05e2e90b8bc0d825e85e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c6f183685c1b5fbd7a89926556a545a20ef87aa604184d5a90223f234780a5a63dd0d69a7e72c864b7dfe79c1fad917f51cb0a8785496400e28d481d7356a27
|
7
|
+
data.tar.gz: 70c6768539ac3214339352902e752ae3178eb109d6cb1f791f62719ba18473cc65a7d950435dac40e936e854d4c06b6a4e1050a95edbe39aa7c190810ad2eb89
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/README.md
CHANGED
@@ -237,8 +237,8 @@ For example:
|
|
237
237
|
|
238
238
|
1. Start a PostgreSQL server.
|
239
239
|
2. Either:
|
240
|
-
* Create a DB user called `vorpal` with password `pass
|
241
|
-
* Modify `spec/
|
240
|
+
* Create a DB user called `vorpal` with password `pass`. OR:
|
241
|
+
* Modify `spec/helpers/db_helpers.rb`.
|
242
242
|
3. Run `rake` from the terminal.
|
243
243
|
|
244
244
|
## Contributors
|
@@ -87,21 +87,13 @@ module Vorpal
|
|
87
87
|
|
88
88
|
# @private
|
89
89
|
def build_db_class
|
90
|
-
|
91
|
-
parent_module = @domain_class.parent
|
92
|
-
|
93
|
-
return parent_module.const_get(db_class_name) if parent_module.const_defined?(db_class_name)
|
94
|
-
|
95
|
-
db_class = @db_driver.build_db_class(table_name)
|
96
|
-
parent_module.const_set(db_class_name, db_class)
|
97
|
-
|
98
|
-
db_class
|
90
|
+
@db_driver.build_db_class(table_name)
|
99
91
|
end
|
100
92
|
|
101
93
|
private
|
102
94
|
|
103
95
|
def db_class_name
|
104
|
-
@domain_class.name
|
96
|
+
ActiveSupport::Inflector.demodulize(@domain_class.name) + 'DB'
|
105
97
|
end
|
106
98
|
|
107
99
|
def build_class_config
|
@@ -125,11 +117,13 @@ module Vorpal
|
|
125
117
|
end
|
126
118
|
|
127
119
|
def foreign_key(name)
|
128
|
-
ActiveSupport::Inflector.
|
120
|
+
ActiveSupport::Inflector.foreign_key(name.to_s)
|
129
121
|
end
|
130
122
|
|
131
123
|
def child_class(association_name)
|
132
|
-
|
124
|
+
# Module#parent comes from 'active_support/core_ext/module/introspection'
|
125
|
+
parent_module = @domain_class.parent
|
126
|
+
parent_module.const_get(ActiveSupport::Inflector.classify(association_name.to_s))
|
133
127
|
end
|
134
128
|
|
135
129
|
def build_has_ones
|
data/lib/vorpal/configuration.rb
CHANGED
@@ -21,8 +21,9 @@ module Vorpal
|
|
21
21
|
#
|
22
22
|
# @param domain_class [Class] Type of the domain model to be mapped
|
23
23
|
# @param options [Hash] Configure how to map the domain model
|
24
|
-
# @option options [String] :to
|
24
|
+
# @option options [String] :to
|
25
25
|
# Class of the ActiveRecord object that will map this domain class to the DB.
|
26
|
+
# Optional, if one is not specified, it will be generated.
|
26
27
|
# @option options [Object] :serializer (map the {ConfigBuilder#attributes} directly)
|
27
28
|
# Object that will convert the domain objects into a hash.
|
28
29
|
#
|
data/lib/vorpal/db_driver.rb
CHANGED
@@ -60,7 +60,24 @@ module Vorpal
|
|
60
60
|
# @param table_name [String] Name of the DB table the DB class should interface with.
|
61
61
|
# @return [Class] ActiveRecord::Base Class
|
62
62
|
def build_db_class(table_name)
|
63
|
-
db_class = Class.new(ActiveRecord::Base)
|
63
|
+
db_class = Class.new(ActiveRecord::Base) do
|
64
|
+
# This is overridden for two reasons:
|
65
|
+
# 1) For anonymous classes, #name normally returns nil. Class names in Ruby come from the
|
66
|
+
# name of the constant they are assigned to.
|
67
|
+
# 2) Because the default implementation for Class#name for anonymous classes is very, very
|
68
|
+
# slow. https://bugs.ruby-lang.org/issues/11119
|
69
|
+
# Remove this override once #2 has been fixed!
|
70
|
+
def self.name
|
71
|
+
@name ||= "Vorpal Generated ActiveRecord::Base Class for #{table_name}. Object ID: #{object_id}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Overridden because, like #name, the default implementation for anonymous classes is very,
|
75
|
+
# very slow.
|
76
|
+
def self.to_s
|
77
|
+
name
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
64
81
|
db_class.table_name = table_name
|
65
82
|
db_class
|
66
83
|
end
|
data/lib/vorpal/engine.rb
CHANGED
@@ -3,6 +3,7 @@ require 'vorpal/aggregate_utils'
|
|
3
3
|
require 'vorpal/db_loader'
|
4
4
|
require 'vorpal/db_driver'
|
5
5
|
require 'vorpal/aggregate_mapper'
|
6
|
+
require 'vorpal/exceptions'
|
6
7
|
|
7
8
|
module Vorpal
|
8
9
|
class Engine
|
@@ -233,9 +234,4 @@ module Vorpal
|
|
233
234
|
objects.each { |object| object.id = nil }
|
234
235
|
end
|
235
236
|
end
|
236
|
-
|
237
|
-
class InvalidPrimaryKeyValue < StandardError
|
238
|
-
end
|
239
|
-
class InvalidAggregateRoot < StandardError
|
240
|
-
end
|
241
237
|
end
|
data/lib/vorpal/identity_map.rb
CHANGED
@@ -28,6 +28,7 @@ module Vorpal
|
|
28
28
|
def key(key_object)
|
29
29
|
return nil unless key_object
|
30
30
|
raise "Cannot put entity '#{key_object.inspect}' into IdentityMap without an id." if key_object.id.nil?
|
31
|
+
raise "Cannot put entity '#{key_object.inspect}' into IdentityMap without a Class with a name." if key_object.class.name.nil?
|
31
32
|
[key_object.id, key_object.class.name]
|
32
33
|
end
|
33
34
|
end
|
data/lib/vorpal/version.rb
CHANGED
data/spec/helpers/db_helpers.rb
CHANGED
@@ -1,8 +1,41 @@
|
|
1
1
|
module DbHelpers
|
2
|
+
module_function
|
3
|
+
|
4
|
+
CONNECTION_SETTINGS = {
|
5
|
+
adapter: 'postgresql',
|
6
|
+
host: 'localhost',
|
7
|
+
database: 'vorpal_test',
|
8
|
+
min_messages: 'error',
|
9
|
+
# Change the following to reflect your database settings
|
10
|
+
# username: 'vorpal',
|
11
|
+
# password: 'pass',
|
12
|
+
}
|
13
|
+
|
14
|
+
def ensure_database_exists
|
15
|
+
test_database_name = CONNECTION_SETTINGS.fetch(:database)
|
16
|
+
if !db_exists?(test_database_name)
|
17
|
+
db_connection.create_database(test_database_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def db_exists?(db_name)
|
22
|
+
ActiveRecord::Base.establish_connection(CONNECTION_SETTINGS.merge(database: 'template1'))
|
23
|
+
|
24
|
+
return db_connection.exec_query("SELECT 1 from pg_database WHERE datname='#{db_name}';").present?
|
25
|
+
end
|
26
|
+
|
27
|
+
def db_connection
|
28
|
+
ActiveRecord::Base.connection
|
29
|
+
end
|
30
|
+
|
31
|
+
def establish_connection
|
32
|
+
ActiveRecord::Base.establish_connection(CONNECTION_SETTINGS)
|
33
|
+
end
|
34
|
+
|
2
35
|
# when you change a table's columns, set force to true to re-generate the table in the DB
|
3
36
|
def define_table(table_name, columns, force)
|
4
|
-
if !
|
5
|
-
|
37
|
+
if !db_connection.table_exists?(table_name) || force
|
38
|
+
db_connection.create_table(table_name, force: true) do |t|
|
6
39
|
columns.each do |name, type|
|
7
40
|
t.send(type, name)
|
8
41
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ruby-prof'
|
2
|
+
|
3
|
+
# In order to use these helpers do the following:
|
4
|
+
# 1) Add `spec.add_development_dependency "ruby-prof"` to the vorpal.gemspec file.
|
5
|
+
# 2) Do a `bundle update`
|
6
|
+
# 3) Add `require 'helpers/profile_helpers'` to the spec where you wish to profile.
|
7
|
+
module ProfileHelpers
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# Runs a block a given number of times and outputs the profiling results in a 'Call Tree'
|
11
|
+
# format suitable for display by a tool like KCacheGrind.
|
12
|
+
#
|
13
|
+
# - Installing QCacheGrind on OSX: http://nickology.com/2014/04/16/view-xdebug-cachegrind-files-on-mac-os/
|
14
|
+
def output_callgrind(description, times=1, &block)
|
15
|
+
RubyProf.measure_mode = RubyProf::PROCESS_TIME
|
16
|
+
RubyProf.start
|
17
|
+
|
18
|
+
times.times(&block)
|
19
|
+
|
20
|
+
result = RubyProf.stop
|
21
|
+
printer = RubyProf::CallTreePrinter.new(result)
|
22
|
+
File.open("#{description}_#{DateTime.now.strftime("%FT%H:%M:%S%z")}.callgrind", "w") do |file|
|
23
|
+
printer.print(file)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,18 +1,10 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'pg'
|
3
|
-
|
4
|
-
# Change the following to reflect your database settings
|
5
|
-
ActiveRecord::Base.establish_connection(
|
6
|
-
adapter: 'postgresql',
|
7
|
-
host: 'localhost',
|
8
|
-
database: 'vorpal_test',
|
9
|
-
username: 'vorpal',
|
10
|
-
password: 'pass',
|
11
|
-
min_messages: 'error'
|
12
|
-
)
|
13
|
-
|
14
3
|
require 'helpers/db_helpers'
|
15
4
|
|
5
|
+
DbHelpers.ensure_database_exists
|
6
|
+
DbHelpers.establish_connection
|
7
|
+
|
16
8
|
RSpec.configure do |config|
|
17
9
|
config.include DbHelpers
|
18
10
|
|
@@ -65,7 +65,7 @@ describe 'AggregateMapper' do
|
|
65
65
|
tree = Tree.new(name: 'backyard tree')
|
66
66
|
test_mapper.persist(tree)
|
67
67
|
|
68
|
-
tree_db =
|
68
|
+
tree_db = db_class_for(Tree, test_mapper).first
|
69
69
|
expect(tree_db.name).to eq 'backyard tree'
|
70
70
|
end
|
71
71
|
|
@@ -77,7 +77,7 @@ describe 'AggregateMapper' do
|
|
77
77
|
|
78
78
|
expect(tree.id).to_not be nil
|
79
79
|
|
80
|
-
tree_db =
|
80
|
+
tree_db = db_class_for(Tree, test_mapper).first
|
81
81
|
expect(tree_db.id).to eq tree.id
|
82
82
|
end
|
83
83
|
|
@@ -98,7 +98,7 @@ describe 'AggregateMapper' do
|
|
98
98
|
db_driver = Vorpal::DbDriver.new
|
99
99
|
test_mapper = configure(db_driver: db_driver)
|
100
100
|
|
101
|
-
tree_db =
|
101
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
102
102
|
|
103
103
|
expect(db_driver).to receive(:update).and_raise('not so good')
|
104
104
|
|
@@ -124,7 +124,7 @@ describe 'AggregateMapper' do
|
|
124
124
|
tree.name = 'big tree'
|
125
125
|
test_mapper.persist(tree)
|
126
126
|
|
127
|
-
tree_db =
|
127
|
+
tree_db = db_class_for(Tree, test_mapper).first
|
128
128
|
expect(tree_db.name).to eq 'big tree'
|
129
129
|
end
|
130
130
|
|
@@ -151,40 +151,40 @@ describe 'AggregateMapper' do
|
|
151
151
|
tree.name = 'change it'
|
152
152
|
test_mapper.persist(tree)
|
153
153
|
|
154
|
-
expect(
|
154
|
+
expect(db_class_for(Tree, test_mapper).count).to eq 1
|
155
155
|
end
|
156
156
|
|
157
157
|
it 'removes orphans' do
|
158
158
|
test_mapper = configure
|
159
159
|
|
160
|
-
tree_db =
|
161
|
-
|
160
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
161
|
+
db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
|
162
162
|
|
163
163
|
tree = Tree.new(id: tree_db.id, branches: [])
|
164
164
|
|
165
165
|
test_mapper.persist(tree)
|
166
166
|
|
167
|
-
expect(
|
167
|
+
expect(db_class_for(Branch, test_mapper).count).to eq 0
|
168
168
|
end
|
169
169
|
|
170
170
|
it 'does not remove orphans from unowned associations' do
|
171
171
|
test_mapper = configure_unowned
|
172
172
|
|
173
|
-
tree_db =
|
174
|
-
|
173
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
174
|
+
db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
|
175
175
|
|
176
176
|
tree = Tree.new(id: tree_db.id, branches: [])
|
177
177
|
|
178
178
|
test_mapper.persist(tree)
|
179
179
|
|
180
|
-
expect(
|
180
|
+
expect(db_class_for(Branch, test_mapper).count).to eq 1
|
181
181
|
end
|
182
182
|
end
|
183
183
|
|
184
184
|
it 'copies attributes to domain' do
|
185
185
|
test_mapper = configure
|
186
186
|
|
187
|
-
tree_db =
|
187
|
+
tree_db = db_class_for(Tree, test_mapper).create! name: 'tree name'
|
188
188
|
tree = test_mapper.load_one(tree_db)
|
189
189
|
|
190
190
|
expect(tree.id).to eq tree_db.id
|
@@ -194,7 +194,7 @@ describe 'AggregateMapper' do
|
|
194
194
|
it 'hydrates ActiveRecord::Base associations' do
|
195
195
|
test_mapper = configure
|
196
196
|
|
197
|
-
tree_db =
|
197
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
198
198
|
Fissure.create! length: 21, tree_id: tree_db.id
|
199
199
|
|
200
200
|
tree = test_mapper.load_one(tree_db)
|
@@ -212,14 +212,14 @@ describe 'AggregateMapper' do
|
|
212
212
|
|
213
213
|
test_mapper.persist(tree)
|
214
214
|
|
215
|
-
expect(
|
215
|
+
expect(db_class_for(Tree, test_mapper).count).to eq 1
|
216
216
|
end
|
217
217
|
|
218
218
|
it 'hydrates' do
|
219
219
|
test_mapper = configure_with_cycle
|
220
220
|
|
221
|
-
tree_db =
|
222
|
-
|
221
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
222
|
+
db_class_for(Branch, test_mapper).create!(length: 50, tree_id: tree_db.id)
|
223
223
|
|
224
224
|
tree = test_mapper.load_one(tree_db)
|
225
225
|
|
@@ -239,15 +239,15 @@ describe 'AggregateMapper' do
|
|
239
239
|
|
240
240
|
test_mapper.persist(tree)
|
241
241
|
|
242
|
-
expect(
|
242
|
+
expect(db_class_for(Branch, test_mapper).count).to eq 2
|
243
243
|
end
|
244
244
|
|
245
245
|
it 'hydrates' do
|
246
246
|
test_mapper = configure_recursive
|
247
247
|
|
248
|
-
tree_db =
|
249
|
-
long_branch =
|
250
|
-
|
248
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
249
|
+
long_branch = db_class_for(Branch, test_mapper).create!(length: 100, tree_id: tree_db.id)
|
250
|
+
db_class_for(Branch, test_mapper).create!(length: 50, branch_id: long_branch.id)
|
251
251
|
|
252
252
|
tree = test_mapper.load_one(tree_db)
|
253
253
|
|
@@ -263,7 +263,7 @@ describe 'AggregateMapper' do
|
|
263
263
|
|
264
264
|
test_mapper.persist(tree)
|
265
265
|
|
266
|
-
trunk_db =
|
266
|
+
trunk_db = db_class_for(Trunk, test_mapper).first
|
267
267
|
expect(trunk_db.length).to eq 12
|
268
268
|
end
|
269
269
|
|
@@ -274,7 +274,7 @@ describe 'AggregateMapper' do
|
|
274
274
|
|
275
275
|
test_mapper.persist(tree)
|
276
276
|
|
277
|
-
tree_db =
|
277
|
+
tree_db = db_class_for(Tree, test_mapper).first
|
278
278
|
expect(tree_db.trunk_id).to eq trunk.id
|
279
279
|
end
|
280
280
|
|
@@ -287,7 +287,7 @@ describe 'AggregateMapper' do
|
|
287
287
|
|
288
288
|
trunk.length = 21
|
289
289
|
|
290
|
-
expect{ test_mapper.persist(tree) }.to_not change{
|
290
|
+
expect{ test_mapper.persist(tree) }.to_not change{ db_class_for(Trunk, test_mapper).count }
|
291
291
|
end
|
292
292
|
|
293
293
|
it 'only saves entities that are owned' do
|
@@ -298,13 +298,13 @@ describe 'AggregateMapper' do
|
|
298
298
|
|
299
299
|
test_mapper.persist(tree)
|
300
300
|
|
301
|
-
expect(
|
301
|
+
expect(db_class_for(Trunk, test_mapper).count).to eq 0
|
302
302
|
end
|
303
303
|
|
304
304
|
it 'hydrates' do
|
305
305
|
test_mapper = configure
|
306
|
-
trunk_db =
|
307
|
-
tree_db =
|
306
|
+
trunk_db = db_class_for(Trunk, test_mapper).create!(length: 21)
|
307
|
+
tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
|
308
308
|
|
309
309
|
new_tree = test_mapper.load_one(tree_db)
|
310
310
|
expect(new_tree.trunk.length).to eq 21
|
@@ -320,7 +320,7 @@ describe 'AggregateMapper' do
|
|
320
320
|
|
321
321
|
test_mapper.persist(tree)
|
322
322
|
|
323
|
-
branches =
|
323
|
+
branches = db_class_for(Branch, test_mapper).all
|
324
324
|
expect(branches.size).to eq 2
|
325
325
|
expect(branches.first.length).to eq 100
|
326
326
|
expect(branches.second.length).to eq 3
|
@@ -333,7 +333,7 @@ describe 'AggregateMapper' do
|
|
333
333
|
|
334
334
|
test_mapper.persist(tree)
|
335
335
|
|
336
|
-
branches =
|
336
|
+
branches = db_class_for(Branch, test_mapper).all
|
337
337
|
expect(branches.first.tree_id).to eq tree.id
|
338
338
|
end
|
339
339
|
|
@@ -349,7 +349,7 @@ describe 'AggregateMapper' do
|
|
349
349
|
|
350
350
|
test_mapper.persist(tree)
|
351
351
|
|
352
|
-
branches =
|
352
|
+
branches = db_class_for(Branch, test_mapper).all
|
353
353
|
expect(branches.first.length).to eq 120
|
354
354
|
end
|
355
355
|
|
@@ -362,14 +362,14 @@ describe 'AggregateMapper' do
|
|
362
362
|
|
363
363
|
test_mapper.persist(tree)
|
364
364
|
|
365
|
-
expect(
|
365
|
+
expect(db_class_for(Branch, test_mapper).count).to eq 0
|
366
366
|
end
|
367
367
|
|
368
368
|
it 'hydrates' do
|
369
369
|
test_mapper = configure
|
370
370
|
|
371
|
-
tree_db =
|
372
|
-
|
371
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
372
|
+
db_class_for(Branch, test_mapper).create!(length: 50, tree_id: tree_db.id)
|
373
373
|
|
374
374
|
tree = test_mapper.load_one(tree_db)
|
375
375
|
|
@@ -385,7 +385,7 @@ describe 'AggregateMapper' do
|
|
385
385
|
|
386
386
|
test_mapper.persist(trunk)
|
387
387
|
|
388
|
-
expect(
|
388
|
+
expect(db_class_for(Tree, test_mapper).first.name).to eq 'big tree'
|
389
389
|
end
|
390
390
|
|
391
391
|
it 'saves foreign keys' do
|
@@ -395,7 +395,7 @@ describe 'AggregateMapper' do
|
|
395
395
|
|
396
396
|
test_mapper.persist(trunk)
|
397
397
|
|
398
|
-
expect(
|
398
|
+
expect(db_class_for(Tree, test_mapper).first.trunk_id).to eq trunk.id
|
399
399
|
end
|
400
400
|
|
401
401
|
it 'only saves entities that are owned' do
|
@@ -405,14 +405,14 @@ describe 'AggregateMapper' do
|
|
405
405
|
|
406
406
|
test_mapper.persist(trunk)
|
407
407
|
|
408
|
-
expect(
|
408
|
+
expect(db_class_for(Tree, test_mapper).count).to eq 0
|
409
409
|
end
|
410
410
|
|
411
411
|
it 'hydrates' do
|
412
412
|
test_mapper = configure_has_one
|
413
413
|
|
414
|
-
trunk_db =
|
415
|
-
|
414
|
+
trunk_db = db_class_for(Trunk, test_mapper).create!
|
415
|
+
db_class_for(Tree, test_mapper).create!(name: 'big tree', trunk_id: trunk_db.id)
|
416
416
|
|
417
417
|
trunk = test_mapper.load_one(trunk_db)
|
418
418
|
|
@@ -434,17 +434,17 @@ describe 'AggregateMapper' do
|
|
434
434
|
|
435
435
|
test_mapper.persist(tree)
|
436
436
|
|
437
|
-
expect(
|
438
|
-
expect(
|
437
|
+
expect(db_class_for(Bug, test_mapper).find(trunk_bug.id).lives_on_type).to eq Trunk.name
|
438
|
+
expect(db_class_for(Bug, test_mapper).find(branch_bug.id).lives_on_type).to eq Branch.name
|
439
439
|
end
|
440
440
|
|
441
441
|
it 'restores with has_manys' do
|
442
442
|
test_mapper = configure_polymorphic_has_many
|
443
443
|
|
444
|
-
trunk_db =
|
445
|
-
tree_db =
|
446
|
-
|
447
|
-
|
444
|
+
trunk_db = db_class_for(Trunk, test_mapper).create!
|
445
|
+
tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
|
446
|
+
db_class_for(Bug, test_mapper).create!(name: 'trunk bug', lives_on_id: trunk_db.id, lives_on_type: Trunk.name)
|
447
|
+
db_class_for(Bug, test_mapper).create!(name: 'not a trunk bug!', lives_on_id: trunk_db.id, lives_on_type: 'some other table')
|
448
448
|
|
449
449
|
tree = test_mapper.load_one(tree_db)
|
450
450
|
|
@@ -459,8 +459,8 @@ describe 'AggregateMapper' do
|
|
459
459
|
|
460
460
|
test_mapper.persist([trunk_bug, branch_bug])
|
461
461
|
|
462
|
-
expect(
|
463
|
-
expect(
|
462
|
+
expect(db_class_for(Bug, test_mapper).find(trunk_bug.id).lives_on_type).to eq Trunk.name
|
463
|
+
expect(db_class_for(Bug, test_mapper).find(branch_bug.id).lives_on_type).to eq Branch.name
|
464
464
|
end
|
465
465
|
|
466
466
|
it 'saves associations to unowned entities via belongs_to' do
|
@@ -471,7 +471,7 @@ describe 'AggregateMapper' do
|
|
471
471
|
|
472
472
|
test_mapper.persist(trunk_bug)
|
473
473
|
|
474
|
-
expect(
|
474
|
+
expect(db_class_for(Bug, test_mapper).find(trunk_bug.id).lives_on_type).to eq Trunk.name
|
475
475
|
end
|
476
476
|
|
477
477
|
it 'restores with belongs_tos' do
|
@@ -479,14 +479,14 @@ describe 'AggregateMapper' do
|
|
479
479
|
|
480
480
|
# makes sure that we are using the fk_type to discriminate against
|
481
481
|
# two entities with the same primary key value
|
482
|
-
trunk_db =
|
482
|
+
trunk_db = db_class_for(Trunk, test_mapper).new(length: 99)
|
483
483
|
trunk_db.id = 99
|
484
484
|
trunk_db.save!
|
485
|
-
trunk_bug_db =
|
486
|
-
branch_db =
|
485
|
+
trunk_bug_db = db_class_for(Bug, test_mapper).create!(lives_on_id: trunk_db.id, lives_on_type: Trunk.name)
|
486
|
+
branch_db = db_class_for(Branch, test_mapper).new(length: 5)
|
487
487
|
branch_db.id = 99
|
488
488
|
branch_db.save!
|
489
|
-
branch_bug_db =
|
489
|
+
branch_bug_db = db_class_for(Bug, test_mapper).create!(lives_on_id: branch_db.id, lives_on_type: Branch.name)
|
490
490
|
|
491
491
|
trunk_bug, branch_bug = test_mapper.load_many([trunk_bug_db, branch_bug_db])
|
492
492
|
|
@@ -498,7 +498,7 @@ describe 'AggregateMapper' do
|
|
498
498
|
test_mapper = configure_ar_polymorphic_belongs_to
|
499
499
|
|
500
500
|
swamp = Swamp.create!
|
501
|
-
tree_db =
|
501
|
+
tree_db = db_class_for(Tree, test_mapper).create!(environment_id: swamp.id, environment_type: Swamp.name)
|
502
502
|
|
503
503
|
tree = test_mapper.load_one(tree_db)
|
504
504
|
|
@@ -510,8 +510,8 @@ describe 'AggregateMapper' do
|
|
510
510
|
it 'loads many' do
|
511
511
|
test_mapper = configure
|
512
512
|
|
513
|
-
|
514
|
-
tree_db =
|
513
|
+
db_class_for(Tree, test_mapper).create!
|
514
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
515
515
|
|
516
516
|
trees = test_mapper.query.where(id: tree_db.id).load_many
|
517
517
|
|
@@ -521,8 +521,8 @@ describe 'AggregateMapper' do
|
|
521
521
|
it 'loads one' do
|
522
522
|
test_mapper = configure
|
523
523
|
|
524
|
-
|
525
|
-
tree_db =
|
524
|
+
db_class_for(Tree, test_mapper).create!
|
525
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
526
526
|
|
527
527
|
trees = test_mapper.query.where(id: tree_db.id).load_one
|
528
528
|
|
@@ -534,8 +534,8 @@ describe 'AggregateMapper' do
|
|
534
534
|
it 'maps given db objects' do
|
535
535
|
test_mapper = configure
|
536
536
|
|
537
|
-
|
538
|
-
tree_db =
|
537
|
+
db_class_for(Tree, test_mapper).create!
|
538
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
539
539
|
|
540
540
|
trees = test_mapper.load_many([tree_db])
|
541
541
|
|
@@ -545,9 +545,9 @@ describe 'AggregateMapper' do
|
|
545
545
|
it 'only returns roots' do
|
546
546
|
test_mapper = configure
|
547
547
|
|
548
|
-
|
549
|
-
tree_db =
|
550
|
-
|
548
|
+
db_class_for(Tree, test_mapper).create!
|
549
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
550
|
+
db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
|
551
551
|
|
552
552
|
trees = test_mapper.load_many([tree_db])
|
553
553
|
|
@@ -559,39 +559,39 @@ describe 'AggregateMapper' do
|
|
559
559
|
it 'removes the entity from the database' do
|
560
560
|
test_mapper = configure
|
561
561
|
|
562
|
-
tree_db =
|
562
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
563
563
|
|
564
564
|
test_mapper.destroy([Tree.new(id: tree_db.id)])
|
565
565
|
|
566
|
-
expect(
|
566
|
+
expect(db_class_for(Tree, test_mapper).count).to eq 0
|
567
567
|
end
|
568
568
|
|
569
569
|
it 'removes has many children from the database' do
|
570
570
|
test_mapper = configure
|
571
571
|
|
572
|
-
tree_db =
|
573
|
-
|
572
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
573
|
+
db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
|
574
574
|
|
575
575
|
test_mapper.destroy(Tree.new(id: tree_db.id))
|
576
576
|
|
577
|
-
expect(
|
577
|
+
expect(db_class_for(Branch, test_mapper).count).to eq 0
|
578
578
|
end
|
579
579
|
|
580
580
|
it 'removes belongs to children from the database' do
|
581
581
|
test_mapper = configure
|
582
582
|
|
583
|
-
trunk_db =
|
584
|
-
tree_db =
|
583
|
+
trunk_db = db_class_for(Trunk, test_mapper).create!
|
584
|
+
tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
|
585
585
|
|
586
586
|
test_mapper.destroy(Tree.new(id: tree_db.id))
|
587
587
|
|
588
|
-
expect(
|
588
|
+
expect(db_class_for(Trunk, test_mapper).count).to eq 0
|
589
589
|
end
|
590
590
|
|
591
591
|
it 'removes AR children from the database' do
|
592
592
|
test_mapper = configure
|
593
593
|
|
594
|
-
tree_db =
|
594
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
595
595
|
Fissure.create!(tree_id: tree_db.id)
|
596
596
|
|
597
597
|
test_mapper.destroy(Tree.new(id: tree_db.id))
|
@@ -602,23 +602,23 @@ describe 'AggregateMapper' do
|
|
602
602
|
it 'leaves unowned belongs to children in the database' do
|
603
603
|
test_mapper = configure_unowned
|
604
604
|
|
605
|
-
trunk_db =
|
606
|
-
tree_db =
|
605
|
+
trunk_db = db_class_for(Trunk, test_mapper).create!
|
606
|
+
tree_db = db_class_for(Tree, test_mapper).create!(trunk_id: trunk_db.id)
|
607
607
|
|
608
608
|
test_mapper.destroy(Tree.new(id: tree_db.id))
|
609
609
|
|
610
|
-
expect(
|
610
|
+
expect(db_class_for(Trunk, test_mapper).count).to eq 1
|
611
611
|
end
|
612
612
|
|
613
613
|
it 'leaves unowned has many children in the database' do
|
614
614
|
test_mapper = configure_unowned
|
615
615
|
|
616
|
-
tree_db =
|
617
|
-
|
616
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
617
|
+
db_class_for(Branch, test_mapper).create!(tree_id: tree_db.id)
|
618
618
|
|
619
619
|
test_mapper.destroy(Tree.new(id: tree_db.id))
|
620
620
|
|
621
|
-
expect(
|
621
|
+
expect(db_class_for(Branch, test_mapper).count).to eq 1
|
622
622
|
end
|
623
623
|
end
|
624
624
|
|
@@ -626,11 +626,11 @@ describe 'AggregateMapper' do
|
|
626
626
|
it 'removes the entity from the database' do
|
627
627
|
test_mapper = configure
|
628
628
|
|
629
|
-
tree_db =
|
629
|
+
tree_db = db_class_for(Tree, test_mapper).create!
|
630
630
|
|
631
631
|
test_mapper.destroy_by_id([tree_db.id])
|
632
632
|
|
633
|
-
expect(
|
633
|
+
expect(db_class_for(Tree, test_mapper).count).to eq 0
|
634
634
|
end
|
635
635
|
end
|
636
636
|
|
@@ -733,6 +733,10 @@ describe 'AggregateMapper' do
|
|
733
733
|
|
734
734
|
private
|
735
735
|
|
736
|
+
def db_class_for(clazz, mapper)
|
737
|
+
mapper.engine.mapper_for(clazz).db_class
|
738
|
+
end
|
739
|
+
|
736
740
|
def configure_polymorphic_has_many
|
737
741
|
engine = Vorpal.define do
|
738
742
|
map Tree do
|
@@ -3,6 +3,7 @@ require 'vorpal'
|
|
3
3
|
require 'virtus'
|
4
4
|
require 'activerecord-import/base'
|
5
5
|
|
6
|
+
module Performance
|
6
7
|
describe 'performance' do
|
7
8
|
|
8
9
|
class Bug
|
@@ -62,19 +63,12 @@ describe 'performance' do
|
|
62
63
|
|
63
64
|
before(:all) do
|
64
65
|
define_table('branches_perf', {length: :decimal, tree_id: :integer, branch_id: :integer}, false)
|
65
|
-
BranchDB = defineAr('branches_perf')
|
66
|
-
|
67
66
|
define_table('bugs_perf', {name: :text, lives_on_id: :integer, lives_on_type: :string}, false)
|
68
|
-
BugDB = defineAr('bugs_perf')
|
69
|
-
|
70
67
|
define_table('trees_perf', {name: :text, trunk_id: :integer}, false)
|
71
|
-
TreeDB = defineAr('trees_perf')
|
72
|
-
|
73
68
|
define_table('trunks_perf', {length: :decimal}, false)
|
74
|
-
TrunkDB = defineAr('trunks_perf')
|
75
69
|
end
|
76
70
|
|
77
|
-
let(:
|
71
|
+
let(:tree_mapper) { build_mapper }
|
78
72
|
|
79
73
|
# Vorpal 0.0.5:
|
80
74
|
# user system total real
|
@@ -92,10 +86,10 @@ describe 'performance' do
|
|
92
86
|
it 'benchmarks all operations' do
|
93
87
|
trees = build_trees(1000)
|
94
88
|
Benchmark.bm(7) do |x|
|
95
|
-
x.report('create') {
|
96
|
-
x.report('update') {
|
97
|
-
x.report('load') {
|
98
|
-
x.report('destroy') {
|
89
|
+
x.report('create') { tree_mapper.persist(trees) }
|
90
|
+
x.report('update') { tree_mapper.persist(trees) }
|
91
|
+
x.report('load') { tree_mapper.query.where(id: trees.map(&:id)).load_many }
|
92
|
+
x.report('destroy') { tree_mapper.destroy(trees) }
|
99
93
|
end
|
100
94
|
end
|
101
95
|
|
@@ -104,39 +98,39 @@ describe 'performance' do
|
|
104
98
|
|
105
99
|
puts 'starting persistence benchmark'
|
106
100
|
puts Benchmark.measure {
|
107
|
-
|
101
|
+
tree_mapper.persist(trees)
|
108
102
|
}
|
109
103
|
end
|
110
104
|
|
111
105
|
it 'updates aggregates quickly' do
|
112
106
|
trees = build_trees(1000)
|
113
107
|
|
114
|
-
|
108
|
+
tree_mapper.persist(trees)
|
115
109
|
|
116
110
|
puts 'starting update benchmark'
|
117
111
|
puts Benchmark.measure {
|
118
|
-
|
112
|
+
tree_mapper.persist(trees)
|
119
113
|
}
|
120
114
|
end
|
121
115
|
|
122
116
|
it 'loads aggregates quickly' do
|
123
117
|
trees = build_trees(1000)
|
124
|
-
|
118
|
+
tree_mapper.persist(trees)
|
125
119
|
ids = trees.map(&:id)
|
126
120
|
|
127
121
|
puts 'starting loading benchmark'
|
128
122
|
puts Benchmark.measure {
|
129
|
-
|
123
|
+
tree_mapper.query.where(id: ids).load_many
|
130
124
|
}
|
131
125
|
end
|
132
126
|
|
133
127
|
it 'destroys aggregates quickly' do
|
134
128
|
trees = build_trees(1000)
|
135
|
-
|
129
|
+
tree_mapper.persist(trees)
|
136
130
|
|
137
131
|
puts 'starting destruction benchmark'
|
138
132
|
puts Benchmark.measure {
|
139
|
-
|
133
|
+
tree_mapper.destroy(trees)
|
140
134
|
}
|
141
135
|
end
|
142
136
|
|
@@ -164,26 +158,26 @@ describe 'performance' do
|
|
164
158
|
|
165
159
|
def build_mapper
|
166
160
|
engine = Vorpal.define do
|
167
|
-
map Tree do
|
161
|
+
map Tree, table_name: "trees_perf" do
|
168
162
|
attributes :name
|
169
163
|
belongs_to :trunk
|
170
164
|
has_many :branches
|
171
165
|
end
|
172
166
|
|
173
|
-
map Trunk do
|
167
|
+
map Trunk, table_name: "trunks_perf" do
|
174
168
|
attributes :length
|
175
169
|
has_one :tree
|
176
170
|
has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
|
177
171
|
end
|
178
172
|
|
179
|
-
map Branch do
|
173
|
+
map Branch, table_name: "branches_perf" do
|
180
174
|
attributes :length
|
181
175
|
belongs_to :tree
|
182
176
|
has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
|
183
177
|
has_many :branches
|
184
178
|
end
|
185
179
|
|
186
|
-
map Bug do
|
180
|
+
map Bug, table_name: "bugs_perf" do
|
187
181
|
attributes :name
|
188
182
|
belongs_to :lives_on, fk: :lives_on_id, fk_type: :lives_on_type, child_classes: [Trunk, Branch]
|
189
183
|
end
|
@@ -191,3 +185,4 @@ describe 'performance' do
|
|
191
185
|
engine.mapper_for(Tree)
|
192
186
|
end
|
193
187
|
end
|
188
|
+
end
|
@@ -29,31 +29,6 @@ describe Vorpal::ConfigBuilder do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
describe 'build_db_class' do
|
33
|
-
it 'sets the class name' do
|
34
|
-
driver = instance_double(Vorpal::DbDriver)
|
35
|
-
new_class = Class.new
|
36
|
-
expect(driver).to receive(:build_db_class).and_return(new_class)
|
37
|
-
|
38
|
-
builder = Vorpal::ConfigBuilder.new(A::B::C::Test, {}, driver)
|
39
|
-
|
40
|
-
builder.build_db_class
|
41
|
-
|
42
|
-
expect(A::B::C::TestDB).to eq(new_class)
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'does not redefine constants' do
|
46
|
-
stub_const('A::B::C::TestDB', 1)
|
47
|
-
|
48
|
-
builder = Vorpal::ConfigBuilder.new(A::B::C::Test, {}, nil)
|
49
|
-
|
50
|
-
db_class = builder.build_db_class
|
51
|
-
|
52
|
-
expect(A::B::C::TestDB).to eq(1)
|
53
|
-
expect(db_class).to eq(1)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
32
|
private
|
58
33
|
|
59
34
|
module A
|
@@ -5,7 +5,7 @@ require 'vorpal/identity_map'
|
|
5
5
|
describe Vorpal::IdentityMap do
|
6
6
|
let(:map) { Vorpal::IdentityMap.new }
|
7
7
|
|
8
|
-
it
|
8
|
+
it "does not rely on the key object's implementation of hash and eql?" do
|
9
9
|
funny1 = build_funny_entity(1)
|
10
10
|
map.set(funny1, 'funny 1')
|
11
11
|
|
@@ -15,12 +15,23 @@ describe Vorpal::IdentityMap do
|
|
15
15
|
expect(map.get(funny1)).to eq('funny 1')
|
16
16
|
end
|
17
17
|
|
18
|
-
it
|
18
|
+
it "raises an exception when the key object does not have an id set" do
|
19
19
|
entity = build_entity(nil)
|
20
20
|
|
21
21
|
expect { map.set(entity, 'something') }.to raise_error(/Cannot put entity/)
|
22
22
|
end
|
23
23
|
|
24
|
+
it 'raises an exception when the key object extends a class with no name (such as anonymous classes)' do
|
25
|
+
anonymous_class = Class.new do
|
26
|
+
attr_accessor :id
|
27
|
+
end
|
28
|
+
|
29
|
+
entity = anonymous_class.new
|
30
|
+
entity.id = 1
|
31
|
+
|
32
|
+
expect { map.set(entity, 'something') }.to raise_error(/Cannot put entity/)
|
33
|
+
end
|
34
|
+
|
24
35
|
def build_entity(id)
|
25
36
|
entity = Entity.new
|
26
37
|
entity.id = id
|
data/vorpal.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
-
spec.add_development_dependency "activerecord", "~> 4.0
|
27
|
+
spec.add_development_dependency "activerecord", "~> 4.0"
|
28
28
|
spec.add_development_dependency "pg", "~> 0.17.0"
|
29
29
|
spec.add_development_dependency "virtus", "~> 1.0"
|
30
30
|
spec.add_development_dependency "activerecord-import", "~> 0.10.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vorpal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.1.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Kirby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: simple_serializer
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 4.0
|
89
|
+
version: '4.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 4.0
|
96
|
+
version: '4.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: pg
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -145,6 +145,7 @@ extensions: []
|
|
145
145
|
extra_rdoc_files: []
|
146
146
|
files:
|
147
147
|
- ".gitignore"
|
148
|
+
- ".rspec"
|
148
149
|
- ".ruby-version"
|
149
150
|
- ".yardopts"
|
150
151
|
- CHANGELOG.md
|
@@ -162,12 +163,14 @@ files:
|
|
162
163
|
- lib/vorpal/db_driver.rb
|
163
164
|
- lib/vorpal/db_loader.rb
|
164
165
|
- lib/vorpal/engine.rb
|
166
|
+
- lib/vorpal/exceptions.rb
|
165
167
|
- lib/vorpal/identity_map.rb
|
166
168
|
- lib/vorpal/loaded_objects.rb
|
167
169
|
- lib/vorpal/util/array_hash.rb
|
168
170
|
- lib/vorpal/util/hash_initialization.rb
|
169
171
|
- lib/vorpal/version.rb
|
170
172
|
- spec/helpers/db_helpers.rb
|
173
|
+
- spec/helpers/profile_helpers.rb
|
171
174
|
- spec/integration_spec_helper.rb
|
172
175
|
- spec/unit_spec_helper.rb
|
173
176
|
- spec/vorpal/acceptance/aggregate_mapper_spec.rb
|
@@ -193,9 +196,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
193
196
|
version: '0'
|
194
197
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
198
|
requirements:
|
196
|
-
- - "
|
199
|
+
- - ">"
|
197
200
|
- !ruby/object:Gem::Version
|
198
|
-
version:
|
201
|
+
version: 1.3.1
|
199
202
|
requirements: []
|
200
203
|
rubyforge_project:
|
201
204
|
rubygems_version: 2.4.8
|
@@ -204,6 +207,7 @@ specification_version: 4
|
|
204
207
|
summary: Separate your domain model from your persistence mechanism.
|
205
208
|
test_files:
|
206
209
|
- spec/helpers/db_helpers.rb
|
210
|
+
- spec/helpers/profile_helpers.rb
|
207
211
|
- spec/integration_spec_helper.rb
|
208
212
|
- spec/unit_spec_helper.rb
|
209
213
|
- spec/vorpal/acceptance/aggregate_mapper_spec.rb
|