cached-models 0.0.2

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.
@@ -0,0 +1,379 @@
1
+ # FIXME load paths
2
+ require File.dirname(__FILE__) + '/associations/association_proxy'
3
+ require File.dirname(__FILE__) + '/associations/association_collection'
4
+ require File.dirname(__FILE__) + '/associations/has_many_association'
5
+
6
+ module ActiveRecord
7
+ module Associations
8
+ module ClassMethods
9
+ # Adds the following methods for retrieval and query of collections of associated objects:
10
+ # +collection+ is replaced with the symbol passed as the first argument, so
11
+ # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
12
+ # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
13
+ # An empty array is returned if none are found.
14
+ # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
15
+ # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by setting their foreign keys to +NULL+.
16
+ # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
17
+ # * <tt>collection=objects</tt> - Replaces the collections content by deleting and adding objects as appropriate.
18
+ # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids
19
+ # * <tt>collection_singular_ids=ids</tt> - Replace the collection with the objects identified by the primary keys in +ids+
20
+ # * <tt>collection.clear</tt> - Removes every object from the collection. This destroys the associated objects if they
21
+ # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>,
22
+ # otherwise sets their foreign keys to +NULL+.
23
+ # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
24
+ # * <tt>collection.size</tt> - Returns the number of associated objects.
25
+ # * <tt>collection.find</tt> - Finds an associated object according to the same rules as Base.find.
26
+ # * <tt>collection.build(attributes = {}, ...)</tt> - Returns one or more new objects of the collection type that have been instantiated
27
+ # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
28
+ # associated object already exists, not if it's +nil+!
29
+ # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
30
+ # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
31
+ # *Note:* This only works if an associated object already exists, not if it's +nil+!
32
+ #
33
+ # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
34
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
35
+ # * <tt>Firm#clients<<</tt>
36
+ # * <tt>Firm#clients.delete</tt>
37
+ # * <tt>Firm#clients=</tt>
38
+ # * <tt>Firm#client_ids</tt>
39
+ # * <tt>Firm#client_ids=</tt>
40
+ # * <tt>Firm#clients.clear</tt>
41
+ # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
42
+ # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
43
+ # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
44
+ # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
45
+ # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
46
+ # The declaration can also include an options hash to specialize the behavior of the association.
47
+ #
48
+ # Options are:
49
+ # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
50
+ # from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
51
+ # if the real class name is SpecialProduct, you'll have to specify it with this option.
52
+ # * <tt>:conditions</tt> - Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
53
+ # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash
54
+ # is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
55
+ # or <tt>@blog.posts.build</tt>.
56
+ # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
57
+ # such as <tt>last_name, first_name DESC</tt>.
58
+ # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
59
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
60
+ # as the default <tt>:foreign_key</tt>.
61
+ # * <tt>:dependent</tt> - If set to <tt>:destroy</tt> all the associated objects are destroyed
62
+ # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
63
+ # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
64
+ # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
65
+ # the <tt>:through</tt> option.
66
+ # * <tt>:finder_sql</tt> - Specify a complete SQL statement to fetch the association. This is a good way to go for complex
67
+ # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
68
+ # * <tt>:counter_sql</tt> - Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
69
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
70
+ # * <tt>:extend</tt> - Specify a named module for extending the proxy. See "Association extensions".
71
+ # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
72
+ # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
73
+ # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
74
+ # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
75
+ # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
76
+ # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will rise an error.
77
+ # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
78
+ # * <tt>:through</tt> - Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
79
+ # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
80
+ # or <tt>has_many</tt> association on the join model.
81
+ # * <tt>:source</tt> - Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
82
+ # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
83
+ # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
84
+ # * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
85
+ # association is a polymorphic +belongs_to+.
86
+ # * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
87
+ # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
88
+ # * <tt>:cached</tt> - If true, all the associated objects will be cached.
89
+ #
90
+ # Option examples:
91
+ # has_many :comments, :order => "posted_on"
92
+ # has_many :comments, :include => :author
93
+ # has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
94
+ # has_many :tracks, :order => "position", :dependent => :destroy
95
+ # has_many :comments, :dependent => :nullify
96
+ # has_many :tags, :as => :taggable
97
+ # has_many :reports, :readonly => true
98
+ # has_many :posts, :cached => true
99
+ # has_many :subscribers, :through => :subscriptions, :source => :user
100
+ # has_many :subscribers, :class_name => "Person", :finder_sql =>
101
+ # 'SELECT DISTINCT people.* ' +
102
+ # 'FROM people p, post_subscriptions ps ' +
103
+ # 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
104
+ # 'ORDER BY p.first_name'
105
+ def has_many(association_id, options = {}, &extension)
106
+ reflection = create_has_many_reflection(association_id, options, &extension)
107
+
108
+ configure_dependency_for_has_many(reflection)
109
+
110
+ add_multiple_associated_save_callbacks(reflection.name)
111
+ add_association_callbacks(reflection.name, reflection.options)
112
+
113
+ if options[:through]
114
+ collection_accessor_methods(reflection, HasManyThroughAssociation, options)
115
+ else
116
+ collection_accessor_methods(reflection, HasManyAssociation, options)
117
+ end
118
+
119
+ add_cache_callbacks if options[:cached]
120
+ end
121
+
122
+ # Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
123
+ # +association+ is replaced with the symbol passed as the first argument, so
124
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
125
+ # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
126
+ # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, and sets it as the foreign key.
127
+ # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
128
+ # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
129
+ # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
130
+ # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
131
+ # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
132
+ #
133
+ # Example: A Post class declares <tt>belongs_to :author</tt>, which will add:
134
+ # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
135
+ # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
136
+ # * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
137
+ # * <tt>Post#author.nil?</tt>
138
+ # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
139
+ # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
140
+ # The declaration can also include an options hash to specialize the behavior of the association.
141
+ #
142
+ # Options are:
143
+ # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
144
+ # from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
145
+ # if the real class name is Person, you'll have to specify it with this option.
146
+ # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
147
+ # SQL fragment, such as <tt>authorized = 1</tt>.
148
+ # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
149
+ # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
150
+ # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
151
+ # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
152
+ # "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
153
+ # will use a foreign key of "favorite_person_id".
154
+ # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
155
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
156
+ # <tt>belongs_to</tt> is used in conjunction with a <tt>has_many</tt> relationship on another class because of the potential to leave
157
+ # orphaned records behind.
158
+ # * <tt>:counter_cache</tt> - Caches the number of belonging objects on the associate class through the use of +increment_counter+
159
+ # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
160
+ # destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
161
+ # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing
162
+ # a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
163
+ # When creating a counter cache column, the database statement or migration must specify a default value of <tt>0</tt>, failing to do
164
+ # this results in a counter with +NULL+ value, which will never increment.
165
+ # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
166
+ # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
167
+ # * <tt>:polymorphic</tt> - Specify this association is a polymorphic association by passing +true+.
168
+ # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
169
+ # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
170
+ # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
171
+ # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +false+ by default.
172
+ #
173
+ # Option examples:
174
+ # belongs_to :firm, :foreign_key => "client_of"
175
+ # belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
176
+ # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
177
+ # :conditions => 'discounts > #{payments_count}'
178
+ # belongs_to :attachable, :polymorphic => true
179
+ # belongs_to :project, :readonly => true
180
+ # belongs_to :post, :counter_cache => true
181
+ # belongs_to :blog, :cached => true
182
+ def belongs_to(association_id, options = {})
183
+ reflection = create_belongs_to_reflection(association_id, options)
184
+
185
+ ivar = "@#{reflection.name}"
186
+
187
+ if reflection.options[:polymorphic]
188
+ association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
189
+
190
+ method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
191
+ define_method(method_name) do
192
+ association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
193
+
194
+ if association && association.target
195
+ if association.new_record?
196
+ association.save(true)
197
+ end
198
+
199
+ if association.updated?
200
+ self["#{reflection.primary_key_name}"] = association.id
201
+ self["#{reflection.options[:foreign_type]}"] = association.class.base_class.name.to_s
202
+ end
203
+ end
204
+ end
205
+ before_save method_name
206
+ else
207
+ association_accessor_methods(reflection, BelongsToAssociation)
208
+ association_constructor_method(:build, reflection, BelongsToAssociation)
209
+ association_constructor_method(:create, reflection, BelongsToAssociation)
210
+
211
+ method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
212
+ define_method(method_name) do
213
+ association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
214
+
215
+ if !association.nil?
216
+ if association.new_record?
217
+ association.save(true)
218
+ end
219
+
220
+ if association.updated?
221
+ self["#{reflection.primary_key_name}"] = association.id
222
+ end
223
+ end
224
+ end
225
+ before_save method_name
226
+ end
227
+
228
+ # Create the callbacks to update counter cache
229
+ if options[:counter_cache]
230
+ cache_column = options[:counter_cache] == true ?
231
+ "#{self.to_s.underscore.pluralize}_count" :
232
+ options[:counter_cache]
233
+
234
+ method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
235
+ define_method(method_name) do
236
+ association = send("#{reflection.name}")
237
+ association.class.increment_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil?
238
+ end
239
+ after_create method_name
240
+
241
+ method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
242
+ define_method(method_name) do
243
+ association = send("#{reflection.name}")
244
+ association.class.decrement_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil?
245
+ end
246
+ before_destroy method_name
247
+
248
+ module_eval(
249
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
250
+ )
251
+ end
252
+
253
+ if options[:cached]
254
+ method_name = "belongs_to_after_save_for_#{reflection.name}".to_sym
255
+ define_method(method_name) do
256
+ send(reflection.name).expire_cache_for(self.class.name)
257
+ end
258
+ after_save method_name
259
+ end
260
+
261
+ add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true
262
+
263
+ configure_dependency_for_belongs_to(reflection)
264
+ end
265
+
266
+ def collection_reader_method(reflection, association_proxy_class, options)
267
+ define_method(reflection.name) do |*params|
268
+ ivar = "@#{reflection.name}"
269
+
270
+ force_reload = params.first unless params.empty?
271
+
272
+ association = if options[:cached]
273
+ cache_read(reflection)
274
+ else
275
+ instance_variable_get(ivar) if instance_variable_defined?(ivar)
276
+ end
277
+
278
+ unless association.respond_to?(:loaded?)
279
+ association = association_proxy_class.new(self, reflection)
280
+ instance_variable_set(ivar, association)
281
+ end
282
+
283
+ if force_reload
284
+ association.reload
285
+ cache_delete(reflection) if options[:cached]
286
+ end
287
+
288
+ if options[:cached]
289
+ cache_fetch(reflection, association)
290
+ else
291
+ association
292
+ end
293
+ end
294
+
295
+ method_name = "#{reflection.name.to_s.singularize}_ids"
296
+ define_method(method_name) do
297
+ if options[:cached]
298
+ cache_fetch("#{cache_key}/#{method_name}", send("calculate_#{method_name}"))
299
+ else
300
+ send("calculate_#{method_name}")
301
+ end
302
+ end
303
+
304
+ define_method("calculate_#{method_name}") do
305
+ send(reflection.name).map { |record| record.id }
306
+ end
307
+ end
308
+
309
+ def collection_accessor_methods(reflection, association_proxy_class, options, writer = true)
310
+ collection_reader_method(reflection, association_proxy_class, options)
311
+
312
+ if writer
313
+ define_method("#{reflection.name}=") do |new_value|
314
+ # Loads proxy class instance (defined in collection_reader_method) if not already loaded
315
+ association = send(reflection.name)
316
+ association.replace(new_value)
317
+
318
+ cache_write(reflection, association) if options[:cached]
319
+
320
+ association
321
+ end
322
+
323
+ define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
324
+ ids = (new_value || []).reject { |nid| nid.blank? }
325
+ send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
326
+ end
327
+ end
328
+ end
329
+
330
+ def create_has_many_reflection(association_id, options, &extension)
331
+ options.assert_valid_keys(
332
+ :class_name, :table_name, :foreign_key, :primary_key,
333
+ :dependent,
334
+ :select, :conditions, :include, :order, :group, :limit, :offset,
335
+ :as, :through, :source, :source_type,
336
+ :uniq,
337
+ :finder_sql, :counter_sql,
338
+ :before_add, :after_add, :before_remove, :after_remove,
339
+ :extend, :readonly,
340
+ :validate, :accessible,
341
+ :cached
342
+ )
343
+
344
+ options[:extend] = create_extension_modules(association_id, extension, options[:extend])
345
+
346
+ create_reflection(:has_many, association_id, options, self)
347
+ end
348
+
349
+ def create_belongs_to_reflection(association_id, options)
350
+ options.assert_valid_keys(
351
+ :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
352
+ :counter_cache, :extend, :polymorphic, :readonly, :validate, :cached
353
+ )
354
+
355
+ reflection = create_reflection(:belongs_to, association_id, options, self)
356
+
357
+ if options[:polymorphic]
358
+ reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type"
359
+ end
360
+
361
+ reflection
362
+ end
363
+
364
+ def add_cache_callbacks
365
+ method_name = :after_save_cache_expire
366
+ return if respond_to? method_name
367
+
368
+ define_method(method_name) do
369
+ return unless self[:updated_at]
370
+
371
+ self.class.reflections.each do |name, reflection|
372
+ cache_delete(reflection) if reflection.options[:cached]
373
+ end
374
+ end
375
+ after_save method_name
376
+ end
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,69 @@
1
+ module ActiveRecord
2
+ class Base
3
+ @@rails_cache = nil
4
+ cattr_accessor :rails_cache
5
+
6
+ protected
7
+ def rails_cache
8
+ self.class.rails_cache
9
+ end
10
+
11
+ # Expire the cache for the associations which contains the given class.
12
+ #
13
+ # Example:
14
+ # class Blog < ActiveRecord::Base
15
+ # has_many :posts, :cached => true
16
+ # has_many :recent_posts, :class_name => 'Post',
17
+ # :limit => 10, :order => 'id DESC', :cached => true
18
+ #
19
+ # has_many :readers, :class_name => 'Person'
20
+ # end
21
+ #
22
+ # If one of the most recent posts will be updated, #expire_cache_for
23
+ # will be invoked with the "Post" parameter, in order to expire the
24
+ # cache for the first to associations.
25
+ def expire_cache_for(class_name)
26
+ self.class.reflections.each do |name, reflection|
27
+ if reflection.options[:cached] and reflection.class_name == class_name
28
+ cache_delete(reflection)
29
+ end
30
+ end
31
+ end
32
+
33
+ def cache_read(reflection)
34
+ return unless cached_associations[reflection.name]
35
+ rails_cache.read(reflection_cache_key(reflection))
36
+ end
37
+
38
+ def cache_write(reflection, value)
39
+ cached_associations[reflection.name] = rails_cache.write(reflection_cache_key(reflection), value)
40
+ end
41
+
42
+ def cache_delete(reflection)
43
+ return unless cached_associations[reflection.name]
44
+ cached_associations[reflection.name] = !rails_cache.delete(reflection_cache_key(reflection))
45
+ end
46
+
47
+ def cache_fetch(reflection, value)
48
+ reflection_name, key = extract_options_for_cache(reflection)
49
+ cached_associations[reflection_name] = true
50
+ rails_cache.fetch(key) { value }
51
+ end
52
+
53
+ def extract_options_for_cache(reflection)
54
+ if reflection.is_a?(AssociationReflection)
55
+ [ reflection.name, reflection_cache_key(reflection) ]
56
+ else
57
+ [ reflection.split('/').last, reflection ]
58
+ end
59
+ end
60
+
61
+ def reflection_cache_key(reflection)
62
+ "#{cache_key}/#{reflection.name}"
63
+ end
64
+
65
+ def cached_associations
66
+ @cached_associations ||= {}
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,4 @@
1
+ # FIXME load paths
2
+ path = File.dirname(__FILE__)
3
+ require "#{path}/active_record/base"
4
+ require "#{path}/active_record/associations"
@@ -0,0 +1 @@
1
+ require 'cached_models'
@@ -0,0 +1,4 @@
1
+ require 'activesupport' unless defined? Rails
2
+ require File.dirname(__FILE__) + '/activerecord/lib/active_record'
3
+
4
+ ActiveRecord::Base.rails_cache = Rails.cache if defined? Rails