Module: When::Parts::MethodCash

Included in:
Coordinates::Temporal, Ephemeris::Formula
Defined in:
lib/when_exe/parts/method_cash.rb

Overview

メソッドの実行結果をキャッシュし処理の高速化を行う

fn というメソッドをキャッシュ化

* fn 

a_to_b と b_to_a という互いに逆関数のメソッドをキャッシュ化

* a_to_b 

特記事項

Argument identification

The eql? method of When::TM::(Temporal)Position is not overridden.
It seems that the cost of identification of the argument exceeds the merit of the method cash.
I do not recommend applying the methodcash to the method which takes
When::TM::(Temporal)Position as an argument.

Multi-thread critical situation

There is a problem in consistency of hash when this function is used in multi-thread environment.
If the initialize method sets Mutex in instance variable @_m_cash_lock_,
this function gives up use of hash in the critical situation.

class Foo
  include MethodCash

  def initialize
    ...
    @_m_cash_lock_ = Mutex.new
    ...
   end
end

Constant Summary collapse

Escape =
{:to_str  => true,
:to_ary  => true,
:to_hash => true}

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ void

This method returns an undefined value.

最初に発生する method_missing で、キャッシュ機能を登録する



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/when_exe/parts/method_cash.rb', line 136

def method_missing(name, *args, &block)

  return method_missing_(name, *args, &block) unless respond_to?("#{name}_", true)
  return send("#{name}_",  *args, &block) if MethodCash.direct

  if ((name.to_s =~ /\A(_*)(.+?)_to_(.+)\z/) && respond_to?("#{$1}#{$3}_to_#{$2}_", true))
    prefix, from, to = $~[1..3]
    begin
      if (@_m_cash_lock_)
        return send("#{prefix}#{from}_to_#{to}_", *args, &block) unless @_m_cash_lock_.try_lock
        unlock = "ensure ; @_m_cash_lock_.locked? && @_m_cash_lock_.unlock"
      end
      [[from, to],[to, from]].each do |pair|
        a, b = pair
        lock = @_m_cash_lock_ ? "  return #{prefix}#{a}_to_#{b}_(*args) unless @_m_cash_lock_.try_lock" : ''
        instance_eval %Q{
          def #{prefix}#{a}_to_#{b}(*args)
            key = _key_simplefy(args)
            inv = @_m_cash_["#{prefix}#{a}_to_#{b}"][key]
            return inv if inv
            begin
            #{lock}
              inv = #{prefix}#{a}_to_#{b}_(*args)
              @_m_cash_["#{prefix}#{b}_to_#{a}"][_key_simplefy(inv)] = args
              @_m_cash_["#{prefix}#{a}_to_#{b}"][key] = inv
              return inv
            #{unlock}
            end
          end
        }
      end
      key = _key_simplefy(args)
      inv = send("#{prefix}#{from}_to_#{to}_", *args)
      @_m_cash_ ||= {}
      @_m_cash_["#{prefix}#{to}_to_#{from}"]    ||= {}
      @_m_cash_["#{prefix}#{from}_to_#{to}"]    ||= {}
      @_m_cash_["#{prefix}#{to}_to_#{from}"][_key_simplefy(inv)] = args
      @_m_cash_["#{prefix}#{from}_to_#{to}"][key] = inv
      return inv
    ensure
      @_m_cash_lock_ && @_m_cash_lock_.locked? && @_m_cash_lock_.unlock
    end

  else
    begin
      respond = respond_to?("#{name}_setup", true)
      setup   = respond ? "#{name}_setup(key, *args)" :
                          "(@_m_cash_[\"#{name}\"][key] = #{name}_(*args))"
      if (@_m_cash_lock_)
        return send("#{name}_", *args, &block) unless @_m_cash_lock_.try_lock
        lock   = "  return #{name}_(*args) unless @_m_cash_lock_.try_lock"
        unlock = "ensure ; @_m_cash_lock_.locked? && @_m_cash_lock_.unlock"
      end
      instance_eval %Q{
        def #{name}(*args)
          key = _key_simplefy(args)
          ret = @_m_cash_["#{name}"][key]
          return ret if ret
          begin
          #{lock}
            return #{setup}
          #{unlock}
          end
        end
      }
      key = _key_simplefy(args)
      @_m_cash_ ||= {}
      @_m_cash_["#{name}"] ||= {}
      if (respond)
        return send("#{name}_setup", key, *args)
      else
        return(@_m_cash_["#{name}"][key] ||= send("#{name}_", *args))
      end
    ensure
      @_m_cash_lock_ && @_m_cash_lock_.locked? && @_m_cash_lock_.unlock
    end
  end
end

Class Attribute Details

.directBoolean

‘_’ で終わるメソッドをキャッシュせずに毎回計算するか否か



66
67
68
# File 'lib/when_exe/parts/method_cash.rb', line 66

def direct
  @direct
end

Class Method Details

._setup_(options = {}) ⇒ void

Note:

When::TM::Calendar クラスの一部はキャッシュ使用を前提としているため :direct=>true では動作しません

This method returns an undefined value.

When::Parts::MethodCash のグローバルな設定を行う

Options Hash (options):

  • :direct (Boolean)

    ‘_’ で終わるメソッドをキャッシュせずに毎回計算するか否か

  • :escape (Hash{Symbol=>boolean})

    毎回 method_missing を発生させるメソッドを true にする

  • :escape (false, nil)

    to_str, to_ary, to_hash のみ毎回 method_missing を発生させる

  • :escape (true)

    すべて毎回 method_missing を発生させる



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
# File 'lib/when_exe/parts/method_cash.rb', line 80

def _setup_(options={})
  @_setup_info = options
  @direct      = options[:direct]
  case options[:escape]
  when true
    instance_eval %Q{
      def escape(method)
        true
      end
    }
  when Hash
    @escape = Escape.merge(options[:escape])
    instance_eval %Q{
      def escape(method)
        @escape[method]
      end
    }
  else
    instance_eval %Q{
      def escape(method)
        Escape.key?(method)
      end
    }
  end
end

._setup_infoHash

設定情報を取得する



110
111
112
# File 'lib/when_exe/parts/method_cash.rb', line 110

def _setup_info
  @_setup_info ||= {}
end

.escape(method) ⇒ boolean

method_missing メソッドを forward するか否か



120
121
122
# File 'lib/when_exe/parts/method_cash.rb', line 120

def escape(method)
  Escape.key?(method)
end

Instance Method Details

#method_missing_Object



125
# File 'lib/when_exe/parts/method_cash.rb', line 125

alias :method_missing_ :method_missing