Ruby templates: How to pass variables into inlined ERB?

前端 未结 10 1714
陌清茗
陌清茗 2020-11-30 21:18

I have an ERB template inlined into Ruby code:

require \'erb\'

DATA = {
    :a => \"HELLO\",
    :b => \"WORLD\",
}

template = ERB.new <<-EOF
          


        
10条回答
  •  伪装坚强ぢ
    2020-11-30 21:44

    For a simple solution, use OpenStruct:

    require 'erb'
    require 'ostruct'
    namespace = OpenStruct.new(name: 'Joan', last: 'Maragall')
    template = 'Name: <%= name %> <%= last %>'
    result = ERB.new(template).result(namespace.instance_eval { binding })
    #=> Name: Joan Maragall
    

    The code above is simple enough but has (at least) two problems: 1) Since it relies on OpenStruct, an access to a non-existing variable returns nil while you'd probably prefer that it failed noisily. 2) binding is called within a block, that's it, in a closure, so it includes all the local variables in the scope (in fact, these variables will shadow the attributes of the struct!).

    So here is another solution, more verbose but without any of these problems:

    class Namespace
      def initialize(hash)
        hash.each do |key, value|
          singleton_class.send(:define_method, key) { value }
        end 
      end
    
      def get_binding
        binding
      end
    end
    
    template = 'Name: <%= name %> <%= last %>'
    ns = Namespace.new(name: 'Joan', last: 'Maragall')
    ERB.new(template).result(ns.get_binding)
    #=> Name: Joan Maragall
    

    Of course, if you are going to use this often, make sure you create a String#erb extension that allows you to write something like "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2).

提交回复
热议问题