vorpal 0.0.5.1 → 0.0.6.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,6 +23,10 @@ class IdentityMap
23
23
  key_objects.map { |k| @entities[key(k)] }
24
24
  end
25
25
 
26
+ def map_raw(key_ids, key_class)
27
+ key_ids.map { |key_id| @entities[[key_id, key_class.name]] }
28
+ end
29
+
26
30
  private
27
31
 
28
32
  def key(key_object)
@@ -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
@@ -0,0 +1,13 @@
1
+ module Vorpal
2
+
3
+ # @private
4
+ module ArrayHash
5
+ def add_to_hash(h, key, values)
6
+ if h[key].nil? || h[key].empty?
7
+ h[key] = []
8
+ end
9
+ h[key].concat(Array(values))
10
+ end
11
+ end
12
+
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Vorpal
2
- VERSION = "0.0.5.1"
2
+ VERSION = "0.0.6.rc1"
3
3
  end
@@ -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
- trunk_db = TrunkDB.create!(length: 99)
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.create!(length: 5)
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", "~> 1.0"
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.5.1
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-19 00:00:00.000000000 Z
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: '1.0'
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: '1.0'
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/hash_initialization.rb
146
+ - lib/vorpal/db_driver.rb
147
+ - lib/vorpal/db_loader.rb
117
148
  - lib/vorpal/identity_map.rb
118
- - lib/vorpal/traversal.rb
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: '0'
176
+ version: 1.3.1
144
177
  requirements: []
145
178
  rubyforge_project:
146
179
  rubygems_version: 2.4.5
@@ -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