将变量传递给Liquid模板中的模型实例方法

aar*_*ell 6 ruby template-engine liquid

本周末我一直在玩液体模板引擎,我想知道以下是否可行.

假设我latest_postsBlog模型中有一个方法,我可以传递一个整数来获取最新的N个帖子.是否可以在液体模板中使用该方法?

例如:

class Blog

  has_many :posts

  def latest_posts(n)
    posts.latest(n) # using a named scope
  end

  def to_liquid(*args)
    {
      'all_posts' => posts.all,  # allows me to use {% for posts in blog.all_posts %}
      'last_post' => post.last,  # allows me to use {% assign recent = blog.last_post %}
      'latest_posts' => posts.latest_posts(args[0])  # how do I pass variables to this?
    }
  end

end
Run Code Online (Sandbox Code Playgroud)

在上面的简单的例子,在我的液体模板,我可以使用blog.all_postsblog.last_post,但不知道我会怎么做像什么blog.latest_posts: 10.

有人能指出我正确的方向吗?

我想到的一个想法是创建一个Liquid过滤器并将Blog对象和整数传递给它.就像是:

{% for post in blog | latest_posts(10) %}
Run Code Online (Sandbox Code Playgroud)
  • 但是还没试过,因为我觉得我在黑暗中刺伤了一下.非常感谢更有经验的Liquid用户提供的一些帮助.

aar*_*ell 9

在这里回答我自己的问题,我在Liquid groups页面中找到了一个解决方案.

从本质上讲,我需要为最新的帖子创建一个drop - a LatestPostsDrop- 以及使用该before_method方法将变量传递给它的hack .这是完整的解决方案:

class Blog

  has_many :posts

  def latest_posts
    LatestPostsDrop.new(posts)
  end

  def to_liquid
    {
      'all_posts' => posts.all,
      'last_post' => post.last,
      'latest_posts' => latest_posts
    }
  end

end

class LatestPostsDrop < Liquid::Drop

  def initialize(posts)
    @posts = posts
  end

  def before_method(num)
    @posts.latest(num)    # Post.latest is a named scope
  end

end
Run Code Online (Sandbox Code Playgroud)

执行上述操作后,您可以使用以下内容迭代任意数量的最新帖子:

{% for post in blog.latest_posts.10 %}  # the last attribute can be any integer
  <p>{{ post.title }}</p>
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

它似乎有点hacky,但它的工作:)


Lar*_*y K 5

我认为液体是一个很棒的模板系统.恭喜您调查/使用它.

默认情况下,液体模板无法使用任何模型的方法.这是一件好事.然后指定哪些方法可用.(白名单.)

我使用在邮件列表上发送的Module的扩展.完整的扩展如下.它通过向类和模块添加一个简单的#liquid_methods方法来为您处理Liquid :: Drop创建.

然后,在你的模型中,只需:

class Blog
  # id
  # name
  has_many :posts

  def latest_posts(n)
    posts.latest(n) # using a named scope
  end

  def latest_10_posts;latest_posts(10); end

  liquid_methods :id, :name, :posts, :latest_10_posts
end
Run Code Online (Sandbox Code Playgroud)

我不确定如何/如果你可以将params传递到一滴.在Liquid邮件列表上询问.我想你可以.

补充:我现在重新阅读你的问题,看看你真的想要将该参数发送给该方法.您可以向Liquid过滤器发送多个参数/参数.所以你可以有一个过滤器:

# Define as a Liquid filter
def latest_posts(blog, n)
  blog.latest(n)
end

# then call the filter in a template:
{{ blog2 | latest_posts: 10 }}  
# Note that the second param is after the filter name.
Run Code Online (Sandbox Code Playgroud)

在这个例子中,还要记住你也需要在Post类中声明液体方法.

这是模块扩展.

# By dd -- http://groups.google.com/group/liquid-templates/browse_thread/thread/bf48cfebee9fafd9
# This extension is usesd in order to expose the object of the implementing class
# to liquid as it were a Drop. It also limits the liquid-callable methods of the instance
# to the allowed method passed with the liquid_methods call
# Example:
#
# class SomeClass
#   liquid_methods :an_allowed_method
#
#   def an_allowed_method
#     'this comes from an allowed method'
#   end
#   def unallowed_method
#     'this will never be an output'
#   end
# end
#
# if you want to extend the drop to other methods you can define more methods
# in the class <YourClass>::LiquidDropClass
#
#   class SomeClass::LiquidDropClass
#     def another_allowed_method
#       'and this is another allowed method'
#     end
#   end
# end
#
# usage:
# @something = SomeClass.new
#
# template:
# {{something.an_allowed_method}}{{something.unallowed_method}}{{something.another_allowed_method}}
#
# output:
# 'this comes from an allowed method and this is another allowed method'
#
# You can also chain associations, by adding the liquid_method calls in the
# association models.
#
class Module

  def liquid_methods(*allowed_methods)
    drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end"
    define_method :to_liquid do
      drop_class.new(self)
    end

    drop_class.class_eval do
      allowed_methods.each do |sym|
        define_method sym do
          @object.send sym
        end
      end
      def initialize(object)
        @object = object
      end
    end

  end
end 
Run Code Online (Sandbox Code Playgroud)