vorpal 1.0.0 → 1.2.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.
- checksums.yaml +4 -4
- data/README.md +85 -47
- data/lib/vorpal/aggregate_mapper.rb +11 -0
- data/lib/vorpal/config/class_config.rb +71 -0
- data/lib/vorpal/configs.rb +9 -66
- data/lib/vorpal/driver/postgresql.rb +40 -4
- data/lib/vorpal/dsl/config_builder.rb +12 -50
- data/lib/vorpal/dsl/configuration.rb +131 -42
- data/lib/vorpal/dsl/defaults_generator.rb +14 -4
- data/lib/vorpal/engine.rb +16 -3
- data/lib/vorpal/identity_map.rb +15 -14
- data/lib/vorpal/version.rb +1 -1
- data/vorpal.gemspec +11 -10
- metadata +43 -63
- data/.editorconfig +0 -13
- data/.gitignore +0 -16
- data/.rspec +0 -1
- data/.yardopts +0 -1
- data/Gemfile +0 -4
- data/Rakefile +0 -10
- data/spec/helpers/db_helpers.rb +0 -51
- data/spec/helpers/profile_helpers.rb +0 -26
- data/spec/integration_spec_helper.rb +0 -36
- data/spec/unit_spec_helper.rb +0 -0
- data/spec/vorpal/acceptance/aggregate_mapper_spec.rb +0 -911
- data/spec/vorpal/integration/driver/postgresql_spec.rb +0 -42
- data/spec/vorpal/performance/performance_spec.rb +0 -195
- data/spec/vorpal/unit/configs_spec.rb +0 -117
- data/spec/vorpal/unit/db_loader_spec.rb +0 -103
- data/spec/vorpal/unit/dsl/config_builder_spec.rb +0 -18
- data/spec/vorpal/unit/dsl/defaults_generator_spec.rb +0 -75
- data/spec/vorpal/unit/identity_map_spec.rb +0 -62
- data/spec/vorpal/unit/loaded_objects_spec.rb +0 -22
- data/spec/vorpal/unit/util/string_utils_spec.rb +0 -25
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'integration_spec_helper'
|
2
|
-
require 'vorpal'
|
3
|
-
|
4
|
-
describe Vorpal::Driver::Postgresql do
|
5
|
-
describe '#build_db_class' do
|
6
|
-
let(:db_class) { subject.build_db_class(PostgresDriverSpec::Foo, 'example') }
|
7
|
-
|
8
|
-
it 'generates a valid class name so that rails auto-reloading works' do
|
9
|
-
expect { Vorpal.const_defined?(db_class.name) }.to_not raise_error
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'does not let the user access the generated class' do
|
13
|
-
expect { Vorpal.const_get(db_class.name) }.to raise_error(NameError)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'isolates two POROs that map to the same db table' do
|
17
|
-
db_class1 = build_db_class(PostgresDriverSpec::Foo)
|
18
|
-
db_class2 = build_db_class(PostgresDriverSpec::Bar)
|
19
|
-
|
20
|
-
expect(db_class1).to_not eq(db_class2)
|
21
|
-
expect(db_class1.name).to_not eq(db_class2.name)
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'uses the model class name to make the generated AR::Base class name unique' do
|
25
|
-
db_class = build_db_class(PostgresDriverSpec::Foo)
|
26
|
-
|
27
|
-
expect(db_class.name).to match("PostgresDriverSpec__Foo")
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
module PostgresDriverSpec
|
34
|
-
class Foo; end
|
35
|
-
class Bar; end
|
36
|
-
end
|
37
|
-
|
38
|
-
def build_db_class(clazz)
|
39
|
-
db_driver = Vorpal::Driver::Postgresql.new
|
40
|
-
db_driver.build_db_class(clazz, 'example')
|
41
|
-
end
|
42
|
-
end
|
@@ -1,195 +0,0 @@
|
|
1
|
-
require 'integration_spec_helper'
|
2
|
-
require 'vorpal'
|
3
|
-
require 'virtus'
|
4
|
-
require 'activerecord-import/base'
|
5
|
-
|
6
|
-
module Performance
|
7
|
-
describe 'performance' do
|
8
|
-
|
9
|
-
class Bug
|
10
|
-
include Virtus.model
|
11
|
-
|
12
|
-
attribute :id, Integer
|
13
|
-
attribute :name, String
|
14
|
-
attribute :lives_on, Object
|
15
|
-
end
|
16
|
-
|
17
|
-
class Tree; end
|
18
|
-
|
19
|
-
class Trunk
|
20
|
-
include Virtus.model
|
21
|
-
|
22
|
-
attribute :id, Integer
|
23
|
-
attribute :length, Decimal
|
24
|
-
attribute :bugs, Array[Bug]
|
25
|
-
attribute :tree, Tree
|
26
|
-
end
|
27
|
-
|
28
|
-
class Branch
|
29
|
-
include Virtus.model
|
30
|
-
|
31
|
-
attribute :id, Integer
|
32
|
-
attribute :length, Decimal
|
33
|
-
attribute :tree, Tree
|
34
|
-
attribute :branches, Array[Branch]
|
35
|
-
attribute :bugs, Array[Bug]
|
36
|
-
|
37
|
-
def add_branch(branch_options)
|
38
|
-
branch = Branch.new(branch_options.merge(branch: self))
|
39
|
-
branches << branch
|
40
|
-
branch
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
class Tree
|
45
|
-
include Virtus.model
|
46
|
-
|
47
|
-
attribute :id, Integer
|
48
|
-
attribute :name, String
|
49
|
-
attribute :trunk, Trunk
|
50
|
-
attribute :branches, Array[Branch]
|
51
|
-
|
52
|
-
def set_trunk(trunk)
|
53
|
-
trunk.tree = self
|
54
|
-
self.trunk = trunk
|
55
|
-
end
|
56
|
-
|
57
|
-
def add_branch(branch_options)
|
58
|
-
branch = Branch.new(branch_options.merge(tree: self))
|
59
|
-
branches << branch
|
60
|
-
branch
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
before(:all) do
|
65
|
-
define_table('branches_perf', {length: :decimal, tree_id: :integer, branch_id: :integer}, false)
|
66
|
-
define_table('bugs_perf', {name: :text, lives_on_id: :integer, lives_on_type: :string}, false)
|
67
|
-
define_table('trees_perf', {name: :text, trunk_id: :integer}, false)
|
68
|
-
define_table('trunks_perf', {length: :decimal}, false)
|
69
|
-
end
|
70
|
-
|
71
|
-
let(:tree_mapper) { build_mapper }
|
72
|
-
|
73
|
-
# Vorpal 0.0.5:
|
74
|
-
# user system total real
|
75
|
-
# create 4.160000 0.440000 4.600000 ( 6.071752)
|
76
|
-
# update 7.990000 0.730000 8.720000 ( 15.281017)
|
77
|
-
# load 10.120000 0.730000 10.850000 ( 21.087785)
|
78
|
-
# destroy 6.090000 0.620000 6.710000 ( 12.541420)
|
79
|
-
#
|
80
|
-
# Vorpal 0.0.6:
|
81
|
-
# user system total real
|
82
|
-
# create 0.990000 0.100000 1.090000 ( 1.415715)
|
83
|
-
# update 2.240000 0.180000 2.420000 ( 2.745321)
|
84
|
-
# load 2.130000 0.020000 2.150000 ( 2.223182)
|
85
|
-
# destroy 0.930000 0.010000 0.940000 ( 1.038624)
|
86
|
-
#
|
87
|
-
# Vorpal 0.1.0:
|
88
|
-
# user system total real
|
89
|
-
# create 0.870000 0.100000 0.970000 ( 1.320534)
|
90
|
-
# update 1.820000 0.210000 2.030000 ( 2.351518)
|
91
|
-
# load 1.310000 0.010000 1.320000 ( 1.394192)
|
92
|
-
# destroy 0.930000 0.010000 0.940000 ( 1.030910)
|
93
|
-
it 'benchmarks all operations' do
|
94
|
-
trees = build_trees(1000)
|
95
|
-
Benchmark.bm(7) do |x|
|
96
|
-
x.report('create') { tree_mapper.persist(trees) }
|
97
|
-
x.report('update') { tree_mapper.persist(trees) }
|
98
|
-
x.report('load') { tree_mapper.query.where(id: trees.map(&:id)).load_many }
|
99
|
-
x.report('destroy') { tree_mapper.destroy(trees) }
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# it 'creates aggregates quickly' do
|
104
|
-
# trees = build_trees(1000)
|
105
|
-
#
|
106
|
-
# puts 'starting persistence benchmark'
|
107
|
-
# puts Benchmark.measure {
|
108
|
-
# tree_mapper.persist(trees)
|
109
|
-
# }
|
110
|
-
# end
|
111
|
-
#
|
112
|
-
# it 'updates aggregates quickly' do
|
113
|
-
# trees = build_trees(1000)
|
114
|
-
#
|
115
|
-
# tree_mapper.persist(trees)
|
116
|
-
#
|
117
|
-
# puts 'starting update benchmark'
|
118
|
-
# puts Benchmark.measure {
|
119
|
-
# tree_mapper.persist(trees)
|
120
|
-
# }
|
121
|
-
# end
|
122
|
-
#
|
123
|
-
# it 'loads aggregates quickly' do
|
124
|
-
# trees = build_trees(1000)
|
125
|
-
# tree_mapper.persist(trees)
|
126
|
-
# ids = trees.map(&:id)
|
127
|
-
#
|
128
|
-
# puts 'starting loading benchmark'
|
129
|
-
# puts Benchmark.measure {
|
130
|
-
# tree_mapper.query.where(id: ids).load_many
|
131
|
-
# }
|
132
|
-
# end
|
133
|
-
#
|
134
|
-
# it 'destroys aggregates quickly' do
|
135
|
-
# trees = build_trees(1000)
|
136
|
-
# tree_mapper.persist(trees)
|
137
|
-
#
|
138
|
-
# puts 'starting destruction benchmark'
|
139
|
-
# puts Benchmark.measure {
|
140
|
-
# tree_mapper.destroy(trees)
|
141
|
-
# }
|
142
|
-
# end
|
143
|
-
|
144
|
-
def build_trees(count)
|
145
|
-
(1..count).map do |i|
|
146
|
-
tree = Tree.new
|
147
|
-
trunk = Trunk.new(length: i)
|
148
|
-
tree.set_trunk(trunk)
|
149
|
-
|
150
|
-
branch1 = tree.add_branch(length: i * 10)
|
151
|
-
branch2 = tree.add_branch(length: i * 20)
|
152
|
-
branch2.add_branch(length: i * 30)
|
153
|
-
|
154
|
-
build_bug(trunk)
|
155
|
-
build_bug(branch1)
|
156
|
-
|
157
|
-
tree
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
def build_bug(bug_home)
|
162
|
-
bug = Bug.new(lives_on: bug_home)
|
163
|
-
bug_home.bugs = [bug]
|
164
|
-
end
|
165
|
-
|
166
|
-
def build_mapper
|
167
|
-
engine = Vorpal.define do
|
168
|
-
map Tree, table_name: "trees_perf" do
|
169
|
-
attributes :name
|
170
|
-
belongs_to :trunk
|
171
|
-
has_many :branches
|
172
|
-
end
|
173
|
-
|
174
|
-
map Trunk, table_name: "trunks_perf" do
|
175
|
-
attributes :length
|
176
|
-
has_one :tree
|
177
|
-
has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
|
178
|
-
end
|
179
|
-
|
180
|
-
map Branch, table_name: "branches_perf" do
|
181
|
-
attributes :length
|
182
|
-
belongs_to :tree
|
183
|
-
has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
|
184
|
-
has_many :branches
|
185
|
-
end
|
186
|
-
|
187
|
-
map Bug, table_name: "bugs_perf" do
|
188
|
-
attributes :name
|
189
|
-
belongs_to :lives_on, fk: :lives_on_id, fk_type: :lives_on_type, child_classes: [Trunk, Branch]
|
190
|
-
end
|
191
|
-
end
|
192
|
-
engine.mapper_for(Tree)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
@@ -1,117 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
require 'vorpal/configs'
|
3
|
-
|
4
|
-
describe Vorpal::MasterConfig do
|
5
|
-
class Post
|
6
|
-
attr_accessor :comments
|
7
|
-
attr_accessor :best_comment
|
8
|
-
end
|
9
|
-
|
10
|
-
class Comment
|
11
|
-
attr_accessor :post
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:post_config) { Vorpal::ClassConfig.new(domain_class: Post) }
|
15
|
-
let(:comment_config) { Vorpal::ClassConfig.new(domain_class: Comment) }
|
16
|
-
let(:post_has_many_comments_config) { Vorpal::HasManyConfig.new(name: 'comments', fk: 'post_id', child_class: Comment) }
|
17
|
-
let(:post_has_one_comment_config) { Vorpal::HasOneConfig.new(name: 'best_comment', fk: 'post_id', child_class: Comment) }
|
18
|
-
let(:comment_belongs_to_post_config) { Vorpal::BelongsToConfig.new(name: 'post', fk: 'post_id', child_classes: [Post]) }
|
19
|
-
|
20
|
-
describe 'local_association_configs' do
|
21
|
-
it 'builds an association_config for a belongs_to' do
|
22
|
-
comment_config.belongs_tos << comment_belongs_to_post_config
|
23
|
-
|
24
|
-
Vorpal::MasterConfig.new([post_config, comment_config])
|
25
|
-
|
26
|
-
expect(comment_config.local_association_configs.size).to eq(1)
|
27
|
-
expect(post_config.local_association_configs.size).to eq(0)
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'sets the association end configs' do
|
31
|
-
comment_config.belongs_tos << comment_belongs_to_post_config
|
32
|
-
post_config.has_manys << post_has_many_comments_config
|
33
|
-
|
34
|
-
Vorpal::MasterConfig.new([post_config, comment_config])
|
35
|
-
|
36
|
-
association_config = comment_config.local_association_configs.first
|
37
|
-
|
38
|
-
expect(association_config.remote_end_config).to eq(post_has_many_comments_config)
|
39
|
-
expect(association_config.local_end_config).to eq(comment_belongs_to_post_config)
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'builds an association_config for a has_many' do
|
43
|
-
post_config.has_manys << post_has_many_comments_config
|
44
|
-
|
45
|
-
Vorpal::MasterConfig.new([post_config, comment_config])
|
46
|
-
|
47
|
-
expect(comment_config.local_association_configs.size).to eq(1)
|
48
|
-
expect(post_config.local_association_configs.size).to eq(0)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe 'nice user feedback' do
|
53
|
-
it 'lets the user know what the problem is when a configuration is missing' do
|
54
|
-
master_config = Vorpal::MasterConfig.new([])
|
55
|
-
|
56
|
-
expect {
|
57
|
-
master_config.config_for(String)
|
58
|
-
}.to raise_error(Vorpal::ConfigurationNotFound, "No configuration found for String")
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe Vorpal::AssociationConfig do
|
63
|
-
describe 'associate' do
|
64
|
-
let(:post) { Post.new }
|
65
|
-
let(:comment) { Comment.new }
|
66
|
-
|
67
|
-
it 'sets both ends of a one-to-one association' do
|
68
|
-
config = Vorpal::AssociationConfig.new(comment_config, 'post_id', nil)
|
69
|
-
config.add_remote_class_config(post_config)
|
70
|
-
|
71
|
-
config.local_end_config = comment_belongs_to_post_config
|
72
|
-
config.remote_end_config = post_has_one_comment_config
|
73
|
-
|
74
|
-
config.associate(comment, post)
|
75
|
-
|
76
|
-
expect(comment.post).to eq(post)
|
77
|
-
expect(post.best_comment).to eq(comment)
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'sets both ends of a one-to-many association' do
|
81
|
-
config = Vorpal::AssociationConfig.new(comment_config, 'post_id', nil)
|
82
|
-
config.add_remote_class_config(post_config)
|
83
|
-
|
84
|
-
config.local_end_config = comment_belongs_to_post_config
|
85
|
-
config.remote_end_config = post_has_many_comments_config
|
86
|
-
|
87
|
-
config.associate(comment, post)
|
88
|
-
|
89
|
-
expect(comment.post).to eq(post)
|
90
|
-
expect(post.comments).to eq([comment])
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe 'remote_class_config' do
|
95
|
-
it 'works with non-polymorphic associations' do
|
96
|
-
config = Vorpal::AssociationConfig.new(comment_config, 'post_id', nil)
|
97
|
-
config.add_remote_class_config(post_config)
|
98
|
-
|
99
|
-
post = Post.new
|
100
|
-
class_config = config.remote_class_config(post)
|
101
|
-
|
102
|
-
expect(class_config).to eq(post_config)
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'works with polymorphic associations' do
|
106
|
-
config = Vorpal::AssociationConfig.new(comment_config, 'commented_upon_id', 'commented_upon_type')
|
107
|
-
config.add_remote_class_config(post_config)
|
108
|
-
config.add_remote_class_config(comment_config)
|
109
|
-
|
110
|
-
comment = double('comment', commented_upon_type: 'Comment')
|
111
|
-
class_config = config.remote_class_config(comment)
|
112
|
-
|
113
|
-
expect(class_config).to eq(comment_config)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
@@ -1,103 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
require 'vorpal'
|
3
|
-
require 'virtus'
|
4
|
-
|
5
|
-
describe Vorpal::DbLoader do
|
6
|
-
|
7
|
-
class Post; end
|
8
|
-
|
9
|
-
class Comment
|
10
|
-
include Virtus.model
|
11
|
-
|
12
|
-
attribute :id, Integer
|
13
|
-
attribute :post, Post
|
14
|
-
end
|
15
|
-
|
16
|
-
class Post
|
17
|
-
include Virtus.model
|
18
|
-
|
19
|
-
attribute :id, Integer
|
20
|
-
attribute :best_comment, Comment
|
21
|
-
attribute :comments, Array[Comment]
|
22
|
-
end
|
23
|
-
|
24
|
-
class PostDB
|
25
|
-
include Virtus.model
|
26
|
-
attribute :id, Integer
|
27
|
-
attribute :best_comment_id, Integer
|
28
|
-
end
|
29
|
-
|
30
|
-
class CommentDB
|
31
|
-
include Virtus.model
|
32
|
-
attribute :id, Integer
|
33
|
-
attribute :post_id, Integer
|
34
|
-
end
|
35
|
-
|
36
|
-
before(:all) do
|
37
|
-
# define_table('comments', {post_id: :integer}, false)
|
38
|
-
# CommentDB = defineAr('comments')
|
39
|
-
|
40
|
-
# define_table('posts', {best_comment_id: :integer}, false)
|
41
|
-
# PostDB = defineAr('posts')
|
42
|
-
end
|
43
|
-
|
44
|
-
# it 'loads an object once even when referred to by different associations of different types2' do
|
45
|
-
# post_config = Vorpal.build_class_config(Post) do
|
46
|
-
# attributes :name
|
47
|
-
# belongs_to :best_comment, child_class: Comment
|
48
|
-
# has_many :comments
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# comment_config = Vorpal.build_class_config(Comment) do
|
52
|
-
# attributes :length
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# master_config = Vorpal::MasterConfig.new([post_config, comment_config])
|
56
|
-
#
|
57
|
-
# driver = Vorpal::Postgresql.new
|
58
|
-
#
|
59
|
-
# best_comment_db = CommentDB.create!
|
60
|
-
# post_db = PostDB.create!(best_comment_id: best_comment_db.id)
|
61
|
-
# best_comment_db.update_attributes!(post_id: post_db.id)
|
62
|
-
#
|
63
|
-
# loader = Vorpal::DbLoader.new(false, driver)
|
64
|
-
# loaded_objects = loader.load_from_db([post_db.id], master_config.config_for(Post))
|
65
|
-
# p loaded_objects.all_objects
|
66
|
-
# # expect(loaded_objects.all_objects.size).to eq(2)
|
67
|
-
#
|
68
|
-
# repo = Vorpal::AggregateMapper.new(driver, master_config)
|
69
|
-
# post = repo.load(post_db.id, Post)
|
70
|
-
# p post
|
71
|
-
# expect(post.comments.size).to eq(1)
|
72
|
-
# end
|
73
|
-
|
74
|
-
it 'loads an object once even when referred to by different associations of different types with stubs' do
|
75
|
-
post_config = Vorpal.build_class_config(Post, to: PostDB) do
|
76
|
-
attributes :name
|
77
|
-
belongs_to :best_comment, child_class: Comment
|
78
|
-
has_many :comments
|
79
|
-
end
|
80
|
-
|
81
|
-
comment_config = Vorpal.build_class_config(Comment, to: CommentDB) do
|
82
|
-
attributes :length
|
83
|
-
end
|
84
|
-
|
85
|
-
Vorpal::MasterConfig.new([post_config, comment_config])
|
86
|
-
|
87
|
-
best_comment_db = CommentDB.new
|
88
|
-
best_comment_db.id = 99
|
89
|
-
post_db = PostDB.new(best_comment_id: best_comment_db.id)
|
90
|
-
post_db.id = 100
|
91
|
-
best_comment_db.post_id = post_db.id
|
92
|
-
|
93
|
-
driver = instance_double("Vorpal::Driver::Postgresql")
|
94
|
-
expect(driver).to receive(:load_by_id).with(PostDB, [post_db.id]).and_return([post_db])
|
95
|
-
expect(driver).to receive(:load_by_id).with(CommentDB, [best_comment_db.id]).and_return([best_comment_db])
|
96
|
-
expect(driver).to receive(:load_by_foreign_key).and_return([best_comment_db])
|
97
|
-
|
98
|
-
loader = Vorpal::DbLoader.new(false, driver)
|
99
|
-
loaded_objects = loader.load_from_db([post_db.id], post_config)
|
100
|
-
|
101
|
-
expect(loaded_objects.all_objects).to contain_exactly(post_db, best_comment_db)
|
102
|
-
end
|
103
|
-
end
|