vorpal 0.0.7 → 0.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|