Compare commits

...

30 Commits

Author SHA1 Message Date
Jean Boussier
19a60ccee2 Test gem installation on 1.9.x 2014-06-23 14:14:12 -04:00
Florian Weingarten
114a37d9ba add additional tests for https://github.com/jekyll/jekyll/pull/2505 2014-06-23 09:28:24 -04:00
Arthur Nogueira Neves
30bd9ad957 Merge pull request #368 from Shopify/add-round-ceil-and-floor
[Liquid] Add round, ceil and floor standard filters
2014-06-16 10:32:17 -05:00
Christian Blais
2239921804 [Liquid] Add round, ceil and floor standard filters 2014-06-16 11:15:53 -04:00
Florian Weingarten
1ea178e7a8 Merge pull request #363 from rrrene/patch-2
Update docs badge in README
2014-06-05 10:32:47 -04:00
René Föhring
5650c7eea1 Update docs badge in README
Update the URL of the docs badge to include it from inch-ci.org instead of inch-pages.github.io (the former being the successor of the Inch Pages project).

[ci skip]
2014-06-05 12:25:26 +02:00
Arthur Neves
553b0926ae Merge pull request #352 from gaiottino/master
Add error messages for missing variables when :strict

Conflicts:
	History.md
2014-05-06 10:16:45 -04:00
Daniel Gaiottino
2bac6267f9 Add error messages for missing variables when :strict 2014-05-06 16:12:46 +02:00
Florian Weingarten
628ab3dc6a add test for numerical sort 2014-05-04 19:50:38 -04:00
Florian Weingarten
2eb552c65d Merge pull request #354 from Dillon-Benson/patch-3
use attr_writer instead of error_mode= method
2014-05-02 18:15:33 -04:00
Dillon Benson
6e40746ce4 use attr_writer instead of error_mode= method 2014-05-02 17:18:56 -04:00
Thierry Joyal
75068e8fa4 Merge pull request #349 from Shopify/render_bang_allow_not_safe_contexts
render! will properly force rethrow of errors if context is passed as an argument
2014-05-01 12:52:06 -04:00
Thierry Joyal
ad1152853a render! will properly force rethrow of errors if context is passed as an argument 2014-05-01 16:44:00 +00:00
David Cornu
73098ac5bc Merge pull request #351 from Shopify/date-cleanup
Move date coercion to #to_date
2014-04-30 20:47:39 -04:00
David Cornu
8bc3792c0e Move date coercion to #to_date 2014-04-30 22:32:36 +00:00
Florian Weingarten
3ef29c624c Merge pull request #347 from Dillon-Benson/patch-1
remove template return
2014-04-30 08:23:36 -04:00
Dillon Benson
a85fb38769 remove template return 2014-04-30 03:00:16 -04:00
Florian Weingarten
6a1c3cff1a update history.md 2014-04-29 15:26:22 -04:00
Florian Weingarten
bde32018dd Merge pull request #346 from Shopify/false_rendering
Fix broken rendering of variables which are equal to false (closes #345)
2014-04-29 15:21:30 -04:00
Florian Weingarten
2a12f253bf Fix broken rendering of variables which are equal to false (closes #345) 2014-04-29 14:33:30 -04:00
Arthur Nogueira Neves
f15d24509d Merge pull request #338 from here/docs-filter-date-strftime
Update standardfilters.rb add docs to date filter
2014-04-12 20:14:44 -04:00
mikey dubs
09e4378cfb Update standardfilters.rb add docs to date filter
Added '%s' - Number of seconds since 1970-01-01 00:00:00 UTC to included list of flags
Added link to Ruby docs on Time#strftime() to allow easier discovery of unlisted filter options.
2014-04-12 15:21:06 -07:00
Dylan Thacker-Smith
af0e26fb16 Merge pull request #337 from Shopify/remove-rails-dev-dependancy
Remove active_support from the development dependancies.
2014-04-11 15:08:34 -04:00
Dylan Thacker-Smith
f5502e8152 Remove active_support from the development dependancies. 2014-04-11 14:26:40 -04:00
Arthur Nogueira Neves
c098235baa Merge pull request #335 from rrrene/patch-1
Add docs badge to README
2014-04-05 11:46:32 -04:00
René Föhring
0e2bf768ba Add docs badge to README 2014-04-05 12:24:56 +02:00
Dylan Thacker-Smith
4c477c2087 Merge pull request #333 from Shopify/remove-liquid-view
Remove ActionView template handler
2014-03-31 10:16:37 -04:00
Dylan Thacker-Smith
cd7fc050b1 Remove ActionView template handler
Fixes #21
2014-03-29 15:59:42 -04:00
Dylan Thacker-Smith
8291c5e72c Merge pull request #329 from Shopify/seperate-integration-tests
Separate unit and integration tests.
2014-03-26 16:04:39 -04:00
Dylan Thacker-Smith
7e45155aa9 Seperate unit and integration tests.
This makes it easier to re-use the integration tests in a seperate gem that
optimizes parts of liquid with a native implementation.
2014-03-26 15:47:07 -04:00
56 changed files with 344 additions and 275 deletions

View File

@@ -1,7 +1,8 @@
rvm:
- 1.9.3
- 2.0.0
- 2.1.0
- 2.1
- 2.1.1
- jruby-19mode
- jruby-head
- rbx-19mode
@@ -10,7 +11,7 @@ matrix:
- rvm: rbx-19mode
- rvm: jruby-head
script: "rake test"
script: "gem build liquid.gemspec && gem install liquid-3.0.0.gem"
notifications:
disable: true

View File

@@ -3,6 +3,10 @@
## 3.0.0 / not yet released / branch "master"
* ...
* Add error messages for missing variables when :strict, see #352 [Daniel Gaiottino]
* Properly set context rethrow_errors on render! #349 [Thierry Joyal, tjoyal]
* Fix broken rendering of variables which are equal to false, see #345 [Florian Weingarten, fw42]
* Remove ActionView template handler [Dylan Thacker-Smith, dylanahsmith]
* Freeze lots of string literals for new Ruby 2.1 optimization, see #297 [Florian Weingarten, fw42]
* Allow newlines in tags and variables, see #324 [Dylan Thacker-Smith, dylanahsmith]
* Tag#parse is called after initialize, which now takes options instead of tokens as the 3rd argument. See #321 [Dylan Thacker-Smith, dylanahsmith]

View File

@@ -1,4 +1,6 @@
[![Build Status](https://secure.travis-ci.org/Shopify/liquid.png?branch=master)](http://travis-ci.org/Shopify/liquid)
[![Inline docs](http://inch-ci.org/github/Shopify/liquid.png)](http://inch-ci.org/github/Shopify/liquid)
# Liquid template engine
* [Contributing guidelines](CONTRIBUTING.md)

View File

@@ -8,7 +8,7 @@ task :default => 'test'
desc 'run test suite with default parser'
Rake::TestTask.new(:base_test) do |t|
t.libs << '.' << 'lib' << 'test'
t.test_files = FileList['test/liquid/**/*_test.rb']
t.test_files = FileList['test/{integration,unit}/**/*_test.rb']
t.verbose = false
end

View File

@@ -1,8 +0,0 @@
require 'liquid'
require 'extras/liquid_view'
if defined? ActionView::Template and ActionView::Template.respond_to? :register_template_handler
ActionView::Template
else
ActionView::Base
end.register_template_handler(:liquid, LiquidView)

View File

@@ -1,51 +0,0 @@
# LiquidView is a action view extension class. You can register it with rails
# and use liquid as an template system for .liquid files
#
# Example
#
# ActionView::Base::register_template_handler :liquid, LiquidView
class LiquidView
PROTECTED_ASSIGNS = %w( template_root response _session template_class action_name request_origin session template
_response url _request _cookies variables_added _flash params _headers request cookies
ignore_missing_templates flash _params logger before_filter_chain_aborted headers )
PROTECTED_INSTANCE_VARIABLES = %w( @_request @controller @_first_render @_memoized__pick_template @view_paths
@helpers @assigns_added @template @_render_stack @template_format @assigns )
def self.call(template)
"LiquidView.new(self).render(template, local_assigns)"
end
def initialize(view)
@view = view
end
def render(template, local_assigns = nil)
@view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
# Rails 2.2 Template has source, but not locals
if template.respond_to?(:source) && !template.respond_to?(:locals)
assigns = (@view.instance_variables - PROTECTED_INSTANCE_VARIABLES).inject({}) do |hash, ivar|
hash[ivar[1..-1]] = @view.instance_variable_get(ivar)
hash
end
else
assigns = @view.assigns.reject{ |k,v| PROTECTED_ASSIGNS.include?(k) }
end
source = template.respond_to?(:source) ? template.source : template
local_assigns = (template.respond_to?(:locals) ? template.locals : local_assigns) || {}
if content_for_layout = @view.instance_variable_get("@content_for_layout")
assigns['content_for_layout'] = content_for_layout
end
assigns.merge!(local_assigns.stringify_keys)
liquid = Liquid::Template.parse(source)
liquid.render(assigns, :filters => [@view.controller.master_helper_module], :registers => {:action_view => @view, :controller => @view.controller})
end
def compilable?
false
end
end

View File

@@ -15,6 +15,8 @@ module Liquid
class Context
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
attr_accessor :rethrow_errors
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = {})
@environments = [environments].flatten
@scopes = [(outer_scope || {})]
@@ -194,7 +196,8 @@ module Liquid
if scope.nil?
@environments.each do |e|
if variable = lookup_and_evaluate(e, key)
variable = lookup_and_evaluate(e, key)
unless variable.nil?
scope = e
break
end
@@ -202,6 +205,7 @@ module Liquid
end
scope ||= @environments.last || @scopes.last
handle_not_found(key) unless scope.has_key?(key)
variable ||= lookup_and_evaluate(scope, key)
variable = variable.to_liquid
@@ -251,6 +255,7 @@ module 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
handle_not_found(markup)
return nil
end
@@ -280,6 +285,10 @@ module Liquid
end
end
end # squash_instance_assigns_with_environments
def handle_not_found(variable)
@errors << "Variable {{#{variable}}} not found" if Template.error_mode == :strict
end
end # Context
end # Liquid

View File

@@ -162,7 +162,7 @@ module Liquid
input.to_s.gsub(/\n/, "<br />\n".freeze)
end
# Reformat a date
# Reformat a date using Ruby's core Time#strftime( string ) -> string
#
# %a - The abbreviated weekday name (``Sun'')
# %A - The full weekday name (``Sunday'')
@@ -176,6 +176,7 @@ module Liquid
# %m - Month of the year (01..12)
# %M - Minute of the hour (00..59)
# %p - Meridian indicator (``AM'' or ``PM'')
# %s - Number of seconds since 1970-01-01 00:00:00 UTC.
# %S - Second of the minute (00..60)
# %U - Week number of the current year,
# starting with the first Sunday as the first
@@ -190,34 +191,14 @@ module Liquid
# %Y - Year with century
# %Z - Time zone name
# %% - Literal ``%'' character
#
# See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime
def date(input, format)
return input if format.to_s.empty?
if format.to_s.empty?
return input.to_s
end
return input unless date = to_date(input)
if ((input.is_a?(String) && !/\A\d+\z/.match(input.to_s).nil?) || input.is_a?(Integer)) && input.to_i > 0
input = Time.at(input.to_i)
end
date = if input.is_a?(String)
case input.downcase
when 'now'.freeze, 'today'.freeze
Time.now
else
Time.parse(input)
end
else
input
end
if date.respond_to?(:strftime)
date.strftime(format.to_s)
else
input
end
rescue
input
date.strftime(format.to_s)
end
# Get the first element of the passed in array
@@ -262,6 +243,21 @@ module Liquid
apply_operation(input, operand, :%)
end
def round(input, n = 0)
result = to_number(input).round(to_number(n))
result = result.to_f if result.is_a?(BigDecimal)
result = result.to_i if n == 0
result
end
def ceil(input)
to_number(input).ceil.to_i
end
def floor(input)
to_number(input).floor.to_i
end
def default(input, default_value = "".freeze)
is_blank = input.respond_to?(:empty?) ? input.empty? : !input
is_blank ? default_value : input
@@ -293,6 +289,23 @@ module Liquid
end
end
def to_date(obj)
return obj if obj.respond_to?(:strftime)
case obj
when 'now'.freeze, 'today'.freeze
Time.now
when /\A\d+\z/, Integer
Time.at(obj.to_i)
when String
Time.parse(obj)
else
nil
end
rescue ArgumentError
nil
end
def apply_operation(input, operand, operation)
result = to_number(input).send(operation, to_number(operand))
result.is_a?(BigDecimal) ? result.to_f : result

View File

@@ -22,6 +22,12 @@ module Liquid
@@file_system = BlankFileSystem.new
class << self
# Sets how strict the parser should be.
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
# :warn is the default and will give deprecation warnings when invalid syntax is used.
# :strict will enforce correct syntax.
attr_writer :error_mode
def file_system
@@file_system
end
@@ -38,14 +44,6 @@ module Liquid
@tags ||= {}
end
# Sets how strict the parser should be.
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
# :warn is the default and will give deprecation warnings when invalid syntax is used.
# :strict will enforce correct syntax.
def error_mode=(mode)
@error_mode = mode
end
def error_mode
@error_mode || :lax
end
@@ -60,7 +58,6 @@ module Liquid
def parse(source, options = {})
template = Template.new
template.parse(source, options)
template
end
end
@@ -114,7 +111,9 @@ module Liquid
context = case args.first
when Liquid::Context
args.shift
c = args.shift
c.rethrow_errors = true if @rethrow_errors
c
when Liquid::Drop
drop = args.shift
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
@@ -157,7 +156,8 @@ module Liquid
end
def render!(*args)
@rethrow_errors = true; render(*args)
@rethrow_errors = true
render(*args)
end
private

View File

@@ -26,5 +26,4 @@ Gem::Specification.new do |s|
s.add_development_dependency 'stackprof' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
s.add_development_dependency 'rake'
s.add_development_dependency 'activesupport'
end

View File

@@ -1,4 +1,3 @@
require 'rubygems'
require 'benchmark'
require File.dirname(__FILE__) + '/theme_runner'

View File

@@ -1,6 +1,6 @@
require 'yaml'
module Database
module Database
# Load the standard vision toolkit database and re-arrage it to be simply exportable
# to liquid as assigns. All this is based on Shopify
def self.tables

View File

@@ -1,7 +1,9 @@
require 'json'
module JsonFilter
def json(object)
object.reject {|k,v| k == "collections" }.to_json
JSON.dump(object.reject {|k,v| k == "collections" })
end
end

View File

@@ -6,11 +6,6 @@
# Shopify which is likely the biggest user of liquid in the world which something to the tune of several
# million Template#render calls a day.
require 'rubygems'
require 'active_support'
require 'active_support/json'
require 'yaml'
require 'digest/md5'
require File.dirname(__FILE__) + '/shopify/liquid'
require File.dirname(__FILE__) + '/shopify/database.rb'
@@ -81,6 +76,3 @@ class ThemeRunner
end
end
end

View File

@@ -0,0 +1,23 @@
require 'test_helper'
class ContextTest < Test::Unit::TestCase
include Liquid
def test_override_global_filter
global = Module.new do
def notice(output)
"Global #{output}"
end
end
local = Module.new do
def notice(output)
"Local #{output}"
end
end
Template.register_filter(global)
assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
end
end

View File

@@ -3,12 +3,9 @@ require 'test_helper'
class ParsingQuirksTest < Test::Unit::TestCase
include Liquid
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}
def test_parsing_css
text = " div { font-weight: bold; } "
assert_equal text, Template.parse(text).render!
end
def test_raise_on_single_close_bracet

View File

@@ -54,10 +54,10 @@ class SecurityTest < Test::Unit::TestCase
def test_does_not_add_drop_methods_to_symbol_table
current_symbols = Symbol.all_symbols
drop = Drop.new
drop.invoke_drop("custom_method_1")
drop.invoke_drop("custom_method_2")
drop.invoke_drop("custom_method_3")
assigns = { 'drop' => Drop.new }
assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render!
assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render!
assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render!
assert_equal [], (Symbol.all_symbols - current_symbols)
end

View File

@@ -115,6 +115,13 @@ class StandardFiltersTest < Test::Unit::TestCase
assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
end
def test_numerical_vs_lexicographical_sort
assert_equal [2, 10], @filters.sort([10, 2])
assert_equal [{"a" => 2}, {"a" => 10}], @filters.sort([{"a" => 10}, {"a" => 2}], "a")
assert_equal ["10", "2"], @filters.sort(["10", "2"])
assert_equal [{"a" => "10"}, {"a" => "2"}], @filters.sort([{"a" => "10"}, {"a" => "2"}], "a")
end
def test_reverse
assert_equal [4,3,2,1], @filters.reverse([1,2,3,4])
end
@@ -186,7 +193,6 @@ class StandardFiltersTest < Test::Unit::TestCase
assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
end
def test_first_last
assert_equal 1, @filters.first([1,2,3])
assert_equal 3, @filters.last([1,2,3])
@@ -267,6 +273,22 @@ class StandardFiltersTest < Test::Unit::TestCase
assert_template_result "1", "{{ 3 | modulo:2 }}"
end
def test_round
assert_template_result "5", "{{ input | round }}", 'input' => 4.6
assert_template_result "4", "{{ '4.3' | round }}"
assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612
end
def test_ceil
assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6
assert_template_result "5", "{{ '4.3' | ceil }}"
end
def test_floor
assert_template_result "4", "{{ input | floor }}", 'input' => 4.6
assert_template_result "4", "{{ '4.3' | floor }}"
end
def test_append
assigns = {'a' => 'bc', 'b' => 'd' }
assert_template_result('bcd',"{{ a | append: 'd'}}",assigns)

View File

@@ -1,5 +1,11 @@
require 'test_helper'
class ThingWithValue < Liquid::Drop
def value
3
end
end
class ForTagTest < Test::Unit::TestCase
include Liquid
@@ -34,6 +40,20 @@ HERE
assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}')
end
def test_for_with_variable_range
assert_template_result(' 1 2 3 ','{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
end
def test_for_with_hash_value_range
foobar = { "value" => 3 }
assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
end
def test_for_with_drop_value_range
foobar = ThingWithValue.new
assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar)
end
def test_for_with_variable
assert_template_result(' 1 2 3 ','{%for item in array%} {{item}} {%endfor%}','array' => [1,2,3])
assert_template_result('123','{%for item in array%}{{item}}{%endfor%}','array' => [1,2,3])
@@ -295,16 +315,6 @@ HERE
assert_template_result(expected, template, assigns)
end
def test_for_nodelist
template = Liquid::Template.parse('{% for item in items %}FOR{% endfor %}')
assert_equal ['FOR'], template.root.nodelist[0].nodelist
end
def test_for_else_nodelist
template = Liquid::Template.parse('{% for item in items %}FOR{% else %}ELSE{% endfor %}')
assert_equal ['FOR', 'ELSE'], template.root.nodelist[0].nodelist
end
class LoaderDrop < Liquid::Drop
attr_accessor :each_called, :load_slice_called

View File

@@ -158,14 +158,9 @@ class IfElseTagTest < Test::Unit::TestCase
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
end
def test_if_nodelist
template = Liquid::Template.parse('{% if true %}IF{% else %}ELSE{% endif %}')
assert_equal ['IF', 'ELSE'], template.root.nodelist[0].nodelist
end
def test_operators_are_whitelisted
assert_raise(SyntaxError) do
assert_template_result('', %({% if 1 or throw or or 1 %}yes{% endif %}))
end
end
end # IfElseTest
end

View File

@@ -3,12 +3,6 @@ require 'test_helper'
class StandardTagTest < Test::Unit::TestCase
include Liquid
def test_tag
tag = Tag.parse('tag', [], [], {})
assert_equal 'liquid::tag', tag.name
assert_equal '', tag.render(Context.new)
end
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...')

View File

@@ -22,29 +22,15 @@ class SomethingWithLength
liquid_methods :length
end
class ErroneousDrop < Liquid::Drop
def bad_method
raise 'ruby error in drop'
end
end
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
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
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
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!
@@ -158,17 +144,13 @@ class TemplateTest < Test::Unit::TestCase
assert_equal 'haha', t.parse("{{baz}}").render!(drop)
end
def test_sets_default_localization_in_document
t = Template.new
t.parse('')
assert_instance_of I18n, t.root.options[:locale]
end
def test_render_bang_force_rethrow_errors_on_passed_context
context = Context.new({'drop' => ErroneousDrop.new})
t = Template.new.parse('{{ drop.bad_method }}')
def test_sets_default_localization_in_context_with_quick_initialization
t = Template.new
t.parse('{{foo}}', :locale => I18n.new(fixture("en_locale.yml")))
assert_instance_of I18n, t.root.options[:locale]
assert_equal fixture("en_locale.yml"), t.root.options[:locale].path
e = assert_raises RuntimeError do
t.render!(context)
end
assert_equal 'ruby error in drop', e.message
end
end

View File

@@ -0,0 +1,72 @@
require 'test_helper'
class VariableTest < Test::Unit::TestCase
include Liquid
def test_simple_variable
template = Template.parse(%|{{test}}|)
assert_equal 'worked', template.render!('test' => 'worked')
assert_equal 'worked wonderfully', template.render!('test' => 'worked wonderfully')
end
def test_simple_with_whitespaces
template = Template.parse(%| {{ test }} |)
assert_equal ' worked ', template.render!('test' => 'worked')
assert_equal ' worked wonderfully ', template.render!('test' => 'worked wonderfully')
end
def test_ignore_unknown
template = Template.parse(%|{{ test }}|)
assert_equal '', template.render!
end
def test_hash_scoping
template = Template.parse(%|{{ test.test }}|)
assert_equal 'worked', template.render!('test' => {'test' => 'worked'})
end
def test_false_renders_as_false
assert_equal 'false', Template.parse("{{ foo }}").render!('foo' => false)
end
def test_preset_assigns
template = Template.parse(%|{{ test }}|)
template.assigns['test'] = 'worked'
assert_equal 'worked', template.render!
end
def test_reuse_parsed_template
template = Template.parse(%|{{ greeting }} {{ name }}|)
template.assigns['greeting'] = 'Goodbye'
assert_equal 'Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi')
assert_equal 'Hello ', template.render!('greeting' => 'Hello', 'unknown' => 'Tobi')
assert_equal 'Hello Brian', template.render!('greeting' => 'Hello', 'name' => 'Brian')
assert_equal 'Goodbye Brian', template.render!('name' => 'Brian')
assert_equal({'greeting'=>'Goodbye'}, template.assigns)
end
def test_assigns_not_polluted_from_template
template = Template.parse(%|{{ test }}{% assign test = 'bar' %}{{ test }}|)
template.assigns['test'] = 'baz'
assert_equal 'bazbar', template.render!
assert_equal 'bazbar', template.render!
assert_equal 'foobar', template.render!('test' => 'foo')
assert_equal 'bazbar', template.render!
end
def test_hash_with_default_proc
template = Template.parse(%|Hello {{ test }}|)
assigns = Hash.new { |h,k| raise "Unknown variable '#{k}'" }
assigns['test'] = 'Tobi'
assert_equal 'Hello Tobi', template.render!(assigns)
assigns.delete('test')
e = assert_raises(RuntimeError) {
template.render!(assigns)
}
assert_equal "Unknown variable 'test'", e.message
end
def test_multiline_variable
assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked')
end
end

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class BlockTest < Test::Unit::TestCase
class BlockUnitTest < Test::Unit::TestCase
include Liquid
def test_blankspace

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class ConditionTest < Test::Unit::TestCase
class ConditionUnitTest < Test::Unit::TestCase
include Liquid
def test_basic_condition

View File

@@ -63,7 +63,7 @@ class ArrayLike
end
end
class ContextTest < Test::Unit::TestCase
class ContextUnitTest < Test::Unit::TestCase
include Liquid
def setup
@@ -162,24 +162,6 @@ class ContextTest < Test::Unit::TestCase
end
def test_override_global_filter
global = Module.new do
def notice(output)
"Global #{output}"
end
end
local = Module.new do
def notice(output)
"Local #{output}"
end
end
Template.register_filter(global)
assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
end
def test_only_intended_filters_make_it_there
filter = Module.new do
@@ -475,4 +457,22 @@ class ContextTest < Test::Unit::TestCase
assert_kind_of CategoryDrop, @context['category']
assert_equal @context, @context['category'].context
end
def test_strict_variables_not_found
with_error_mode(:strict) do
@context['does_not_exist']
assert(@context.errors.length == 1)
assert_equal(@context.errors[0], 'Variable {{does_not_exist}} not found')
end
end
def test_strict_nested_variables_not_found
with_error_mode(:strict) do
@context['hash'] = {'this' => 'exists'}
@context['hash.does_not_exist']
assert(@context.errors.length == 1)
assert_equal(@context.errors[0], 'Variable {{hash.does_not_exist}} not found')
end
end
end # ContextTest

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class FileSystemTest < Test::Unit::TestCase
class FileSystemUnitTest < Test::Unit::TestCase
include Liquid
def test_default

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class I18nTest < Test::Unit::TestCase
class I18nUnitTest < Test::Unit::TestCase
include Liquid
def setup

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class LexerTest < Test::Unit::TestCase
class LexerUnitTest < Test::Unit::TestCase
include Liquid
def test_strings

View File

@@ -36,7 +36,7 @@ class TestClassC::LiquidDropClass
end
end
class ModuleExTest < Test::Unit::TestCase
class ModuleExUnitTest < Test::Unit::TestCase
include Liquid
def setup

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class ParserTest < Test::Unit::TestCase
class ParserUnitTest < Test::Unit::TestCase
include Liquid
def test_consume

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class RegexpTest < Test::Unit::TestCase
class RegexpUnitTest < Test::Unit::TestCase
include Liquid
def test_empty

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class StrainerTest < Test::Unit::TestCase
class StrainerUnitTest < Test::Unit::TestCase
include Liquid
module AccessScopeFilters

View File

@@ -0,0 +1,11 @@
require 'test_helper'
class TagUnitTest < Test::Unit::TestCase
include Liquid
def test_tag
tag = Tag.parse('tag', [], [], {})
assert_equal 'liquid::tag', tag.name
assert_equal '', tag.render(Context.new)
end
end

View File

@@ -1,10 +1,10 @@
require 'test_helper'
class CaseTagTest < Test::Unit::TestCase
class CaseTagUnitTest < Test::Unit::TestCase
include Liquid
def test_case_nodelist
template = Liquid::Template.parse('{% case var %}{% when true %}WHEN{% else %}ELSE{% endcase %}')
assert_equal ['WHEN', 'ELSE'], template.root.nodelist[0].nodelist
end
end # CaseTest
end

View File

@@ -0,0 +1,13 @@
require 'test_helper'
class ForTagUnitTest < Test::Unit::TestCase
def test_for_nodelist
template = Liquid::Template.parse('{% for item in items %}FOR{% endfor %}')
assert_equal ['FOR'], template.root.nodelist[0].nodelist
end
def test_for_else_nodelist
template = Liquid::Template.parse('{% for item in items %}FOR{% else %}ELSE{% endfor %}')
assert_equal ['FOR', 'ELSE'], template.root.nodelist[0].nodelist
end
end

View File

@@ -0,0 +1,8 @@
require 'test_helper'
class IfTagUnitTest < Test::Unit::TestCase
def test_if_nodelist
template = Liquid::Template.parse('{% if true %}IF{% else %}ELSE{% endif %}')
assert_equal ['IF', 'ELSE'], template.root.nodelist[0].nodelist
end
end

View File

@@ -0,0 +1,19 @@
require 'test_helper'
class TemplateUnitTest < Test::Unit::TestCase
include Liquid
def test_sets_default_localization_in_document
t = Template.new
t.parse('')
assert_instance_of I18n, t.root.options[:locale]
end
def test_sets_default_localization_in_context_with_quick_initialization
t = Template.new
t.parse('{{foo}}', :locale => I18n.new(fixture("en_locale.yml")))
assert_instance_of I18n, t.root.options[:locale]
assert_equal fixture("en_locale.yml"), t.root.options[:locale].path
end
end

View File

@@ -0,0 +1,29 @@
require 'test_helper'
class TokenizerTest < Test::Unit::TestCase
def test_tokenize_strings
assert_equal [' '], tokenize(' ')
assert_equal ['hello world'], tokenize('hello world')
end
def test_tokenize_variables
assert_equal ['{{funk}}'], tokenize('{{funk}}')
assert_equal [' ', '{{funk}}', ' '], tokenize(' {{funk}} ')
assert_equal [' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], tokenize(' {{funk}} {{so}} {{brother}} ')
assert_equal [' ', '{{ funk }}', ' '], tokenize(' {{ funk }} ')
end
def test_tokenize_blocks
assert_equal ['{%comment%}'], tokenize('{%comment%}')
assert_equal [' ', '{%comment%}', ' '], tokenize(' {%comment%} ')
assert_equal [' ', '{%comment%}', ' ', '{%endcomment%}', ' '], tokenize(' {%comment%} {%endcomment%} ')
assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], tokenize(" {% comment %} {% endcomment %} ")
end
private
def tokenize(source)
Liquid::Template.new.send(:tokenize, source)
end
end

View File

@@ -1,6 +1,6 @@
require 'test_helper'
class VariableTest < Test::Unit::TestCase
class VariableUnitTest < Test::Unit::TestCase
include Liquid
def test_variable
@@ -134,71 +134,3 @@ class VariableTest < Test::Unit::TestCase
end
end
end
class VariableResolutionTest < Test::Unit::TestCase
include Liquid
def test_simple_variable
template = Template.parse(%|{{test}}|)
assert_equal 'worked', template.render!('test' => 'worked')
assert_equal 'worked wonderfully', template.render!('test' => 'worked wonderfully')
end
def test_simple_with_whitespaces
template = Template.parse(%| {{ test }} |)
assert_equal ' worked ', template.render!('test' => 'worked')
assert_equal ' worked wonderfully ', template.render!('test' => 'worked wonderfully')
end
def test_ignore_unknown
template = Template.parse(%|{{ test }}|)
assert_equal '', template.render!
end
def test_hash_scoping
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'
assert_equal 'worked', template.render!
end
def test_reuse_parsed_template
template = Template.parse(%|{{ greeting }} {{ name }}|)
template.assigns['greeting'] = 'Goodbye'
assert_equal 'Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi')
assert_equal 'Hello ', template.render!('greeting' => 'Hello', 'unknown' => 'Tobi')
assert_equal 'Hello Brian', template.render!('greeting' => 'Hello', 'name' => 'Brian')
assert_equal 'Goodbye Brian', template.render!('name' => 'Brian')
assert_equal({'greeting'=>'Goodbye'}, template.assigns)
end
def test_assigns_not_polluted_from_template
template = Template.parse(%|{{ test }}{% assign test = 'bar' %}{{ test }}|)
template.assigns['test'] = 'baz'
assert_equal 'bazbar', template.render!
assert_equal 'bazbar', template.render!
assert_equal 'foobar', template.render!('test' => 'foo')
assert_equal 'bazbar', template.render!
end
def test_hash_with_default_proc
template = Template.parse(%|Hello {{ test }}|)
assigns = Hash.new { |h,k| raise "Unknown variable '#{k}'" }
assigns['test'] = 'Tobi'
assert_equal 'Hello Tobi', template.render!(assigns)
assigns.delete('test')
e = assert_raises(RuntimeError) {
template.render!(assigns)
}
assert_equal "Unknown variable 'test'", e.message
end
def test_multiline_variable
assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked')
end
end # VariableTest