3
\$\begingroup\$

In my previous Watir question I was making a module with session_FF accepting a block.
Now I want to make the same but via RSpec. Here is my try:

require "rspec"
require "watir"

module Session_FF
  def method_missing m, *args, &block
    @browser.send m, *args, &block
  end
  def element *args
    elements = @browser.elements *args
    case elements.size
    when 1 ; elements.first
    when 0 ; raise "found no nodes using search args: #{args}"
    else   ; raise "found #{es.size} nodes -- use #elements method or improve args"
    end
  end
end

RSpec.configure do |config|
  config.include Session_FF
  config.before{ @browser ||= Watir::Browser.new :ff}
  config.after{ @browser.quit }
end

describe "it" do
  it "is alive" do
    goto "http://example.com/"
    element(class: "userpanel").click
  end
end

I see a problem, that previously I was defining method_missing in my own module but now include may redefine RSpec::Core::ExampleGroup or smth's #method_missing if it already exists or may appear in RSpec implementation. What is a better way for method_missing approach to forward browser interaction methods into @browser?

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

As far as I can tell, you have 4 options:

  1. Use the current code as-is (and, as you say, risk stepping on an existing method_missing).

  2. You can use RSpec's config.extend to at least limit the inclusion.

  3. You can skip method_missing in favor of creating a module explicit method declarations for anything you want to forward to @browser. This is no doubt the safest, but also the most laborious, solution. Still, I'd consider it

  4. You can do something like below, which, admittedly, is somewhat hacky.

module Session_FF

  # Alias existing method_missing implementation when being included
  def Session_FF.append_features(target)
    if target.instance_methods.include?(:method_missing)
      target.send(:alias_method, :overriden_method_missing, :method_missing)
    end
    super
  end

  # Define your own method_missing which forwards to @browser
  # or to the previous method_missing (if any)
  def method_missing(name, *args, &block)
    if @browser.respond_to?(name)
      @browser.send(name, *args, &block)
    elsif respond_to?(:overriden_method_missing)
      overriden_method_missing(name, *args, &block)
    end
  end

  def element(*args)
    # ...
  end

end

Should work, although if @browser also relies on method_missing without a reliable respond_to?, you might have better luck simply trying to call a method @browser and rescue a NoMethodError. Not very pretty, though.

On a side-note, I think it's perhaps a little strange that the module is named _FF when the @browser could be something other than Firefox. WatirSession might be a more inclusive name.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.