Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

template accessor inside tools #50

Open
AlexWayfer opened this issue Jul 10, 2020 · 3 comments
Open

template accessor inside tools #50

AlexWayfer opened this issue Jul 10, 2020 · 3 comments

Comments

@AlexWayfer
Copy link
Contributor

Hello.

I use Toys::Template. It's a good way to create gems with a set of toys tools which can be expanded.

But it's hard to configure: you have to receive options (of expand) in Template#initialize, then you have to define tools inside Template.on_expand block, and if you want to access options from Template#initialize — you have to access template argument of on_expand block and pass it to tools code. Also, you have to write to_run do instead of def run for accessing local variable. And there is many problems and template variable passing through methods if you split code to modules, files, like separate tools or a module for common code between tools.

#46 can help a bit, but there is another idea and a way to simplify a code:

Instead of receiving template argument inside on_expand block make it available through getter inside tools.

Some work-around via instance variable you can see here: https://github.com/AlexWayfer/gem_toys/blob/715dd1e/lib/gem_toys/template.rb

Do I explain the subject clearly? What do you think about this?

@dazuma
Copy link
Owner

dazuma commented Jul 11, 2020

I agree, this is a bit awkward. The challenge, though, is that the scope of a template is lexical (that is, they generate code), whereas the scope of a getter in a tool is dynamic (that is, it exists during a tool's execution). As a result, there isn't a clean mapping from one to the other, so it's not easy to decide which template a getter should return.

For example, suppose we wanted to define a getter called current_template to return the current template object. However, more than one template could participate in the generation of a tool, so which template should the getter return?

class Template1
  include Toys::Template
  to_expand do
    def template1_class
      current_template.class
    end
  end
end

class Template2
  include Toys::Template
  to_expand do
    def template2_class
      current_template.class
    end
  end
end

tool "foo" do
  expand Template1
  expand Template2
  def run
    # What would we expect to see here?
    puts template1_class
    puts template2_class
    puts current_template.class
  end
end

So while I agree that I wish there was a simpler way to pass information into a template, it's not clear to me what that should look like.

If you want to avoid using to_run (which I agree is ugly), you can create your own getter using static.

class MyTemplate
  include Toys::Template

  def initialize(foo: nil, bar: nil)
    @foo = foo
    @bar = bar
  end
  attr_reader :foo, :bar

  to_expand do |template|
    tool "display-my-template" do
      # Create a static context value with a getter method
      static :my_template, template

      def run
        # Now you can use it here.
        puts "foo is #{my_template.foo}"
        puts "bar is #{my_template.bar}"
      end
    end
  end
end

I know your next question is, can you do that inside subtool_apply so that it conveniently gets added to all the tools generated by your template. Yes you can, but I would be cautious because subtool_apply could "leak" that template information outside what's generated by the template, and that could be confusing. For example:

class LeakyTemplate
  include Toys::Template

  def initialize(foo: nil, bar: nil)
    @foo = foo
    @bar = bar
  end
  attr_reader :foo, :bar

  to_expand do |template|
    subtool_apply do
      # Create this getter method for all tools
      static :the_template, template
    end
    tool "a-generated-tool" do
      def run
        puts "foo is #{the_template.foo}"
      end
    end
    tool "another-generated-tool" do
      def run
        puts "bar is #{the_template.bar}"
      end
    end
  end
end

tool "namespace" do
  # this defines "a-generated-tool" and "another-generated-tool"
  expand LeakyTemplate, foo: 1, bar: 2

  tool "unrelated-tool" do
    # Because LeakyTemplate used subtool_apply, the getter
    # method is added to this subtool also.
    def run
      puts "leaked foo: #{the_template.foo}"
    end
  end
end

@AlexWayfer
Copy link
Contributor Author

For example, suppose we wanted to define a getter called current_template to return the current template object. However, more than one template could participate in the generation of a tool, so which template should the getter return?

Some unified template.

Hm…

Maybe these things (templates) should be more like module instead of class?

If you want to avoid using to_run (which I agree is ugly), you can create your own getter using static.

OK, thank you, I'll try.

@AlexWayfer
Copy link
Contributor Author

If you want to avoid using to_run (which I agree is ugly), you can create your own getter using static.

Seems non-working in my gem_toys project. IDK why exactly: because I'm trying to use it in a separate module for include, or because it in sub-tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants