# # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# This is an explanation of the framework watir element accessors
# this module does not run it is just for an example
# after thei module is the code that runs when you run this file: ruby watir_example.rb
# # # # # # # # # # # # # # # # # # # # # # # # # # # # 
module example
# In the app under test there are a lot of deeply nested frames and divs, in addition there is lots of javascipt and 
# Ajax going on that can change page layouts often.
# Every call to a Watir method should step up the DOM stack (actually a tree but I will logically call it a stack) 
# to Browser to ensure we have the latest handles on all elements.
# 
# For example:
  def context__frame
    @browser.frame(:name, 'Context')
  end
  def application__frame
    context__frame.frame(:name, 'Application')
  end
  def main__frame
    application__frame.frame(:name, 'Main')
  end
  def main__div
    main__frame.div(:id, 'Main')
  end
# 
# A call to main__div() calls main_frame() that calls application__frame() that calls context__frame().
# 
# All good, but I should extract out the watir how, what info to a single source. I created hashes of the how whats:
# 
    @elements = {
      :context__frame       => {:how => :name, :what => 'Context'},
      :application__frame   => {:how => :name, :what => 'Application'},
      :main__frame          => {:how => :name, :what => 'Main'},
      :main__div            => {:how => :id, :what => 'Main'},
    }

# Now the methods look like this:
  def context__frame
    field = @elements[:context__frame]
    @browser.frame field[:how], field[:what]
  end
  def application__frame
    field = @elements[:application__frame]
    context__frame.frame field[:how], field[:what]
  end
  def main__frame
    field = @elements[:main__frame]
    application__frame.frame field[:how], field[:what]
  end
  def main__div
    field = @elements[:main__div]
    main__frame.div field[:how], field[:what]
  end

# The concept here is that each method up the stack represents the container of the caller.
# Note the method names are the same as the hash keys.
# This is a trivial example, but you get the picture, I ended up with 50 or so methods that look essentially the same, 
# and more would be added all the time.
# 
# Bring on method_missing and ghost methods. All I needed to do is add the container of each element 
# to my hash (@elements in this case). The hash now looks like this:

    @elements = {
      :context__frame       => {:how => :name, :what => 'Context'},
      :application__frame   => {:who => :context__frame, :how => :name, :what => 'Application'},
      :main__frame          => {:who => :application__frame, :how => :name, :what => 'Main'},
      :main__div            => {:who => :main__frame, :how => :id, :what => 'Main'},
    }

# Now I have the who as well as the how and what.
# Next I wrote the method_missing and the handler method:

  def method_missing(method, *args, &block)
    if method.to_s =~ /__(\w+)$/
      get_field_how_what($1.to_sym, method.to_sym)
    else
      super # let Ruby continue with method lookup
    end
  end
 
  def get_field_how_what(watir_method, method_key)
    container = @elements[method_key][:who]
    how = @elements[method_key][:how]
    what = @elements[method_key][:what]
 
    # if the method exists - call it
    if self.respond_to? container
      # method exists
      element = method(container).call
    else
      # method not exist
      element = self.send :method_missing, container
    end
    # parent element exists - call the watir method on it
    element.method(watir_method).call how, what
  end

# Then I deleted all the methods (in this example main__div(), main_frame() 
# and application__frame()) but left context__frame() as it is the top level method that uses @browser.
# 
# This works by using 'ghost methods', methods that do not exist until you define them at runtime.
# In method_missing() I catch any missing method that ends with __frame, __div, __button, etc, and 
# pass these off to get_field_how_what().
# I know the watir method to call from the regex match on the missing method name. 
# 
# Using the method name I can access the global hash of all the fields (@elements) and know what the 
# parent container is (:who) and how to access the element (:how, :what).
# If the parent container method exists, I call it, returning the parent element.
# If the container method does not exist, I call method_missing again with the container (:who). 
# This gets done repeatedly until we get to the top level method, then each parent is returned, 
# effectively moving back down the stack to the element we want.
# 
# I have created an example that hits the Ruby web site and accesses some elements:
end

# # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# this is the code that runs when this file is run:
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
require 'rubygems'
require 'watir-classic'

class Watir_example
  def initialize
    @elements = {
      :page__div            => {:how => :id, :what => 'page'},
      :main_wrapper__div    => {:who => :page__div, :how => :id, :what => 'main-wrapper'},
      :main__div            => {:who => :main_wrapper__div, :how => :id, :what => 'main'},
      :content_wrapper__div => {:who => :main__div, :how => :id, :what => 'content-wrapper'},
      :head_wrapper_1__div  => {:who => :content_wrapper__div, :how => :id, :what => 'head-wrapper-1'},
      :head_wrapper_2__div  => {:who => :head_wrapper_1__div, :how => :id, :what => 'head-wrapper-2'},
      :head__div            => {:who => :head_wrapper_2__div, :how => :id, :what => 'head'},
      :intro__div            => {:who => :head__div, :how => :id, :what => 'intro'},
    }
  
    @browser = Watir::Browser.new
    @browser.goto 'http://www.ruby-lang.org/en/'
  end
  
  def page__div
    field = @elements[:page__div]
    @browser.div(field[:how], field[:what])
  end
  
  def method_missing(method, *args, &block)
    if method.to_s =~ /__(\w+)$/
      get_field_how_what($1.to_sym, method.to_sym)
    else
      super # let Ruby continue with method lookup
    end
  end
  
  def get_field_how_what(watir_method, method_key)
    container = @elements[method_key][:who]
    how = @elements[method_key][:how]
    what = @elements[method_key][:what]
  
    puts "get_field_how_what: #{method_key}"
    puts "container: #{container}"
    # if the method exists - call it
    if self.respond_to? container
      # method exists
      puts "  ->method(#{container}).call"
      element = method(container).call
    else
      # method not exist
      puts "  ->method_missing(#{container})"
      element = self.send :method_missing, container
    end
    # parent element exists - call the watir method on it
    puts "  ->#{element}.#{watir_method}(:#{how.to_s}, '#{what}')"
    element.method(watir_method).call how, what
  end
end

intro_div = Watir_example.new.intro__div
puts intro_div.text

