vorpal 0.0.5.1 → 0.0.6.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 +8 -8
- data/lib/vorpal/aggregate_repository.rb +152 -236
- data/lib/vorpal/aggregate_traversal.rb +50 -0
- data/lib/vorpal/aggregate_utils.rb +37 -0
- data/lib/vorpal/config_builder.rb +2 -2
- data/lib/vorpal/configs.rb +73 -74
- data/lib/vorpal/db_driver.rb +50 -0
- data/lib/vorpal/db_loader.rb +131 -0
- data/lib/vorpal/identity_map.rb +4 -0
- data/lib/vorpal/loaded_objects.rb +53 -0
- data/lib/vorpal/util/array_hash.rb +13 -0
- data/lib/vorpal/{hash_initialization.rb → util/hash_initialization.rb} +0 -0
- data/lib/vorpal/version.rb +1 -1
- data/spec/integration_spec_helper.rb +3 -1
- data/spec/vorpal/aggregate_repository_spec.rb +60 -7
- data/vorpal.gemspec +3 -1
- metadata +41 -8
- data/lib/vorpal/traversal.rb +0 -98
data/lib/vorpal/identity_map.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'vorpal/util/array_hash'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Vorpal
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class LoadedObjects
|
8
|
+
include ArrayHash
|
9
|
+
extend Forwardable
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :objects
|
13
|
+
def_delegators :objects, :each
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@objects = Hash.new([])
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(config, objects)
|
20
|
+
add_to_hash(@objects, config, objects)
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_by_id(object, config)
|
24
|
+
@objects[config].detect { |obj| obj.id == object.id }
|
25
|
+
end
|
26
|
+
|
27
|
+
def loaded_ids(config)
|
28
|
+
@objects[config].map(&:id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def loaded_fk_values(config, fk_info)
|
32
|
+
if fk_info.polymorphic?
|
33
|
+
@objects[config].
|
34
|
+
find_all { |db_object| fk_info.matches_polymorphic_type?(db_object) }.
|
35
|
+
map(&(fk_info.fk_column.to_sym))
|
36
|
+
else
|
37
|
+
@objects[config].map(&(fk_info.fk_column.to_sym))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def all_objects
|
42
|
+
@objects.values.flatten
|
43
|
+
end
|
44
|
+
|
45
|
+
def id_lookup_done?(config, id)
|
46
|
+
loaded_ids(config).include?(id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def fk_lookup_done?(config, fk_info, fk_value)
|
50
|
+
loaded_fk_values(config, fk_info).include?(fk_value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
File without changes
|
data/lib/vorpal/version.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'pg' # or 'mysql2' or 'sqlite3'
|
3
|
+
# require 'logger'
|
3
4
|
|
4
5
|
# Change the following to reflect your database settings
|
5
6
|
ActiveRecord::Base.establish_connection(
|
@@ -12,10 +13,11 @@ ActiveRecord::Base.establish_connection(
|
|
12
13
|
)
|
13
14
|
|
14
15
|
RSpec.configure do |config|
|
15
|
-
# implements use_transactional_fixtures = true
|
16
|
+
# implements `use_transactional_fixtures = true`
|
16
17
|
# from lib/active_record/fixtures.rb
|
17
18
|
# works with Rails 3.2. Probably not with Rails 4
|
18
19
|
config.before(:each) do
|
20
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
19
21
|
connection = ActiveRecord::Base.connection
|
20
22
|
connection.increment_open_transactions
|
21
23
|
connection.transaction_joinable = false
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'integration_spec_helper'
|
2
|
-
|
3
2
|
require 'vorpal'
|
4
|
-
|
5
3
|
require 'virtus'
|
4
|
+
require 'activerecord-import/base'
|
6
5
|
|
7
6
|
describe 'Aggregate Repository' do
|
8
7
|
|
@@ -109,10 +108,9 @@ describe 'Aggregate Repository' do
|
|
109
108
|
|
110
109
|
tree_db = TreeDB.create!
|
111
110
|
|
111
|
+
expect(Vorpal::DbDriver).to receive(:update).and_raise('not so good')
|
112
|
+
|
112
113
|
fissure = Fissure.new
|
113
|
-
def fissure.save!
|
114
|
-
raise 'something bad happened!'
|
115
|
-
end
|
116
114
|
tree = Tree.new(id: tree_db.id, fissures: [fissure])
|
117
115
|
|
118
116
|
expect {
|
@@ -486,9 +484,15 @@ describe 'Aggregate Repository' do
|
|
486
484
|
it 'restores with belongs_tos' do
|
487
485
|
test_repository = configure_polymorphic_belongs_to
|
488
486
|
|
489
|
-
|
487
|
+
# makes sure that we are using the fk_type to discriminate against
|
488
|
+
# two entities with the same primary key value
|
489
|
+
trunk_db = TrunkDB.new(length: 99)
|
490
|
+
trunk_db.id = 99
|
491
|
+
trunk_db.save!
|
490
492
|
trunk_bug_db = BugDB.create!(lives_on_id: trunk_db.id, lives_on_type: Trunk.name)
|
491
|
-
branch_db = BranchDB.
|
493
|
+
branch_db = BranchDB.new(length: 5)
|
494
|
+
branch_db.id = 99
|
495
|
+
branch_db.save!
|
492
496
|
branch_bug_db = BugDB.create!(lives_on_id: branch_db.id, lives_on_type: Branch.name)
|
493
497
|
|
494
498
|
trunk_bug, branch_bug = test_repository.load_all([trunk_bug_db.id, branch_bug_db.id], Bug)
|
@@ -576,6 +580,55 @@ describe 'Aggregate Repository' do
|
|
576
580
|
end
|
577
581
|
end
|
578
582
|
|
583
|
+
describe 'lots of data' do
|
584
|
+
it 'avoids N+1s on load' do
|
585
|
+
test_repository = Vorpal.define do
|
586
|
+
map Tree do
|
587
|
+
fields :name
|
588
|
+
belongs_to :trunk
|
589
|
+
# has_many :fissures
|
590
|
+
has_many :branches
|
591
|
+
end
|
592
|
+
|
593
|
+
map Trunk do
|
594
|
+
fields :length
|
595
|
+
has_one :tree
|
596
|
+
has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
|
597
|
+
end
|
598
|
+
|
599
|
+
map Branch do
|
600
|
+
fields :length
|
601
|
+
belongs_to :tree
|
602
|
+
has_many :bugs, fk: :lives_on_id, fk_type: :lives_on_type
|
603
|
+
has_many :branches
|
604
|
+
end
|
605
|
+
|
606
|
+
map Bug do
|
607
|
+
fields :name
|
608
|
+
belongs_to :lives_on, fk: :lives_on_id, fk_type: :lives_on_type, child_classes: [Trunk, Branch]
|
609
|
+
end
|
610
|
+
# map Fissure, to: Fissure
|
611
|
+
end
|
612
|
+
|
613
|
+
ids = (1..3).map do
|
614
|
+
trunk_db = TrunkDB.create!
|
615
|
+
tree_db = TreeDB.create!(trunk_id: trunk_db.id)
|
616
|
+
branch_db1 = BranchDB.create!(tree_id: tree_db.id)
|
617
|
+
branch_db2 = BranchDB.create!(tree_id: tree_db.id)
|
618
|
+
branch_db3 = BranchDB.create!(branch_id: branch_db2.id)
|
619
|
+
BugDB.create!(name: 'trunk bug', lives_on_id: trunk_db.id, lives_on_type: Trunk.name)
|
620
|
+
BugDB.create!(name: 'branch bug!', lives_on_id: branch_db1.id, lives_on_type: Branch.name)
|
621
|
+
tree_db.id
|
622
|
+
end
|
623
|
+
|
624
|
+
puts '*************************'
|
625
|
+
puts '*************************'
|
626
|
+
puts '*************************'
|
627
|
+
puts '*************************'
|
628
|
+
test_repository.load_all(ids, Tree)
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
579
632
|
# when you change a table's columns, set force to true to re-generate the table in the DB
|
580
633
|
def define_table(table_name, columns, force)
|
581
634
|
if !ActiveRecord::Base.connection.table_exists?(table_name) || force
|
data/vorpal.gemspec
CHANGED
@@ -18,11 +18,13 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "simple_serializer", "~>
|
21
|
+
spec.add_runtime_dependency "simple_serializer", "~> 0.0.1"
|
22
|
+
spec.add_runtime_dependency "equalizer"
|
22
23
|
|
23
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
25
|
spec.add_development_dependency "rspec", "~> 3.0.0"
|
25
26
|
spec.add_development_dependency "activerecord", "~> 3.2.0"
|
26
27
|
spec.add_development_dependency "pg", "~> 0.17.0"
|
27
28
|
spec.add_development_dependency "virtus", "~> 1.0"
|
29
|
+
spec.add_development_dependency "activerecord-import", "~> 0.3.1"
|
28
30
|
end
|
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.0.6.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: 2015-03-
|
11
|
+
date: 2015-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: simple_serializer
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: equalizer
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +108,20 @@ dependencies:
|
|
94
108
|
- - ~>
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '1.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: activerecord-import
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.3.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.3.1
|
97
125
|
description: An ORM framelet that fits on top of ActiveRecord to give you 'Data Mapper'
|
98
126
|
semantics.
|
99
127
|
email:
|
@@ -110,12 +138,17 @@ files:
|
|
110
138
|
- Rakefile
|
111
139
|
- lib/vorpal.rb
|
112
140
|
- lib/vorpal/aggregate_repository.rb
|
141
|
+
- lib/vorpal/aggregate_traversal.rb
|
142
|
+
- lib/vorpal/aggregate_utils.rb
|
113
143
|
- lib/vorpal/config_builder.rb
|
114
144
|
- lib/vorpal/configs.rb
|
115
145
|
- lib/vorpal/configuration.rb
|
116
|
-
- lib/vorpal/
|
146
|
+
- lib/vorpal/db_driver.rb
|
147
|
+
- lib/vorpal/db_loader.rb
|
117
148
|
- lib/vorpal/identity_map.rb
|
118
|
-
- lib/vorpal/
|
149
|
+
- lib/vorpal/loaded_objects.rb
|
150
|
+
- lib/vorpal/util/array_hash.rb
|
151
|
+
- lib/vorpal/util/hash_initialization.rb
|
119
152
|
- lib/vorpal/version.rb
|
120
153
|
- spec/integration_spec_helper.rb
|
121
154
|
- spec/unit_spec_helper.rb
|
@@ -138,9 +171,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
171
|
version: '0'
|
139
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
173
|
requirements:
|
141
|
-
- - ! '
|
174
|
+
- - ! '>'
|
142
175
|
- !ruby/object:Gem::Version
|
143
|
-
version:
|
176
|
+
version: 1.3.1
|
144
177
|
requirements: []
|
145
178
|
rubyforge_project:
|
146
179
|
rubygems_version: 2.4.5
|
data/lib/vorpal/traversal.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
|
2
|
-
# @private
|
3
|
-
class Traversal
|
4
|
-
def initialize(configs)
|
5
|
-
@configs = configs
|
6
|
-
end
|
7
|
-
|
8
|
-
def accept_for_domain(object, visitor, already_visited=[])
|
9
|
-
return if object.nil?
|
10
|
-
|
11
|
-
config = @configs.config_for(object.class)
|
12
|
-
return if config.nil?
|
13
|
-
|
14
|
-
return if already_visited.include?(object)
|
15
|
-
already_visited << object
|
16
|
-
|
17
|
-
visitor.visit_object(object, config)
|
18
|
-
|
19
|
-
config.belongs_tos.each do |belongs_to_config|
|
20
|
-
child = belongs_to_config.get_child(object)
|
21
|
-
accept_for_domain(child, visitor, already_visited) if visitor.continue_traversal?(belongs_to_config)
|
22
|
-
|
23
|
-
visitor.visit_belongs_to(object, child, belongs_to_config)
|
24
|
-
end
|
25
|
-
|
26
|
-
config.has_ones.each do |has_one_config|
|
27
|
-
child = has_one_config.get_child(object)
|
28
|
-
accept_for_domain(child, visitor, already_visited) if visitor.continue_traversal?(has_one_config)
|
29
|
-
|
30
|
-
visitor.visit_has_one(object, child, has_one_config)
|
31
|
-
end
|
32
|
-
|
33
|
-
config.has_manys.each do |has_many_config|
|
34
|
-
children = has_many_config.get_children(object)
|
35
|
-
children.each do |child|
|
36
|
-
accept_for_domain(child, visitor, already_visited) if visitor.continue_traversal?(has_many_config)
|
37
|
-
end
|
38
|
-
visitor.visit_has_many(object, children, has_many_config)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def accept_for_db(db_object, visitor, already_visited=[])
|
43
|
-
return if db_object.nil?
|
44
|
-
|
45
|
-
config = @configs.config_for_db(db_object.class)
|
46
|
-
return if config.nil?
|
47
|
-
|
48
|
-
return if already_visited.include?(db_object)
|
49
|
-
already_visited << db_object
|
50
|
-
|
51
|
-
visitor.visit_object(db_object, config)
|
52
|
-
|
53
|
-
config.belongs_tos.each do |belongs_to_config|
|
54
|
-
child = belongs_to_config.load_child(db_object)
|
55
|
-
accept_for_db(child, visitor, already_visited) if visitor.continue_traversal?(belongs_to_config)
|
56
|
-
|
57
|
-
visitor.visit_belongs_to(db_object, child, belongs_to_config)
|
58
|
-
end
|
59
|
-
|
60
|
-
config.has_ones.each do |has_one_config|
|
61
|
-
child = has_one_config.load_child(db_object)
|
62
|
-
accept_for_db(child, visitor, already_visited) if visitor.continue_traversal?(has_one_config)
|
63
|
-
|
64
|
-
visitor.visit_belongs_to(db_object, child, has_one_config)
|
65
|
-
end
|
66
|
-
|
67
|
-
config.has_manys.each do |has_many_config|
|
68
|
-
children = has_many_config.load_children(db_object)
|
69
|
-
children.each do |child|
|
70
|
-
accept_for_db(child, visitor, already_visited) if visitor.continue_traversal?(has_many_config)
|
71
|
-
end
|
72
|
-
visitor.visit_has_many(db_object, children, has_many_config)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# @private
|
78
|
-
module AggregateVisitorTemplate
|
79
|
-
def visit_object(object, config)
|
80
|
-
# override me!
|
81
|
-
end
|
82
|
-
|
83
|
-
def visit_belongs_to(parent, child, belongs_to_config)
|
84
|
-
# override me!
|
85
|
-
end
|
86
|
-
|
87
|
-
def visit_has_one(parent, child, has_one_config)
|
88
|
-
# override me!
|
89
|
-
end
|
90
|
-
|
91
|
-
def visit_has_many(parent, children, has_many_config)
|
92
|
-
# override me!
|
93
|
-
end
|
94
|
-
|
95
|
-
def continue_traversal?(association_config)
|
96
|
-
true
|
97
|
-
end
|
98
|
-
end
|