From 9bf3af8ff324049149916316dbf7eb0a80a22010 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Thu, 30 Oct 2008 11:57:47 +0100 Subject: [PATCH] Make sure of use Memcached as test store. Compatibility changes for ActiveRecord 2.2.0 --- CHANGELOG | 2 + README | 9 +- .../lib/active_record/associations.rb | 256 +++--------------- .../associations/association_collection.rb | 29 +- .../associations/has_many_association_test.rb | 128 +++++---- test/test_helper.rb | 20 +- 6 files changed, 144 insertions(+), 300 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 38da739..e2a8861 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Make sure of use Memcached as test store. Compatibility changes for ActiveRecord 2.2.0 + * Make sure of require frameworks configured in environment.rb diff --git a/README b/README index 46b257d..aa568ba 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ Check for news and tutorials at the {project home page}[http://www.lucaguidi.com = Usage -Using Memcached and Rails 2.1.1 +Using Memcached and Rails 2.2.0 Make sure to configure your current environment with: @@ -77,7 +77,12 @@ Standalone: require 'cached-models' ActiveRecord::Base.rails_cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, 'localhost') - + + + += Test + Make sure your current store is Memcached + = Contribute diff --git a/lib/activerecord/lib/active_record/associations.rb b/lib/activerecord/lib/active_record/associations.rb index afe95e6..558e212 100644 --- a/lib/activerecord/lib/active_record/associations.rb +++ b/lib/activerecord/lib/active_record/associations.rb @@ -6,107 +6,12 @@ module ActiveRecord module Associations module ClassMethods - # Adds the following methods for retrieval and query of collections of associated objects: - # +collection+ is replaced with the symbol passed as the first argument, so - # has_many :clients would add among others clients.empty?. - # * collection(force_reload = false) - Returns an array of all the associated objects. - # An empty array is returned if none are found. - # * collection<<(object, ...) - Adds one or more objects to the collection by setting their foreign keys to the collection's primary key. - # * collection.delete(object, ...) - Removes one or more objects from the collection by setting their foreign keys to +NULL+. - # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model. - # * collection=objects - Replaces the collections content by deleting and adding objects as appropriate. - # * collection_singular_ids - Returns an array of the associated objects' ids - # * collection_singular_ids=ids - Replace the collection with the objects identified by the primary keys in +ids+ - # * collection.clear - Removes every object from the collection. This destroys the associated objects if they - # are associated with :dependent => :destroy, deletes them directly from the database if :dependent => :delete_all, - # otherwise sets their foreign keys to +NULL+. - # * collection.empty? - Returns +true+ if there are no associated objects. - # * collection.size - Returns the number of associated objects. - # * collection.find - Finds an associated object according to the same rules as Base.find. - # * collection.build(attributes = {}, ...) - Returns one or more new objects of the collection type that have been instantiated - # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an - # associated object already exists, not if it's +nil+! - # * collection.create(attributes = {}) - Returns a new object of the collection type that has been instantiated - # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation). - # *Note:* This only works if an associated object already exists, not if it's +nil+! - # - # Example: A Firm class declares has_many :clients, which will add: - # * Firm#clients (similar to Clients.find :all, :conditions => "firm_id = #{id}") - # * Firm#clients<< - # * Firm#clients.delete - # * Firm#clients= - # * Firm#client_ids - # * Firm#client_ids= - # * Firm#clients.clear - # * Firm#clients.empty? (similar to firm.clients.size == 0) - # * Firm#clients.size (similar to Client.count "firm_id = #{id}") - # * Firm#clients.find (similar to Client.find(id, :conditions => "firm_id = #{id}")) - # * Firm#clients.build (similar to Client.new("firm_id" => id)) - # * Firm#clients.create (similar to c = Client.new("firm_id" => id); c.save; c) - # The declaration can also include an options hash to specialize the behavior of the association. - # - # Options are: - # * :class_name - Specify the class name of the association. Use it only if that name can't be inferred - # from the association name. So has_many :products will by default be linked to the Product class, but - # if the real class name is SpecialProduct, you'll have to specify it with this option. - # * :conditions - Specify the conditions that the associated objects must meet in order to be included as a +WHERE+ - # SQL fragment, such as price > 5 AND name LIKE 'B%'. Record creations from the association are scoped if a hash - # is used. has_many :posts, :conditions => {:published => true} will create published posts with @blog.posts.create - # or @blog.posts.build. - # * :order - Specify the order in which the associated objects are returned as an ORDER BY SQL fragment, - # such as last_name, first_name DESC. - # * :foreign_key - Specify the foreign key used for the association. By default this is guessed to be the name - # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id" - # as the default :foreign_key. - # * :dependent - If set to :destroy all the associated objects are destroyed - # alongside this object by calling their +destroy+ method. If set to :delete_all all associated - # objects are deleted *without* calling their +destroy+ method. If set to :nullify all associated - # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using - # the :through option. - # * :finder_sql - Specify a complete SQL statement to fetch the association. This is a good way to go for complex - # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added. - # * :counter_sql - Specify a complete SQL statement to fetch the size of the association. If :finder_sql is - # specified but not :counter_sql, :counter_sql will be generated by replacing SELECT ... FROM with SELECT COUNT(*) FROM. - # * :extend - Specify a named module for extending the proxy. See "Association extensions". - # * :include - Specify second-order associations that should be eager loaded when the collection is loaded. - # * :group - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause. - # * :limit - An integer determining the limit on the number of rows that should be returned. - # * :offset - An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows. - # * :select - By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join - # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will rise an error. - # * :as - Specifies a polymorphic interface (See belongs_to). - # * :through - Specifies a Join Model through which to perform the query. Options for :class_name and :foreign_key - # are ignored, as the association uses the source reflection. You can only use a :through query through a belongs_to - # or has_many association on the join model. - # * :source - Specifies the source association name used by has_many :through queries. Only use it if the name cannot be - # inferred from the association. has_many :subscribers, :through => :subscriptions will look for either :subscribers or - # :subscriber on Subscription, unless a :source is given. - # * :source_type - Specifies type of the source association used by has_many :through queries where the source - # association is a polymorphic +belongs_to+. - # * :uniq - If true, duplicates will be omitted from the collection. Useful in conjunction with :through. - # * :readonly - If true, all the associated objects are readonly through the association. - # * :cached - If true, all the associated objects will be cached. - # - # Option examples: - # has_many :comments, :order => "posted_on" - # has_many :comments, :include => :author - # has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name" - # has_many :tracks, :order => "position", :dependent => :destroy - # has_many :comments, :dependent => :nullify - # has_many :tags, :as => :taggable - # has_many :reports, :readonly => true - # has_many :posts, :cached => true - # has_many :subscribers, :through => :subscriptions, :source => :user - # has_many :subscribers, :class_name => "Person", :finder_sql => - # 'SELECT DISTINCT people.* ' + - # 'FROM people p, post_subscriptions ps ' + - # 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' + - # 'ORDER BY p.first_name' - def has_many(association_id, options = {}, &extension) + def has_many(association_id, options = {}, &extension) #:nodoc: reflection = create_has_many_reflection(association_id, options, &extension) configure_dependency_for_has_many(reflection) + add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false add_multiple_associated_save_callbacks(reflection.name) add_association_callbacks(reflection.name, reflection.options) @@ -116,70 +21,10 @@ def has_many(association_id, options = {}, &extension) collection_accessor_methods(reflection, HasManyAssociation, options) end - add_cache_callbacks if options[:cached] + add_has_many_cache_callbacks if options[:cached] end - # Adds the following methods for retrieval and query for a single associated object for which this object holds an id: - # +association+ is replaced with the symbol passed as the first argument, so - # belongs_to :author would add among others author.nil?. - # * association(force_reload = false) - Returns the associated object. +nil+ is returned if none is found. - # * association=(associate) - Assigns the associate object, extracts the primary key, and sets it as the foreign key. - # * association.nil? - Returns +true+ if there is no associated object. - # * build_association(attributes = {}) - Returns a new object of the associated type that has been instantiated - # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. - # * create_association(attributes = {}) - Returns a new object of the associated type that has been instantiated - # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation). - # - # Example: A Post class declares belongs_to :author, which will add: - # * Post#author (similar to Author.find(author_id)) - # * Post#author=(author) (similar to post.author_id = author.id) - # * Post#author? (similar to post.author == some_author) - # * Post#author.nil? - # * Post#build_author (similar to post.author = Author.new) - # * Post#create_author (similar to post.author = Author.new; post.author.save; post.author) - # The declaration can also include an options hash to specialize the behavior of the association. - # - # Options are: - # * :class_name - Specify the class name of the association. Use it only if that name can't be inferred - # from the association name. So has_one :author will by default be linked to the Author class, but - # if the real class name is Person, you'll have to specify it with this option. - # * :conditions - Specify the conditions that the associated object must meet in order to be included as a +WHERE+ - # SQL fragment, such as authorized = 1. - # * :select - By default, this is * as in SELECT * FROM, but can be changed if, for example, you want to do a join - # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. - # * :foreign_key - Specify the foreign key used for the association. By default this is guessed to be the name - # of the association with an "_id" suffix. So a class that defines a belongs_to :person association will use - # "person_id" as the default :foreign_key. Similarly, belongs_to :favorite_person, :class_name => "Person" - # will use a foreign key of "favorite_person_id". - # * :dependent - If set to :destroy, the associated object is destroyed when this object is. If set to - # :delete, the associated object is deleted *without* calling its destroy method. This option should not be specified when - # belongs_to is used in conjunction with a has_many relationship on another class because of the potential to leave - # orphaned records behind. - # * :counter_cache - Caches the number of belonging objects on the associate class through the use of +increment_counter+ - # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's - # destroyed. This requires that a column named #{table_name}_count (such as +comments_count+ for a belonging Comment class) - # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing - # a column name instead of a +true+/+false+ value to this option (e.g., :counter_cache => :my_custom_counter.) - # When creating a counter cache column, the database statement or migration must specify a default value of 0, failing to do - # this results in a counter with +NULL+ value, which will never increment. - # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+. - # * :include - Specify second-order associations that should be eager loaded when this object is loaded. - # * :polymorphic - Specify this association is a polymorphic association by passing +true+. - # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute - # to the +attr_readonly+ list in the associated classes (e.g. class Post; attr_readonly :comments_count; end). - # * :readonly - If true, the associated object is readonly through the association. - # * :validate - If false, don't validate the associated objects when saving the parent object. +false+ by default. - # - # Option examples: - # belongs_to :firm, :foreign_key => "client_of" - # belongs_to :author, :class_name => "Person", :foreign_key => "author_id" - # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id", - # :conditions => 'discounts > #{payments_count}' - # belongs_to :attachable, :polymorphic => true - # belongs_to :project, :readonly => true - # belongs_to :post, :counter_cache => true - # belongs_to :blog, :cached => true - def belongs_to(association_id, options = {}) + def belongs_to(association_id, options = {}) #:nodoc: reflection = create_belongs_to_reflection(association_id, options) ivar = "@#{reflection.name}" @@ -189,7 +34,7 @@ def belongs_to(association_id, options = {}) method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym define_method(method_name) do - association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") + association = instance_variable_get(ivar) if instance_variable_defined?(ivar) if association && association.target if association.new_record? @@ -197,8 +42,8 @@ def belongs_to(association_id, options = {}) end if association.updated? - self["#{reflection.primary_key_name}"] = association.id - self["#{reflection.options[:foreign_type]}"] = association.class.base_class.name.to_s + self[reflection.primary_key_name] = association.id + self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s end end end @@ -210,7 +55,7 @@ def belongs_to(association_id, options = {}) method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym define_method(method_name) do - association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}") + association = instance_variable_get(ivar) if instance_variable_defined?(ivar) if !association.nil? if association.new_record? @@ -218,7 +63,7 @@ def belongs_to(association_id, options = {}) end if association.updated? - self["#{reflection.primary_key_name}"] = association.id + self[reflection.primary_key_name] = association.id end end end @@ -233,15 +78,15 @@ def belongs_to(association_id, options = {}) method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym define_method(method_name) do - association = send("#{reflection.name}") - association.class.increment_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil? + association = send(reflection.name) + association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? end after_create method_name method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym define_method(method_name) do - association = send("#{reflection.name}") - association.class.decrement_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil? + association = send(reflection.name) + association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? end before_destroy method_name @@ -250,17 +95,7 @@ def belongs_to(association_id, options = {}) ) end - if options[:cached] - after_save_method_name = "belongs_to_after_save_for_#{reflection.name}".to_sym - after_destroy_method_name = "belongs_to_after_destroy_for_#{reflection.name}".to_sym - define_method(after_save_method_name) do - send(reflection.name).expire_cache_for(self.class.name) - end - - alias_method after_destroy_method_name, after_save_method_name - after_save after_save_method_name - after_destroy after_destroy_method_name - end + add_belongs_to_cache_callbacks(association_id) if options[:cached] add_single_associated_validation_callbacks(reflection.name) if options[:validate] == true @@ -300,17 +135,19 @@ def collection_reader_method(reflection, association_proxy_class, options) define_method(method_name) do if options[:cached] cache_fetch("#{cache_key}/#{method_name}", send("calculate_#{method_name}")) - else + elsif send(reflection.name).loaded? || reflection.options[:finder_sql] send("calculate_#{method_name}") + else + send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id) end end - + define_method("calculate_#{method_name}") do - send(reflection.name).map { |record| record.id } + send(reflection.name).map(&:id) end end - def has_and_belongs_to_many(association_id, options = {}, &extension) + def has_and_belongs_to_many(association_id, options = {}, &extension) #:nodoc: reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension) add_multiple_associated_validation_callbacks(reflection.name) unless options[:validate] == false @@ -352,42 +189,11 @@ def collection_accessor_methods(reflection, association_proxy_class, options, wr end end - def create_has_many_reflection(association_id, options, &extension) - options.assert_valid_keys( - :class_name, :table_name, :foreign_key, :primary_key, - :dependent, - :select, :conditions, :include, :order, :group, :limit, :offset, - :as, :through, :source, :source_type, - :uniq, - :finder_sql, :counter_sql, - :before_add, :after_add, :before_remove, :after_remove, - :extend, :readonly, - :validate, :accessible, - :cached - ) - - options[:extend] = create_extension_modules(association_id, extension, options[:extend]) - - create_reflection(:has_many, association_id, options, self) - end - - def create_belongs_to_reflection(association_id, options) - options.assert_valid_keys( - :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent, - :counter_cache, :extend, :polymorphic, :readonly, :validate, :cached - ) + valid_keys_for_has_many_association << :cached + valid_keys_for_belongs_to_association << :cached - reflection = create_reflection(:belongs_to, association_id, options, self) - - if options[:polymorphic] - reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type" - end - - reflection - end - - def add_cache_callbacks - method_name = :after_save_cache_expire + def add_has_many_cache_callbacks + method_name = :has_many_after_save_cache_expire return if respond_to? method_name define_method(method_name) do @@ -399,6 +205,20 @@ def add_cache_callbacks end after_save method_name end + + def add_belongs_to_cache_callbacks(reflection_name) + after_save_method_name = "belongs_to_after_save_for_#{reflection_name}".to_sym + after_destroy_method_name = "belongs_to_after_destroy_for_#{reflection_name}".to_sym + return if respond_to? after_save_method_name + + define_method(after_save_method_name) do + send(reflection_name).expire_cache_for(self.class.name) + end + + alias_method after_destroy_method_name, after_save_method_name + after_save after_save_method_name + after_destroy after_destroy_method_name + end end end end diff --git a/lib/activerecord/lib/active_record/associations/association_collection.rb b/lib/activerecord/lib/active_record/associations/association_collection.rb index 7d61063..c5c94a0 100644 --- a/lib/activerecord/lib/active_record/associations/association_collection.rb +++ b/lib/activerecord/lib/active_record/associations/association_collection.rb @@ -54,7 +54,7 @@ def <<(*records) result = true load_target if @owner.new_record? - @owner.transaction do + transaction do flatten_deeper(records).each do |record| raise_on_type_mismatch(record) add_record_to_target_with_callbacks(record) do |r| @@ -68,12 +68,18 @@ def <<(*records) result && self end - # Remove +records+ from this association. Does not destroy +records+. + # Removes +records+ from this association calling +before_remove+ and + # +after_remove+ callbacks. + # + # This method is abstract in the sense that +delete_records+ has to be + # provided by descendants. Note this method does not imply the records + # are actually removed from the database, that depends precisely on + # +delete_records+. They are in any case removed from the collection. def delete(*records) records = flatten_deeper(records) records.each { |record| raise_on_type_mismatch(record) } - @owner.transaction do + transaction do records.each { |record| callback(:before_remove, record) } old_records = records.reject {|r| r.new_record? } @@ -94,7 +100,7 @@ def clear if @reflection.options[:dependent] && @reflection.options[:dependent] == :destroy destroy_all - else + else delete_all end @@ -103,9 +109,16 @@ def clear self end - # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and - # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero - # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length. + # Returns the size of the collection by executing a SELECT COUNT(*) + # query if the collection hasn't been loaded, and calling + # collection.size if it has. + # + # If the collection has been already loaded +size+ and +length+ are + # equivalent. If not and you are going to need the records anyway + # +length+ will take one less query. Otherwise +size+ is more efficient. + # + # This method is abstract in the sense that it relies on + # +count_records+, which is a method descendants have to provide. def size if @reflection.options[:cached] returning result = self.to_ary do @@ -115,6 +128,8 @@ def size if @owner.new_record? || (loaded? && !@reflection.options[:uniq]) @target.size + elsif !loaded? && @reflection.options[:group] + load_target.size elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array) unsaved_records = @target.select { |r| r.new_record? } unsaved_records.size + count_records diff --git a/test/active_record/associations/has_many_association_test.rb b/test/active_record/associations/has_many_association_test.rb index 3426a9b..1c82dd1 100644 --- a/test/active_record/associations/has_many_association_test.rb +++ b/test/active_record/associations/has_many_association_test.rb @@ -216,85 +216,83 @@ def test_should_use_cache_for_collection_include end end - uses_memcached 'HasManyAssociationTest' do - def test_should_refresh_caches_when_pushing_element_to_association_belonging_to_another_model - post = authors(:chuck).cached_posts.last - authors(:luca).cached_posts << post - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - assert_equal posts_by_author(:chuck), authors(:chuck).cached_posts - end + def test_should_refresh_caches_when_pushing_element_to_association_belonging_to_another_model + post = authors(:chuck).cached_posts.last + authors(:luca).cached_posts << post + assert_equal posts_by_author(:luca), authors(:luca).cached_posts + assert_equal posts_by_author(:chuck), authors(:chuck).cached_posts + end - def test_should_refresh_caches_when_pushing_element_to_polymorphic_association_belonging_to_another_model - tag = posts(:welcome).cached_tags.last - posts(:cached_models).cached_tags << tag + def test_should_refresh_caches_when_pushing_element_to_polymorphic_association_belonging_to_another_model + tag = posts(:welcome).cached_tags.last + posts(:cached_models).cached_tags << tag - # NOTE for some weird reason the assertion fails, even if the collections are equals. - # I forced the comparision between the ids. - assert_equal tags_by_post(:cached_models).map(&:id).sort, - posts(:cached_models).cached_tags.map(&:id).sort - end + # NOTE for some weird reason the assertion fails, even if the collections are equals. + # I forced the comparision between the ids. + assert_equal tags_by_post(:cached_models).map(&:id).sort, + posts(:cached_models).cached_tags.map(&:id).sort + end - def test_should_update_cache_when_pushing_element_with_build - author = authors(:luca) - post = author.cached_posts.build post_options - post.save - assert_equal posts_by_author(:luca), author.cached_posts - end + def test_should_update_cache_when_pushing_element_with_build + author = authors(:luca) + post = author.cached_posts.build post_options + post.save + assert_equal posts_by_author(:luca), author.cached_posts + end - def test_should_update_cache_when_pushing_element_with_create - author = authors(:luca) - author.cached_posts.create post_options(:title => "CM Overview") - assert_equal posts_by_author(:luca), author.cached_posts - end + def test_should_update_cache_when_pushing_element_with_create + author = authors(:luca) + author.cached_posts.create post_options(:title => "CM Overview") + assert_equal posts_by_author(:luca), author.cached_posts + end - def test_should_update_cache_when_pushing_element_with_create_bang_method - author = authors(:luca) - author.cached_posts.create! post_options(:title => "CM Overview!!") - assert_equal posts_by_author(:luca), author.cached_posts - end + def test_should_update_cache_when_pushing_element_with_create_bang_method + author = authors(:luca) + author.cached_posts.create! post_options(:title => "CM Overview!!") + assert_equal posts_by_author(:luca), author.cached_posts + end - def test_should_expire_cache_when_delete_all_elements_from_collection - authors(:luca).cached_posts.delete_all - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - end + def test_should_expire_cache_when_delete_all_elements_from_collection + authors(:luca).cached_posts.delete_all + assert_equal posts_by_author(:luca), authors(:luca).cached_posts + end - def test_should_expire_cache_when_destroy_all_elements_from_collection - authors(:luca).cached_posts.destroy_all - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - end + def test_should_expire_cache_when_destroy_all_elements_from_collection + authors(:luca).cached_posts.destroy_all + assert_equal posts_by_author(:luca), authors(:luca).cached_posts + end - def test_should_update_cache_when_clearing_collection - authors(:luca).cached_posts.clear - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - end + def test_should_update_cache_when_clearing_collection + authors(:luca).cached_posts.clear + assert_equal posts_by_author(:luca), authors(:luca).cached_posts + end - def test_should_update_cache_when_clearing_collection_with_dependent_destroy_option - authors(:luca).cached_dependent_posts.clear - assert_equal posts_by_author(:luca), authors(:luca).cached_dependent_posts - end + def test_should_update_cache_when_clearing_collection_with_dependent_destroy_option + authors(:luca).cached_dependent_posts.clear + assert_equal posts_by_author(:luca), authors(:luca).cached_dependent_posts + end - def test_should_update_cache_when_deleting_element_from_collection - authors(:luca).cached_posts.delete(posts_by_author(:luca).first) - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - end + def test_should_update_cache_when_deleting_element_from_collection + authors(:luca).cached_posts.delete(posts_by_author(:luca).first) + assert_equal posts_by_author(:luca), authors(:luca).cached_posts + end - def test_should_update_cache_when_replace_collection - post = create_post; post.save - posts = [ posts_by_author(:luca).first, post ] - authors(:luca).cached_posts.replace(posts) - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - end + def test_should_update_cache_when_replace_collection + post = create_post; post.save + posts = [ posts_by_author(:luca).first, post ] + authors(:luca).cached_posts.replace(posts) + assert_equal posts_by_author(:luca), authors(:luca).cached_posts + end - def test_should_not_expire_cache_on_update_on_missing_updated_at - author = authors(:luca) - old_cache_key = author.cache_key + def test_should_not_expire_cache_on_update_on_missing_updated_at + author = authors(:luca) + old_cache_key = author.cache_key - author.cached_posts # force cache loading - author.update_attributes :first_name => author.first_name.upcase + author.cached_posts # force cache loading + author.update_attributes :first_name => author.first_name.upcase - # assert_not_equal old_cache_key, author.cache_key - assert_equal posts_by_author(:luca), authors(:luca).cached_posts - end + # assert_not_equal old_cache_key, author.cache_key + assert_equal posts_by_author(:luca), authors(:luca).cached_posts end private diff --git a/test/test_helper.rb b/test/test_helper.rb index 4558521..261a1eb 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -18,6 +18,18 @@ Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures" ActionController::IntegrationTest.fixture_path = Test::Unit::TestCase.fixture_path +silence_warnings do + cache = ActiveSupport::Cache.lookup_store :mem_cache_store + Object.const_set "RAILS_CACHE", cache + ActiveRecord::Base.rails_cache = cache +end + +begin + require 'memcache' + MemCache.new('localhost').stats +rescue MemCache::MemCacheError + $stderr.puts "[WARNING] Memcache is not running!" +end module WillPaginate #:nodoc: def paginate(*args) @@ -65,14 +77,6 @@ def uses_mocha(description) $stderr.puts "Skipping #{description} tests. `gem install mocha` and try again." end -def uses_memcached(description) - require 'memcache' - MemCache.new('localhost').stats - yield -rescue MemCache::MemCacheError - $stderr.puts "Skipping #{description} tests. Start memcached and try again." -end - if ENV['SKIP_MOCHA'] == 'true' class Object def expects(*args)