6986


Rubyで`selenium-webdriver`を使用してるときに

[code]

    Selenium::WebDriver.for :phantomjs
    driver.find_element(:id, "foo").clear
    driver.find_element(:id, "foo").send_keys("bar")
    
[/code]

のようにテキストボックスを埋める前に`clear`で空にすることがよくあるために  
`send_keys(arg1, clear: true)`で事前に内容を消してから実行できるメソッドで置き換えをしようとしましたがうまくいきませんでした。

[code]

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

ですと、`SystemStackError: stack level too deep`になってしまいました。  
またメソッド名と同名のハッシュキーが使えなかったため`clear_flag`にしています。

現状は別名のメソッドを使っています。既存クラスのメソッド置き換えは危険なのは理解してるのですが、個人的に使うものなので、長いメソッド名よりもキーワード引数で指定したく考えています。

[code]

    class Selenium::WebDriver::Element
      def send_keys_after_clear(*args)
        clear
        send_keys(args)
      end
    end
    
[/code]

どのようにすれば`send_keys(args, clear: true)`の形で上記の処理が実現できますか？

### stack level too deep に関しての追記

問題が再現できなくなってしまっていたのですが、再現方法がわかりましたので追記しておきます。

`send_keys`をオーバーライドしたメソッドを定義したファイルを`selenium_webdriver_ext.rb`という名前で保存して`pry`上で動かしていたのですが、他の部分を読み込み直すために`load
'selenium_webdriver_ext.rb'`を複数回読んでしまっていたためにオーバーライドしたメソッドを更にオーバーライドしようとし続けてエラーになっていたようです。

具体的を上げておきます。

[code]

    [1] pry(main)> require 'selenium-webdriver'
    => true
    [2] pry(main)> driver = Selenium::WebDriver.for :firefox
    => #<Selenium::WebDriver::Driver:0x..f9315747cd0887642 browser=:firefox>
    [3] pry(main)> driver.navigate.to("http://google.co.jp")
    => ""
    [6] pry(main)> load 'selenium_webdriver_ext.rb'
    => true
    [7] pry(main)> driver.find_element(:id, "lst-ib").send_keys("foo")
    => ""
    [8] pry(main)> load 'selenium_webdriver_ext.rb'
    => true
    [9] pry(main)> driver.find_element(:id, "lst-ib").send_keys("foo")
    SystemStackError: stack level too deep
    
[/code]


CC BY-SA 3.0
2015-02-23T02:22:33.553
6938
2015-02-27T11:01:46.823
2015-02-27T01:57:33.570
3271
3271
question
ruby selenium-webdriver
既存クラスのメソッドに追加のオプションをつける方法


実行・検証してないので、間違っていればゴメンナサイ。

# メソッド定義

ソース [element.rb -
selenium](https://code.google.com/p/selenium/source/browse/rb/lib/selenium/webdriver/common/element.rb)
から `send_keys` メソッドの定義部分を引き抜いていじりました。

[code]

    module Selenium
      module WebDriver
        class Element
          alias_method :send_keys_orig, :send_keys
          def send_keys(*args, clear_flag: false)
            clear if clear_flag
            bridge.sendKeysToElement @id, Keys.encode(args)
          end
        end
      end
    end
    
[/code]

としてみてはいかがでしょうか。

# キーワード引数と既存メソッド名の重複

テキトーにクラス作ってメソッド aaa を作って、他のメソッドのキーワード引数に aaa
を使って確認し、特に問題生じないですよ、と書こうとしたのですが、今回は

[code]

    clear if clear # 前者はメソッド、後者はローカル変数/引数
    
[/code]

を実現したいのだ、ということに気付きました。

## 1. `__send__` でメソッドへ送ってしまう

上のメソッド定義と組み合わせて

[code]

    module Selenium
      module WebDriver
        class Element
          alias_method :send_keys_orig, :send_keys
          def send_keys(*args, clear: false)
            __send__(:clear) if clear
            bridge.sendKeysToElement @id, Keys.encode(args)
          end
        end
      end
    end
    
[/code]

でどうでしょう。

## 2. メソッドであることを `()` で明示する

[ruby2.2.0ではメソッドの引数のデフォルト値に同じ名前の変数を使えない -
Qiita](http://qiita.com/a_ishidaaa/items/47a8459aa5fe5512871a)  
の回避法で Matz が

>
[code]

>     In other part of Ruby, foo = foo makes foo to nil.
>     To get old behavior, try foo = foo().
>  
[/code]

>
> [Bug #10314: Default argument lookup fails in Ruby 2.2 for circular  
>  shadowed variable names - Ruby trunk - Ruby Issue Tracking  
>  System](https://bugs.ruby-lang.org/issues/10314#note-4)

と指摘してます。  
つまり、 `()` を付けちゃえばメソッド、無ければローカル変数。

[code]

    module Selenium
      module WebDriver
        class Element
          alias_method :send_keys_orig, :send_keys
          def send_keys(*args, clear: false)
            clear() if clear
            bridge.sendKeysToElement @id, Keys.encode(args)
          end
        end
      end
    end
    
[/code]


CC BY-SA 3.0
2015-02-23T04:20:10.450
6947
2015-02-23T09:03:06.317
2015-02-23T09:03:06.317
4114
4114
6938
answer


同じ環境を作成してエラーを再現しようとしましたができませんでした。

こちらにコードを置いています。

<https://github.com/JunichiIto/sample-app-for-send_keys-alias/>

エイリアスメソッドはここで定義しています。

<https://github.com/JunichiIto/sample-app-for-send_keys-alias/blob/so-
sandbox/spec/support/selenium_support.rb>

`send_keys`メソッドを呼び出しているのはこちらです。

<https://github.com/JunichiIto/sample-app-for-send_keys-alias/blob/so-
sandbox/spec/features/users_spec.rb#L19>

RSpecでこのコードを実行すると、問題なくパスします。

![画像の説明をここに入力](https://i.stack.imgur.com/Ktb0s.png)

Rubyのバージョンは2.2.0、selenium-webdriverのバージョンは2.45.0.dev3です。  
（dev3を使っているのは[このエラー](https://stackoverflow.com/questions/28159499/seleniumwebdrivererrorjavascripterror-
arguments0-is-undefined)をなくすためです）

ご自身の環境に僕のコードをダウンロードしてみて問題が再現するか確認してみてください。


CC BY-SA 3.0
2015-02-23T23:15:18.870
6986
2015-02-23T23:15:18.870
2017-05-23T12:38:56.083
-1
85
6938
answer


Ruby 2.0以上を使っているのであれば[Module#prepend](http://docs.ruby-
lang.org/ja/2.2.0/method/Module/i/prepend.html)を使うとalias method
chainのようなことをしなくても同じようなことが出来て便利です。

参考: [» Ruby2.0のModule#prependは如何にしてalias_method_chainを撲滅するのか！？ TECHSCORE
BLOG](http://www.techscore.com/blog/2013/01/22/ruby2-0%E3%81%AEmodule-
prepend%E3%81%AF%E5%A6%82%E4%BD%95%E3%81%AB%E3%81%97%E3%81%A6alias_method_chain%E3%82%92%E6%92%B2%E6%BB%85%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%81%EF%BC%9F/)

[code]

    module SendKeysWithOption
      def send_keys(*args, clear: false)
        __send__(:clear) if clear
        super(*args)
      end
    end
    
    Selenium::WebDriver::Element.prepend(SendKeysWithOption)
    
[/code]


CC BY-SA 3.0
2015-02-27T11:01:46.823
7226
2015-02-27T11:01:46.823
2599
6938
answer