Ruby:捕获块中的多行

时间:2014-01-11 15:32:29

标签: ruby-on-rails ruby

我有一个rails生成器,可以根据某些erb模板创建文件。

我的目标是:

没有太复杂的模板文件。像这样:

# example of erb template file
<%= wrap_in_modules do
  "class Engine < ::Rails::Engine"
  if mountable?
    "isolate_namespace #{modules.first}"
  end
  "end"
end %>

在解析和创建之后,上面的例子应该变成:

module Naming
  module Extension
    class Engine < ::Rails::Engine
      isolate_namespace Naming
    end
  end
end

定义wrap_in_modules的方法应该 all 模板文件中do..end之间的行,以便使用该模块嵌套来处理它。

但目前却没有。它只需要最后一行。

这是我生成的文件的实际输出(与上面相同的例子)

module Naming
  module Extension
    end
  end
end

正如您所看到的,只有do..end之间的最后一行被包含在模块中。

好的,现在来了实际的wrap_in_modules方法。

def wrap_in_modules
  modules.reverse.inject(yield) do |content, mod|
    "module #{mod}\n" << content.lines.map { |line| "  #{line}" }.join << "\nend"
  end
end

我知道,它的documented:yield只返回块上计算的最后一个表达式。而proc.call

也是如此

我查看了导轨content_tag helper的来源,并尝试复制它,因为它与我想要的基本相同。但是将带有&符号的&block传递给方法并捕获它也不起作用。它只是空的!?

顺便说一下:另一件事是这个表示法将因erb:

的语法错误而失败
<%= wrap_in_modules do -%>
  Test test
<% end %>

lib/ruby/2.0.0/erb.rb:849:in `eval': (erb):1: syntax error, unexpected ')' (SyntaxError) ...r.concat(( wrap_in_modules do ).to_s); @output_buffer.concat...
...                               ^
(erb):4: syntax error, unexpected end-of-input, expecting ')'

@ output_buffer.force_encoding(的编码

我现在已经把我的大脑震撼了好几个小时......我怎么能得到的不仅仅是方法的最后一行...有人有想法吗?

3 个答案:

答案 0 :(得分:0)

您无法实现任何Ruby代码来完全按照您所希望的方式使用您的代码块。您有多个文字值但不对它们执行任何操作。这与询问如何执行此操作相同:

sum do
  1
  2
  3
end #=> 6

无法做到。相反,您需要调用某种方法来累积字符串。例如:

# Evaluate the block in a context where `s` is defined to add strings to an array
wrap_in_modules do
  s "class Engine < ::Rails::Engine"
  if mountable?
    s "isolate_namespace #{modules.first}"
  end
  s "end"
end

# Monkeypatch String class unary plus during this block invocation
# to add to your own collection
wrap_in_modules do
  +"class Engine < ::Rails::Engine"
  if mountable?
    +"isolate_namespace #{modules.first}"
  end
  +"end"
end

以下是使用细化和全局变量(bleah!)的示例:

module UglyAggregator
  refine String do
    def +@
      $all << self
    end
  end
end

# Do this within your actual scope    
using UglyAggregator
def many_strings(&block)
  $all = []
  yield
  $all.join
end

p many_strings{
  +"a"
  +"b"
}

#=> "ab"

答案 1 :(得分:0)

我自己定制了几个生成器,你基本上使用它们类似于rails视图:所有“固定”的文本只是“按原样”放置。它周围的红宝石意味着创建变量东西

类似的东西:

require 'spec_helper'

<% module_namespacing do -%>
describe <%= class_name %> do
<% for attribute in attributes -%>
  it { should validate_presence_of(:<%= attribute.name %>) }  
  it { should validate_uniqueness_of(:<%= attribute.name %>) }  
  it { should ensure_length_of(:<%= attribute.name %>).is_at_least(3).is_at_most(255) }
<% end -%>
end
<% end -%>

这是一种完全不同的方法,但更简单,因为您可以简单地编写代码,然后使用ruby“自定义”它...

答案 2 :(得分:0)

这将有效:

<%= wrap_in_modules do
  <<-STRING 
    class Engine < ::Rails::Engine
      #{mountable? ? ('isolate_namespace ' + modules.first.to_s) : ''}
    end
  STRING 
end %>