Ruby 在元素声明中为id使用变量

Ruby 在元素声明中为id使用变量,ruby,metaprogramming,watir-webdriver,page-object-gem,Ruby,Metaprogramming,Watir Webdriver,Page Object Gem,我有一个超类DemoPage和几个子类SDemoPage,EDemoPage,等等。所有这些页面上都有相同的元素,SDemoPage上的id看起来像s-demo-test1上的相应id看起来像e-demo-test1。在我的DemoPage类中,我声明了所有元素,我需要一种方法来添加子类(调用类)的id。这就是我目前拥有的 class DemoPage include PageObject @@subclass_ids = {"SDemoPage" => "s-demo-",

我有一个超类
DemoPage
和几个子类
SDemoPage
EDemoPage
,等等。所有这些页面上都有相同的元素,
SDemoPage
上的id看起来像
s-demo-test1
上的相应id看起来像
e-demo-test1
。在我的
DemoPage
类中,我声明了所有元素,我需要一种方法来添加子类(调用类)的id。这就是我目前拥有的

class DemoPage
  include PageObject

  @@subclass_ids = {"SDemoPage" => "s-demo-",
   "EDemoPage" => "e-demo-"}

  # class << self; attr_accessor :current_class_id; end

  def initialize(browser, something)
    initialize_elements
    super(browser, something)
  end

  .........

  def initialize_elements
    self.instance_eval do
      select_list(:facility, id: "#{@current_class_id}facility")
      text_field(:account, id: "#{@current_class_id}account")
      text_field(:service, id: "#{@current_class_id}service")
   end
  end
end
但这不允许我设置基于其他调用类的id

我还留下了一些其他尝试的代码,比如使用哈希查找,但在页面对象方法已经意识到变量/method@current_class_id不存在之后,这些仍然在initialize方法中设置实例变量

我不知道从这里到哪里去


编辑: 所以我终于找到了答案,但我不确定这是否是解决这个问题的最佳方法:

class DemoPage
  include PageObject
  include DataMagic

  def initialize(browser, visit)
    super(browser, visit)
    instantiate_elements self.class
  end

  def instantiate_elements klass
    klass.class_eval do
      select_list(:facility, id: "#{@class_id}facility")
      text_field(:account, id: "#{@class_id}account")
      text_field(:service, id: "#{@class_id}service")
    end
  end
end

class-SDemoPage
这样做有什么不对吗?我确实需要对initialize方法进行修补,因为在这里我没有展示其中的其他内容。既然我已经需要这样做了,那么有什么理由不在
SDemoPage
的上下文中而不是在实际的
DemoPage
中“创建”元素呢?还是贾斯汀的回答完全是更好的做事方式

编辑2:
我接受了Justin的答案,因为在极少数情况下,我不需要在PageObject中对initialize方法进行猴子补丁,他的答案会减少重复和代码总量

我认为最简单的解决办法是:

  • 为调用访问器方法的DemoPage定义类方法
  • 在子页面中调用此方法,将id部分作为参数传递
  • 演示页面将是:

    class DemoPage
        include PageObject
    
        def self.common_demo_elements(class_id)
            select_list(:facility, id: "#{class_id}facility")
            text_field(:account, id: "#{class_id}account")
            text_field(:service, id: "#{class_id}service")      
        end
    end
    
    class SDemoPage < DemoPage    
        common_demo_elements("s-demo-")
    end
    
    而子页面将是:

    class DemoPage
        include PageObject
    
        def self.common_demo_elements(class_id)
            select_list(:facility, id: "#{class_id}facility")
            text_field(:account, id: "#{class_id}account")
            text_field(:service, id: "#{class_id}service")      
        end
    end
    
    class SDemoPage < DemoPage    
        common_demo_elements("s-demo-")
    end
    
    class-SDemoPage
    select\u list是哪一类的方法?页面对象模块hmm这是一个好方法。事实上,我不久前就知道了,但我正在测试以确保它能正常工作。我会试试你的方法,看看是否更好。我会发布我的方法,你能告诉我是否有任何理由让我不这样做吗?事实上我喜欢你的答案。如果我实际上不需要对initialize方法进行修补,那么您的答案会更好。谢谢你的帮助,我知道你会是那个回应并帮助我的人;)我看不出任何明显的理由说明你的方法行不通。虽然我想知道我们是不是把事情弄得更复杂了。为什么不对id使用regexs呢?例如
    select_list(:facility,id:/facility/)
    ?啊,因为我们使用的是GWT,GWT的一个副作用是整个html被加载到页面中。因此,即使某个“模块”不可见,也永远不会可见,html仍然存在。因此,id仍然存在,我们必须使用精确匹配。否则,watir webdriver可能会在遇到可见id之前找到一个不可见的id,然后会出现
    ElementNotClickable
    错误或其他错误。
    class SDemoPage < DemoPage    
        common_demo_elements("s-demo-")
    end