Compare commits

..

1 Commits

Author SHA1 Message Date
Jean Boussier
19a60ccee2 Test gem installation on 1.9.x 2014-06-23 14:14:12 -04:00
29 changed files with 137 additions and 351 deletions

View File

@@ -1,16 +1,17 @@
rvm:
- 1.9
- 2.0
- 1.9.3
- 2.0.0
- 2.1
- 2.1.1
- jruby-19mode
- jruby-head
- rbx-2
- rbx-19mode
matrix:
allow_failures:
- rvm: rbx-2
- 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

@@ -1,8 +1,3 @@
source 'https://rubygems.org'
gemspec
gem 'stackprof', platforms: :mri_21
group :test do
gem 'spy', '0.4.1'
end

View File

@@ -3,9 +3,7 @@
## 3.0.0 / not yet released / branch "master"
* ...
* Add exception_handler feature, see #397 and #254 [Bogdan Gusiev, bogdan and Florian Weingarten, fw42]
* Optimize variable parsing to avoid repeated regex evaluation during template rendering #383 [Jason Hiltz-Laforge, jasonhl]
* Optimize checking for block interrupts to reduce object allocation #380 [Jason Hiltz-Laforge, jasonhl]
* 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]
@@ -32,13 +30,7 @@
* Make map filter work on enumerable drops, see #233 [Florian Weingarten, fw42]
* Improved whitespace stripping for blank blocks, related to #216 [Florian Weingarten, fw42]
## 2.6.1 / 2014-01-10 / branch "2-6-stable"
Security fix, cherry-picked from master (4e14a65):
* Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk]
* Prevent arbitrary method invocation on condition objects, see #274 [Dylan Thacker-Smith, dylanahsmith]
## 2.6.0 / 2013-11-25
## 2.6.0 / 2013-11-25 / branch "2.6-stable"
IMPORTANT: Liquid 2.6 is going to be the last version of Liquid which maintains explicit Ruby 1.8 compatability.
The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are likely to break on Ruby 1.8.
@@ -62,13 +54,7 @@ The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are
* Better documentation for 'include' tag (closes #163) [Peter Schröder, phoet]
* Use of BigDecimal on filters to have better precision (closes #155) [Arthur Nogueira Neves, arthurnn]
## 2.5.5 / 2014-01-10 / branch "2-5-stable"
Security fix, cherry-picked from master (4e14a65):
* Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk]
* Prevent arbitrary method invocation on condition objects, see #274 [Dylan Thacker-Smith, dylanahsmith]
## 2.5.4 / 2013-11-11
## 2.5.4 / 2013-11-11 / branch "2.5-stable"
* Fix "can't convert Fixnum into String" for "replace", see #173, [wǒ_is神仙, jsw0528]

View File

@@ -3,4 +3,4 @@
<p>It is {{date}}</p>
<p>Check out the <a href="/products">Products</a> screen </p>
<p>Check out the <a href="http://localhost:3000/products">Products</a> screen </p>

View File

@@ -38,9 +38,6 @@ module Liquid
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
singleton_class.send(:attr_accessor, :cache_classes)
self.cache_classes = true
end
require "liquid/version"

View File

@@ -1,12 +1,12 @@
module Liquid
class Block < Tag
IsTag = /\A#{TagStart}/o
IsVariable = /\A#{VariableStart}/o
FullToken = /\A#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
ContentOfVariable = /\A#{VariableStart}(.*)#{VariableEnd}\z/om
TAGSTART = "{%".freeze
VARSTART = "{{".freeze
def blank?
@blank
@blank || false
end
def parse(tokens)
@@ -18,41 +18,41 @@ module Liquid
@children = []
while token = tokens.shift
unless token.empty?
case
when token.start_with?(TAGSTART)
if token =~ FullToken
case token
when IsTag
if token =~ FullToken
# if we found the proper block delimiter just end parsing here and let the outer block
# proceed
if block_delimiter == $1
end_tag
return
end
# fetch the tag from registered blocks
if tag = Template.tags[$1]
new_tag = tag.parse($1, $2, tokens, @options)
@blank &&= new_tag.blank?
@nodelist << new_tag
@children << new_tag
else
# this tag is not registered with the system
# pass it to the current block for special handling or error reporting
unknown_tag($1, $2, tokens)
end
else
raise SyntaxError.new(options[:locale].t("errors.syntax.tag_termination".freeze, :token => token, :tag_end => TagEnd.inspect))
# if we found the proper block delimiter just end parsing here and let the outer block
# proceed
if block_delimiter == $1
end_tag
return
end
# fetch the tag from registered blocks
if tag = Template.tags[$1]
new_tag = tag.parse($1, $2, tokens, @options)
@blank &&= new_tag.blank?
@nodelist << new_tag
@children << new_tag
else
# this tag is not registered with the system
# pass it to the current block for special handling or error reporting
unknown_tag($1, $2, tokens)
end
when token.start_with?(VARSTART)
new_var = create_variable(token)
@nodelist << new_var
@children << new_var
@blank = false
else
@nodelist << token
@blank &&= (token =~ /\A\s*\z/)
raise SyntaxError.new(options[:locale].t("errors.syntax.tag_termination".freeze, :token => token, :tag_end => TagEnd.inspect))
end
when IsVariable
new_var = create_variable(token)
@nodelist << new_var
@children << new_var
@blank = false
when ''.freeze
# pass
else
@nodelist << token
@blank &&= (token =~ /\A\s*\z/)
end
end
@@ -91,12 +91,12 @@ module Liquid
end
end
def block_name
@tag_name
def block_delimiter
"end#{block_name}"
end
def block_delimiter
@block_delimiter ||= "end#{block_name}"
def block_name
@tag_name
end
def create_variable(token)

View File

@@ -94,16 +94,12 @@ module Liquid
left, right = context[left], context[right]
operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
operation = self.class.operators[op] || raise(ArgumentError.new("Unknown operator #{op}"))
if operation.respond_to?(:call)
operation.call(self, left, right)
elsif left.respond_to?(operation) and right.respond_to?(operation)
begin
left.send(operation, right)
rescue ::ArgumentError => e
raise Liquid::ArgumentError.new(e.message)
end
left.send(operation, right)
else
nil
end

View File

@@ -14,25 +14,20 @@ module Liquid
# context['bob'] #=> nil class Context
class Context
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
attr_accessor :exception_handler
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
attr_accessor :rethrow_errors
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = {})
@environments = [environments].flatten
@scopes = [(outer_scope || {})]
@registers = registers
@errors = []
@rethrow_errors = rethrow_errors
@resource_limits = (resource_limits || {}).merge!({ :render_score_current => 0, :assign_score_current => 0 })
squash_instance_assigns_with_environments
if rethrow_errors
self.exception_handler = ->(e) { true }
end
@interrupts = []
@filters = []
@parsed_variables = Hash.new{ |cache, markup| cache[markup] = variable_parse(markup) }
end
def increment_used_resources(key, obj)
@@ -78,7 +73,7 @@ module Liquid
# are there any not handled interrupts?
def has_interrupt?
!@interrupts.empty?
@interrupts.any?
end
# push an interrupt to the stack. this interrupt is considered not handled.
@@ -93,8 +88,7 @@ module Liquid
def handle_error(e)
errors.push(e)
raise if exception_handler && exception_handler.call(e)
raise if @rethrow_errors
case e
when SyntaxError
@@ -197,12 +191,7 @@ module Liquid
# Fetches an object starting at the local scope and then moving up the hierachy
def find_variable(key)
# This was changed from find() to find_index() because this is a very hot
# path and find_index() is optimized in MRI to reduce object allocation
index = @scopes.find_index { |s| s.has_key?(key) }
scope = @scopes[index] if index
scope = @scopes.find { |s| s.has_key?(key) }
variable = nil
if scope.nil?
@@ -216,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
@@ -224,16 +214,6 @@ module Liquid
return variable
end
def variable_parse(markup)
parts = markup.scan(VariableParser)
needs_resolution = false
if parts.first =~ SQUARE_BRACKETED
needs_resolution = true
parts[0] = $1
end
{:first => parts.shift, :needs_resolution => needs_resolution, :rest => parts}
end
# Resolves namespaced queries gracefully.
#
# Example
@@ -241,17 +221,19 @@ module Liquid
# assert_equal 'tobi', @context['hash.name']
# assert_equal 'tobi', @context['hash["name"]']
def variable(markup)
parts = @parsed_variables[markup]
parts = markup.scan(VariableParser)
square_bracketed = /\A\[(.*)\]\z/m
first_part = parts[:first]
if parts[:needs_resolution]
first_part = resolve(parts[:first])
first_part = parts.shift
if first_part =~ square_bracketed
first_part = resolve($1)
end
if object = find_variable(first_part)
parts[:rest].each do |part|
part = resolve($1) if part_resolved = (part =~ SQUARE_BRACKETED)
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
@@ -273,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
@@ -302,5 +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

