方法用例太复杂?
现在我已经三天左右对抗这个问题了。 我创建了一个模拟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=
时,在实例的元数据中设置一个标志,说明调用者被调用。