Compare commits

..

31 Commits

Author SHA1 Message Date
DBA
0526348cae Gemspec
- rewritten
2010-08-24 11:57:26 +08:00
Tobias Lütke
a3fb7ba2b3 Merge branch 'master' of github.com:tobi/liquid
Conflicts:
	liquid.gemspec
2010-08-23 20:24:08 -04:00
Tobias Lütke
77cc0f2ed9 Version bump 2010-08-23 20:22:35 -04:00
DBA
3d43efe2bc Ruby compatibility issues
- regexp engines are different from 1.8 to 1.9, fixed the literal shorthand regexp accordingly
  - changed the shorthand regexp text from a match to a string scan
  - test_helper now loads rubygems unless RUBY_VERSION is > 1.9
2010-08-24 08:17:42 +08:00
DBA
772233d881 Readme: pre to code 2010-08-24 08:17:42 +08:00
DBA
90d1bc26d8 History
- updated (2.2.0 & 2.2.1)

Manifest
  - updated readme reference

Readme
  - Converted to markdown
  - cleaned up

Gemspec
  - updated to 2.2.1
2010-08-24 08:17:42 +08:00
DBA
c00a650492 Literal
- added support for literals [closes #6]

Code beautifier
  - indented some files
2010-08-24 08:17:42 +08:00
DBA
4819eb1a92 IF tag
- now properly allows operands to have conditions (eg and, or) [closes #13]
2010-08-24 08:17:41 +08:00
DBA
8579807d29 Conditions
- added test to assert that conditions can contain conditions within its value (eg 'a-and-b')

Tags
  - indented the if tag

Tests
  - added ruby-debug to the test_helper
  - indented some tests
2010-08-24 08:17:41 +08:00
DBA
daf786fd28 Test Helper
- added assert_template_result_matches
  - fixed indentation / white spacing
2010-08-24 08:17:41 +08:00
Tobias Lütke
101137045e remove swp files 2010-08-22 13:33:26 -04:00
DBA
8a0a8cfd99 FiltersTest
- added test that asserts nonexistent filters are ignored

Liquid
  - Bill's mind blowing liquid patch to support filter separators (|) in quoted strings (svn r7516).
  - This is a consolidation effort based on newrelic's liquid fork commit 88a5b891d009054d56b994c9448725c74e2b1e13
2010-08-23 01:30:05 +08:00
DBA
8304a046c9 Context
- Check arity of proc before calling, to prevent error when using ruby 1.9.1+

Code beautifer
  - context.rb
2010-08-23 01:30:03 +08:00
DBA
c72c84ea9b Tests
- Organized the files
  - Cleaned up some of the white spacing issues
  - A lot can still be done to make the tests more readable to the new developers
2010-08-23 01:30:01 +08:00
DBA
01145f872b Test Helper
- Removed unnecessary test helper file. The file being used is helper.rb
2010-08-23 01:30:00 +08:00
DBA
5409814552 Code beautifier
- standard_filter_test.rb
2010-08-23 01:30:00 +08:00
DBA
2d9331a234 StandardFilters
- Ruby 1.9.2-rc changed the float precision, thus the tests are now more generic and backwards compatible.
2010-08-23 01:30:00 +08:00
DBA
c59cde9d17 Code beautifier
- standardfilters.rb
  - standard_filter_test.rb
2010-08-23 01:30:00 +08:00
DBA
29e140b655 StandardFilters
- added escape_once, based on ActionView
2010-08-23 01:30:00 +08:00
DBA
a48332871a Test helper
- extras path now uses File.join instead of string concatenation
  - extras path is only loaded into $LOAD_PATH if it's not already part of it
2010-08-23 01:30:00 +08:00
DBA
bd7f867759 Code beautifier
- strainer_test.rb
2010-08-23 01:30:00 +08:00
DBA
8e4573a7bf Strainer
- respond_to_missing? is now a required method
2010-08-23 01:29:59 +08:00
DBA
5425679a96 Rakefile
- Updated to run with Ruby 1.9.2-p0
  - Fixed some indentations / white spacing
2010-08-23 01:29:59 +08:00
Tobias Lütke
6831eac902 Released gem 2.1.3 2010-08-05 18:07:05 -04:00
Dennis Theisen
13f98de7f3 Change behavior of capture tag to use existing variables if they already have been initialized in an outer scope. 2010-08-06 06:02:37 +08:00
Dennis Theisen
e26f509277 Fixed minor typos in inline documentation for assign and capture 2010-08-06 06:02:37 +08:00
James MacAulay
0417c9e723 Gem v2.1.2 2010-07-09 09:17:10 -04:00
James MacAulay
ffd48880e2 Gem v2.1.1 2010-07-09 09:11:49 -04:00
James MacAulay
6b79f25c87 rake release 2010-07-09 09:11:41 -04:00
James MacAulay
ff829e7996 fix if tag parsing with expressions starting with and/or 2010-07-07 16:48:23 -04:00
James MacAulay
d53a4e1834 rake default task is 'test' 2010-07-06 16:01:15 -04:00
48 changed files with 1170 additions and 982 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.gem
*.swp
pkg

View File

@@ -1,3 +1,5 @@
* Make context and assign work the same
* Ruby 1.9.1 bugfixes
* Fix LiquidView for Rails 2.2. Fix local assigns for all versions of Rails

View File

@@ -1,3 +1,12 @@
2.2.1 / 2010-08-23
* Added support for literal tags
2.2.0 / 2010-08-22
* Compatible with Ruby 1.8.7, 1.9.1 and 1.9.2-p0
* Merged some changed made by the community
1.9.0 / 2008-03-04
* Fixed gem install rake task

View File

@@ -2,7 +2,7 @@ CHANGELOG
History.txt
MIT-LICENSE
Manifest.txt
README.txt
README.md
Rakefile
init.rb
lib/extras/liquid_view.rb

42
README.md Normal file
View File

@@ -0,0 +1,42 @@
# Liquid template engine
## Introduction
Liquid is a template engine which I wrote for very specific requirements
* It has to have beautiful and simple markup. Template engines which don't produce good looking markup are no fun to use.
* It needs to be non evaling and secure. Liquid templates are made so that users can edit them. You don't want to run code on your server which your users wrote.
* It has to be stateless. Compile and render steps have to be seperate so that the expensive parsing and compiling can be done once and later on you can just render it passing in a hash with local variables and objects.
## Why should I use Liquid
* You want to allow your users to edit the appearance of your application but don't want them to run **insecure code on your server**.
* You want to render templates directly from the database
* You like smarty (PHP) style template engines
* You need a template engine which does HTML just as well as emails
* You don't like the markup of your current templating engine
## What does it look like?
<code>
<ul id="products">
{% for product in products %}
<li>
<h2>{{product.name}}</h2>
Only {{product.price | price }}
{{product.description | prettyprint | paragraph }}
</li>
{% endfor %}
</ul>
</code>
## Howto use Liquid
Liquid supports a very simple API based around the Liquid::Template class.
For standard use you can just pass it the content of a file and call render with a parameters hash.
<pre>
@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
@template.render( 'name' => 'tobi' ) # => "hi tobi"
</pre>

View File

@@ -1,38 +0,0 @@
= Liquid template engine
Liquid is a template engine which I wrote for very specific requirements
* It has to have beautiful and simple markup.
Template engines which don't produce good looking markup are no fun to use.
* It needs to be non evaling and secure. Liquid templates are made so that users can edit them. You don't want to run code on your server which your users wrote.
* It has to be stateless. Compile and render steps have to be seperate so that the expensive parsing and compiling can be done once and later on you can
just render it passing in a hash with local variables and objects.
== Why should i use Liquid
* You want to allow your users to edit the appearance of your application but don't want them to run insecure code on your server.
* You want to render templates directly from the database
* You like smarty style template engines
* You need a template engine which does HTML just as well as Emails
* You don't like the markup of your current one
== What does it look like?
<ul id="products">
{% for product in products %}
<li>
<h2>{{product.name}}</h2>
Only {{product.price | price }}
{{product.description | prettyprint | paragraph }}
</li>
{% endfor %}
</ul>
== Howto use Liquid
Liquid supports a very simple API based around the Liquid::Template class.
For standard use you can just pass it the content of a file and call render with a parameters hash.
@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
@template.render( 'name' => 'tobi' ) # => "hi tobi"

View File

@@ -1,13 +1,16 @@
#!/usr/bin/env ruby
$:.unshift File.join(File.dirname(__FILE__), 'test') unless $:.include? File.join(File.dirname(__FILE__), 'test')
require 'rubygems'
require 'rake'
require 'rake/testtask'
require 'rake/gempackagetask'
task :default => 'test'
Rake::TestTask.new(:test) do |t|
t.libs << "lib"
t.libs << "test"
t.pattern = 'test/*_test.rb'
t.libs << '.' << 'lib' << 'test'
t.pattern = 'test/lib/**/*_test.rb'
t.verbose = false
end
@@ -16,22 +19,25 @@ Rake::GemPackageTask.new(gemspec) do |pkg|
pkg.gem_spec = gemspec
end
desc "build the gem and release it to rubygems.org"
task :release => :gem do
sh "gem push pkg/liquid-#{gemspec.version}.gem"
end
namespace :profile do
task :default => [:run]
desc "Run the liquid profile/perforamce coverage"
task :run do
ruby "performance/shopify.rb"
end
desc "Run KCacheGrind"
desc "Run KCacheGrind"
task :grind => :run do
system "kcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
end
end

View File

@@ -38,13 +38,14 @@ module Liquid
StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|,\:,\,]+/
FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
SpacelessFilter = /#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
AnyStartingTag = /\{\{|\{\%/
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/
LiteralShorthand = /^(?:\{\{\{\s?)(.*?)(?:\s*\}\}\})$/
end
require 'liquid/drop'

View File

@@ -28,8 +28,9 @@ module Liquid
@strainer ||= Strainer.create(self)
end
# adds filters to this context.
# this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
# Adds filters to this context.
#
# Note that this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
# for that
def add_filters(filters)
filters = [filters].flatten.compact
@@ -52,7 +53,6 @@ module Liquid
end
end
def invoke(method, *args)
if strainer.respond_to?(method)
strainer.__send__(method, *args)
@@ -61,43 +61,44 @@ module Liquid
end
end
# push new local scope on the stack. use <tt>Context#stack</tt> instead
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
def push(new_scope={})
raise StackLevelError, "Nesting too deep" if @scopes.length > 100
@scopes.unshift(new_scope)
end
# merge a hash of variables in the current local scope
# Merge a hash of variables in the current local scope
def merge(new_scopes)
@scopes[0].merge!(new_scopes)
end
# pop from the stack. use <tt>Context#stack</tt> instead
# Pop from the stack. use <tt>Context#stack</tt> instead
def pop
raise ContextError if @scopes.size == 1
@scopes.shift
end
# pushes a new local scope on the stack, pops it at the end of the block
# Pushes a new local scope on the stack, pops it at the end of the block
#
# Example:
#
# context.stack do
# context['var'] = 'hi'
# end
# context['var] #=> nil
#
# context['var] #=> nil
def stack(new_scope={},&block)
result = nil
push(new_scope)
begin
result = yield
ensure
pop
end
result
end
def clear_instance_assigns
@scopes[0] = {}
end
@@ -116,139 +117,133 @@ module Liquid
end
private
# Look up variable, either resolve directly after considering the name. We can directly handle
# Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and
# later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
# Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
#
# Example:
#
# products == empty #=> products.empty?
#
def resolve(key)
case key
when nil, 'nil', 'null', ''
nil
when 'true'
true
when 'false'
false
when 'blank'
:blank?
when 'empty'
:empty?
# Single quoted strings
when /^'(.*)'$/
$1.to_s
# Double quoted strings
when /^"(.*)"$/
$1.to_s
# Integer and floats
when /^(\d+)$/
$1.to_i
# Ranges
when /^\((\S+)\.\.(\S+)\)$/
(resolve($1).to_i..resolve($2).to_i)
# Floats
when /^(\d[\d\.]+)$/
$1.to_f
else
variable(key)
end
end
# fetches an object starting at the local scope and then moving up
# the hierachy
def find_variable(key)
scope = @scopes.find { |s| s.has_key?(key) }
if scope.nil?
@environments.each do |e|
if variable = lookup_and_evaluate(e, key)
scope = e
break
end
end
end
scope ||= @environments.last || @scopes.last
variable ||= lookup_and_evaluate(scope, key)
variable = variable.to_liquid
variable.context = self if variable.respond_to?(:context=)
return variable
end
# resolves namespaced queries gracefully.
#
# Example
#
# @context['hash'] = {"name" => 'tobi'}
# assert_equal 'tobi', @context['hash.name']
# assert_equal 'tobi', @context['hash["name"]']
#
def variable(markup)
parts = markup.scan(VariableParser)
square_bracketed = /^\[(.*)\]$/
first_part = parts.shift
if first_part =~ square_bracketed
first_part = resolve($1)
end
if object = find_variable(first_part)
parts.each do |part|
part = resolve($1) if part_resolved = (part =~ square_bracketed)
# If object is a hash- or array-like object we look for the
# presence of the key and if its available we return it
if object.respond_to?(:[]) and
((object.respond_to?(:has_key?) and object.has_key?(part)) or
(object.respond_to?(:fetch) and part.is_a?(Integer)))
# if its a proc we will replace the entry with the proc
res = lookup_and_evaluate(object, part)
object = res.to_liquid
# Some special cases. If the part wasn't in square brackets and
# no key with the same name was found we interpret following calls
# as commands and call them on the current object
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
object = object.send(part.intern).to_liquid
# No key was present with the desired value and it wasn't one of the directly supported
# keywords either. The only thing we got left is to return nil
else
return nil
end
# If we are dealing with a drop here we have to
object.context = self if object.respond_to?(:context=)
# Look up variable, either resolve directly after considering the name. We can directly handle
# Strings, digits, floats and booleans (true,false).
# If no match is made we lookup the variable in the current scope and
# later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
# Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
#
# Example:
# products == empty #=> products.empty?
def resolve(key)
case key
when nil, 'nil', 'null', ''
nil
when 'true'
true
when 'false'
false
when 'blank'
:blank?
when 'empty' # Single quoted strings
:empty?
when /^'(.*)'$/ # Double quoted strings
$1.to_s
when /^"(.*)"$/ # Integer and floats
$1.to_s
when /^(\d+)$/ # Ranges
$1.to_i
when /^\((\S+)\.\.(\S+)\)$/ # Floats
(resolve($1).to_i..resolve($2).to_i)
when /^(\d[\d\.]+)$/
$1.to_f
else
variable(key)
end
end
object
end
def lookup_and_evaluate(obj, key)
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
obj[key] = value.call(self)
else
value
end
end
def squash_instance_assigns_with_environments
@scopes.last.each_key do |k|
@environments.each do |env|
if env.has_key?(k)
scopes.last[k] = lookup_and_evaluate(env, k)
break
# Fetches an object starting at the local scope and then moving up the hierachy
def find_variable(key)
scope = @scopes.find { |s| s.has_key?(key) }
if scope.nil?
@environments.each do |e|
if variable = lookup_and_evaluate(e, key)
scope = e
break
end
end
end
scope ||= @environments.last || @scopes.last
variable ||= lookup_and_evaluate(scope, key)
variable = variable.to_liquid
variable.context = self if variable.respond_to?(:context=)
return variable
end
end
end
end
# Resolves namespaced queries gracefully.
#
# Example
# @context['hash'] = {"name" => 'tobi'}
# assert_equal 'tobi', @context['hash.name']
# assert_equal 'tobi', @context['hash["name"]']
def variable(markup)
parts = markup.scan(VariableParser)
square_bracketed = /^\[(.*)\]$/
first_part = parts.shift
if first_part =~ square_bracketed
first_part = resolve($1)
end
if object = find_variable(first_part)
parts.each do |part|
part = resolve($1) if part_resolved = (part =~ square_bracketed)
# If object is a hash- or array-like object we look for the
# presence of the key and if its available we return it
if object.respond_to?(:[]) and
((object.respond_to?(:has_key?) and object.has_key?(part)) or
(object.respond_to?(:fetch) and part.is_a?(Integer)))
# if its a proc we will replace the entry with the proc
res = lookup_and_evaluate(object, part)
object = res.to_liquid
# Some special cases. If the part wasn't in square brackets and
# no key with the same name was found we interpret following calls
# as commands and call them on the current object
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
object = object.send(part.intern).to_liquid
# No key was present with the desired value and it wasn't one of the directly supported
# keywords either. The only thing we got left is to return nil
else
return nil
end
# If we are dealing with a drop here we have to
object.context = self if object.respond_to?(:context=)
end
end
object
end # variable
def lookup_and_evaluate(obj, key)
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
obj[key] = (value.arity == 0) ? value.call : value.call(self)
else
value
end
end # lookup_and_evaluate
def squash_instance_assigns_with_environments
@scopes.last.each_key do |k|
@environments.each do |env|
if env.has_key?(k)
scopes.last[k] = lookup_and_evaluate(env, k)
break
end
end
end
end # squash_instance_assigns_with_environments
end # Context
end # Liquid

View File

@@ -1,36 +1,40 @@
require 'cgi'
module Liquid
module StandardFilters
# Return the size of an array or of an string
def size(input)
input.respond_to?(:size) ? input.size : 0
end
end
# convert a input string to DOWNCASE
def downcase(input)
input.to_s.downcase
end
end
# convert a input string to UPCASE
def upcase(input)
input.to_s.upcase
end
# capitalize words in the input centence
def capitalize(input)
input.to_s.capitalize
end
def escape(input)
CGI.escapeHTML(input) rescue input
end
def escape_once(input)
ActionView::Helpers::TagHelper.escape_once(input) rescue input
end
alias_method :h, :escape
# Truncate a string down to x characters
def truncate(input, length = 50, truncate_string = "...")
if input.nil? then return end
@@ -44,19 +48,19 @@ module Liquid
wordlist = input.to_s.split
l = words.to_i - 1
l = 0 if l < 0
wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
end
def strip_html(input)
input.to_s.gsub(/<script.*?<\/script>/, '').gsub(/<.*?>/, '')
end
# Remove all newlines from the string
def strip_newlines(input)
input.to_s.gsub(/\n/, '')
end
# Remove all newlines from the string
def strip_newlines(input)
input.to_s.gsub(/\n/, '')
end
# Join elements of the array with certain character between them
def join(input, glue = ' ')
[input].flatten.join(glue)
@@ -73,8 +77,8 @@ module Liquid
elsif ary.first.respond_to?(property)
ary.sort {|a,b| a.send(property) <=> b.send(property) }
end
end
end
# map/collect on a given property
def map(input, property)
ary = [input].flatten
@@ -84,42 +88,42 @@ module Liquid
ary.map {|e| e.send(property) }
end
end
# Replace occurrences of a string with another
def replace(input, string, replacement = '')
input.to_s.gsub(string, replacement)
end
# Replace the first occurrences of a string with another
def replace_first(input, string, replacement = '')
input.to_s.sub(string, replacement)
end
end
# remove a substring
def remove(input, string)
input.to_s.gsub(string, '')
input.to_s.gsub(string, '')
end
# remove the first occurrences of a substring
def remove_first(input, string)
input.to_s.sub(string, '')
end
input.to_s.sub(string, '')
end
# add one string to another
def append(input, string)
input.to_s + string.to_s
end
# prepend a string to another
def prepend(input, string)
string.to_s + input.to_s
end
# Add <br /> tags in front of all newlines in input string
def newline_to_br(input)
input.to_s.gsub(/\n/, "<br />\n")
def newline_to_br(input)
input.to_s.gsub(/\n/, "<br />\n")
end
# Reformat a date
#
# %a - The abbreviated weekday name (``Sun'')
@@ -149,74 +153,74 @@ module Liquid
# %Z - Time zone name
# %% - Literal ``%'' character
def date(input, format)
if format.to_s.empty?
return input.to_s
end
date = input.is_a?(String) ? Time.parse(input) : input
if date.respond_to?(:strftime)
date.strftime(format.to_s)
else
input
end
rescue => e
rescue => e
input
end
# Get the first element of the passed in array
#
# Get the first element of the passed in array
#
# Example:
# {{ product.images | first | to_img }}
#
#
def first(array)
array.first if array.respond_to?(:first)
end
# Get the last element of the passed in array
#
# Get the last element of the passed in array
#
# Example:
# {{ product.images | last | to_img }}
#
#
def last(array)
array.last if array.respond_to?(:last)
end
# addition
def plus(input, operand)
to_number(input) + to_number(operand)
end
# subtraction
def minus(input, operand)
to_number(input) - to_number(operand)
end
# multiplication
def times(input, operand)
to_number(input) * to_number(operand)
end
# division
def divided_by(input, operand)
to_number(input) / to_number(operand)
end
private
def to_number(obj)
case obj
when Numeric
obj
when String
(obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i
else
0
def to_number(obj)
case obj
when Numeric
obj
when String
(obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i
else
0
end
end
end
end
Template.register_filter(StandardFilters)
end

View File

@@ -16,6 +16,9 @@ module Liquid
INTERNAL_METHOD = /^__/
@@required_methods = Set.new([:__id__, :__send__, :respond_to?, :extend, :methods, :class, :object_id])
# Ruby 1.9.2 introduces Object#respond_to_missing?, which is invoked by Object#respond_to?
@@required_methods << :respond_to_missing? if Object.respond_to? :respond_to_missing?
@@filters = {}
def initialize(context)

View File

@@ -1,6 +1,7 @@
module Liquid
class Tag
attr_accessor :nodelist
def initialize(tag_name, markup, tokens)
@@ -19,8 +20,7 @@ module Liquid
def render(context)
''
end
end
end # Tag
end
end # Tag

View File

@@ -6,7 +6,7 @@ module Liquid
#
# You can then use the variable later in the page.
#
# {{ monkey }}
# {{ foo }}
#
class Assign < Tag
Syntax = /(#{VariableSignature}+)\s*=\s*(#{QuotedFragment}+)/
@@ -23,11 +23,11 @@ module Liquid
end
def render(context)
context.scopes.last[@to.to_s] = context[@from]
context.scopes.last[@to] = context[@from]
''
end
end
Template.register_tag('assign', Assign)
end
end

View File

@@ -6,7 +6,7 @@ module Liquid
# Monkeys!
# {% endcapture %}
# ...
# <h1>{{ monkeys }}</h1>
# <h1>{{ heading }}</h1>
#
# Capture is useful for saving content for use later in your template, such as
# in a sidebar or footer.
@@ -26,7 +26,7 @@ module Liquid
def render(context)
output = super
context[@to] = output.join
context.scopes.last[@to] = output.join
''
end
end

View File

@@ -1,9 +1,9 @@
module Liquid
class Comment < Block
class Comment < Block
def render(context)
''
end
end
end
Template.register_tag('comment', Comment)
end
Template.register_tag('comment', Comment)
end

View File

@@ -14,17 +14,16 @@ module Liquid
class If < Block
SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/
ExpressionsAndOperators = /(?:and|or|(?:\s*(?!\b(?:and|or)\b)(?:#{QuotedFragment}|\S+)\s*)+)/
def initialize(tag_name, markup, tokens)
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/
def initialize(tag_name, markup, tokens)
@blocks = []
push_block('if', markup)
super
super
end
def unknown_tag(tag, markup, tokens)
if ['elsif', 'else'].include?(tag)
push_block(tag, markup)
@@ -32,49 +31,49 @@ module Liquid
super
end
end
def render(context)
context.stack do
@blocks.each do |block|
if block.evaluate(context)
return render_all(block.attachment, context)
if block.evaluate(context)
return render_all(block.attachment, context)
end
end
end
''
end
end
private
def push_block(tag, markup)
block = if tag == 'else'
ElseCondition.new
else
expressions = markup.scan(ExpressionsAndOperators).reverse
raise(SyntaxError, SyntaxHelp) unless expressions.shift =~ Syntax
condition = Condition.new($1, $2, $3)
while not expressions.empty?
operator = expressions.shift
raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax
new_condition = Condition.new($1, $2, $3)
new_condition.send(operator.to_sym, condition)
condition = new_condition
end
condition
private
def push_block(tag, markup)
block = if tag == 'else'
ElseCondition.new
else
expressions = markup.scan(ExpressionsAndOperators).reverse
raise(SyntaxError, SyntaxHelp) unless expressions.shift =~ Syntax
condition = Condition.new($1, $2, $3)
while not expressions.empty?
operator = (expressions.shift).to_s.strip
raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax
new_condition = Condition.new($1, $2, $3)
new_condition.send(operator.to_sym, condition)
condition = new_condition
end
condition
end
@blocks.push(block)
@nodelist = block.attach(Array.new)
end
@blocks.push(block)
@nodelist = block.attach(Array.new)
end
end
Template.register_tag('if', If)
end
end

View File

@@ -0,0 +1,42 @@
module Liquid
class Literal < Block
# Class methods
# Converts a shorthand Liquid literal into its long representation.
#
# Currently the Template parser only knows how to handle the long version.
# So, it always checks if it is in the presence of a literal, in which case it gets converted through this method.
#
# Example:
# Liquid::Literal "{{{ hello world }}}" #=> "{% literal %} hello world {% endliteral %}"
def self.from_shorthand(literal)
literal =~ LiteralShorthand ? "{% literal %}#{$1}{% endliteral %}" : literal
end
# Public instance methods
def parse(tokens) # :nodoc:
@nodelist ||= []
@nodelist.clear
while token = tokens.shift
if token =~ FullToken && block_delimiter == $1
end_tag
return
else
@nodelist << token
end
end
# Make sure that its ok to end parsing in the current block.
# Effectively this method will throw and exception unless the current block is
# of type Document
assert_missing_delimitation!
end # parse
end
Template.register_tag('literal', Literal)
end

View File

@@ -55,7 +55,7 @@ module Liquid
# Parse source code.
# Returns self for easy chaining
def parse(source)
@root = Document.new(tokenize(source))
@root = Document.new(tokenize(Liquid::Literal.from_shorthand(source)))
self
end

View File

@@ -1,29 +1,23 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{liquid}
s.version = "2.1.0"
s.platform = Gem::Platform::RUBY
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Tobias Luetke"]
s.date = %q{2009-04-13}
s.description = %q{A secure, non-evaling end user template engine with aesthetic markup.}
s.email = %q{tobi@leetsoft.com}
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
s.files = ["CHANGELOG", "History.txt", "MIT-LICENSE", "Manifest.txt", "README.txt", "Rakefile", "lib/extras/liquid_view.rb", "lib/liquid.rb", "lib/liquid/block.rb", "lib/liquid/condition.rb", "lib/liquid/context.rb", "lib/liquid/document.rb", "lib/liquid/drop.rb", "lib/liquid/errors.rb", "lib/liquid/extensions.rb", "lib/liquid/file_system.rb", "lib/liquid/htmltags.rb", "lib/liquid/module_ex.rb", "lib/liquid/standardfilters.rb", "lib/liquid/strainer.rb", "lib/liquid/tag.rb", "lib/liquid/tags/assign.rb", "lib/liquid/tags/capture.rb", "lib/liquid/tags/case.rb", "lib/liquid/tags/comment.rb", "lib/liquid/tags/cycle.rb", "lib/liquid/tags/for.rb", "lib/liquid/tags/if.rb", "lib/liquid/tags/ifchanged.rb", "lib/liquid/tags/include.rb", "lib/liquid/tags/unless.rb", "lib/liquid/template.rb", "lib/liquid/variable.rb"]
s.has_rdoc = true
s.homepage = %q{http://www.liquidmarkup.org}
s.rdoc_options = ["--main", "README.txt"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{liquid}
s.rubygems_version = %q{1.3.1}
s.summary = %q{A secure, non-evaling end user template engine with aesthetic markup.}
s.name = "liquid"
s.version = '2.2.2'
s.summary = "A secure, non-evaling end user template engine with aesthetic markup."
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2
s.authors = ["Tobias Luetke"]
s.email = ["tobi@leetsoft.com"]
s.homepage = "http://www.liquidmarkup.org"
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
else
end
else
end
s.description = "A secure, non-evaling end user template engine with aesthetic markup."
s.required_rubygems_version = ">= 1.3.7"
s.files = Dir.glob("{lib}/**/*") + %w(MIT-LICENSE README.md)
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.md"]
s.require_path = 'lib'
end

View File

@@ -1,11 +0,0 @@
require File.dirname(__FILE__) + '/helper'
class AssignTest < Test::Unit::TestCase
include Liquid
def test_assigned_variable
assert_template_result('.foo.','{% assign foo = values %}.{{ foo[0] }}.', 'values' => %w{foo bar baz})
assert_template_result('.bar.','{% assign foo = values %}.{{ foo[1] }}.', 'values' => %w{foo bar baz})
end
end

View File

@@ -1,20 +0,0 @@
#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.dirname(__FILE__)+ '/extra')
require 'test/unit'
require 'test/unit/assertions'
require 'caller'
require 'breakpoint'
require File.dirname(__FILE__) + '/../lib/liquid'
module Test
module Unit
module Assertions
include Liquid
def assert_template_result(expected, template, assigns={}, message=nil)
assert_equal expected, Template.parse(template).render(assigns)
end
end
end
end

View File

@@ -1,129 +0,0 @@
require File.dirname(__FILE__) + '/helper'
class TestFileSystem
def read_template_file(template_path)
case template_path
when "product"
"Product: {{ product.title }} "
when "locale_variables"
"Locale: {{echo1}} {{echo2}}"
when "variant"
"Variant: {{ variant.title }}"
when "nested_template"
"{% include 'header' %} {% include 'body' %} {% include 'footer' %}"
when "body"
"body {% include 'body_detail' %}"
when "nested_product_template"
"Product: {{ nested_product_template.title }} {%include 'details'%} "
when "recursively_nested_template"
"-{% include 'recursively_nested_template' %}"
when "pick_a_source"
"from TestFileSystem"
else
template_path
end
end
end
class OtherFileSystem
def read_template_file(template_path)
'from OtherFileSystem'
end
end
class IncludeTagTest < Test::Unit::TestCase
include Liquid
def setup
Liquid::Template.file_system = TestFileSystem.new
end
def test_include_tag_looks_for_file_system_in_registers_first
assert_equal 'from OtherFileSystem',
Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => OtherFileSystem.new})
end
def test_include_tag_with
assert_equal "Product: Draft 151cm ",
Template.parse("{% include 'product' with products[0] %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] )
end
def test_include_tag_with_default_name
assert_equal "Product: Draft 151cm ",
Template.parse("{% include 'product' %}").render( "product" => {'title' => 'Draft 151cm'} )
end
def test_include_tag_for
assert_equal "Product: Draft 151cm Product: Element 155cm ",
Template.parse("{% include 'product' for products %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] )
end
def test_include_tag_with_local_variables
assert_equal "Locale: test123 ",
Template.parse("{% include 'locale_variables' echo1: 'test123' %}").render
end
def test_include_tag_with_multiple_local_variables
assert_equal "Locale: test123 test321",
Template.parse("{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}").render
end
def test_include_tag_with_multiple_local_variables_from_context
assert_equal "Locale: test123 test321",
Template.parse("{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}").render('echo1' => 'test123', 'more_echos' => { "echo2" => 'test321'})
end
def test_nested_include_tag
assert_equal "body body_detail",
Template.parse("{% include 'body' %}").render
assert_equal "header body body_detail footer",
Template.parse("{% include 'nested_template' %}").render
end
def test_nested_include_with_variable
assert_equal "Product: Draft 151cm details ",
Template.parse("{% include 'nested_product_template' with product %}").render("product" => {"title" => 'Draft 151cm'})
assert_equal "Product: Draft 151cm details Product: Element 155cm details ",
Template.parse("{% include 'nested_product_template' for products %}").render("products" => [{"title" => 'Draft 151cm'}, {"title" => 'Element 155cm'}])
end
def test_recursively_included_template_does_not_produce_endless_loop
infinite_file_system = Class.new do
def read_template_file(template_path)
"-{% include 'loop' %}"
end
end
Liquid::Template.file_system = infinite_file_system.new
assert_raise(Liquid::StackLevelError) do
Template.parse("{% include 'loop' %}").render!
end
end
def test_dynamically_choosen_template
assert_equal "Test123", Template.parse("{% include template %}").render("template" => 'Test123')
assert_equal "Test321", Template.parse("{% include template %}").render("template" => 'Test321')
assert_equal "Product: Draft 151cm ", Template.parse("{% include template for product %}").render("template" => 'product', 'product' => { 'title' => 'Draft 151cm'})
end
end

View File

@@ -0,0 +1,15 @@
require 'test_helper'
class AssignTest < Test::Unit::TestCase
include Liquid
def test_assigned_variable
assert_template_result('.foo.',
'{% assign foo = values %}.{{ foo[0] }}.',
'values' => %w{foo bar baz})
assert_template_result('.bar.',
'{% assign foo = values %}.{{ foo[1] }}.',
'values' => %w{foo bar baz})
end
end # AssignTest

View File

@@ -1,4 +1,4 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class VariableTest < Test::Unit::TestCase
include Liquid
@@ -33,26 +33,26 @@ class VariableTest < Test::Unit::TestCase
def test_variable_many_embedded_fragments
template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
assert_equal 7, template.root.nodelist.size
assert_equal [String, Variable, String, Variable, String, Variable, String], block_types(template.root.nodelist)
assert_equal [String, Variable, String, Variable, String, Variable, String],
block_types(template.root.nodelist)
end
def test_with_block
template = Liquid::Template.parse(" {% comment %} {% endcomment %} ")
assert_equal [String, Comment, String], block_types(template.root.nodelist)
assert_equal [String, Comment, String], block_types(template.root.nodelist)
assert_equal 3, template.root.nodelist.size
end
def test_with_custom_tag
Liquid::Template.register_tag("testtag", Block)
assert_nothing_thrown do
template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
end
def test_with_custom_tag
Liquid::Template.register_tag("testtag", Block)
assert_nothing_thrown do
template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
end
end
private
def block_types(nodelist)
nodelist.collect { |node| node.class }
end
end
def block_types(nodelist)
nodelist.collect { |node| node.class }
end
end # VariableTest

View File

@@ -0,0 +1,40 @@
require 'test_helper'
class CaptureTest < Test::Unit::TestCase
include Liquid
def test_captures_block_content_in_variable
assert_template_result("test string", "{% capture 'var' %}test string{% endcapture %}{{var}}", {})
end
def test_capture_to_variable_from_outer_scope_if_existing
template_source = <<-END_TEMPLATE
{% assign var = '' %}
{% if true %}
{% capture var %}first-block-string{% endcapture %}
{% endif %}
{% if true %}
{% capture var %}test-string{% endcapture %}
{% endif %}
{{var}}
END_TEMPLATE
template = Template.parse(template_source)
rendered = template.render
assert_equal "test-string", rendered.gsub(/\s/, '')
end
def test_assigning_from_capture
template_source = <<-END_TEMPLATE
{% assign first = '' %}
{% assign second = '' %}
{% for number in (1..3) %}
{% capture first %}{{number}}{% endcapture %}
{% assign second = first %}
{% endfor %}
{{ first }}-{{ second }}
END_TEMPLATE
template = Template.parse(template_source)
rendered = template.render
assert_equal "3-3", rendered.gsub(/\s/, '')
end
end # CaptureTest

View File

@@ -1,8 +1,8 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class ConditionTest < Test::Unit::TestCase
include Liquid
def test_basic_condition
assert_equal false, Condition.new('1', '==', '2').evaluate
assert_equal true, Condition.new('1', '==', '1').evaluate
@@ -47,69 +47,76 @@ class ConditionTest < Test::Unit::TestCase
def test_contains_works_on_arrays
@context = Liquid::Context.new
@context['array'] = [1,2,3,4,5]
assert_evalutes_false "array", 'contains', '0'
assert_evalutes_true "array", 'contains', '1'
assert_evalutes_true "array", 'contains', '2'
assert_evalutes_true "array", 'contains', '3'
assert_evalutes_true "array", 'contains', '4'
assert_evalutes_true "array", 'contains', '5'
assert_evalutes_false "array", 'contains', '6'
assert_evalutes_false "array", 'contains', '"1"'
end
assert_evalutes_false "array", 'contains', '0'
assert_evalutes_true "array", 'contains', '1'
assert_evalutes_true "array", 'contains', '2'
assert_evalutes_true "array", 'contains', '3'
assert_evalutes_true "array", 'contains', '4'
assert_evalutes_true "array", 'contains', '5'
assert_evalutes_false "array", 'contains', '6'
assert_evalutes_false "array", 'contains', '"1"'
end
def test_contains_returns_false_for_nil_operands
@context = Liquid::Context.new
assert_evalutes_false "not_assigned", 'contains', '0'
assert_evalutes_false "0", 'contains', 'not_assigned'
end
end
def test_or_condition
def test_or_condition
condition = Condition.new('1', '==', '2')
assert_equal false, condition.evaluate
condition.or Condition.new('2', '==', '1')
assert_equal false, condition.evaluate
condition.or Condition.new('1', '==', '1')
condition.or Condition.new('1', '==', '1')
assert_equal true, condition.evaluate
end
def test_and_condition
def test_and_condition
condition = Condition.new('1', '==', '1')
assert_equal true, condition.evaluate
condition.and Condition.new('2', '==', '2')
assert_equal true, condition.evaluate
condition.and Condition.new('2', '==', '1')
condition.and Condition.new('2', '==', '1')
assert_equal false, condition.evaluate
end
def test_should_allow_custom_proc_operator
Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}}}
assert_evalutes_true "'bob'", 'starts_with', "'b'"
assert_evalutes_false "'bob'", 'starts_with', "'o'"
ensure
Condition.operators.delete 'starts_with'
Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}} }
assert_evalutes_true "'bob'", 'starts_with', "'b'"
assert_evalutes_false "'bob'", 'starts_with', "'o'"
ensure
Condition.operators.delete 'starts_with'
end
def test_left_or_right_may_contain_operators
@context = Liquid::Context.new
@context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
assert_evalutes_true "one", '==', "another"
end
private
def assert_evalutes_true(left, op, right)
assert Condition.new(left, op, right).evaluate(@context || Liquid::Context.new), "Evaluated false: #{left} #{op} #{right}"
assert Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
"Evaluated false: #{left} #{op} #{right}"
end
def assert_evalutes_false(left, op, right)
assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new), "Evaluated true: #{left} #{op} #{right}"
assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
"Evaluated true: #{left} #{op} #{right}"
end
end
end # ConditionTest

View File

@@ -1,4 +1,5 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class HundredCentes
def to_liquid
100
@@ -62,7 +63,6 @@ class ArrayLike
end
end
class ContextTest < Test::Unit::TestCase
include Liquid
@@ -262,10 +262,10 @@ class ContextTest < Test::Unit::TestCase
def test_hash_to_array_transition
@context['colors'] = {
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
'Green' => ['003300','336633', '669966', '99CC99'],
'Yellow' => ['CC9900','FFCC00', 'FFFF99', 'FFFFCC'],
'Red' => ['660000','993333', 'CC6666', 'FF9999']
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
'Green' => ['003300','336633', '669966', '99CC99'],
'Yellow' => ['CC9900','FFCC00', 'FFFF99', 'FFFFCC'],
'Red' => ['660000','993333', 'CC6666', 'FF9999']
}
assert_equal '003366', @context['colors.Blue[0]']
@@ -475,5 +475,4 @@ class ContextTest < Test::Unit::TestCase
assert_kind_of CategoryDrop, @context['category']
assert_equal @context, @context['category'].context
end
end
end # ContextTest

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class ContextDrop < Liquid::Drop
def scopes
@@ -10,7 +8,7 @@ class ContextDrop < Liquid::Drop
def scopes_as_array
(1..@context.scopes.size).to_a
end
def loop_pos
@context['forloop.index']
end
@@ -18,13 +16,12 @@ class ContextDrop < Liquid::Drop
def break
Breakpoint.breakpoint
end
def before_method(method)
return @context[method]
end
end
class ProductDrop < Liquid::Drop
class TextDrop < Liquid::Drop
@@ -50,42 +47,41 @@ class ProductDrop < Liquid::Drop
def catchall
CatchallDrop.new
end
def context
ContextDrop.new
end
protected
def callmenot
"protected"
end
end
end
class EnumerableDrop < Liquid::Drop
class EnumerableDrop < Liquid::Drop
def size
3
end
def each
yield 1
yield 2
yield 3
end
end
end
class DropsTest < Test::Unit::TestCase
include Liquid
def test_product_drop
assert_nothing_raised do
tpl = Liquid::Template.parse( ' ' )
tpl.render('product' => ProductDrop.new)
end
end
def test_text_drop
output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render('product' => ProductDrop.new)
assert_equal ' text1 ', output
@@ -102,61 +98,56 @@ class DropsTest < Test::Unit::TestCase
output = Liquid::Template.parse( '{% for text in product.texts.array %} {{text}} {% endfor %}' ).render('product' => ProductDrop.new)
assert_equal ' text1 text2 ', output
end
def test_context_drop
output = Liquid::Template.parse( ' {{ context.bar }} ' ).render('context' => ContextDrop.new, 'bar' => "carrot")
assert_equal ' carrot ', output
end
def test_nested_context_drop
output = Liquid::Template.parse( ' {{ product.context.foo }} ' ).render('product' => ProductDrop.new, 'foo' => "monkey")
assert_equal ' monkey ', output
end
end
def test_protected
output = Liquid::Template.parse( ' {{ product.callmenot }} ' ).render('product' => ProductDrop.new)
assert_equal ' ', output
assert_equal ' ', output
end
def test_scope
assert_equal '1', Liquid::Template.parse( '{{ context.scopes }}' ).render('context' => ContextDrop.new)
assert_equal '1', Liquid::Template.parse( '{{ context.scopes }}' ).render('context' => ContextDrop.new)
assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ context.scopes }}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
end
def test_scope_though_proc
assert_equal '1', Liquid::Template.parse( '{{ s }}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] })
assert_equal '1', Liquid::Template.parse( '{{ s }}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] })
assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ s }}{%endfor%}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1])
assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1])
end
def test_scope_with_assigns
assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render('context' => ContextDrop.new)
assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal 'test', Liquid::Template.parse( '{% assign header_gif = "test"%}{{header_gif}}' ).render('context' => ContextDrop.new)
assert_equal 'test', Liquid::Template.parse( "{% assign header_gif = 'test'%}{{header_gif}}" ).render('context' => ContextDrop.new)
assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render('context' => ContextDrop.new)
assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal 'test', Liquid::Template.parse( '{% assign header_gif = "test"%}{{header_gif}}' ).render('context' => ContextDrop.new)
assert_equal 'test', Liquid::Template.parse( "{% assign header_gif = 'test'%}{{header_gif}}" ).render('context' => ContextDrop.new)
end
def test_scope_from_tags
assert_equal '1', Liquid::Template.parse( '{% for i in context.scopes_as_array %}{{i}}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal '12', Liquid::Template.parse( '{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal '1', Liquid::Template.parse( '{% for i in context.scopes_as_array %}{{i}}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal '12', Liquid::Template.parse( '{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
end
def test_access_context_from_drop
assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3])
end
def test_enumerable_drop
assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3])
end
def test_enumerable_drop
assert_equal '123', Liquid::Template.parse( '{% for c in collection %}{{c}}{% endfor %}').render('collection' => EnumerableDrop.new)
end
def test_enumerable_drop_size
def test_enumerable_drop_size
assert_equal '3', Liquid::Template.parse( '{{collection.size}}').render('collection' => EnumerableDrop.new)
end
end
end # DropsTest