@@ -92,16 +92,16 @@ module Liquid
# Join elements of the array with certain character between them
def join(input, glue = ' '.freeze)
InputIterator.new(input).join(glue)
[input].flatten.join(glue)
end
# Sort elements of the array
# provide optional property with which to sort an array of hashes or drops
def sort(input, property = nil)
ary = InputIterator.new(input)
ary = flatten_if_necessary(input)
if property.nil?
ary.sort
elsif ary.first.respond_to?('[]'.freeze) && !ary.first[property].nil?
elsif ary.first.respond_to?('[]'.freeze) and !ary.first[property].nil?
ary.sort {|a,b| a[property] <=> b[property] }
elsif ary.first.respond_to?(property)
ary.sort {|a,b| a.send(property) <=> b.send(property) }
@@ -110,13 +110,13 @@ module Liquid
# Reverse the elements of an array
def reverse(input)
ary = InputIterator.new(input)
ary = [input].flatten
ary.reverse
end
# map/collect on a given property
def map(input, property)
InputIterator.new(input).map do |e|
flatten_if_necessary(input).map do |e|
e = e.call if e.is_a?(Proc)
if property == "to_liquid".freeze
@@ -265,6 +265,17 @@ module Liquid
private
def flatten_if_necessary(input)
ary = if input.is_a?(Array)
input.flatten
elsif input.is_a?(Enumerable) && !input.is_a?(Hash)
input
else
[input].flatten
end
ary.map{ |e| e.respond_to?(:to_liquid) ? e.to_liquid : e }
end
def to_number(obj)
case obj
when Float
@@ -299,36 +310,6 @@ module Liquid
result = to_number(input).send(operation, to_number(operand))
result.is_a?(BigDecimal) ? result.to_f : result
end
class InputIterator
include Enumerable
def initialize(input)
@input = if input.is_a?(Array)
input.flatten
elsif input.is_a?(Hash)
[input]
elsif input.is_a?(Enumerable)
input
else
Array(input)
end
end
def join(glue)
to_a.join(glue)
end
def reverse
reverse_each.to_a
end
def each
@input.each do |e|
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
end
end
end
end
Template.register_filter(StandardFilters)

