How do you make ruby variables and methods in scope using Thor Templates?

老子叫甜甜 提交于 2019-12-13 00:53:36

问题


I'm trying to use the Thor::Actions template method to generate some C++ test file templates, but erb keeps telling me that I have undefined variables and methods.

Here's the calling code:

def test (name, dir)
  template "tasks/templates/new_test_file", "src/#{dir}/test/#{name}Test.cpp"
  insert_into_file "src/#{dir}/test/CMakeLists.txt", 
       "#{dir}/test/#{name}Test ", :after => "set(Local "
end

Here's the template:

<% test_name = name + "Test" %>
#include <gtest/gtest.h>
#include "<%= dir %>/<%= name %>.h"

class <%= test_name %> : public testing::Test {
protected:
    <%= test_name %> () {}
    ~<%= test_name %> () {}
    virtual void SetUp () {}
    virtual void TearDown () {}
};

// Don't forget to write your tests before you write your implementation!
TEST_F (<%= test_name %>, Sample) {
   ASSERT_EQ(1 + 1, 3);
}

What do I have to do to get name and dir into scope here? I have more complex templates that I need this functionality for too.


回答1:


ERB uses ruby's binding object to retrieve the variables that you want. Every object in ruby has a binding, but access to the binding is limited to the object itself, by default. you can work around this, and pass the binding that you wish into your ERB template, by creating a module that exposes an object's binding, like this:

module GetBinding
  def get_binding
    binding
  end
end

Then you need to extend any object that has the vars you want with this module.

something.extend GetBinding

and pass the binding of that object into erb

something.extend GetBinding
some_binding = something.get_binding

erb = ERB.new template
output = erb.result(some_binding)

for a complete example of working with ERB, see this wiki page for one of my projects: https://github.com/derickbailey/Albacore/wiki/Custom-Tasks




回答2:


I realize you already solved this, but I'm posting this answer in case someone else turns up looking for the solution to the question you asked (as I was).

Inside the class that #test belongs to, make an attr_accessor, then set its value in the same method that calls the template.

class MyGenerator < Thor

  attr_accessor :name, :dir

  def test (name, dir)
    self.name = name
    self.dir = dir
    template "tasks/templates/new_test_file", "src/#{dir}/test/#{name}Test.cpp"
  end
end

Note: that if you chain methods using #invoke, then a new instance of the class will be used for each invocation. Therefore you have to set the instance variable in the method with the template call. For example, the following wont work.

class MyGenerator < Thor

  attr_accessor :name

  def one (name)
    self.name = name
    invoke :two
  end

  def two (name)
    # by the time we get here, this is another instance of MyGenerator, so @name is empty
    template "tasks/templates/new_test_file", "src/#{name}Test.cpp"
  end

end

You should put self.name = name inside #two instead

For making generators, if you inherit from Thor::Group instead, all the methods are called in order, and the attr_accessor will be set up for you with the instance variables set for each method. In my case, I had to use Invocations instead of Thor::Group because I couldn't get Thor::Group classes to be recognized as subcommands of an executable.



来源:https://stackoverflow.com/questions/6629967/how-do-you-make-ruby-variables-and-methods-in-scope-using-thor-templates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!