あなたが言うように、alias_methodは注意深く使用されなければなりません。この不自然な例を考えると:
module CustomClient
...
host.class_eval do
alias :old_account_balance :account_balance
def account_balance ...
old_account_balance
end
...
class CoreClass
def old_account_balance ... defined here or in a superclass or
in another included module
def account_balance
# some new stuff ...
old_account_balance # some old stuff ...
end
include CustomClient
end
エイリアスの後、old_account_balanceはaccount_balanceのコピーであり、これは自分自身を呼び出すため、無限ループになります。
$ ruby -w t4.rb
t4.rb:21: warning: method redefined; discarding old old_account_balance
t4.rb:2: warning: previous definition of old_account_balance was here
[ output of puts removed ]
t4.rb:6: stack level too deep (SystemStackError)
[つるはしから]このテクニック[alias_method]の問題は、old_xxxという既存のメソッドがないことに依存していることです。より良い代替策は、事実上匿名であるメソッドオブジェクトを利用することです。
そうは言っても、ソースコードを所有している場合は、単純なエイリアスで十分です。しかし、より一般的なケースでは、Jörgのメソッドラッピング手法を使用します。
class CoreClass
def account_balance
puts 'CoreClass#account_balance, stuff deferred to the original method.'
end
end
module CustomClient
def self.included host
@is_defined_account_balance = host.new.respond_to? :account_balance
puts "is_defined_account_balance=#{@is_defined_account_balance}"
# pass this flag from CustomClient to host :
host.instance_variable_set(:@is_defined_account_balance,
@is_defined_account_balance)
host.class_eval do
old_account_balance = instance_method(:account_balance) if
@is_defined_account_balance
define_method(:account_balance) do |*args|
puts 'CustomClient#account_balance, additional stuff'
# like super :
old_account_balance.bind(self).call(*args) if
self.class.instance_variable_get(:@is_defined_account_balance)
end
end
end
end
class CoreClass
include CustomClient
end
print 'CoreClass.new.account_balance : '
CoreClass.new.account_balance
出力:
$ ruby -w t5.rb
is_defined_account_balance=true
CoreClass.new.account_balance : CustomClient#account_balance, additional stuff
CoreClass#account_balance, stuff deferred to the original method.
クラス変数@@is_defined_account_balanceはどうですか?[つるはしから]インクルードを含むモジュールまたはクラス定義は、インクルードするモジュールの定数、クラス変数、およびインスタンスメソッドにアクセスできます。
CustomClientからホストに渡すことを避け、テストを簡素化します。
old_account_balance if @@is_defined_account_balance # = super
しかし、グローバル変数と同じくらいクラス変数を嫌う人もいます。