View File

@@ -31,7 +31,7 @@ module Liquid
end
def blank?
false
@blank || false
end
def parse_with_selected_parser(markup)
@@ -50,12 +50,11 @@ module Liquid
end
private
def strict_parse_with_error_context(markup)
strict_parse(markup)
rescue SyntaxError => e
e.message << " in \"#{markup.strip}\""
raise e
end
end
end
end # Tag
end # Liquid

View File

@@ -1,4 +1,5 @@
module Liquid
# Capture stores the result of a block into a variable without rendering it inplace.
#
# {% capture heading %}

View File

@@ -43,8 +43,11 @@ module Liquid
end
end
private
def blank?
false
end
private
def variables_from_string(markup)
markup.split(',').collect do |var|
var =~ /\s*(#{QuotedFragment})\s*/o

View File

@@ -38,6 +38,10 @@ module Liquid
def parse(tokens)
end
def blank?
false
end
def render(context)
partial = load_cached_partial(context)
variable = context[@variable_name || @template_name[1..-2]]

View File

@@ -25,6 +25,10 @@ module Liquid
context.environments.first[@variable] = value + 1
value.to_s
end
def blank?
false
end
end
Template.register_tag('increment'.freeze, Increment)

View File

@@ -21,36 +21,6 @@ module Liquid
attr_accessor :root, :resource_limits
@@file_system = BlankFileSystem.new
class TagRegistry
def initialize
@tags = {}
@cache = {}
end
def [](tag_name)
return nil unless @tags.has_key?(tag_name)
return @cache[tag_name] if Liquid.cache_classes
lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o }
end
def []=(tag_name, klass)
@tags[tag_name] = klass.name
@cache[tag_name] = klass
end
def delete(tag_name)
@tags.delete(tag_name)
@cache.delete(tag_name)
end
private
def lookup_class(name)
name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) }
end
end
class << self
# Sets how strict the parser should be.
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
@@ -71,7 +41,7 @@ module Liquid
end
def tags
@tags ||= TagRegistry.new
@tags ||= {}
end
def error_mode
@@ -142,11 +112,7 @@ module Liquid
context = case args.first
when Liquid::Context
c = args.shift
if @rethrow_errors
c.exception_handler = ->(e) { true }
end
c.rethrow_errors = true if @rethrow_errors
c
when Liquid::Drop
drop = args.shift
@@ -171,9 +137,6 @@ module Liquid
context.add_filters(options[:filters])
end
if options[:exception_handler]
context.exception_handler = options[:exception_handler]
end
when Module
context.add_filters(args.pop)
when Array