View File

@@ -1,89 +1,69 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class ErrorDrop < Liquid::Drop
def standard_error
raise Liquid::StandardError, 'standard error'
end
end
def argument_error
raise Liquid::ArgumentError, 'argument error'
end
end
def syntax_error
raise Liquid::SyntaxError, 'syntax error'
end
end
end
end
class ErrorHandlingTest < Test::Unit::TestCase
include Liquid
def test_standard_error
assert_nothing_raised do
assert_nothing_raised do
template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
assert_equal 1, template.errors.size
assert_equal StandardError, template.errors.first.class
end
end
def test_syntax
assert_nothing_raised do
def test_syntax
assert_nothing_raised do
template = Liquid::Template.parse( ' {{ errors.syntax_error }} ' )
assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
assert_equal 1, template.errors.size
assert_equal SyntaxError, template.errors.first.class
end
end
def test_argument
assert_nothing_raised do
end
end
def test_argument
assert_nothing_raised do
template = Liquid::Template.parse( ' {{ errors.argument_error }} ' )
assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
assert_equal 1, template.errors.size
assert_equal ArgumentError, template.errors.first.class
end
end
def test_missing_endtag_parse_time_error
assert_raise(Liquid::SyntaxError) do
template = Liquid::Template.parse(' {% for a in b %} ... ')
end
end
def test_missing_endtag_parse_time_error
assert_raise(Liquid::SyntaxError) do
template = Liquid::Template.parse(' {% for a in b %} ... ')
end
end
def test_unrecognized_operator
assert_nothing_raised do
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ')
assert_equal ' Liquid error: Unknown operator =! ', template.render
assert_equal 1, template.errors.size
assert_equal Liquid::ArgumentError, template.errors.first.class
end
end
end
end # ErrorHandlingTest

