ar-simple-idmap 0.2.4 → 0.3.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.
- data/VERSION +1 -1
- data/lib/identity_map.rb +1 -0
- data/lib/identity_map/active_record/association_preload.rb +62 -0
- data/lib/identity_map/active_record/base.rb +27 -9
- data/spec/identity_map_spec.rb +17 -0
- data/spec/spec_helper.rb +27 -1
- metadata +6 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/identity_map.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AssociationPreload #:nodoc:
|
3
|
+
module ClassMethods
|
4
|
+
def preload_has_and_belongs_to_many_association_with_identity_map(records, reflection, preload_options={})
|
5
|
+
unless reflection.klass.respond_to?(:id_map)
|
6
|
+
return preload_has_and_belongs_to_many_association_without_identity_map(records, reflection, preload_options)
|
7
|
+
end
|
8
|
+
table_name = reflection.klass.quoted_table_name
|
9
|
+
records = records.find_all{|record| !record.send(reflection.name).loaded?}
|
10
|
+
return if records.empty?
|
11
|
+
id_to_record_map, ids = construct_id_map(records)
|
12
|
+
records.each {|record| record.send(reflection.name).loaded}
|
13
|
+
options = reflection.options
|
14
|
+
|
15
|
+
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
|
16
|
+
conditions << append_conditions(reflection, preload_options)
|
17
|
+
|
18
|
+
joins = connection.select_all(sanitize_sql([
|
19
|
+
"select t0.#{reflection.primary_key_name} as prnt_id, t0.#{reflection.association_foreign_key} as chld_id
|
20
|
+
from #{connection.quote_table_name options[:join_table]} t0
|
21
|
+
where #{conditions}
|
22
|
+
", ids]))
|
23
|
+
child_record_ids = joins.map{|j| j['chld_id']}.uniq
|
24
|
+
|
25
|
+
associated_records = reflection.klass.with_exclusive_scope do
|
26
|
+
reflection.klass.find(:all, :conditions => {reflection.klass.primary_key => child_record_ids},
|
27
|
+
:include => options[:include],
|
28
|
+
:select => options[:select].presence,
|
29
|
+
:order => options[:order])
|
30
|
+
end
|
31
|
+
associated_record_map = associated_records.inject({}){|h, r| h[r.id.to_s] = r; h}
|
32
|
+
joins.each do |j|
|
33
|
+
mapped_records = id_to_record_map[j['prnt_id'].to_s]
|
34
|
+
add_preloaded_records_to_collection(mapped_records, reflection.name, associated_record_map[j['chld_id'].to_s])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
alias_method_chain :preload_has_and_belongs_to_many_association, :identity_map
|
38
|
+
|
39
|
+
if Array.respond_to?(:wrap)
|
40
|
+
def add_preloaded_records_to_collection(parent_records, reflection_name, associated_record)
|
41
|
+
parent_records.each do |parent_record|
|
42
|
+
association_proxy = parent_record.send(reflection_name)
|
43
|
+
association_proxy.loaded
|
44
|
+
associated_records = Array.wrap(associated_record) - association_proxy.target
|
45
|
+
association_proxy.target.push(*associated_records)
|
46
|
+
association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
else
|
50
|
+
def add_preloaded_records_to_collection(parent_records, reflection_name, associated_record)
|
51
|
+
parent_records.each do |parent_record|
|
52
|
+
association_proxy = parent_record.send(reflection_name)
|
53
|
+
association_proxy.loaded
|
54
|
+
associated_records = [associated_record].flatten - association_proxy.target
|
55
|
+
association_proxy.target.push(*associated_records)
|
56
|
+
association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -27,6 +27,7 @@ module ActiveRecord # :nodoc:
|
|
27
27
|
end
|
28
28
|
alias_method_chain :create, :identity_map
|
29
29
|
alias_method_chain :destroy, :identity_map
|
30
|
+
alias_method_chain :reload, :identity_map
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -46,7 +47,7 @@ module ActiveRecord # :nodoc:
|
|
46
47
|
private
|
47
48
|
|
48
49
|
def fetch_single(map, id)
|
49
|
-
if (obj = map[id]) && obj.attribute_names
|
50
|
+
if (obj = map[id]) && !(column_names - obj.attribute_names).present?
|
50
51
|
obj
|
51
52
|
end
|
52
53
|
end
|
@@ -69,21 +70,25 @@ module ActiveRecord # :nodoc:
|
|
69
70
|
|
70
71
|
def find_with_identity_map( *args )
|
71
72
|
if_id_map do |map|
|
72
|
-
|
73
|
-
|
73
|
+
if args.size > 1 && args.all?{|a| a.is_a?(Integer) || a.is_a?(String)}
|
74
|
+
args = [ args ]
|
75
|
+
end
|
76
|
+
args1 = args[1]
|
77
|
+
from_arg0 = args1.nil? ||
|
78
|
+
args1.is_a?(Hash) && !args1.values.any?
|
74
79
|
from_condition_ids = !from_arg0 &&
|
75
80
|
(args[0] == :all || args[0] == :first) &&
|
76
81
|
args.size == 2 && args[1].is_a?(Hash) &&
|
77
|
-
|
78
|
-
|
79
|
-
|
82
|
+
args1.all?{|key, value| key == :conditions || key == :include || !value.present?} &&
|
83
|
+
args1[:conditions].is_a?(Hash) &&
|
84
|
+
(args1[:conditions].keys == [:id] || args1[:conditions].keys == ['id'])
|
80
85
|
if from_arg0 || from_condition_ids
|
81
|
-
ids = from_arg0 ? args[0] :
|
82
|
-
if ids.is_a?(Array)
|
86
|
+
ids = from_arg0 ? args[0] : (args1[:conditions][:id] || args1[:conditions]['id'])
|
87
|
+
records = if ids.is_a?(Array)
|
83
88
|
if from_arg0
|
84
89
|
fetch_from_map( map, ids, &method(:find_without_identity_map) )
|
85
90
|
elsif args[0] == :all
|
86
|
-
fetch_from_map( map, ids ){|not_cached|
|
91
|
+
fetch_from_map( map, ids ){|not_cached|
|
87
92
|
find_without_identity_map(:all, {:conditions=>{:id=>not_cached}})
|
88
93
|
}
|
89
94
|
elsif args[0] == :first
|
@@ -98,6 +103,13 @@ module ActiveRecord # :nodoc:
|
|
98
103
|
else
|
99
104
|
fetch_single(map, ids)
|
100
105
|
end
|
106
|
+
if method_defined?(:merge_includes) &&
|
107
|
+
(include_associations = merge_includes(scope(:find, :include), args1.try(:[],:include))).any?
|
108
|
+
preload_associations(records, include_associations)
|
109
|
+
elsif args1.try(:[], :include)
|
110
|
+
preload_associations(records, args1[:include])
|
111
|
+
end
|
112
|
+
records
|
101
113
|
end
|
102
114
|
end || find_without_identity_map(*args)
|
103
115
|
end
|
@@ -145,6 +157,12 @@ module ActiveRecord # :nodoc:
|
|
145
157
|
self.class.if_id_map{|map| map.delete(id) }
|
146
158
|
res
|
147
159
|
end
|
160
|
+
|
161
|
+
def reload_with_identity_map
|
162
|
+
self.class.without_id_map do
|
163
|
+
reload_without_identity_map
|
164
|
+
end
|
165
|
+
end
|
148
166
|
end
|
149
167
|
end
|
150
168
|
|
data/spec/identity_map_spec.rb
CHANGED
@@ -138,6 +138,23 @@ describe "Customers" do
|
|
138
138
|
c3.__id__.should == c2.__id__
|
139
139
|
end
|
140
140
|
end
|
141
|
+
|
142
|
+
context "has and belongs to many" do
|
143
|
+
before(:each) do
|
144
|
+
gotwo = Building.create(:name=>'GoTwo')
|
145
|
+
Address.find(:all).each do |address|
|
146
|
+
gotwo.addresses << address
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should load habtm adequatly" do
|
151
|
+
buildings = Building.find(:all, :include=>:addresses)
|
152
|
+
buildings[0].addresses.loaded?.should be_true
|
153
|
+
buildings[0].addresses.to_a.size.should == 2
|
154
|
+
buildings[1].addresses.loaded?.should be_true
|
155
|
+
buildings[1].addresses.to_a.size.should == 2
|
156
|
+
end
|
157
|
+
end
|
141
158
|
|
142
159
|
after(:each) do
|
143
160
|
ActiveRecord::Base.drop_identity_map
|
data/spec/spec_helper.rb
CHANGED
@@ -31,7 +31,17 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
31
31
|
end
|
32
32
|
create_table :phone_numbers, :force => true do |t|
|
33
33
|
t.string :number
|
34
|
-
t.
|
34
|
+
t.references :customer
|
35
|
+
end
|
36
|
+
create_table :buildings, :force => true do |t|
|
37
|
+
t.string :name
|
38
|
+
end
|
39
|
+
create_table :addresses, :force => true do |t|
|
40
|
+
t.string :name
|
41
|
+
end
|
42
|
+
create_table :addresses_buildings, :force => true, :id => false do |t|
|
43
|
+
t.references :building
|
44
|
+
t.references :address
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
@@ -49,3 +59,19 @@ end
|
|
49
59
|
|
50
60
|
phone_number = customer.phone_numbers.create(:number => "8675309")
|
51
61
|
|
62
|
+
class Building < ActiveRecord::Base
|
63
|
+
use_id_map
|
64
|
+
has_and_belongs_to_many :addresses
|
65
|
+
end
|
66
|
+
|
67
|
+
building = Building.create(:name => 'GoOne')
|
68
|
+
|
69
|
+
class Address < ActiveRecord::Base
|
70
|
+
use_id_map
|
71
|
+
has_and_belongs_to_many :customers
|
72
|
+
end
|
73
|
+
|
74
|
+
address1 = Address.create(:name=>'volga')
|
75
|
+
address2 = Address.create(:name=>'don')
|
76
|
+
building.addresses << address1
|
77
|
+
building.addresses << address2
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar-simple-idmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sokolov Yura aka funny_falcon
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-05 00:00:00 +03:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- install.rb
|
51
51
|
- lib/identity_map.rb
|
52
52
|
- lib/identity_map/action_controller/dispatcher.rb
|
53
|
+
- lib/identity_map/active_record/association_preload.rb
|
53
54
|
- lib/identity_map/active_record/base.rb
|
54
55
|
- lib/identity_map/cache.rb
|
55
56
|
- spec/identity_map_spec.rb
|