root/branches/1-2-stable/activesupport/lib/active_support/caching_tools.rb
| Revision 4059, 2.3 kB (checked in by ulysses, 3 years ago) |
|---|
| Line | |
|---|---|
| 1 | module ActiveSupport |
| 2 | module CachingTools #:nodoc: |
| 3 | |
| 4 | # Provide shortcuts to simply the creation of nested default hashes. This |
| 5 | # pattern is useful, common practice, and unsightly when done manually. |
| 6 | module HashCaching |
| 7 | # Dynamically create a nested hash structure used to cache calls to +method_name+ |
| 8 | # The cache method is named +#{method_name}_cache+ unless :as => :alternate_name |
| 9 | # is given. |
| 10 | # |
| 11 | # The hash structure is created using nested Hash.new. For example: |
| 12 | # |
| 13 | # def slow_method(a, b) a ** b end |
| 14 | # |
| 15 | # can be cached using hash_cache :slow_method, which will define the method |
| 16 | # slow_method_cache. We can then find the result of a ** b using: |
| 17 | # |
| 18 | # slow_method_cache[a][b] |
| 19 | # |
| 20 | # The hash structure returned by slow_method_cache would look like this: |
| 21 | # |
| 22 | # Hash.new do |as, a| |
| 23 | # as[a] = Hash.new do |bs, b| |
| 24 | # bs[b] = slow_method(a, b) |
| 25 | # end |
| 26 | # end |
| 27 | # |
| 28 | # The generated code is actually compressed onto a single line to maintain |
| 29 | # sensible backtrace signatures. |
| 30 | # |
| 31 | def hash_cache(method_name, options = {}) |
| 32 | selector = options[:as] || "#{method_name}_cache" |
| 33 | method = self.instance_method(method_name) |
| 34 | |
| 35 | args = [] |
| 36 | code = "def #{selector}(); @#{selector} ||= " |
| 37 | |
| 38 | (1..method.arity).each do |n| |
| 39 | args << "v#{n}" |
| 40 | code << "Hash.new {|h#{n}, v#{n}| h#{n}[v#{n}] = " |
| 41 | end |
| 42 | |
| 43 | # Add the method call with arguments, followed by closing braces and end. |
| 44 | code << "#{method_name}(#{args * ', '}) #{'}' * method.arity} end" |
| 45 | |
| 46 | # Extract the line number information from the caller. Exceptions arising |
| 47 | # in the generated code should point to the +hash_cache :...+ line. |
| 48 | if caller[0] && /^(.*):(\d+)$/ =~ caller[0] |
| 49 | file, line_number = $1, $2.to_i |
| 50 | else # We can't give good trackback info; fallback to this line: |
| 51 | file, line_number = __FILE__, __LINE__ |
| 52 | end |
| 53 | |
| 54 | # We use eval rather than building proc's because it allows us to avoid |
| 55 | # linking the Hash's to this method's binding. Experience has shown that |
| 56 | # doing so can cause obtuse memory leaks. |
| 57 | class_eval code, file, line_number |
| 58 | end |
| 59 | end |
| 60 | |
| 61 | end |
| 62 | end |
Note: See TracBrowser for help on using the browser.