View File

@@ -36,13 +36,13 @@ module Liquid
def lax_parse(markup)
@filters = []
if markup =~ /\s*(#{QuotedFragment})(.*)/om
@name = Regexp.last_match(1)
if Regexp.last_match(2) =~ /#{FilterSeparator}\s*(.*)/om
if match = markup.match(/\s*(#{QuotedFragment})(.*)/om)
@name = match[1]
if match[2].match(/#{FilterSeparator}\s*(.*)/om)
filters = Regexp.last_match(1).scan(FilterParser)
filters.each do |f|
if f =~ /\s*(\w+)/
filtername = Regexp.last_match(1)
if matches = f.match(/\s*(\w+)/)
filtername = matches[1]
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
@filters << [filtername, filterargs]
end

View File

@@ -1,4 +1,4 @@
# encoding: utf-8
module Liquid
VERSION = "3.0.0.rc1"
VERSION = "3.0.0".freeze
end

View File

@@ -24,5 +24,6 @@ Gem::Specification.new do |s|
s.require_path = "lib"
s.add_development_dependency 'stackprof' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.1.0")
s.add_development_dependency 'rake'
end

View File

@@ -4,14 +4,10 @@ require File.dirname(__FILE__) + '/theme_runner'
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
profiler = ThemeRunner.new
profiler.run
[:cpu, :object].each do |profile_type|
puts "Profiling in #{profile_type.to_s} mode..."
results = StackProf.run(mode: profile_type) do
100.times do
profiler.run
end
results = StackProf.run(mode: :cpu) do
100.times do
profiler.run
end
StackProf::Report.new(results).print_text(false, 20)
File.write(ENV['FILENAME'] + "." + profile_type.to_s, Marshal.dump(results)) if ENV['FILENAME']
end
StackProf::Report.new(results).print_text(false, 20)
File.write(ENV['FILENAME'], Marshal.dump(results)) if ENV['FILENAME']

View File

@@ -20,14 +20,4 @@ class ContextTest < Test::Unit::TestCase
assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
end
def test_has_key_will_not_add_an_error_for_missing_keys
Template.error_mode = :strict
context = Context.new
context.has_key?('unknown')
assert_empty context.errors
end
end

View File

@@ -67,12 +67,12 @@ class FiltersTest < Test::Unit::TestCase
@context['value'] = 3
@context['numbers'] = [2,1,4,3]
@context['words'] = ['expected', 'as', 'alphabetic']
@context['arrays'] = ['flower', 'are']
@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 [3], Variable.new("value | sort").render(@context)
assert_equal ['are', 'flower'], Variable.new("arrays | sort").render(@context)
assert_equal ['are', 'flattened'], Variable.new("arrays | sort").render(@context)
end
def test_strip_html

View File

@@ -84,11 +84,4 @@ class ParsingQuirksTest < Test::Unit::TestCase
assert_template_result('',"{% if #{markup} %} YES {% endif %}")
end
end
def test_raise_on_invalid_tag_delimiter
assert_raise(Liquid::SyntaxError) do
Template.new.parse('{% end %}')
end
end
end # ParsingQuirksTest

View File

@@ -7,8 +7,6 @@ class Filters
end
class TestThing
attr_reader :foo
def initialize
@foo = 0
end
@@ -117,10 +115,6 @@ 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_legacy_sort_hash
assert_equal [{a:1, b:2}], @filters.sort({a:1, b:2})
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")
@@ -132,10 +126,6 @@ class StandardFiltersTest < Test::Unit::TestCase
assert_equal [4,3,2,1], @filters.reverse([1,2,3,4])
end
def test_legacy_reverse_hash
assert_equal [{a:1, b:2}], @filters.reverse(a:1, b:2)
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' }}",
@@ -157,16 +147,9 @@ class StandardFiltersTest < Test::Unit::TestCase
"thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] }
end
def test_legacy_map_on_hashes_with_dynamic_key
template = "{% assign key = 'foo' %}{{ thing | map: key | map: 'bar' }}"
hash = { "foo" => { "bar" => 42 } }
assert_template_result "42", template, "thing" => hash
end
def test_sort_calls_to_liquid
t = TestThing.new
Liquid::Template.parse('{{ foo | sort: "whatever" }}').render("foo" => [t])
assert t.foo > 0
assert_template_result "woot: 1", '{{ foo | sort: "whatever" }}', "foo" => [t]
end
def test_map_over_proc

View File

@@ -60,6 +60,10 @@ class CustomInclude < Liquid::Tag
def parse(tokens)
end
def blank?
false
end
def render(context)
@template_name[1..-2]
end
@@ -201,12 +205,4 @@ class IncludeTagTest < Test::Unit::TestCase
Liquid::Template.tags['include'] = original_tag
end
end
def test_does_not_add_error_in_strict_mode_for_missing_variable
Liquid::Template.file_system = TestFileSystem.new
a = Liquid::Template.parse(' {% include "nested_template" %}')
a.render!
assert_empty a.errors
end
end # IncludeTagTest

View File

@@ -153,18 +153,4 @@ class TemplateTest < Test::Unit::TestCase
end
assert_equal 'ruby error in drop', e.message
end
def test_exception_handler_doesnt_reraise_if_it_returns_false
exception = nil
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_handler: ->(e) { exception = e; false })
assert exception.is_a?(ZeroDivisionError)
end
def test_exception_handler_does_reraise_if_it_returns_true
exception = nil
assert_raises(ZeroDivisionError) do
Template.parse("{{ 1 | divided_by: 0 }}").render({}, exception_handler: ->(e) { exception = e; true })
end
assert exception.is_a?(ZeroDivisionError)
end
end

View File

@@ -2,7 +2,6 @@
require 'test/unit'
require 'test/unit/assertions'
require 'spy/integration'
$:.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib'))
require 'liquid.rb'

View File

@@ -49,17 +49,6 @@ class ConditionUnitTest < Test::Unit::TestCase
assert_evalutes_false "'bob'", 'contains', "'---'"
end
def test_invalid_comparation_operator
assert_evaluates_argument_error "1", '~~', '0'
end
def test_comparation_of_int_and_str
assert_evaluates_argument_error "'1'", '>', '0'
assert_evaluates_argument_error "'1'", '<', '0'
assert_evaluates_argument_error "'1'", '>=', '0'
assert_evaluates_argument_error "'1'", '<=', '0'
end
def test_contains_works_on_arrays
@context = Liquid::Context.new
@context['array'] = [1,2,3,4,5]
@@ -135,11 +124,4 @@ class ConditionUnitTest < Test::Unit::TestCase
assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
"Evaluated true: #{left} #{op} #{right}"
end
def assert_evaluates_argument_error(left, op, right)
assert_raises(Liquid::ArgumentError) do
Condition.new(left, op, right).evaluate(@context || Liquid::Context.new)
end
end
end # ConditionTest

View File

@@ -70,10 +70,6 @@ class ContextUnitTest < Test::Unit::TestCase
@context = Liquid::Context.new
end
def teardown
Spy.teardown
end
def test_variables
@context['string'] = 'string'
assert_equal 'string', @context['string']
@@ -462,25 +458,21 @@ class ContextUnitTest < Test::Unit::TestCase
assert_equal @context, @context['category'].context
end
def test_use_empty_instead_of_any_in_interrupt_handling_to_avoid_lots_of_unnecessary_object_allocations
mock_any = Spy.on_instance_method(Array, :any?)
mock_empty = Spy.on_instance_method(Array, :empty?)
mock_has_interrupt = Spy.on(@context, :has_interrupt?).and_call_through
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
@context.has_interrupt?
refute mock_any.has_been_called?
assert mock_empty.has_been_called?
end
def test_variable_lookup_caches_markup
mock_scan = Spy.on_instance_method(String, :scan).and_return(["string"])
@context['string'] = 'string'
@context['string']
@context['string']
assert_equal 1, mock_scan.calls.size
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

@@ -16,54 +16,4 @@ class TemplateUnitTest < Test::Unit::TestCase
assert_instance_of I18n, t.root.options[:locale]
assert_equal fixture("en_locale.yml"), t.root.options[:locale].path
end
def test_with_cache_classes_tags_returns_the_same_class
original_cache_setting = Liquid.cache_classes
Liquid.cache_classes = true
original_klass = Class.new
Object.send(:const_set, :CustomTag, original_klass)
Template.register_tag('custom', CustomTag)
Object.send(:remove_const, :CustomTag)
new_klass = Class.new
Object.send(:const_set, :CustomTag, new_klass)
assert Template.tags['custom'].equal?(original_klass)
ensure
Object.send(:remove_const, :CustomTag)
Template.tags.delete('custom')
Liquid.cache_classes = original_cache_setting
end
def test_without_cache_classes_tags_reloads_the_class
original_cache_setting = Liquid.cache_classes
Liquid.cache_classes = false
original_klass = Class.new
Object.send(:const_set, :CustomTag, original_klass)
Template.register_tag('custom', CustomTag)
Object.send(:remove_const, :CustomTag)
new_klass = Class.new
Object.send(:const_set, :CustomTag, new_klass)
assert Template.tags['custom'].equal?(new_klass)
ensure
Object.send(:remove_const, :CustomTag)
Template.tags.delete('custom')
Liquid.cache_classes = original_cache_setting
end
class FakeTag; end
def test_tags_delete
Template.register_tag('fake', FakeTag)
assert_equal FakeTag, Template.tags['fake']
Template.tags.delete('fake')
assert_nil Template.tags['fake']
end
end