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.
@@ -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