在Sinatra辅助方法中使用here string和lambda

时间:2018-06-18 21:22:00

标签: ruby sinatra

我正在尝试创建一个返回动态生成的HTML的Sinatra帮助器。我以为我会在这里使用一个字符串作为静态位,使用一个lambda来计算动态部分。

foo_helper.rb

require 'erb'
module FooHelper

  def tabs(selected)

    template = ERB.new <<~HERE
      <ul class="nav nav-tabs">
        <li class="nav-item"><a class="nav-link <%= 'active' if selected == 'favorites' %>" href="/foo/favorites">Favorites</a></li>
        <li class="nav-item"><a class="nav-link <%= 'active' if selected == 'all' %>" href="/foo">All</a></li>
        <%= alpha.call %>
      </ul>
      HERE

    # binding to a string works as expected    
    # alpha = "<li class='nav-item'><a class='nav-link' href='/foo/a'>A</a></li>"

    # binding to a lambda, doesn't
    alpha = lambda {
      ('a'..'z').each do |letter|
        "<li class='nav-item'><a class='nav-link #{ 'active' if selected == letter }' href='/foo/#{letter}'>#{letter}</a></li>"
      end
    }

    template.result(binding)

  end

end

foo_controller.rb

class FooController < ApplicationController
  helpers FooHelper if defined?(FooHelper)
  ...
end

index.erb

...
<%= tabs('favorites') %>
...

结果:

enter image description here

显示范围,而不是单个li

我在lambda中遗漏了什么吗?

**编辑**

纠正了众多错误。

2 个答案:

答案 0 :(得分:1)

您的代码中存在太多错误。

  1. @nav在模块级别定义,但在实例级别访问,因此您在需要时获得nil
  2. 当您调用lambda时,您需要在变量名称和左括号之间加一个点,例如foo.(123)
  3. @nav.foo(binding),真的吗?什么是(假设的)@nav类型?该类型是否具有实例方法foo
  4. <%= foo %>不会执行foo,因为它是局部变量,而不是方法。

答案 1 :(得分:0)

之所以渲染a..z是因为Range#each方法(从lambda内部调用)对每个元素执行给定的块,然后再次返回范围本身。

您想在这里使用的是Enumerable#map方法。与#each类似,它也为每个元素执行一个块,但是该块的返回值将在新数组中返回。

为进行比较:

p ("a".."c").each { |x| x.upcase }
#=> "a".."c"

p ("a".."c").map  { |x| x.upcase }
#=> ["A", "B", "C"]