eager_record 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,7 +47,7 @@ generate two SQL queries. A couple of caveats:
47
47
  tell eager record you want to use this feature (put this line in a file in
48
48
  `config/initializers`:
49
49
 
50
- EagerRecord.use_scoped_preload = true
50
+ EagerRecord::ScopedPreloading.install
51
51
 
52
52
  === Does it work with all association types?
53
53
 
@@ -4,182 +4,13 @@ require 'digest'
4
4
 
5
5
  module EagerRecord
6
6
  autoload :VERSION, File.join(File.dirname(__FILE__), 'eager_record', 'version')
7
-
8
- TEMPORARY_SCOPED_PRELOAD_ASSOCIATION = :"_temporary_association_for_scoped_preloading"
7
+ autoload :EagerPreloading, File.join(File.dirname(__FILE__), 'eager_record', 'eager_preloading')
8
+ autoload :ScopedPreloading, File.join(File.dirname(__FILE__), 'eager_record', 'scoped_preloading')
9
9
 
10
10
  class <<self
11
11
  def install
12
- ActiveRecord::Base.module_eval do
13
- extend(EagerRecord::BaseExtensions::ClassMethods)
14
- include(EagerRecord::BaseExtensions::InstanceMethods)
15
- end
16
- ActiveRecord::Associations::AssociationProxy.module_eval { include(EagerRecord::AssociationProxyExtensions) }
17
- ActiveRecord::Associations::AssociationCollection.module_eval { include(EagerRecord::AssociationCollectionExtensions) }
18
- ActiveRecord::Associations::HasManyAssociation.module_eval { include(EagerRecord::HasManyAssociationExtensions) }
19
- end
20
-
21
- def use_scoped_preload=(flag)
22
- @use_scoped_preload = flag
23
- end
24
-
25
- def use_scoped_preload?
26
- !!@use_scoped_preload
27
- end
28
- end
29
-
30
- module BaseExtensions
31
- module ClassMethods
32
- def self.extended(base)
33
- (class <<base; self; end).module_eval do
34
- alias_method_chain :find_by_sql, :eager_preloading
35
- end
36
- end
37
-
38
- def find_by_sql_with_eager_preloading(*args)
39
- collection = find_by_sql_without_eager_preloading(*args)
40
- grouped_collections = collection.group_by { |record| record.class }
41
- grouped_collections.values.each do |grouped_collection|
42
- if grouped_collection.length > 1
43
- grouped_collection.each do |record|
44
- record.instance_variable_set(:@originating_collection, grouped_collection)
45
- end
46
- end
47
- end
48
- collection
49
- end
50
- end
51
-
52
- module InstanceMethods
53
- def self.included(base)
54
- base.has_many TEMPORARY_SCOPED_PRELOAD_ASSOCIATION, :readonly => true
55
- end
56
-
57
- private
58
-
59
- def scoped_preloaded_associations
60
- @scoped_preloaded_associations ||= Hash.new { |h, k| h[k] = {}}
61
- end
62
-
63
- def scoped_preloaded_associations_for(association_name)
64
- scoped_preloaded_associations[association_name.to_sym]
65
- end
66
- end
67
- end
68
-
69
- module AssociationProxyExtensions
70
-
71
- def self.included(base)
72
- base.module_eval do
73
- alias_method_chain :load_target, :eager_preloading
74
- end
75
- end
76
-
77
- def load_target_with_eager_preloading
78
- return nil unless defined?(@loaded)
79
-
80
- if !loaded? and (!@owner.new_record? || foreign_key_present)
81
- if originating_collection = @owner.instance_variable_get(:@originating_collection)
82
- association_name = @reflection.name
83
- @owner.class.__send__(:preload_associations, originating_collection, association_name)
84
- new_association = @owner.__send__(:association_instance_get, association_name)
85
- if new_association && __id__ != new_association.__id__ && new_association.loaded?
86
- @target = new_association.target
87
- @loaded = true
88
- return
89
- end
90
- end
91
- end
92
- load_target_without_eager_preloading
93
- end
94
- end
95
-
96
- module AssociationCollectionExtensions
97
- def self.included(base)
98
- base.module_eval do
99
- alias_method_chain :load_target, :eager_preloading
100
- alias_method_chain :find, :eager_preloading
101
- end
102
- end
103
-
104
- def load_target_with_eager_preloading
105
- if !@owner.new_record? || foreign_key_present
106
- if !loaded?
107
- if originating_collection = @owner.instance_variable_get(:@originating_collection)
108
- @owner.class.__send__(:preload_associations, originating_collection, @reflection.name)
109
- return target if loaded?
110
- end
111
- end
112
- end
113
- load_target_without_eager_preloading
114
- end
115
-
116
- #
117
- # Because of some likely unintentional plumbing in the scoping/association
118
- # delegation chain, current_scoped_methods returns an association proxy's
119
- # scope when called on the association collection. This means that, among
120
- # other things, a named scope called on an association collection will
121
- # duplicate the association collection's SQL restriction.
122
- #
123
- def current_scoped_methods
124
- @reflection.klass.__send__(:current_scoped_methods)
125
- end
126
-
127
- def find_with_eager_preloading(*args)
128
- if EagerRecord.use_scoped_preload? && originating_collection = @owner.instance_variable_get(:@originating_collection)
129
- find_using_scoped_preload(originating_collection, *args)
130
- else
131
- find_without_eager_preloading(*args)
132
- end
133
- end
134
-
135
- private
136
-
137
- #
138
- # Subclasses can override this
139
- #
140
- def find_using_scoped_preload(originating_collection, *args)
141
- find_without_eager_preloading(*args)
142
- end
143
- end
144
-
145
- module HasManyAssociationExtensions
146
- def find_using_scoped_preload(originating_collection, *args)
147
- options = args.extract_options!
148
- reflection_name = @reflection.name
149
- current_scope =
150
- if current_scoped_methods && current_scoped_methods[:find] #XXX regression test
151
- @reflection.options.merge(current_scoped_methods[:find])
152
- else
153
- @reflection.options
154
- end
155
- owner_class = @owner.class
156
- reflection_class = @reflection.klass
157
- scope_key = current_scope.inspect
158
- if preloaded_association = @owner.__send__(:scoped_preloaded_associations_for, reflection_name)[scope_key]
159
- return preloaded_association
160
- end
161
- reflection = owner_class.__send__(
162
- :create_has_many_reflection,
163
- TEMPORARY_SCOPED_PRELOAD_ASSOCIATION,
164
- current_scope.merge(
165
- :class_name => reflection_class.name,
166
- :readonly => true
167
- )
168
- )
169
- originating_collection.each do |record|
170
- association = ActiveRecord::Associations::HasManyAssociation.new(record, reflection)
171
- record.__send__(:association_instance_set, TEMPORARY_SCOPED_PRELOAD_ASSOCIATION, association)
172
- end
173
- owner_class.__send__(:preload_has_many_association, originating_collection, reflection)
174
- originating_collection.each do |record|
175
- record.instance_eval do
176
- @scoped_preloaded_associations ||= Hash.new { |h, k| h[k] = {} }
177
- @scoped_preloaded_associations[reflection_name][scope_key] =
178
- association_instance_get(TEMPORARY_SCOPED_PRELOAD_ASSOCIATION)
179
- association_instance_set(TEMPORARY_SCOPED_PRELOAD_ASSOCIATION, nil)
180
- end
181
- end
182
- @owner.__send__(:scoped_preloaded_associations_for, reflection_name)[scope_key]
12
+ EagerPreloading.install
13
+ ScopedPreloading.install
183
14
  end
184
15
  end
185
16
  end
@@ -0,0 +1,80 @@
1
+ module EagerRecord
2
+ module EagerPreloading
3
+ class <<self
4
+ def install
5
+ ActiveRecord::Base.module_eval do
6
+ extend(EagerRecord::EagerPreloading::BaseExtensions)
7
+ end
8
+ ActiveRecord::Associations::AssociationProxy.module_eval { include(EagerRecord::EagerPreloading::AssociationProxyExtensions) }
9
+ ActiveRecord::Associations::AssociationCollection.module_eval { include(EagerRecord::EagerPreloading::AssociationCollectionExtensions) }
10
+ end
11
+ end
12
+
13
+ module BaseExtensions
14
+ def self.extended(base)
15
+ (class <<base; self; end).module_eval do
16
+ alias_method_chain :find_by_sql, :eager_preloading
17
+ end
18
+ end
19
+
20
+ def find_by_sql_with_eager_preloading(*args)
21
+ collection = find_by_sql_without_eager_preloading(*args)
22
+ grouped_collections = collection.group_by { |record| record.class }
23
+ grouped_collections.values.each do |grouped_collection|
24
+ if grouped_collection.length > 1
25
+ grouped_collection.each do |record|
26
+ record.instance_variable_set(:@originating_collection, grouped_collection)
27
+ end
28
+ end
29
+ end
30
+ collection
31
+ end
32
+ end
33
+
34
+ module AssociationProxyExtensions
35
+ def self.included(base)
36
+ base.module_eval do
37
+ alias_method_chain :load_target, :eager_preloading
38
+ end
39
+ end
40
+
41
+ def load_target_with_eager_preloading
42
+ return nil unless defined?(@loaded)
43
+
44
+ if !loaded? and (!@owner.new_record? || foreign_key_present)
45
+ if originating_collection = @owner.instance_variable_get(:@originating_collection)
46
+ association_name = @reflection.name
47
+ @owner.class.__send__(:preload_associations, originating_collection, association_name)
48
+ new_association = @owner.__send__(:association_instance_get, association_name)
49
+ if new_association && __id__ != new_association.__id__ && new_association.loaded?
50
+ @target = new_association.target
51
+ @loaded = true
52
+ return
53
+ end
54
+ end
55
+ end
56
+ load_target_without_eager_preloading
57
+ end
58
+ end
59
+
60
+ module AssociationCollectionExtensions
61
+ def self.included(base)
62
+ base.module_eval do
63
+ alias_method_chain :load_target, :eager_preloading
64
+ end
65
+ end
66
+
67
+ def load_target_with_eager_preloading
68
+ if !@owner.new_record? || foreign_key_present
69
+ if !loaded?
70
+ if originating_collection = @owner.instance_variable_get(:@originating_collection)
71
+ @owner.class.__send__(:preload_associations, originating_collection, @reflection.name)
72
+ return target if loaded?
73
+ end
74
+ end
75
+ end
76
+ load_target_without_eager_preloading
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,99 @@
1
+ module EagerRecord
2
+
3
+ class ScopedPreloading
4
+ TEMPORARY_SCOPED_PRELOAD_ASSOCIATION = :"_temporary_association_for_scoped_preloading"
5
+
6
+ class <<self
7
+ def install
8
+ ActiveRecord::Base.module_eval { include(EagerRecord::ScopedPreloading::BaseExtensions) }
9
+ ActiveRecord::Associations::AssociationCollection.module_eval { include(EagerRecord::ScopedPreloading::AssociationCollectionExtensions) }
10
+ ActiveRecord::Associations::HasManyAssociation.module_eval { include(EagerRecord::ScopedPreloading::HasManyAssociationExtensions) }
11
+ end
12
+ end
13
+
14
+ module BaseExtensions
15
+ def self.included(base)
16
+ base.has_many TEMPORARY_SCOPED_PRELOAD_ASSOCIATION, :readonly => true
17
+ end
18
+
19
+ private
20
+
21
+ def scoped_preloaded_associations
22
+ @scoped_preloaded_associations ||= Hash.new { |h, k| h[k] = {}}
23
+ end
24
+
25
+ def scoped_preloaded_associations_for(association_name)
26
+ scoped_preloaded_associations[association_name.to_sym]
27
+ end
28
+ end
29
+
30
+ module AssociationCollectionExtensions
31
+ def self.included(base)
32
+ base.module_eval { alias_method_chain :find, :scoped_preloading }
33
+ end
34
+ #
35
+ # Because of some likely unintentional plumbing in the scoping/association
36
+ # delegation chain, current_scoped_methods returns an association proxy's
37
+ # scope when called on the association collection. This means that, among
38
+ # other things, a named scope called on an association collection will
39
+ # duplicate the association collection's SQL restriction.
40
+ #
41
+ def current_scoped_methods
42
+ @reflection.klass.__send__(:current_scoped_methods)
43
+ end
44
+
45
+ def find_with_scoped_preloading(*args)
46
+ if originating_collection = @owner.instance_variable_get(:@originating_collection)
47
+ find_using_scoped_preload(originating_collection, *args)
48
+ else
49
+ find_without_scoped_preloading(*args)
50
+ end
51
+ end
52
+
53
+ def find_using_scoped_preload(originating_collection, *args)
54
+ find_without_scoped_preloading(*args)
55
+ end
56
+ end
57
+
58
+ module HasManyAssociationExtensions
59
+ def find_using_scoped_preload(originating_collection, *args)
60
+ options = args.extract_options!
61
+ reflection_name = @reflection.name
62
+ current_scope =
63
+ if current_scoped_methods && current_scoped_methods[:find] #XXX regression test
64
+ @reflection.options.merge(current_scoped_methods[:find])
65
+ else
66
+ @reflection.options
67
+ end
68
+ owner_class = @owner.class
69
+ reflection_class = @reflection.klass
70
+ scope_key = current_scope.inspect
71
+ if preloaded_association = @owner.__send__(:scoped_preloaded_associations_for, reflection_name)[scope_key]
72
+ return preloaded_association
73
+ end
74
+ reflection = owner_class.__send__(
75
+ :create_has_many_reflection,
76
+ TEMPORARY_SCOPED_PRELOAD_ASSOCIATION,
77
+ current_scope.merge(
78
+ :class_name => reflection_class.name,
79
+ :readonly => true
80
+ )
81
+ )
82
+ originating_collection.each do |record|
83
+ association = ActiveRecord::Associations::HasManyAssociation.new(record, reflection)
84
+ record.__send__(:association_instance_set, TEMPORARY_SCOPED_PRELOAD_ASSOCIATION, association)
85
+ end
86
+ owner_class.__send__(:preload_has_many_association, originating_collection, reflection)
87
+ originating_collection.each do |record|
88
+ record.instance_eval do
89
+ @scoped_preloaded_associations ||= Hash.new { |h, k| h[k] = {} }
90
+ @scoped_preloaded_associations[reflection_name][scope_key] =
91
+ association_instance_get(TEMPORARY_SCOPED_PRELOAD_ASSOCIATION)
92
+ association_instance_set(TEMPORARY_SCOPED_PRELOAD_ASSOCIATION, nil)
93
+ end
94
+ end
95
+ @owner.__send__(:scoped_preloaded_associations_for, reflection_name)[scope_key]
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,3 @@
1
1
  module EagerRecord
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -1 +1 @@
1
- EagerRecord.install
1
+ EagerRecord::EagerPreloading.install
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 3
9
- version: 0.0.3
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Mat Brown
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-04 00:00:00 -04:00
17
+ date: 2010-05-28 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,9 @@ extra_rdoc_files: []
40
40
  files:
41
41
  - rails/init.rb
42
42
  - lib/eager_record.rb
43
+ - lib/eager_record/scoped_preloading.rb
43
44
  - lib/eager_record/version.rb
45
+ - lib/eager_record/eager_preloading.rb
44
46
  - README.rdoc
45
47
  - History.txt
46
48
  has_rdoc: true