把这个haml片段变成一个帮手来干掉Sinatra

时间:2012-12-01 04:03:27

标签: ruby sinatra haml

这一点haml有效:

%select{ :name => 'color', :value => @p.color}
  - Person.color.options[:flags].each do |colors|
    - if @p.color == colors
      %option{:value => colors, :selected => "selected"}= colors
    - else
       %option{:value => colors}= colors

我试图用它创建一个帮助器,以便我可以重复使用它:

helpers do
  def options(prop, p)
    Person.prop.options[:flags].each do |x|
      if p.prop == x
        "%option{:value => #{x}, :selected => 'selected'}= #{x}"
      else
        "%option{:value => #{x}}= #{x}"
      end  
    end
  end
end

然后用:

调用它
%select{ :name => 'color', :value => @p.color}
  - options(color, @p)

但我收到此错误:undefined local variable or method 'color'

我离得很远吗?

编辑2:

循环中正在发生一些时髦的事情。

即便是一个简单的例子:

helpers do
  def options(prop, p)
    Person.send(prop).options[:flags].each do |x|
    "<p>test</p>"
    end
  end
end

= options(:color, @p)

打印一个选项数组(在我的情况下为[:red, :blue, :yellow])并且不插入任何html。但是,如果我执行puts <p>test</p>,它会经历三次循环并正确打印它们 - 它们只是不会显示在HTML中。

2 个答案:

答案 0 :(得分:1)

使用符号代替未定义的方法:

options(:color, @p)

而不是:

options(color, @p)

并使用辅助方法使用send

if p.send(prop) == x

而不是

if p.prop == x

以及:

Person.send(prop)

而不是:

Person.prop

此外,我有疑问HAML会接受像"%option{:value => #{x}"这样的字符串作为标记。

您可以使用HTML代替或只是找到另一种干燥方法,例如渲染部分或使用haml_tag

def options(prop, p)
  Person.send(prop).options[:flags].each do |x|
    haml_tag :option, "#{x}", :value=>x
  end
end

如果您使用haml_tag将其与- options(:color, @p)

一起使用

答案 1 :(得分:1)

这里有几件事情要发生。首先,消息undefined local variable or method 'color'由行

引起
- options(color, @p)
你在哈姆尔。这里color是未定义的局部变量。如果我理解正确,你有一个具有各种属性的Person类,每个类都有几个可能的选项,你希望能够选择使用哪一个而不需要对其进行硬编码并需要多个帮助方法。一种方法是更改​​行

Person.prop.options[:flags].each do |x|

在你的助手

Person.send(prop).options[:flags].each do |x|

然后传递一个符号,指定在调用帮助程序时使用的属性:

- options(:color, @p)

下一个问题是将生成的代码写入输出。您的助手可以返回一个可以包含=的字符串,也可以使用haml_taghaml_concat等助手直接写入输出。请注意,您不应使用haml_taghaml_concat的返回值,否则会产生错误。

所以在这里你可以在帮助器中创建HTML:

if p.prop == x
  "<option value='#{x}' selected='selected'>#{x}</option>"
else
  "<option value='#{x}' />#{x}</option>"
end

然后将其与=一起使用(如果使用-,输出将被忽略):

= options(:color, @p)

如果这样做,您需要确保帮助程序返回要嵌入Haml的字符串。在这种情况下,帮助程序返回调用eachwhich is the array itself的值。您需要使用mapjoin之类的内容来创建所需的字符串:

def options(prop, p)
  Person.send(prop).options[:flags].map do |x|
    if p.prop == x
      "<option value='#{x}' selected='selected'>#{x}</option>"
    else
      "<option value='#{x}' />#{x}</option>"
    end
  end.join("\n")
end

另一种方法是使用haml_tag直接写输出。

if p.prop == x
  haml_tag :option, x, :value=>x, :selected => true
else
  haml_tag :option, x, :value =>x
end

请注意,在Haml中,attribute hash that has a boolean value中的任何条目只会在值为true时输出,并且会根据输出正确格式化,例如selected='selected'获得了XHTML,只有selected用于HTML。所以最后一个例子可以简化为

haml_tag :option, x, :value=>x, :selected => (p.prop == x)

(你总是可以用类似的方式简化另一个例子(返回一个字符串),例如#{"selected='selected'" if p.prop == x},但是Haml内置了它。)