7192


[ruby -
既存クラスのメソッドに追加のオプションをつける方法](https://ja.stackoverflow.com/questions/6938/%e6%97%a2%e5%ad%98%e3%82%af%e3%83%a9%e3%82%b9%e3%81%ae%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89%e3%81%ab%e8%bf%bd%e5%8a%a0%e3%81%ae%e3%82%aa%e3%83%97%e3%82%b7%e3%83%a7%e3%83%b3%e3%82%92%e3%81%a4%e3%81%91%e3%82%8b%e6%96%b9%e6%b3%95)
にて既存クラスのメソッドの書き換えについてお聞きしたのですが、問題が書き換えの方法ではなくファイルの読み込み方にあったために別の質問として立てさせていただきました。

以下のように`selenium-
webdriver`の拡張をしたのですが、pry上で作業している時に`Selenium::Webdriver::Element`に他のメソッドを追加した時など何度も`load
'selenium_webdriver_ext.rb'` で呼び出すと2回目以降から `stack level too
deep`のエラーが発生してしまいます。

### selenium_webdriver_ext.rb

[code]

    class Selenium::WebDriver::Element
      alias_method :send_keys_orig, :send_keys  
      def send_keys(*args, clear: false)
        __send__(:clear) if clear
        send_keys_orig(*args)
      end
    end
    
[/code]

メソッドの書き換えを行ったファイルを`pry`から何度も読みだす時に何か良い方法はないでしょうか？


CC BY-SA 3.0
2015-02-27T02:06:15.010
7174
2015-02-27T05:04:12.333
2017-04-13T12:52:38.920
-1
3271
question
ruby pry
既存クラスのメソッドを書き換えたファイルを何度も読み込む時の扱い方


2回目以降の `alias_method :send_keys_orig, :send_keys`
がまずいですね。オリジナルが消えてますし、メソッド自身を呼び出すことになってしまいます。

[aliasによるメソッドの再定義は危険なのでUnboundMethodかextendを使おう -
るびきち](http://d.hatena.ne.jp/rubikitch/20080504/1209835550)  
と、るびきちさんの著作[Ruby逆引きハンドブック](http://www.c-r.com/book/detail/705)「メソッドを再定義する」
(p.630) を用いて書き直してみました。

[code]

    def once
      unless instance_variable_defined? :@__once_executed__
        yield
        @__once_executed__ = true
      end
    end
    
    class Selenium::WebDriver::Element
      once do
        alias_method :send_keys_orig, :send_keys  
      end
      def send_keys(*args, clear: false)
        __send__(:clear) if clear
        send_keys_orig(*args)
      end
    end
    
[/code]

簡単に言えば、再読込したときに `once` メソッドの引数（ブロック）は読まないので `SystemStackError: stack level too
deep` が出ない、ということです。

他にも、先ほどの引用のタイトル通り、 `Module#instance_method` で `UnboundMethod`
を得てから再定義というやり方、及び `Object#extend`
を使ってインスタンスに再定義メソッドを突っ込むやり方があります。それらに関しては引用先を見てください。


CC BY-SA 3.0
2015-02-27T03:52:38.227
7189
2015-02-27T03:52:38.227
4114
7174
answer


ガード条件を付けてみました。  
`send_keys_orig` がすでに存在していればエイリアスメソッドは作らないようにしています。

[code]

    class Selenium::WebDriver::Element
      unless instance_methods.include?(:send_keys_orig)
        alias_method :send_keys_orig, :send_keys
      end 
    
      def send_keys(*args, clear: false)
        __send__(:clear) if clear
        send_keys_orig(*args)
      end
    end
    
[/code]

参考文献: [Effective
Ruby](http://www.amazon.co.jp/exec/obidos/ASIN/4798139823/junic05-22/ref=nosim/)
第5章


CC BY-SA 3.0
2015-02-27T04:50:26.523
7191
2015-02-27T04:50:26.523
85
7174
answer


[Module#prepend](http://docs.ruby-
lang.org/ja/2.2.0/method/Module/i/prepend.html)を使いましょう。

[code]

    module SendKeysWithOption
      def send_keys(*args, clear: false)
        __send__(:clear) if clear
        super(*args)
      end
    end
    
    # 何度Module#prependが呼ばれても元のメソッドは消えないので問題なく動く
    Selenium::WebDriver::Element.prepend(SendKeysWithOption)
    
[/code]


CC BY-SA 3.0
2015-02-27T05:04:12.333
7192
2015-02-27T05:04:12.333
2599
7174
answer