From 24403498ba8582d29f55aab16ffd5920dec1c669 Mon Sep 17 00:00:00 2001 From: Nicholas Seckar Date: Mon, 27 Mar 2006 03:59:35 +0000 Subject: Add CachingTools::HashCaching to simplify the creation of nested, autofilling hashes. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4059 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activesupport/lib/active_support/caching_tools.rb | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 activesupport/lib/active_support/caching_tools.rb (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/caching_tools.rb b/activesupport/lib/active_support/caching_tools.rb new file mode 100644 index 0000000000..c889c148b4 --- /dev/null +++ b/activesupport/lib/active_support/caching_tools.rb @@ -0,0 +1,62 @@ +module ActiveSupport + module CachingTools #:nodoc: + + # Provide shortcuts to simply the creation of nested default hashes. This + # pattern is useful, common practice, and unsightly when done manually. + module HashCaching + # Dynamically create a nested hash structure used to cache calls to +method_name+ + # The cache method is named +#{method_name}_cache+ unless :as => :alternate_name + # is given. + # + # The hash structure is created using nested Hash.new. For example: + # + # def slow_method(a, b) a ** b end + # + # can be cached using hash_cache :slow_method, which will define the method + # slow_method_cache. We can then find the result of a ** b using: + # + # slow_method_cache[a][b] + # + # The hash structure returned by slow_method_cache would look like this: + # + # Hash.new do |as, a| + # as[a] = Hash.new do |bs, b| + # bs[b] = slow_method(a, b) + # end + # end + # + # The generated code is actually compressed onto a single line to maintain + # sensible backtrace signatures. + # + def hash_cache(method_name, options = {}) + selector = options[:as] || "#{method_name}_cache" + method = self.instance_method(method_name) + + args = [] + code = "def #{selector}(); @#{selector} ||= " + + (1..method.arity).each do |n| + args << "v#{n}" + code << "Hash.new {|h#{n}, v#{n}| h#{n}[v#{n}] = " + end + + # Add the method call with arguments, followed by closing braces and end. + code << "#{method_name}(#{args * ', '}) #{'}' * method.arity} end" + + # Extract the line number information from the caller. Exceptions arising + # in the generated code should point to the +hash_cache :...+ line. + if caller[0] && /^(.*):(\d+)$/ =~ caller[0] + file, line_number = $1, $2.to_i + else # We can't give good trackback info; fallback to this line: + file, line_number = __FILE__, __LINE__ + end + + # We use eval rather than building proc's because it allows us to avoid + # linking the Hash's to this method's binding. Experience has shown that + # doing so can cause obtuse memory leaks. + class_eval code, file, line_number + end + end + + end +end -- cgit v1.2.3