ar-simple-idmap 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
@@ -3,3 +3,4 @@ if defined?(ActionController)
3
3
  require "identity_map/action_controller/dispatcher"
4
4
  end
5
5
  require "identity_map/active_record/base"
6
+ require "identity_map/active_record/association_preload"
@@ -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 == column_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
- from_arg0 = args.size == 1 ||
73
- args[1].is_a?(Hash) && !args[1].values.any?
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
- args[1].all?{|key, value| key == :conditions || value.blank?} &&
78
- args[1][:conditions].is_a?(Hash) &&
79
- args[1][:conditions].keys == [:id]
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] : args[1][:conditions][:id]
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
 
@@ -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
@@ -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.integer :customer_id
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: 31
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 4
10
- version: 0.2.4
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-09-21 00:00:00 +04:00
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