aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/memoizable.rb
blob: 8e9abeaf91635d7b88707504a7df164ae31e5577 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
module ActiveSupport
  class ConcurrentHash
    def initialize(hash = {})
      @backup_cache = hash.dup
      @frozen_cache = hash.dup.freeze
      @mutex = Mutex.new
    end

    def []=(k,v)
      @mutex.synchronize { @backup_cache[k] = v }
      @frozen_cache = @backup_cache.dup.freeze
    end

    def [](k)
      if @frozen_cache.key?(k)
        @frozen_cache[k]
      else
        @mutex.synchronize { @backup_cache[k] }
      end
    end
    
    def empty?
      @backup_cache.empty?
    end
  end
  
  module SafelyMemoizable
    def safely_memoize(*symbols)
      symbols.each do |symbol|
        class_eval <<-RUBY, __FILE__, __LINE__ + 1
          def #{symbol}(*args)
            memoized = @_memoized_#{symbol} || ::ActiveSupport::ConcurrentHash.new
            memoized[args] ||= memoized_#{symbol}(*args)
          end
        RUBY
      end
    end
  end
  
  module Memoizable
    def self.memoized_ivar_for(symbol)
      "@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym
    end

    module InstanceMethods
      def self.included(base)
        base.class_eval do
          unless base.method_defined?(:freeze_without_memoizable)
            alias_method_chain :freeze, :memoizable
          end
        end
      end

      def freeze_with_memoizable
        memoize_all unless frozen?
        freeze_without_memoizable
      end

      def memoize_all
        prime_cache ".*"
      end

      def unmemoize_all
        flush_cache ".*"
      end

      def prime_cache(*syms)
        syms.each do |sym|
          methods.each do |m|
            if m.to_s =~ /^_unmemoized_(#{sym})/
              if method(m).arity == 0
                __send__($1)
              else
                ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
                instance_variable_set(ivar, {})
              end
            end
          end
        end
      end

      def flush_cache(*syms, &block)
        syms.each do |sym|
          methods.each do |m|
            if m.to_s =~ /^_unmemoized_(#{sym})/
              ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
              instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
            end
          end
        end
      end
    end

    def memoize(*symbols)
      symbols.each do |symbol|
        original_method = :"_unmemoized_#{symbol}"
        memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)

        class_eval <<-EOS, __FILE__, __LINE__ + 1
          include InstanceMethods                                                  # include InstanceMethods
                                                                                   #
          if method_defined?(:#{original_method})                                  # if method_defined?(:_unmemoized_mime_type)
            raise "Already memoized #{symbol}"                                     #   raise "Already memoized mime_type"
          end                                                                      # end
          alias #{original_method} #{symbol}                                       # alias _unmemoized_mime_type mime_type
                                                                                   #
          if instance_method(:#{symbol}).arity == 0                                # if instance_method(:mime_type).arity == 0
            def #{symbol}(reload = false)                                          #   def mime_type(reload = false)
              if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty?  #     if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
                #{memoized_ivar} = [#{original_method}.freeze]                     #       @_memoized_mime_type = [_unmemoized_mime_type.freeze]
              end                                                                  #     end
              #{memoized_ivar}[0]                                                  #     @_memoized_mime_type[0]
            end                                                                    #   end
          else                                                                     # else
            def #{symbol}(*args)                                                   #   def mime_type(*args)
              #{memoized_ivar} ||= {} unless frozen?                               #     @_memoized_mime_type ||= {} unless frozen?
              reload = args.pop if args.last == true || args.last == :reload       #     reload = args.pop if args.last == true || args.last == :reload
                                                                                   #
              if defined?(#{memoized_ivar}) && #{memoized_ivar}                    #     if defined?(@_memoized_mime_type) && @_memoized_mime_type
                if !reload && #{memoized_ivar}.has_key?(args)                      #       if !reload && @_memoized_mime_type.has_key?(args)
                  #{memoized_ivar}[args]                                           #         @_memoized_mime_type[args]
                elsif #{memoized_ivar}                                             #       elsif @_memoized_mime_type
                  #{memoized_ivar}[args] = #{original_method}(*args).freeze        #         @_memoized_mime_type[args] = _unmemoized_mime_type(*args).freeze
                end                                                                #       end
              else                                                                 #     else
                #{original_method}(*args)                                          #       _unmemoized_mime_type(*args)
              end                                                                  #     end
            end                                                                    #   end
          end                                                                      # end
        EOS
      end
    end
  end
end