eager_record 0.0.3 → 0.1.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.
@@ -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