View File

@@ -1,18 +1,17 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class FileSystemTest < Test::Unit::TestCase
include Liquid
def test_default
assert_raise(FileSystemError) do
BlankFileSystem.new.read_template_file("dummy")
end
end
def test_local
file_system = Liquid::LocalFileSystem.new("/some/path")
assert_equal "/some/path/_mypartial.liquid" , file_system.full_path("mypartial")
assert_equal "/some/path/_mypartial.liquid" , file_system.full_path("mypartial")
assert_equal "/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial")
assert_raise(FileSystemError) do
@@ -20,11 +19,11 @@ class FileSystemTest < Test::Unit::TestCase
end
assert_raise(FileSystemError) do
file_system.full_path("/dir/../../dir/mypartial")
file_system.full_path("/dir/../../dir/mypartial")
end
assert_raise(FileSystemError) do
file_system.full_path("/etc/passwd")
end
end
end
end # FileSystemTest

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
module MoneyFilter
def money(input)
@@ -27,6 +26,7 @@ class FiltersTest < Test::Unit::TestCase
def test_local_filter
@context['var'] = 1000
@context.add_filters(MoneyFilter)
assert_equal ' 1000$ ', Variable.new("var | money").render(@context)
end
@@ -40,17 +40,20 @@ class FiltersTest < Test::Unit::TestCase
@context['var'] = 1000
@context.add_filters(MoneyFilter)
@context.add_filters(CanadianMoneyFilter)
assert_equal ' 1000$ CAD ', Variable.new("var | money").render(@context)
end
def test_size
@context['var'] = 'abcd'
@context.add_filters(MoneyFilter)
assert_equal 4, Variable.new("var | size").render(@context)
end
def test_join
@context['var'] = [1,2,3,4]
assert_equal "1 2 3 4", Variable.new("var | join").render(@context)
end
@@ -59,22 +62,30 @@ class FiltersTest < Test::Unit::TestCase
@context['numbers'] = [2,1,4,3]
@context['words'] = ['expected', 'as', 'alphabetic']
@context['arrays'] = [['flattened'], ['are']]
assert_equal [1,2,3,4], Variable.new("numbers | sort").render(@context)
assert_equal ['alphabetic', 'as', 'expected'],
Variable.new("words | sort").render(@context)
assert_equal ['alphabetic', 'as', 'expected'], Variable.new("words | sort").render(@context)
assert_equal [3], Variable.new("value | sort").render(@context)
assert_equal ['are', 'flattened'], Variable.new("arrays | sort").render(@context)
end
def test_strip_html
@context['var'] = "<b>bla blub</a>"
assert_equal "bla blub", Variable.new("var | strip_html").render(@context)
end
def test_capitalize
@context['var'] = "blub"
assert_equal "Blub", Variable.new("var | capitalize").render(@context)
end
def test_nonexistent_filter_is_ignored
@context['var'] = 1000
assert_equal 1000, Variable.new("var | xyzzy").render(@context)
end
end
class FiltersInTemplate < Test::Unit::TestCase
@@ -92,4 +103,4 @@ class FiltersInTemplate < Test::Unit::TestCase
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, CanadianMoneyFilter)
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, [CanadianMoneyFilter])
end
end
end # FiltersTest

