方法用例太复杂?

现在我已经三天左右对抗这个问题了。 我创建了一个模拟HTML页面的类,并告诉黄瓜步骤定义填充表单数据的位置:

class FlightSearchPage

  def initialize(browser, page, brand)
    @browser = browser
    @start_url = page

    #Get reference to config file
    config_file = File.join(File.dirname(__FILE__), '..', 'config', 'site_config.yml')

    #Store hash of config values in local variable
    config = YAML.load_file config_file

    @brand = brand #brand is specified by the customer in the features file

    #Define instance variables from the hash keys
    config.each do |k,v|
      instance_variable_set("@#{k}",v)
    end
  end

  def method_missing(sym, *args, &block)
    @browser.send sym, *args, &block
  end

  def page_title
    #Returns contents of <title> tag in current page.
    @browser.title
  end

  def visit
    @browser.goto(@start_url)
  end

  def set_origin(origin)
    self.text_field(@route[:attribute] => @route[:origin]).set origin
  end

  def set_destination(destination)
    self.text_field(@route[:attribute] => @route[:destination]).set destination
  end

  def set_departure_date(outbound)
    self.text_field(@route[:attribute]  => @date[:outgoing_date]).set outbound
  end

  # [...snip]

end

正如你所看到的,我已经使用instance_variable_set创建了变量,这些变量持有引用,并且变量名和值由配置文件提供(它可以被不需要熟悉的人编辑红宝石)。

不幸的是,这是一个很大的毛茸茸的类,每次我想添加一个新的字段时,我将不得不编辑源代码,这显然是糟糕的设计,所以我一直试图进一步创建一个阶段方法使用define_method动态地设置变量名,这就是让我在过去的几个夜晚一直保持清醒到凌晨4点。

这是我所做的:

require File.expand_path(File.dirname(__FILE__) + '/flight_search_page')

class SetFieldsByType <  FlightSearchPage
  def text_field(config_hash)
    define_method(config_hash) do |data|
      self.text_field(config_hash[:attribute] => config_hash[:origin]).set data
    end
  end
end

这个想法是,你需要做的所有事情就是添加一个新字段,在YAML文件中添加一个新条目,并且define_method将创建允许黄瓜填充它的方法。

目前,我遇到了范围问题 - Ruby认为define_method是@browser的成员。 但我想知道的是:这是否可行? 我完全误解了define_method吗?


对于元编程来说这是一个合适的例子,但是看起来你正在以错误的方式去做。

首先,FlightSearchPage的每个实例是否会有不同的配置文件,或者只有一个用于控制所有页面的配置文件? 它看起来像加载相同的配置文件,无论参数initialize所以我猜你的情况是前者。

如果是这样,您需要将所有元程序代码移入类中(方法定义之外)。 也就是说,在定义类时,您希望它加载配置文件,然后根据该配置创建每个实例。 现在你每次创建实例时都会重新载入配置文件,这看起来不正确。 例如, define_method属于Module所以它应该出现在类作用域中,而不是出现在实例方法中。

另一方面,如果您确实需要为每个实例配置不同的配置,则需要将所有元编程代码移入单例类,例如define_singleton_method而不是define_method


你的意思是你希望看到类定义之外的需求和文件加载?

不,在班级定义里面。 Ruby类声明只是以它看到的顺序执行的代码。 像attr_accessor这样的东西只是类方法,它会在被定义的类中对事物做些事情,因为它正在被定义。 这看起来像你想要做的。

在你的情况下,你应该读取YAML文件,然后运行你自己的逻辑来创建访问器,创建任何需要的后备数据等。我不完全赞成这个用例,但它听起来并不奇怪或困难 - 但。

也就是说,通过将这些定义放在YAML文件中可以获得多少“便利”? 考虑一下我曾经创建过的用于驱动Watir的页面实例:

class SomePage < HeavyWatir
  has_text :fname     # Assumed default CSS accessor pattern
  has_text :whatever, accessor: 'some accessor mechanism', option: 'some other option'
end

has_xxx是创建实例变量访问器的类方法(就像attr_accessor一样),构建了一些其他的数据结构,我使用它们来确保页面上应该实际上存在的所有内容,等等。 例如,非常粗略地说:

page = SomePage.new
page.visit
if page.missing_fields?
  # Do something saying the page isn't complete
end

这听起来像是你想要一些模糊的类似的东西:你想要给这个类(或者一个子类,或者你可以把它混合到一个任意的类中,等等)中有一堆“东西”

这些“东西”具有附加功能,该功能以多种方式工作,如:

事情-那-发生-中定义

例如, has_text将该名称添加到页面元数据的类实例散列中,如字段名称。

事情-那-发生-,在使用过程中

例如,当调用fname=时,在实例的元数据中设置一个标志,说明调用者被调用。

链接地址: http://www.djcxy.com/p/25767.html

上一篇: method use case too complex?

下一篇: Setting variable A with name stored in variable B