-
Notifications
You must be signed in to change notification settings - Fork 11
Using Liquid without Rails
Want to use Liquid along with some plain old Ruby code?
First, of course, you need to tell Ruby about the Liquid library.
Assuming that you’re in the liquid
directory, as cloned from GitHub,
just require 'lib/liquid'
.
The big secret, however, is creating a to_liquid
method for any classes you want Liquid to access.
Let’s say you have a Product class:
class Product attr_accessor :name, :price def initialize(name,price) @name = name @price = price end end
And you have a chunk of text that you want to Liquidize by inserting some details about your products:
sentence = "I'm running to the store with {{ product.price }} dollars in my pocket " sentence += "to buy a {{ product.name }}."
And you create a product and parse the sentence:
my_purchase = Product.new('box of sausages', 20) puts Liquid::Template.parse(sentence).render('product' => my_purchase)
You excitedly execute your code, expecting to see your beautiful new sentence, but instead you get:
I'm running to the store with Liquid error: undefined method `to_liquid' for #<Product:0x1388c08 ...
to_liquid
???
Where did that come from?
Turns out you need to add a method to your class that returns your instance variables as part of a hash.
(Liquid was built to be very paranoid about not letting people access something they’re not supposed to,
so you need to explicitly let Liquid know how to access everything in your object.)
So, we update the Product
class description to include that method:
class Product attr_accessor :name, :price def initialize(name, price) @name = name @price = price end def to_liquid { "name" => self.name, "price" => self.price } end end
Run that again and presto:
I'm running to the store with 20 dollars in my pocket to buy a box of sausages.
However, that’s a bit verbose, so you may want to use liquid_methods
, instead:
class Product attr_accessor :name, :price liquid_methods :name, :price def initialize(name, price) @name = name @price = price end end
(Thanks to Tom’s Jekyll code for helping me figuring this out.)
Every variable rendered by liquid must either:
- implement a
to_liquid
method; or - be a Proc. This is called with the
Liquid::Context
as a parameter and must return an object which responds toto_liquid
Liquid defines to_liquid
for the following standard classes: String, Array, Hash, Numeric, Time, DateTime, Date, TrueClass, FalseClass, NilClass. For each of these, to_liquid
just returns the object itself. (See: lib/liquid/extensions.rb)
If the value returned by to_liquid
is inserted into the output, it will be further converted with to_s
(actually this is done implicitly by Array#join – see lib/liquid/template.rb)
When used in a chained expression like foo.bar
or foo['bar']
, the result of to_liquid
on the left-hand subexpression must either:
- respond to
has_key?
and[]
(i.e. duck-type like a Hash); or - respond to
fetch
and[]
with an Integer argument (i.e. duck-type like an Array); or - respond to
size
orfirst
orlast
In each case, the object returned after applying the right-hand subexpression must also respond to to_liquid
or be a Proc which returns an object that responds to to_liquid
. In the latter case, it will only be called if the previous element in the chain also responds to []=
– that is, if the value can be cached in the parent object.
(See: lib/liquid/context.rb)
>> assigns = {"foo"=>{"bar"=>{"baz"=>lambda { |x| Time.now } }}} => {"foo"=>{"bar"=>{"baz"=>#<Proc:0xb7cd9af8@(irb):11>}}} >> t = Liquid::Template.parse("{{ foo.bar.baz }}") => #<Liquid::Template:0xb7d2e738 ... > >> t.render(assigns) => "Fri Jun 05 11:23:56 +0100 2009" >> assigns => {"foo"=>{"bar"=>{"baz"=>Fri Jun 05 11:23:56 +0100 2009}}}