View File

@@ -1,31 +1,29 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class HtmlTagTest < Test::Unit::TestCase
include Liquid
def test_html_table
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
'numbers' => [1,2,3,4,5,6])
assert_template_result("<tr class=\"row1\">\n</tr>\n",
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
'numbers' => [])
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
'numbers' => [])
end
def test_html_table_with_different_cols
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td><td class=\"col4\"> 4 </td><td class=\"col5\"> 5 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 6 </td></tr>\n",
'{% tablerow n in numbers cols:5%} {{n}} {% endtablerow %}',
'{% tablerow n in numbers cols:5%} {{n}} {% endtablerow %}',
'numbers' => [1,2,3,4,5,6])
end
def test_html_col_counter
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n<tr class=\"row2\"><td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n<tr class=\"row3\"><td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n",
'{% tablerow n in numbers cols:2%}{{tablerowloop.col}}{% endtablerow %}',
'{% tablerow n in numbers cols:2%}{{tablerowloop.col}}{% endtablerow %}',
'numbers' => [1,2,3,4,5,6])
end
end
end # HtmlTagTest

View File

@@ -0,0 +1,127 @@
require 'test_helper'
class TestFileSystem
def read_template_file(template_path)
case template_path
when "product"
"Product: {{ product.title }} "
when "locale_variables"
"Locale: {{echo1}} {{echo2}}"
when "variant"
"Variant: {{ variant.title }}"
when "nested_template"
"{% include 'header' %} {% include 'body' %} {% include 'footer' %}"
when "body"
"body {% include 'body_detail' %}"
when "nested_product_template"
"Product: {{ nested_product_template.title }} {%include 'details'%} "
when "recursively_nested_template"
"-{% include 'recursively_nested_template' %}"
when "pick_a_source"
"from TestFileSystem"
else
template_path
end
end
end
class OtherFileSystem
def read_template_file(template_path)
'from OtherFileSystem'
end
end
class IncludeTagTest < Test::Unit::TestCase
include Liquid
def setup
Liquid::Template.file_system = TestFileSystem.new
end
def test_include_tag_looks_for_file_system_in_registers_first
assert_equal 'from OtherFileSystem',
Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => OtherFileSystem.new})
end
def test_include_tag_with
assert_equal "Product: Draft 151cm ",
Template.parse("{% include 'product' with products[0] %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] )
end
def test_include_tag_with_default_name
assert_equal "Product: Draft 151cm ",
Template.parse("{% include 'product' %}").render( "product" => {'title' => 'Draft 151cm'} )
end
def test_include_tag_for
assert_equal "Product: Draft 151cm Product: Element 155cm ",
Template.parse("{% include 'product' for products %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] )
end
def test_include_tag_with_local_variables
assert_equal "Locale: test123 ",
Template.parse("{% include 'locale_variables' echo1: 'test123' %}").render
end
def test_include_tag_with_multiple_local_variables
assert_equal "Locale: test123 test321",
Template.parse("{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}").render
end
def test_include_tag_with_multiple_local_variables_from_context
assert_equal "Locale: test123 test321",
Template.parse("{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}").render('echo1' => 'test123', 'more_echos' => { "echo2" => 'test321'})
end
def test_nested_include_tag
assert_equal "body body_detail",
Template.parse("{% include 'body' %}").render
assert_equal "header body body_detail footer",
Template.parse("{% include 'nested_template' %}").render
end
def test_nested_include_with_variable
assert_equal "Product: Draft 151cm details ",
Template.parse("{% include 'nested_product_template' with product %}").render("product" => {"title" => 'Draft 151cm'})
assert_equal "Product: Draft 151cm details Product: Element 155cm details ",
Template.parse("{% include 'nested_product_template' for products %}").render("products" => [{"title" => 'Draft 151cm'}, {"title" => 'Element 155cm'}])
end
def test_recursively_included_template_does_not_produce_endless_loop
infinite_file_system = Class.new do
def read_template_file(template_path)
"-{% include 'loop' %}"
end
end
Liquid::Template.file_system = infinite_file_system.new
assert_raise(Liquid::StackLevelError) do
Template.parse("{% include 'loop' %}").render!
end
end
def test_dynamically_choosen_template
assert_equal "Test123", Template.parse("{% include template %}").render("template" => 'Test123')
assert_equal "Test321", Template.parse("{% include template %}").render("template" => 'Test321')
assert_equal "Product: Draft 151cm ", Template.parse("{% include template for product %}").render("template" => 'product', 'product' => { 'title' => 'Draft 151cm'})
end
end # IncludeTagTest

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class TestClassA
liquid_methods :allowedA, :chainedB
@@ -39,31 +38,31 @@ end
class ModuleExTest < Test::Unit::TestCase
include Liquid
def setup
@a = TestClassA.new
@b = TestClassB.new
@c = TestClassC.new
end
def test_should_create_LiquidDropClass
assert TestClassA::LiquidDropClass
assert TestClassB::LiquidDropClass
assert TestClassC::LiquidDropClass
end
def test_should_respond_to_liquid
assert @a.respond_to?(:to_liquid)
assert @b.respond_to?(:to_liquid)
assert @c.respond_to?(:to_liquid)
end
def test_should_return_LiquidDropClass_object
assert @a.to_liquid.is_a?(TestClassA::LiquidDropClass)
assert @b.to_liquid.is_a?(TestClassB::LiquidDropClass)
assert @c.to_liquid.is_a?(TestClassC::LiquidDropClass)
end
def test_should_respond_to_liquid_methods
assert @a.to_liquid.respond_to?(:allowedA)
assert @a.to_liquid.respond_to?(:chainedB)
@@ -84,6 +83,5 @@ class ModuleExTest < Test::Unit::TestCase
assert_equal 'another_allowedC', Liquid::Template.parse("{{ a.chainedB.chainedC.another_allowedC }}").render('a'=>@a)
assert_equal '', Liquid::Template.parse("{{ a.restricted }}").render('a'=>@a)
assert_equal '', Liquid::Template.parse("{{ a.unknown }}").render('a'=>@a)
end
end
end
end # ModuleExTest

View File

@@ -1,20 +1,18 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
module FunnyFilter
def make_funny(input)
'LOL'
'LOL'
end
def cite_funny(input)
"LOL: #{input}"
end
end
def add_smiley(input, smiley = ":-)")
"#{input} #{smiley}"
end
def add_tag(input, tag = "p", id = "foo")
%|<#{tag} id="#{id}">#{input}</#{tag}>|
end
@@ -22,25 +20,24 @@ module FunnyFilter
def paragraph(input)
"<p>#{input}</p>"
end
def link_to(name, url)
%|<a href="#{url}">#{name}</a>|
end
end
end
class OutputTest < Test::Unit::TestCase
include Liquid
def setup
@assigns = {
@assigns = {
'best_cars' => 'bmw',
'car' => {'bmw' => 'good', 'gm' => 'bad'}
}
end
def test_variable
def test_variable
text = %| {{best_cars}} |
expected = %| bmw |
@@ -53,35 +50,35 @@ class OutputTest < Test::Unit::TestCase
expected = %| good bad good |
assert_equal expected, Template.parse(text).render(@assigns)
end
def test_variable_piping
text = %( {{ car.gm | make_funny }} )
expected = %| LOL |
expected = %| LOL |
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
end
def test_variable_piping_with_input
text = %( {{ car.gm | cite_funny }} )
expected = %| LOL: bad |
expected = %| LOL: bad |
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
end
def test_variable_piping_with_args
text = %! {{ car.gm | add_smiley : ':-(' }} !
expected = %| bad :-( |
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
end
def test_variable_piping_with_no_args
text = %! {{ car.gm | add_smiley }} !
expected = %| bad :-) |
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
end
def test_multiple_variable_piping_with_args
text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
expected = %| bad :-( :-( |
@@ -105,17 +102,15 @@ class OutputTest < Test::Unit::TestCase
def test_multiple_pipings
text = %( {{ best_cars | cite_funny | paragraph }} )
expected = %| <p>LOL: bmw</p> |
expected = %| <p>LOL: bmw</p> |
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
end
def test_link_to
text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
expected = %| <a href="http://typo.leetsoft.com">Typo</a> |
expected = %| <a href="http://typo.leetsoft.com">Typo</a> |
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
end
end
end # OutputTest

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class ParsingQuirksTest < Test::Unit::TestCase
include Liquid
@@ -7,48 +6,47 @@ class ParsingQuirksTest < Test::Unit::TestCase
def test_error_with_css
text = %| div { font-weight: bold; } |
template = Template.parse(text)
assert_equal text, template.render
assert_equal [String], template.root.nodelist.collect {|i| i.class}
end
def test_raise_on_single_close_bracet
assert_raise(SyntaxError) do
Template.parse("text {{method} oh nos!")
Template.parse("text {{method} oh nos!")
end
end
def test_raise_on_label_and_no_close_bracets
assert_raise(SyntaxError) do
Template.parse("TEST {{ ")
end
end
end
def test_raise_on_label_and_no_close_bracets_percent
assert_raise(SyntaxError) do
Template.parse("TEST {% ")
end
end
def test_error_on_empty_filter
assert_nothing_raised do
Template.parse("{{test |a|b|}}")
Template.parse("{{test}}")
Template.parse("{{|test|}}")
Template.parse("{{test |a|b|}}")
Template.parse("{{test}}")
Template.parse("{{|test|}}")
end
end
def test_meaningless_parens
assigns = {'b' => 'bar', 'c' => 'baz'}
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns)
end
def test_unexpected_characters_silently_eat_logic
markup = "true && false"
assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}")
markup = "false || true"
assert_template_result('',"{% if #{markup} %} YES {% endif %}")
end
end
end # ParsingQuirksTest

View File

@@ -1,20 +1,20 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class RegexpTest < Test::Unit::TestCase
include Liquid
def test_empty
assert_equal [], ''.scan(QuotedFragment)
end
def test_quote
assert_equal ['"arg 1"'], '"arg 1"'.scan(QuotedFragment)
end
def test_words
assert_equal ['arg1', 'arg2'], 'arg1 arg2'.scan(QuotedFragment)
end
def test_tags
assert_equal ['<tr>', '</tr>'], '<tr> </tr>'.scan(QuotedFragment)
assert_equal ['<tr></tr>'], '<tr></tr>'.scan(QuotedFragment)
@@ -32,14 +32,18 @@ class RegexpTest < Test::Unit::TestCase
def test_quoted_words_in_the_middle
assert_equal ['arg1', 'arg2', '"arg 3"', 'arg4'], 'arg1 arg2 "arg 3" arg4 '.scan(QuotedFragment)
end
def test_variable_parser
assert_equal ['var'], 'var'.scan(VariableParser)
assert_equal ['var', 'method'], 'var.method'.scan(VariableParser)
assert_equal ['var', '[method]'], 'var[method]'.scan(VariableParser)
assert_equal ['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser)
assert_equal ['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser)
assert_equal ['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser)
assert_equal ['var'], 'var'.scan(VariableParser)
assert_equal ['var', 'method'], 'var.method'.scan(VariableParser)
assert_equal ['var', '[method]'], 'var[method]'.scan(VariableParser)
assert_equal ['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser)
assert_equal ['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser)
assert_equal ['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser)
end
end
def test_literal_shorthand_regexp
assert_equal [["{% if 'gnomeslab' contains 'liquid' %}yes{% endif %}"]],
"{{{ {% if 'gnomeslab' contains 'liquid' %}yes{% endif %} }}}".scan(LiteralShorthand)
end
end # RegexpTest

View File

@@ -1,4 +1,4 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
module SecurityFilter
def add_one(input)
@@ -12,22 +12,22 @@ class SecurityTest < Test::Unit::TestCase
def test_no_instance_eval
text = %( {{ '1+1' | instance_eval }} )
expected = %| 1+1 |
assert_equal expected, Template.parse(text).render(@assigns)
end
def test_no_existing_instance_eval
text = %( {{ '1+1' | __instance_eval__ }} )
expected = %| 1+1 |
assert_equal expected, Template.parse(text).render(@assigns)
end
def test_no_instance_eval_after_mixing_in_new_filter
text = %( {{ '1+1' | instance_eval }} )
expected = %| 1+1 |
assert_equal expected, Template.parse(text).render(@assigns)
end
@@ -35,7 +35,7 @@ class SecurityTest < Test::Unit::TestCase
def test_no_instance_eval_later_in_chain
text = %( {{ '1+1' | add_one | instance_eval }} )
expected = %| 1+1 + 1 |
assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter)
end
end
end # SecurityTest

View File

@@ -1,175 +1,180 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class Filters
include Liquid::StandardFilters
end
class StandardFiltersTest < Test::Unit::TestCase
include Liquid
def setup
@filters = Filters.new
end
def test_size
assert_equal 3, @filters.size([1,2,3])
assert_equal 0, @filters.size([])
assert_equal 0, @filters.size(nil)
end
def test_downcase
assert_equal 'testing', @filters.downcase("Testing")
assert_equal '', @filters.downcase(nil)
end
def test_upcase
assert_equal 'TESTING', @filters.upcase("Testing")
assert_equal '', @filters.upcase(nil)
end
def test_upcase
assert_equal 'TESTING', @filters.upcase("Testing")
assert_equal '', @filters.upcase(nil)
end
def test_truncate
assert_equal '1234...', @filters.truncate('1234567890', 7)
assert_equal '1234567890', @filters.truncate('1234567890', 20)
assert_equal '...', @filters.truncate('1234567890', 0)
assert_equal '1234567890', @filters.truncate('1234567890')
end
def test_escape
assert_equal '&lt;strong&gt;', @filters.escape('<strong>')
assert_equal '&lt;strong&gt;', @filters.h('<strong>')
assert_equal '&lt;strong&gt;', @filters.h('<strong>')
end
def test_escape_once
assert_equal '&lt;strong&gt;', @filters.escape_once(@filters.escape('<strong>'))
end
def test_truncatewords
assert_equal 'one two three', @filters.truncatewords('one two three', 4)
assert_equal 'one two...', @filters.truncatewords('one two three', 2)
assert_equal 'one two three', @filters.truncatewords('one two three')
assert_equal 'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...', @filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover.', 15)
assert_equal 'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...', @filters.truncatewords('Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover.', 15)
end
def test_strip_html
assert_equal 'test', @filters.strip_html("<div>test</div>")
assert_equal 'test', @filters.strip_html("<div id='test'>test</div>")
assert_equal '', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>")
assert_equal '', @filters.strip_html(nil)
assert_equal 'test', @filters.strip_html("<div>test</div>")
assert_equal 'test', @filters.strip_html("<div id='test'>test</div>")
assert_equal '', @filters.strip_html("<script type='text/javascript'>document.write('some stuff');</script>")
assert_equal '', @filters.strip_html(nil)
end
def test_join
assert_equal '1 2 3 4', @filters.join([1,2,3,4])
assert_equal '1 - 2 - 3 - 4', @filters.join([1,2,3,4], ' - ')
assert_equal '1 2 3 4', @filters.join([1,2,3,4])
assert_equal '1 - 2 - 3 - 4', @filters.join([1,2,3,4], ' - ')
end
def test_sort
assert_equal [1,2,3,4], @filters.sort([4,3,2,1])
assert_equal [1,2,3,4], @filters.sort([4,3,2,1])
assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
end
def test_map
assert_equal [1,2,3,4], @filters.map([{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], 'a')
assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}]
end
def test_date
assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")
assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")
assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B")
assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B")
assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B")
assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B")
assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B")
assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
assert_equal nil, @filters.date(nil, "%B")
assert_equal nil, @filters.date(nil, "%B")
end
def test_first_last
assert_equal 1, @filters.first([1,2,3])
assert_equal 3, @filters.last([1,2,3])
assert_equal nil, @filters.first([])
assert_equal nil, @filters.last([])
assert_equal 1, @filters.first([1,2,3])
assert_equal 3, @filters.last([1,2,3])
assert_equal nil, @filters.first([])
assert_equal nil, @filters.last([])
end
def test_replace
assert_equal 'b b b b', @filters.replace("a a a a", 'a', 'b')
assert_equal 'b b b b', @filters.replace("a a a a", 'a', 'b')
assert_equal 'b a a a', @filters.replace_first("a a a a", 'a', 'b')
assert_template_result 'b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}"
assert_template_result 'b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}"
end
def test_remove
assert_equal ' ', @filters.remove("a a a a", 'a')
assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
assert_equal ' ', @filters.remove("a a a a", 'a')
assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}"
end
def test_pipes_in_string_arguments
def test_pipes_in_string_arguments
assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}"
end
def test_strip_newlines
assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc"
end
def test_newlines_to_br
assert_template_result "a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
end
def test_plus
assert_template_result "2", "{{ 1 | plus:1 }}"
assert_template_result "2.0", "{{ '1' | plus:'1.0' }}"
end
def test_minus
assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
assert_template_result "2.3", "{{ '4.3' | minus:'2' }}"
end
def test_times
assert_template_result "12", "{{ 3 | times:4 }}"
assert_template_result "0", "{{ 'foo' | times:4 }}"
assert_template_result "6.3", "{{ '2.1' | times:3 }}"
# Ruby v1.9.2-rc1, or higher, backwards compatible Float test
assert_match(/(6\.3)|(6\.(0{13})1)/, Template.parse("{{ '2.1' | times:3 }}").render)
assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}"
end
end
def test_divided_by
assert_template_result "4", "{{ 12 | divided_by:3 }}"
assert_template_result "4", "{{ 14 | divided_by:3 }}"
assert_template_result "4.66666666666667", "{{ 14 | divided_by:'3.0' }}"
# Ruby v1.9.2-rc1, or higher, backwards compatible Float test
assert_match(/4\.(6{13,14})7/, Template.parse("{{ 14 | divided_by:'3.0' }}").render)
assert_template_result "5", "{{ 15 | divided_by:3 }}"
assert_template_result "Liquid error: divided by 0", "{{ 5 | divided_by:0 }}"
end
def test_append
assigns = {'a' => 'bc', 'b' => 'd' }
assert_template_result('bcd',"{{ a | append: 'd'}}",assigns)
assert_template_result('bcd',"{{ a | append: b}}",assigns)
assert_template_result('bcd',"{{ a | append: 'd'}}",assigns)
assert_template_result('bcd',"{{ a | append: b}}",assigns)
end
def test_prepend
assigns = {'a' => 'bc', 'b' => 'a' }
assert_template_result('abc',"{{ a | prepend: 'a'}}",assigns)
assert_template_result('abc',"{{ a | prepend: b}}",assigns)
assert_template_result('abc',"{{ a | prepend: 'a'}}",assigns)
assert_template_result('abc',"{{ a | prepend: b}}",assigns)
end
def test_cannot_access_private_methods
assert_template_result('a',"{{ 'a' | to_number }}")
end
end
end # StandardFiltersTest

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class StrainerTest < Test::Unit::TestCase
include Liquid
@@ -12,10 +11,15 @@ class StrainerTest < Test::Unit::TestCase
assert_equal false, strainer.respond_to?('__send__')
assert_equal true, strainer.respond_to?('size') # from the standard lib
end
def test_should_respond_to_two_parameters
strainer = Strainer.create(nil)
assert_equal true, strainer.respond_to?('size', false)
end
end
# Asserts that Object#respond_to_missing? is not being undefined in Ruby versions where it has been implemented
# Currently this method is only present in Ruby v1.9.2, or higher
def test_object_respond_to_missing
assert_equal Object.respond_to?(:respond_to_missing?), Strainer.create(nil).respond_to?(:respond_to_missing?)
end
end # StrainerTest

View File

@@ -1,4 +1,4 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class IfElseTest < Test::Unit::TestCase
include Liquid
@@ -6,7 +6,7 @@ class IfElseTest < Test::Unit::TestCase
def test_if
assert_template_result(' ',' {% if false %} this text should not go into the output {% endif %} ')
assert_template_result(' this text should go into the output ',
' {% if true %} this text should go into the output {% endif %} ')
' {% if true %} this text should go into the output {% endif %} ')
assert_template_result(' you rock ?','{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
end
@@ -15,27 +15,27 @@ class IfElseTest < Test::Unit::TestCase
assert_template_result(' YES ','{% if true %} YES {% else %} NO {% endif %}')
assert_template_result(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}')
end
def test_if_boolean
assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true)
end
def test_if_or
assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => true)
assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => false)
assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => false, 'b' => true)
assert_template_result('', '{% if a or b %} YES {% endif %}', 'a' => false, 'b' => false)
assert_template_result(' YES ','{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => true)
assert_template_result('', '{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => false)
def test_if_boolean
assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true)
end
def test_if_or
assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => true)
assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => false)
assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => false, 'b' => true)
assert_template_result('', '{% if a or b %} YES {% endif %}', 'a' => false, 'b' => false)
assert_template_result(' YES ','{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => true)
assert_template_result('', '{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => false)
end
def test_if_or_with_operators
assert_template_result(' YES ','{% if a == true or b == true %} YES {% endif %}', 'a' => true, 'b' => true)
assert_template_result(' YES ','{% if a == true or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
assert_template_result('','{% if a == false or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
end
assert_template_result(' YES ','{% if a == true or b == true %} YES {% endif %}', 'a' => true, 'b' => true)
assert_template_result(' YES ','{% if a == true or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
assert_template_result('','{% if a == false or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
end
def test_comparison_of_strings_containing_and_or_or
assert_nothing_raised do
awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar"
@@ -43,18 +43,32 @@ class IfElseTest < Test::Unit::TestCase
assert_template_result(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns)
end
end
def test_if_and
assert_template_result(' YES ','{% if true and true %} YES {% endif %}')
assert_template_result('','{% if false and true %} YES {% endif %}')
assert_template_result('','{% if false and true %} YES {% endif %}')
def test_comparison_of_expressions_starting_with_and_or_or
assigns = {'order' => {'items_count' => 0}, 'android' => {'name' => 'Roy'}}
assert_nothing_raised do
assert_template_result( "YES",
"{% if android.name == 'Roy' %}YES{% endif %}",
assigns)
end
assert_nothing_raised do
assert_template_result( "YES",
"{% if order.items_count == 0 %}YES{% endif %}",
assigns)
end
end
def test_if_and
assert_template_result(' YES ','{% if true and true %} YES {% endif %}')
assert_template_result('','{% if false and true %} YES {% endif %}')
assert_template_result('','{% if false and true %} YES {% endif %}')
end
def test_hash_miss_generates_false
assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {})
end
def test_if_from_variable
assert_template_result('','{% if var %} NO {% endif %}', 'var' => false)
assert_template_result('','{% if var %} NO {% endif %}', 'var' => nil)
@@ -62,7 +76,7 @@ class IfElseTest < Test::Unit::TestCase
assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {})
assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => nil)
assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => true)
assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => "text")
assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true)
assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => 1)
@@ -74,12 +88,12 @@ class IfElseTest < Test::Unit::TestCase
assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => 1 })
assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => {} })
assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => [] })
assert_template_result(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var' => false)
assert_template_result(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var' => nil)
assert_template_result(' YES ','{% if var %} YES {% else %} NO {% endif %}', 'var' => true)
assert_template_result(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}', 'var' => "text")
assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {'bar' => false})
assert_template_result(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => {'bar' => true})
assert_template_result(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => {'bar' => "text"})
@@ -87,19 +101,19 @@ class IfElseTest < Test::Unit::TestCase
assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {})
assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'notfoo' => {'bar' => true})
end
def test_nested_if
assert_template_result('', '{% if false %}{% if false %} NO {% endif %}{% endif %}')
assert_template_result('', '{% if false %}{% if true %} NO {% endif %}{% endif %}')
assert_template_result('', '{% if true %}{% if false %} NO {% endif %}{% endif %}')
assert_template_result(' YES ', '{% if true %}{% if true %} YES {% endif %}{% endif %}')
assert_template_result(' YES ', '{% if true %}{% if true %} YES {% else %} NO {% endif %}{% else %} NO {% endif %}')
assert_template_result(' YES ', '{% if true %}{% if false %} NO {% else %} YES {% endif %}{% else %} NO {% endif %}')
assert_template_result(' YES ', '{% if false %}{% if true %} NO {% else %} NONO {% endif %}{% else %} YES {% endif %}')
end
def test_comparisons_on_null
assert_template_result('','{% if null < 10 %} NO {% endif %}')
assert_template_result('','{% if null <= 10 %} NO {% endif %}')
@@ -111,29 +125,36 @@ class IfElseTest < Test::Unit::TestCase
assert_template_result('','{% if 10 >= null %} NO {% endif %}')
assert_template_result('','{% if 10 > null %} NO {% endif %}')
end
def test_else_if
assert_template_result('0','{% if 0 == 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}')
assert_template_result('1','{% if 0 != 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}')
assert_template_result('2','{% if 0 != 0 %}0{% elsif 1 != 1%}1{% else %}2{% endif %}')
assert_template_result('elsif','{% if false %}if{% elsif true %}elsif{% endif %}')
assert_template_result('elsif','{% if false %}if{% elsif true %}elsif{% endif %}')
end
def test_syntax_error_no_variable
assert_raise(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}')}
end
def test_syntax_error_no_expression
assert_raise(SyntaxError) { assert_template_result('', '{% if %}') }
end
def test_if_with_custom_condition
Condition.operators['contains'] = :[]
assert_template_result('yes', %({% if 'bob' contains 'o' %}yes{% endif %}))
assert_template_result('no', %({% if 'bob' contains 'f' %}yes{% else %}no{% endif %}))
ensure
Condition.operators.delete 'contains'
end
end
def test_operators_are_ignored_unless_isolated
Condition.operators['contains'] = :[]
assert_template_result('yes',
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
end
end # IfElseTest

View File

@@ -0,0 +1,39 @@
require 'test_helper'
class LiteralTagTest < Test::Unit::TestCase
include Liquid
def test_empty_literal
assert_template_result '', '{% literal %}{% endliteral %}'
assert_template_result '', '{{{}}}'
end
def test_simple_literal_value
assert_template_result 'howdy',
'{% literal %}howdy{% endliteral %}'
end
def test_literals_ignore_liquid_markup
expected = %({% if 'gnomeslab' contain 'liquid' %}yes{ % endif %})
template = %({% literal %}#{expected}{% endliteral %})
assert_template_result expected, template
end
def test_shorthand_syntax
expected = %({% if 'gnomeslab' contain 'liquid' %}yes{ % endif %})
template = %({{{#{expected}}}})
assert_template_result expected, template
end
# Class methods
def test_from_shorthand
assert_equal '{% literal %}gnomeslab{% endliteral %}', Liquid::Literal.from_shorthand('{{{gnomeslab}}}')
end
def test_from_shorthand_ignores_improper_syntax
text = "{% if 'hi' == 'hi' %}hi{% endif %}"
assert_equal text, Liquid::Literal.from_shorthand(text)
end
end # AssignTest

View File

@@ -1,10 +1,8 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class StandardTagTest < Test::Unit::TestCase
include Liquid
def test_tag
tag = Tag.new('tag', [], [])
assert_equal 'liquid::tag', tag.name
@@ -14,6 +12,7 @@ class StandardTagTest < Test::Unit::TestCase
def test_no_transform
assert_template_result('this text should come out of the template without change...',
'this text should come out of the template without change...')
assert_template_result('blah','blah')
assert_template_result('<blah>','<blah>')
assert_template_result('|,.:','|,.:')
@@ -85,43 +84,48 @@ HERE
def test_for_helpers
assigns = {'array' => [1,2,3] }
assert_template_result(' 1/3 2/3 3/3 ','{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',assigns)
assert_template_result(' 1 2 3 ','{%for item in array%} {{forloop.index}} {%endfor%}',assigns)
assert_template_result(' 0 1 2 ','{%for item in array%} {{forloop.index0}} {%endfor%}',assigns)
assert_template_result(' 2 1 0 ','{%for item in array%} {{forloop.rindex0}} {%endfor%}',assigns)
assert_template_result(' 3 2 1 ','{%for item in array%} {{forloop.rindex}} {%endfor%}',assigns)
assert_template_result(' true false false ','{%for item in array%} {{forloop.first}} {%endfor%}',assigns)
assert_template_result(' false false true ','{%for item in array%} {{forloop.last}} {%endfor%}',assigns)
assert_template_result(' 1/3 2/3 3/3 ',
'{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',
assigns)
assert_template_result(' 1 2 3 ', '{%for item in array%} {{forloop.index}} {%endfor%}', assigns)
assert_template_result(' 0 1 2 ', '{%for item in array%} {{forloop.index0}} {%endfor%}', assigns)
assert_template_result(' 2 1 0 ', '{%for item in array%} {{forloop.rindex0}} {%endfor%}', assigns)
assert_template_result(' 3 2 1 ', '{%for item in array%} {{forloop.rindex}} {%endfor%}', assigns)
assert_template_result(' true false false ', '{%for item in array%} {{forloop.first}} {%endfor%}', assigns)
assert_template_result(' false false true ', '{%for item in array%} {{forloop.last}} {%endfor%}', assigns)
end
def test_for_and_if
assigns = {'array' => [1,2,3] }
assert_template_result('+--', '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}', assigns)
assert_template_result('+--',
'{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
assigns)
end
def test_limiting
assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
assert_template_result('12','{%for i in array limit:2 %}{{ i }}{%endfor%}',assigns)
assert_template_result('1234','{%for i in array limit:4 %}{{ i }}{%endfor%}',assigns)
assert_template_result('3456','{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}',assigns)
assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns)
assert_template_result('12', '{%for i in array limit:2 %}{{ i }}{%endfor%}', assigns)
assert_template_result('1234', '{%for i in array limit:4 %}{{ i }}{%endfor%}', assigns)
assert_template_result('3456', '{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}', assigns)
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
end
def test_dynamic_variable_limiting
assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
assigns['limit'] = 2
assigns['offset'] = 2
assert_template_result('34','{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}',assigns)
assert_template_result('34', '{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}', assigns)
end
def test_nested_for
assigns = {'array' => [[1,2],[3,4],[5,6]] }
assert_template_result('123456','{%for item in array%}{%for i in item%}{{ i }}{%endfor%}{%endfor%}',assigns)
assert_template_result('123456', '{%for item in array%}{%for i in item%}{{ i }}{%endfor%}{%endfor%}', assigns)
end
def test_offset_only
assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
assert_template_result('890','{%for i in array offset:7 %}{{ i }}{%endfor%}',assigns)
assert_template_result('890', '{%for i in array offset:7 %}{{ i }}{%endfor%}', assigns)
end
def test_pause_resume
@@ -199,86 +203,131 @@ HERE
def test_assign
assigns = {'var' => 'content' }
assert_template_result('var2: var2:content','var2:{{var2}} {%assign var2 = var%} var2:{{var2}}',assigns)
assert_template_result('var2: var2:content', 'var2:{{var2}} {%assign var2 = var%} var2:{{var2}}', assigns)
end
def test_hyphenated_assign
assigns = {'a-b' => '1' }
assert_template_result('a-b:1 a-b:2','a-b:{{a-b}} {%assign a-b = 2 %}a-b:{{a-b}}',assigns)
assert_template_result('a-b:1 a-b:2', 'a-b:{{a-b}} {%assign a-b = 2 %}a-b:{{a-b}}', assigns)
end
def test_assign_with_colon_and_spaces
assigns = {'var' => {'a:b c' => {'paged' => '1' }}}
assert_template_result('var2: 1','{%assign var2 = var["a:b c"].paged %}var2: {{var2}}',assigns)
assert_template_result('var2: 1', '{%assign var2 = var["a:b c"].paged %}var2: {{var2}}', assigns)
end
def test_capture
assigns = {'var' => 'content' }
assert_template_result('content foo content foo ','{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}', assigns)
assert_template_result('content foo content foo ',
'{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
assigns)
end
def test_capture_detects_bad_syntax
assert_raise(SyntaxError) do
assert_template_result('content foo content foo ','{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}', {'var' => 'content' })
assert_template_result('content foo content foo ',
'{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
{'var' => 'content' })
end
end
def test_case
assigns = {'condition' => 2 }
assert_template_result(' its 2 ','{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns)
assert_template_result(' its 2 ',
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
assigns)
assigns = {'condition' => 1 }
assert_template_result(' its 1 ','{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns)
assert_template_result(' its 1 ',
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
assigns)
assigns = {'condition' => 3 }
assert_template_result('','{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns)
assert_template_result('',
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
assigns)
assigns = {'condition' => "string here" }
assert_template_result(' hit ','{% case condition %}{% when "string here" %} hit {% endcase %}', assigns)
assert_template_result(' hit ',
'{% case condition %}{% when "string here" %} hit {% endcase %}',
assigns)
assigns = {'condition' => "bad string here" }
assert_template_result('','{% case condition %}{% when "string here" %} hit {% endcase %}', assigns)
assert_template_result('',
'{% case condition %}{% when "string here" %} hit {% endcase %}',\
assigns)
end
def test_case_with_else
assigns = {'condition' => 5 }
assert_template_result(' hit ','{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}', assigns)
assert_template_result(' hit ',
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
assigns)
assigns = {'condition' => 6 }
assert_template_result(' else ','{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}', assigns)
assert_template_result(' else ',
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
assigns)
assigns = {'condition' => 6 }
assert_template_result(' else ','{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}', assigns)
assert_template_result(' else ',
'{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}',
assigns)
end
def test_case_on_size
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [])
assert_template_result('1' , '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1])
assert_template_result('2' , '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [])
assert_template_result('1', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1])
assert_template_result('2', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1])
assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1, 1])
end
def test_case_on_size_with_else
assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [])
assert_template_result('1', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1])
assert_template_result('2', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1])
assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1])
assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1, 1])
assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1, 1, 1])
assert_template_result('else',
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
'a' => [])
assert_template_result('1',
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
'a' => [1])
assert_template_result('2',
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
'a' => [1, 1])
assert_template_result('else',
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
'a' => [1, 1, 1])
assert_template_result('else',
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
'a' => [1, 1, 1, 1])
assert_template_result('else',
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
'a' => [1, 1, 1, 1, 1])
end
def test_case_on_length_with_else
assert_template_result('else', '{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
assert_template_result('false', '{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
assert_template_result('true', '{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
assert_template_result('else', '{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
assert_template_result('else',
'{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
{})
assert_template_result('false',
'{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
{})
assert_template_result('true',
'{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
{})
assert_template_result('else',
'{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
{})
end
def test_assign_from_case
@@ -331,7 +380,8 @@ HERE
end
def test_assign_is_global
assert_equal 'variable', Liquid::Template.parse( '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' ).render
assert_equal 'variable',
Liquid::Template.parse( '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' ).render
end
def test_case_detects_bad_syntax
@@ -345,31 +395,31 @@ HERE
end
def test_cycle
assert_template_result('one','{%cycle "one", "two"%}')
assert_template_result('one two','{%cycle "one", "two"%} {%cycle "one", "two"%}')
assert_template_result(' two','{%cycle "", "two"%} {%cycle "", "two"%}')
assert_template_result('one two one','{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}')
assert_template_result('text-align: left text-align: right','{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}')
assert_template_result('text-align: left text-align: right',
'{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}')
end
def test_multiple_cycles
assert_template_result('1 2 1 1 2 3 1','{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}')
assert_template_result('1 2 1 1 2 3 1',
'{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}')
end
def test_multiple_named_cycles
assert_template_result('one one two two one one','{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}')
assert_template_result('one one two two one one',
'{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}')
end
def test_multiple_named_cycles_with_names_from_context
assigns = {"var1" => 1, "var2" => 2 }
assert_template_result('one one two two one one','{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}', assigns)
assert_template_result('one one two two one one',
'{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}', assigns)
end
def test_size_of_array
@@ -383,10 +433,10 @@ HERE
end
def test_illegal_symbols
assert_template_result('', '{% if true == empty %}?{% endif %}', {})
assert_template_result('', '{% if true == null %}?{% endif %}', {})
assert_template_result('', '{% if empty == true %}?{% endif %}', {})
assert_template_result('', '{% if null == true %}?{% endif %}', {})
assert_template_result('', '{% if true == empty %}?{% endif %}', {})
assert_template_result('', '{% if true == null %}?{% endif %}', {})
assert_template_result('', '{% if empty == true %}?{% endif %}', {})
assert_template_result('', '{% if null == true %}?{% endif %}', {})
end
def test_for_reversed
@@ -402,4 +452,4 @@ HERE
assigns = {'array' => [ 1, 1, 1, 1] }
assert_template_result('1','{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}',assigns)
end
end
end # StandardTagTest

View File

@@ -1,10 +1,8 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class StatementsTest < Test::Unit::TestCase
include Liquid
def test_true_eql_true
text = %| {% if true == true %} true {% else %} false {% endif %} |
expected = %| true |
@@ -69,46 +67,46 @@ class StatementsTest < Test::Unit::TestCase
expected = %| false |
assert_equal expected, Template.parse(text).render
end
def test_var_strings_equal
text = %| {% if var == "hello there!" %} true {% else %} false {% endif %} |
expected = %| true |
assert_equal expected, Template.parse(text).render('var' => 'hello there!')
end
def test_var_strings_are_not_equal
text = %| {% if "hello there!" == var %} true {% else %} false {% endif %} |
expected = %| true |
assert_equal expected, Template.parse(text).render('var' => 'hello there!')
end
def test_var_and_long_string_are_equal
text = %| {% if var == 'hello there!' %} true {% else %} false {% endif %} |
expected = %| true |
assert_equal expected, Template.parse(text).render('var' => 'hello there!')
end
def test_var_and_long_string_are_equal_backwards
text = %| {% if 'hello there!' == var %} true {% else %} false {% endif %} |
expected = %| true |
assert_equal expected, Template.parse(text).render('var' => 'hello there!')
end
#def test_is_nil
#def test_is_nil
# text = %| {% if var != nil %} true {% else %} false {% end %} |
# @template.assigns = { 'var' => 'hello there!'}
# expected = %| true |
# assert_equal expected, @template.parse(text)
#end
def test_is_collection_empty
def test_is_collection_empty
text = %| {% if array == empty %} true {% else %} false {% endif %} |
expected = %| true |
assert_equal expected, Template.parse(text).render('array' => [])
end
def test_is_not_collection_empty
def test_is_not_collection_empty
text = %| {% if array == empty %} true {% else %} false {% endif %} |
expected = %| false |
assert_equal expected, Template.parse(text).render('array' => [1,2,3])
@@ -133,5 +131,4 @@ class StatementsTest < Test::Unit::TestCase
expected = %| true |
assert_equal expected, Template.parse(text).render('var' => 1 )
end
end
end # StatementsTest

View File

@@ -1,4 +1,4 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class UnlessElseTest < Test::Unit::TestCase
include Liquid
@@ -6,7 +6,7 @@ class UnlessElseTest < Test::Unit::TestCase
def test_unless
assert_template_result(' ',' {% unless true %} this text should not go into the output {% endunless %} ')
assert_template_result(' this text should go into the output ',
' {% unless false %} this text should go into the output {% endunless %} ')
' {% unless false %} this text should go into the output {% endunless %} ')
assert_template_result(' you rock ?','{% unless true %} you suck {% endunless %} {% unless false %} you rock {% endunless %}?')
end
@@ -23,5 +23,4 @@ class UnlessElseTest < Test::Unit::TestCase
def test_unless_else_in_loop
assert_template_result ' TRUE 2 3 ', '{% for i in choices %}{% unless i %} {{ forloop.index }} {% else %} TRUE {% endunless %}{% endfor %}', 'choices' => [1, nil, false]
end
end
end # UnlessElseTest

View File

@@ -1,52 +1,52 @@
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class TemplateTest < Test::Unit::TestCase
include Liquid
def test_tokenize_strings
assert_equal [' '], Template.new.send(:tokenize, ' ')
assert_equal ['hello world'], Template.new.send(:tokenize, 'hello world')
end
end
def test_tokenize_variables
assert_equal ['{{funk}}'], Template.new.send(:tokenize, '{{funk}}')
assert_equal [' ', '{{funk}}', ' '], Template.new.send(:tokenize, ' {{funk}} ')
assert_equal [' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], Template.new.send(:tokenize, ' {{funk}} {{so}} {{brother}} ')
assert_equal [' ', '{{ funk }}', ' '], Template.new.send(:tokenize, ' {{ funk }} ')
end
def test_tokenize_blocks
end
def test_tokenize_blocks
assert_equal ['{%comment%}'], Template.new.send(:tokenize, '{%comment%}')
assert_equal [' ', '{%comment%}', ' '], Template.new.send(:tokenize, ' {%comment%} ')
assert_equal [' ', '{%comment%}', ' ', '{%endcomment%}', ' '], Template.new.send(:tokenize, ' {%comment%} {%endcomment%} ')
assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], Template.new.send(:tokenize, " {% comment %} {% endcomment %} ")
end
assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], Template.new.send(:tokenize, " {% comment %} {% endcomment %} ")
end
def test_instance_assigns_persist_on_same_template_object_between_parses
t = Template.new
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
assert_equal 'from instance assigns', t.parse("{{ foo }}").render
end
def test_instance_assigns_persist_on_same_template_parsing_between_renders
t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}")
assert_equal 'foo', t.render
assert_equal 'foofoo', t.render
end
def test_custom_assigns_do_not_persist_on_same_template
t = Template.new
assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns')
assert_equal '', t.parse("{{ foo }}").render
end
def test_custom_assigns_squash_instance_assigns
t = Template.new
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns')
end
def test_persistent_assigns_squash_instance_assigns
t = Template.new
assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
@@ -71,5 +71,4 @@ class TemplateTest < Test::Unit::TestCase
assert_equal '1', t.render(assigns)
@global = nil
end
end
end # TemplateTest

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/helper'
require 'test_helper'
class VariableTest < Test::Unit::TestCase
include Liquid
@@ -131,7 +130,7 @@ class VariableResolutionTest < Test::Unit::TestCase
template = Template.parse(%|{{ test.test }}|)
assert_equal 'worked', template.render('test' => {'test' => 'worked'})
end
def test_preset_assigns
template = Template.parse(%|{{ test }}|)
template.assigns['test'] = 'worked'
@@ -168,5 +167,4 @@ class VariableResolutionTest < Test::Unit::TestCase
}
assert_equal "Unknown variable 'test'", e.message
end
end
end # VariableTest

View File

@@ -1,20 +1,34 @@
#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.dirname(__FILE__)+ '/extra')
extras_path = File.join File.dirname(__FILE__), 'extra'
$LOAD_PATH.unshift(extras_path) unless $LOAD_PATH.include? extras_path
require 'rubygems' unless RUBY_VERSION =~ /^(?:1.9.*)$/
require 'test/unit'
require 'test/unit/assertions'
require 'caller'
require 'breakpoint'
require File.dirname(__FILE__) + '/../lib/liquid'
require 'ruby-debug'
require File.join File.dirname(__FILE__), '..', 'lib', 'liquid'
module Test
module Unit
module Assertions
include Liquid
def assert_template_result(expected, template, assigns={}, message=nil)
assert_equal expected, Template.parse(template).render(assigns)
end
end
end
end
include Liquid
def assert_template_result(expected, template, assigns = {}, message = nil)
assert_equal expected, Template.parse(template).render(assigns)
end
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
assert_match expected, Template.parse(template).render(assigns)
end
end # Assertions
end # Unit
end # Test