module InactiveRecord module Cache # Error raised when no cached result was find for the specified # method and arguments. class NoCacheResult < StandardError; end # Path to where the result fixture files will be written. mattr_accessor :fixture_path @@fixture_path = "#{RAILS_ROOT}/test/fixtures" # Set to +true+ to save the result in the file fixtures. # Can be set through the environment variable +CACHE_RESULT+. def cache_result ENV['CACHE_RESULT'] end # Set to +true+ to bypass the use of cached results. # Do the actual real thing! # Can be set through the environment variable +NO_CACHE+. def no_cache ENV['NO_CACHE'] end # Overrides a method to return its cached result ratter then actualy executing the method. # # If +cached_result+ is set to +true+: # the method will be executed and its result will be stored in its fixture file. # If +no_cache+ is set to +true+: # the method will be executed but its result will not be stored. # In other case the cache result will be return and the method won't be executed. # # The name of the fixture file it determine by the method and its arguments. # For example, the method ClassName#method(args) will be stored in # test/fixture/class_name/method-args file. def cache_result_of(method) class_eval do define_method "#{method}_with_cache" do |*args| normalized_args = args.collect do |arg| case arg when Hash arg.collect { |key, value| "#{key}-#{value}" }.sort else arg.to_s end end.join('-').tr(':/. ', '-') fixture_file = [self.class.fixture_path, self.class.name.underscore, normalized_args] * '/' if self.class.no_cache || self.class.cache_result result = send("#{method}_without_cache", *args) File.open(fixture_file, "w") { |file| YAML.dump(result, file) } if self.class.cache_result else raise NoCacheResult, "Expected cached result for #{self.class}\##{method} " + "with arguments #{args.inspect} in #{fixture_file}" unless File.exist?(fixture_file) result = File.open(fixture_file) { |file| YAML.load(file) } end result end alias_method_chain method, :cache end unless method_defined? "#{method}_with_cache" end end end