如何编写不与区块环境隔离的DSL?

发布于 2024-11-26 06:21:13 字数 730 浏览 3 评论 0原文

我正在尝试编写一个小型 Rub​​y DSL,但偶然发现了一个不便之处。现在我的“DSL”代码是 https://gist.github.com/0379b07f516f4f322204 和我的实现代码(在 .html.erb 文件内)是:

html_table_for @users do |t, user|
  t.field :username, link_to(user.username, :action => 'show', :id => user.id)
  t.field :email
  t.field :roles, user.roles.map(&:code).join(', ')
end

实现代码看起来不太像 DSL,因为t.field 而不是简单的field。通过用 instance_exec(@block) 替换 DSL 代码(第 34 行)中的 @block.call 我几乎得到了我想要的东西,但随后我失去了所有 ActionView 的优点(即link_to)。

有没有办法让我的小 DSL 类的实例方法在块中可用,同时保持 Rails 中包含的 ActionPack 辅助方法的可用性?

I am trying to write a small Ruby DSL and have stumbled upon an inconvenience. Right now my "DSL" code is https://gist.github.com/0379b07f516f4f322204 and my implementation code (inside an .html.erb file) is:

html_table_for @users do |t, user|
  t.field :username, link_to(user.username, :action => 'show', :id => user.id)
  t.field :email
  t.field :roles, user.roles.map(&:code).join(', ')
end

The implementation code does not look much like a DSL because of the t.field instead of simply field. By replacing @block.call in the DSL code (line 34) with instance_exec(@block) I almost get what I want but then I lose all the ActionView goodness (namely link_to).

Is there a way to make available in the block the instance methods of my little DSL class while at the same time keeping availability of the helper methods of ActionPack included in Rails?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半步萧音过轻尘 2024-12-03 06:21:13

我发布了类似的 StackOverflow 问题的详细答案。

简短的回答:查看 Docile gem - 它使您变得非常容易。

I posted a detailed answer to a similar StackOverflow question.

Short answer: check out the Docile gem -- it makes this very easy for you.

暗喜 2024-12-03 06:21:13

可以通过 使用方法委托

class TableHelper
  def initialize(&block)
    # get a reference to 'self' in the block's scope:
    @self_before_instance_eval = eval "self", block.binding
    instance_eval &block
  end

  # delegate all unknown methods to the calling object:
  def method_missing(method, *args, &block)
    @self_before_instance_eval.send method, *args, &block
  end

  # Other helper methods:

  def field(name, url)
    # ...
  end

end

现在您可以像这样使用它:

def some_helper(arg); end

Table.new do
  field :name, some_helper("foo")
end

但是使用此方法时要小心:您仍然无法使用调用对象中的实例变量:

@field_url = "http://foo"

Table.new do
  # url will be nil, since the ivar does not exist on the Table
  field :name, @field_url 
end

It is possible (albeit hacky and confusing) to get both, by using method delegation:

class TableHelper
  def initialize(&block)
    # get a reference to 'self' in the block's scope:
    @self_before_instance_eval = eval "self", block.binding
    instance_eval &block
  end

  # delegate all unknown methods to the calling object:
  def method_missing(method, *args, &block)
    @self_before_instance_eval.send method, *args, &block
  end

  # Other helper methods:

  def field(name, url)
    # ...
  end

end

Now you can use it like this:

def some_helper(arg); end

Table.new do
  field :name, some_helper("foo")
end

Be careful when using this approach though: you still can't use instance variables from the calling object:

@field_url = "http://foo"

Table.new do
  # url will be nil, since the ivar does not exist on the Table
  field :name, @field_url 
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文