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