mirror of
https://github.com/kemko/liquid.git
synced 2026-01-06 18:25:41 +03:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7d1e1d0c1 | ||
|
|
28fd2222c8 | ||
|
|
9913895b81 | ||
|
|
d706db3bd7 | ||
|
|
38b4543bf1 | ||
|
|
1300210f05 | ||
|
|
a48e162237 | ||
|
|
7bcb565668 | ||
|
|
c3e6cde67f | ||
|
|
50bd34fd78 | ||
|
|
ee41b3f4a3 | ||
|
|
05d9976e16 | ||
|
|
6c2fde5eea | ||
|
|
ce76dbf8d9 | ||
|
|
661ff2ccdf | ||
|
|
9c183bea83 | ||
|
|
484fd18612 | ||
|
|
bf86459456 | ||
|
|
d2827c561b | ||
|
|
16c34595a4 | ||
|
|
6e091909ee | ||
|
|
d7cb39ccb3 | ||
|
|
f8d46804fd | ||
|
|
5c6de2d919 |
10
History.md
10
History.md
@@ -1,5 +1,13 @@
|
|||||||
# Liquid Version History
|
# Liquid Version History
|
||||||
|
|
||||||
|
## 2.5.0 / 2013-03-06
|
||||||
|
|
||||||
|
* Prevent Object methods from being called on drops
|
||||||
|
* Avoid symbol injection from liquid
|
||||||
|
* Added break and continue statements
|
||||||
|
* Fix filter parser for args without space separators
|
||||||
|
* Add support for filter keyword arguments
|
||||||
|
|
||||||
## 2.4.0 / 2012-08-03
|
## 2.4.0 / 2012-08-03
|
||||||
|
|
||||||
* Performance improvements
|
* Performance improvements
|
||||||
@@ -8,7 +16,7 @@
|
|||||||
* Ruby 1.8, 1.9, and Rubinius compatibility fixes
|
* Ruby 1.8, 1.9, and Rubinius compatibility fixes
|
||||||
* Add support for `quoted['references']` in `tablerow`
|
* Add support for `quoted['references']` in `tablerow`
|
||||||
* Add support for Enumerable to `tablerow`
|
* Add support for Enumerable to `tablerow`
|
||||||
* `strip\_html` filter removes html comments
|
* `strip_html` filter removes html comments
|
||||||
|
|
||||||
|
|
||||||
## 2.3.0 / 2011-10-16
|
## 2.3.0 / 2011-10-16
|
||||||
|
|||||||
8
Rakefile
8
Rakefile
@@ -9,7 +9,7 @@ task :default => 'test'
|
|||||||
|
|
||||||
Rake::TestTask.new(:test) do |t|
|
Rake::TestTask.new(:test) do |t|
|
||||||
t.libs << '.' << 'lib' << 'test'
|
t.libs << '.' << 'lib' << 'test'
|
||||||
t.pattern = 'test/liquid/**/*_test.rb'
|
t.test_files = FileList['test/liquid/**/*_test.rb']
|
||||||
t.verbose = false
|
t.verbose = false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ namespace :benchmark do
|
|||||||
|
|
||||||
desc "Run the liquid benchmark"
|
desc "Run the liquid benchmark"
|
||||||
task :run do
|
task :run do
|
||||||
ruby "performance/benchmark.rb"
|
ruby "./performance/benchmark.rb"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -37,12 +37,12 @@ namespace :profile do
|
|||||||
|
|
||||||
desc "Run the liquid profile/performance coverage"
|
desc "Run the liquid profile/performance coverage"
|
||||||
task :run do
|
task :run do
|
||||||
ruby "performance/profile.rb"
|
ruby "./performance/profile.rb"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Run KCacheGrind"
|
desc "Run KCacheGrind"
|
||||||
task :grind => :run do
|
task :grind => :run do
|
||||||
system "kcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
|
system "qcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ end
|
|||||||
require 'liquid/drop'
|
require 'liquid/drop'
|
||||||
require 'liquid/extensions'
|
require 'liquid/extensions'
|
||||||
require 'liquid/errors'
|
require 'liquid/errors'
|
||||||
|
require 'liquid/interrupts'
|
||||||
require 'liquid/strainer'
|
require 'liquid/strainer'
|
||||||
require 'liquid/context'
|
require 'liquid/context'
|
||||||
require 'liquid/tag'
|
require 'liquid/tag'
|
||||||
|
|||||||
@@ -89,13 +89,27 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_all(list, context)
|
def render_all(list, context)
|
||||||
list.collect do |token|
|
output = []
|
||||||
|
list.each do |token|
|
||||||
|
# Break out if we have any unhanded interrupts.
|
||||||
|
break if context.has_interrupt?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
token.respond_to?(:render) ? token.render(context) : token
|
# If we get an Interrupt that means the block must stop processing. An
|
||||||
|
# Interrupt is any command that stops block execution such as {% break %}
|
||||||
|
# or {% continue %}
|
||||||
|
if token.is_a? Continue or token.is_a? Break
|
||||||
|
context.push_interrupt(token.interrupt)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
output << (token.respond_to?(:render) ? token.render(context) : token)
|
||||||
rescue ::StandardError => e
|
rescue ::StandardError => e
|
||||||
context.handle_error(e)
|
output << (context.handle_error(e))
|
||||||
end
|
end
|
||||||
end.join
|
end
|
||||||
|
|
||||||
|
output.join
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ module Liquid
|
|||||||
@errors = []
|
@errors = []
|
||||||
@rethrow_errors = rethrow_errors
|
@rethrow_errors = rethrow_errors
|
||||||
squash_instance_assigns_with_environments
|
squash_instance_assigns_with_environments
|
||||||
|
|
||||||
|
@interrupts = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def strainer
|
def strainer
|
||||||
@@ -37,10 +39,26 @@ module Liquid
|
|||||||
|
|
||||||
filters.each do |f|
|
filters.each do |f|
|
||||||
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
|
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
|
||||||
|
Strainer.add_known_filter(f)
|
||||||
strainer.extend(f)
|
strainer.extend(f)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# are there any not handled interrupts?
|
||||||
|
def has_interrupt?
|
||||||
|
!@interrupts.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
# push an interrupt to the stack. this interrupt is considered not handled.
|
||||||
|
def push_interrupt(e)
|
||||||
|
@interrupts.push(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
# pop an interrupt from the stack
|
||||||
|
def pop_interrupt
|
||||||
|
@interrupts.pop
|
||||||
|
end
|
||||||
|
|
||||||
def handle_error(e)
|
def handle_error(e)
|
||||||
errors.push(e)
|
errors.push(e)
|
||||||
raise if @rethrow_errors
|
raise if @rethrow_errors
|
||||||
@@ -54,11 +72,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def invoke(method, *args)
|
def invoke(method, *args)
|
||||||
if strainer.respond_to?(method)
|
strainer.invoke(method, *args)
|
||||||
strainer.__send__(method, *args)
|
|
||||||
else
|
|
||||||
args.first
|
|
||||||
end
|
|
||||||
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
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
require 'set'
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
|
||||||
# A drop in liquid is a class which allows you to export DOM like things to liquid.
|
# A drop in liquid is a class which allows you to export DOM like things to liquid.
|
||||||
@@ -22,6 +24,8 @@ module Liquid
|
|||||||
class Drop
|
class Drop
|
||||||
attr_writer :context
|
attr_writer :context
|
||||||
|
|
||||||
|
EMPTY_STRING = ''.freeze
|
||||||
|
|
||||||
# Catch all for the method
|
# Catch all for the method
|
||||||
def before_method(method)
|
def before_method(method)
|
||||||
nil
|
nil
|
||||||
@@ -29,8 +33,8 @@ module Liquid
|
|||||||
|
|
||||||
# called by liquid to invoke a drop
|
# called by liquid to invoke a drop
|
||||||
def invoke_drop(method_or_key)
|
def invoke_drop(method_or_key)
|
||||||
if method_or_key && method_or_key != '' && self.class.public_method_defined?(method_or_key.to_s.to_sym)
|
if method_or_key && method_or_key != EMPTY_STRING && self.class.invokable?(method_or_key)
|
||||||
send(method_or_key.to_s.to_sym)
|
send(method_or_key)
|
||||||
else
|
else
|
||||||
before_method(method_or_key)
|
before_method(method_or_key)
|
||||||
end
|
end
|
||||||
@@ -45,5 +49,13 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
alias :[] :invoke_drop
|
alias :[] :invoke_drop
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Check for method existence without invoking respond_to?, which creates symbols
|
||||||
|
def self.invokable?(method_name)
|
||||||
|
@invokable_methods ||= Set.new((public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s))
|
||||||
|
@invokable_methods.include?(method_name.to_s)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ module Liquid
|
|||||||
class StandardError < Error; end
|
class StandardError < Error; end
|
||||||
class SyntaxError < Error; end
|
class SyntaxError < Error; end
|
||||||
class StackLevelError < Error; end
|
class StackLevelError < Error; end
|
||||||
end
|
end
|
||||||
|
|||||||
17
lib/liquid/interrupts.rb
Normal file
17
lib/liquid/interrupts.rb
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module Liquid
|
||||||
|
|
||||||
|
# An interrupt is any command that breaks processing of a block (ex: a for loop).
|
||||||
|
class Interrupt
|
||||||
|
attr_reader :message
|
||||||
|
|
||||||
|
def initialize(message=nil)
|
||||||
|
@message = message || "interrupt"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Interrupt that is thrown whenever a {% break %} is called.
|
||||||
|
class BreakInterrupt < Interrupt; end
|
||||||
|
|
||||||
|
# Interrupt that is thrown whenever a {% continue %} is called.
|
||||||
|
class ContinueInterrupt < Interrupt; end
|
||||||
|
end
|
||||||
@@ -2,24 +2,15 @@ require 'set'
|
|||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
|
||||||
parent_object = if defined? BlankObject
|
|
||||||
BlankObject
|
|
||||||
else
|
|
||||||
Object
|
|
||||||
end
|
|
||||||
|
|
||||||
# Strainer is the parent class for the filters system.
|
# Strainer is the parent class for the filters system.
|
||||||
# New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
|
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
|
||||||
#
|
#
|
||||||
# One of the strainer's responsibilities is to keep malicious method calls out
|
# The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
|
||||||
class Strainer < parent_object #:nodoc:
|
# Context#add_filters or Template.register_filter
|
||||||
INTERNAL_METHOD = /^__/
|
class Strainer #:nodoc:
|
||||||
@@required_methods = Set.new([:__id__, :__send__, :respond_to?, :kind_of?, :extend, :methods, :singleton_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 = {}
|
@@filters = {}
|
||||||
|
@@known_filters = Set.new
|
||||||
|
@@known_methods = Set.new
|
||||||
|
|
||||||
def initialize(context)
|
def initialize(context)
|
||||||
@context = context
|
@context = context
|
||||||
@@ -27,28 +18,36 @@ module Liquid
|
|||||||
|
|
||||||
def self.global_filter(filter)
|
def self.global_filter(filter)
|
||||||
raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module)
|
raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module)
|
||||||
|
add_known_filter(filter)
|
||||||
@@filters[filter.name] = filter
|
@@filters[filter.name] = filter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.add_known_filter(filter)
|
||||||
|
unless @@known_filters.include?(filter)
|
||||||
|
@@method_blacklist ||= Set.new(Strainer.instance_methods.map(&:to_s))
|
||||||
|
new_methods = filter.instance_methods.map(&:to_s)
|
||||||
|
new_methods.reject!{ |m| @@method_blacklist.include?(m) }
|
||||||
|
@@known_methods.merge(new_methods)
|
||||||
|
@@known_filters.add(filter)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.create(context)
|
def self.create(context)
|
||||||
strainer = Strainer.new(context)
|
strainer = Strainer.new(context)
|
||||||
@@filters.each { |k,m| strainer.extend(m) }
|
@@filters.each { |k,m| strainer.extend(m) }
|
||||||
strainer
|
strainer
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond_to?(method, include_private = false)
|
def invoke(method, *args)
|
||||||
method_name = method.to_s
|
if invokable?(method)
|
||||||
return false if method_name =~ INTERNAL_METHOD
|
send(method, *args)
|
||||||
return false if @@required_methods.include?(method_name)
|
else
|
||||||
super
|
args.first
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# remove all standard methods from the bucket so circumvent security
|
def invokable?(method)
|
||||||
# problems
|
@@known_methods.include?(method.to_s) && respond_to?(method)
|
||||||
instance_methods.each do |m|
|
|
||||||
unless @@required_methods.include?(m.to_sym)
|
|
||||||
undef_method m
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
21
lib/liquid/tags/break.rb
Normal file
21
lib/liquid/tags/break.rb
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
module Liquid
|
||||||
|
|
||||||
|
# Break tag to be used to break out of a for loop.
|
||||||
|
#
|
||||||
|
# == Basic Usage:
|
||||||
|
# {% for item in collection %}
|
||||||
|
# {% if item.condition %}
|
||||||
|
# {% break %}
|
||||||
|
# {% endif %}
|
||||||
|
# {% endfor %}
|
||||||
|
#
|
||||||
|
class Break < Tag
|
||||||
|
|
||||||
|
def interrupt
|
||||||
|
BreakInterrupt.new
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
Template.register_tag('break', Break)
|
||||||
|
end
|
||||||
21
lib/liquid/tags/continue.rb
Normal file
21
lib/liquid/tags/continue.rb
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
module Liquid
|
||||||
|
|
||||||
|
# Continue tag to be used to break out of a for loop.
|
||||||
|
#
|
||||||
|
# == Basic Usage:
|
||||||
|
# {% for item in collection %}
|
||||||
|
# {% if item.condition %}
|
||||||
|
# {% continue %}
|
||||||
|
# {% endif %}
|
||||||
|
# {% endfor %}
|
||||||
|
#
|
||||||
|
class Continue < Tag
|
||||||
|
|
||||||
|
def interrupt
|
||||||
|
ContinueInterrupt.new
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
Template.register_tag('continue', Continue)
|
||||||
|
end
|
||||||
@@ -69,7 +69,7 @@ module Liquid
|
|||||||
@nodelist = @else_block = []
|
@nodelist = @else_block = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
context.registers[:for] ||= Hash.new(0)
|
context.registers[:for] ||= Hash.new(0)
|
||||||
|
|
||||||
collection = context[@collection_name]
|
collection = context[@collection_name]
|
||||||
@@ -101,8 +101,8 @@ module Liquid
|
|||||||
# Store our progress through the collection for the continue flag
|
# Store our progress through the collection for the continue flag
|
||||||
context.registers[:for][@name] = from + segment.length
|
context.registers[:for][@name] = from + segment.length
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
segment.each_with_index do |item, index|
|
segment.each_with_index do |item, index|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
context['forloop'] = {
|
context['forloop'] = {
|
||||||
'name' => @name,
|
'name' => @name,
|
||||||
@@ -115,6 +115,13 @@ module Liquid
|
|||||||
'last' => (index == length - 1) }
|
'last' => (index == length - 1) }
|
||||||
|
|
||||||
result << render_all(@for_block, context)
|
result << render_all(@for_block, context)
|
||||||
|
|
||||||
|
# Handle any interrupts if they exist.
|
||||||
|
if context.has_interrupt?
|
||||||
|
interrupt = context.pop_interrupt
|
||||||
|
break if interrupt.is_a? BreakInterrupt
|
||||||
|
next if interrupt.is_a? ContinueInterrupt
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module Liquid
|
|||||||
# {{ user | link }}
|
# {{ user | link }}
|
||||||
#
|
#
|
||||||
class Variable
|
class Variable
|
||||||
FilterParser = /(?:#{FilterSeparator}|(?:\s*(?!(?:#{FilterSeparator}))(?:#{QuotedFragment}|\S+)\s*)+)/o
|
FilterParser = /(?:#{FilterSeparator}|(?:\s*(?:#{QuotedFragment}|#{ArgumentSeparator})\s*)+)/o
|
||||||
attr_accessor :filters, :name
|
attr_accessor :filters, :name
|
||||||
|
|
||||||
def initialize(markup)
|
def initialize(markup)
|
||||||
@@ -23,10 +23,10 @@ module Liquid
|
|||||||
if match[2].match(/#{FilterSeparator}\s*(.*)/o)
|
if match[2].match(/#{FilterSeparator}\s*(.*)/o)
|
||||||
filters = Regexp.last_match(1).scan(FilterParser)
|
filters = Regexp.last_match(1).scan(FilterParser)
|
||||||
filters.each do |f|
|
filters.each do |f|
|
||||||
if matches = f.match(/\s*(\w+)/)
|
if matches = f.match(/\s*(\w+)(?:\s*#{FilterArgumentSeparator}(.*))?/)
|
||||||
filtername = matches[1]
|
filtername = matches[1]
|
||||||
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/o).flatten
|
filterargs = matches[2].to_s.scan(/(?:\A|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
|
||||||
@filters << [filtername.to_sym, filterargs]
|
@filters << [filtername, filterargs]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -36,9 +36,16 @@ module Liquid
|
|||||||
def render(context)
|
def render(context)
|
||||||
return '' if @name.nil?
|
return '' if @name.nil?
|
||||||
@filters.inject(context[@name]) do |output, filter|
|
@filters.inject(context[@name]) do |output, filter|
|
||||||
filterargs = filter[1].to_a.collect do |a|
|
filterargs = []
|
||||||
context[a]
|
keyword_args = {}
|
||||||
|
filter[1].to_a.each do |a|
|
||||||
|
if matches = a.match(/\A#{TagAttributes}\z/o)
|
||||||
|
keyword_args[matches[1]] = context[matches[2]]
|
||||||
|
else
|
||||||
|
filterargs << context[a]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
filterargs << keyword_args unless keyword_args.empty?
|
||||||
begin
|
begin
|
||||||
output = context.invoke(filter[0], output, *filterargs)
|
output = context.invoke(filter[0], output, *filterargs)
|
||||||
rescue FilterNotFound
|
rescue FilterNotFound
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "liquid"
|
s.name = "liquid"
|
||||||
s.version = "2.4.0"
|
s.version = "2.5.0"
|
||||||
s.platform = Gem::Platform::RUBY
|
s.platform = Gem::Platform::RUBY
|
||||||
s.summary = "A secure, non-evaling end user template engine with aesthetic markup."
|
s.summary = "A secure, non-evaling end user template engine with aesthetic markup."
|
||||||
s.authors = ["Tobias Luetke"]
|
s.authors = ["Tobias Luetke"]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ require File.dirname(__FILE__) + '/theme_runner'
|
|||||||
profiler = ThemeRunner.new
|
profiler = ThemeRunner.new
|
||||||
|
|
||||||
Benchmark.bmbm do |x|
|
Benchmark.bmbm do |x|
|
||||||
x.report("parse & run:") { 10.times { profiler.run(false) } }
|
x.report("parse:") { 100.times { profiler.compile } }
|
||||||
|
x.report("parse & run:") { 100.times { profiler.run } }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ profiler = ThemeRunner.new
|
|||||||
|
|
||||||
puts 'Running profiler...'
|
puts 'Running profiler...'
|
||||||
|
|
||||||
results = profiler.run(true)
|
results = profiler.run
|
||||||
|
|
||||||
puts 'Success'
|
puts 'Success'
|
||||||
puts
|
puts
|
||||||
|
|
||||||
[RubyProf::FlatPrinter, RubyProf::GraphPrinter, RubyProf::GraphHtmlPrinter, RubyProf::CallTreePrinter].each do |klass|
|
[RubyProf::FlatPrinter, RubyProf::GraphPrinter, RubyProf::GraphHtmlPrinter, RubyProf::CallTreePrinter].each do |klass|
|
||||||
filename = (ENV['TMP'] || '/tmp') + (klass.name.include?('Html') ? "/liquid.#{klass.name.downcase}.html" : "/liquid.#{klass.name.downcase}.txt")
|
filename = (ENV['TMP'] || '/tmp') + (klass.name.include?('Html') ? "/liquid.#{klass.name.downcase}.html" : "/callgrind.liquid.#{klass.name.downcase}.txt")
|
||||||
filename.gsub!(/:+/, '_')
|
filename.gsub!(/:+/, '_')
|
||||||
File.open(filename, "w+") { |fp| klass.new(results).print(fp) }
|
File.open(filename, "w+") { |fp| klass.new(results).print(fp, :print_file => true) }
|
||||||
$stderr.puts "wrote #{klass.name} output to #{filename}"
|
$stderr.puts "wrote #{klass.name} output to #{filename}"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,9 +27,34 @@ class ThemeRunner
|
|||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compile
|
||||||
|
# Dup assigns because will make some changes to them
|
||||||
|
|
||||||
def run(profile = false)
|
@tests.each do |liquid, layout, template_name|
|
||||||
RubyProf.measure_mode = RubyProf::WALL_TIME if profile
|
|
||||||
|
tmpl = Liquid::Template.new
|
||||||
|
tmpl.parse(liquid)
|
||||||
|
tmpl = Liquid::Template.new
|
||||||
|
tmpl.parse(layout)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
# Dup assigns because will make some changes to them
|
||||||
|
assigns = Database.tables.dup
|
||||||
|
|
||||||
|
@tests.each do |liquid, layout, template_name|
|
||||||
|
|
||||||
|
# Compute page_tempalte outside of profiler run, uninteresting to profiler
|
||||||
|
page_template = File.basename(template_name, File.extname(template_name))
|
||||||
|
compile_and_render(liquid, layout, assigns, page_template)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def run_profile
|
||||||
|
RubyProf.measure_mode = RubyProf::WALL_TIME
|
||||||
|
|
||||||
# Dup assigns because will make some changes to them
|
# Dup assigns because will make some changes to them
|
||||||
assigns = Database.tables.dup
|
assigns = Database.tables.dup
|
||||||
@@ -40,26 +65,27 @@ class ThemeRunner
|
|||||||
html = nil
|
html = nil
|
||||||
page_template = File.basename(template_name, File.extname(template_name))
|
page_template = File.basename(template_name, File.extname(template_name))
|
||||||
|
|
||||||
# Profile compiling and rendering both
|
unless @started
|
||||||
|
RubyProf.start
|
||||||
if profile
|
RubyProf.pause
|
||||||
|
@started = true
|
||||||
RubyProf.resume do
|
|
||||||
html = compile_and_render(liquid, layout, assigns, page_template)
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
html = compile_and_render(liquid, layout, assigns, page_template)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
html = nil
|
||||||
|
|
||||||
|
RubyProf.resume
|
||||||
|
html = compile_and_render(liquid, layout, assigns, page_template)
|
||||||
|
RubyProf.pause
|
||||||
|
|
||||||
|
|
||||||
# return the result and the MD5 of the content, this can be used to detect regressions between liquid version
|
# return the result and the MD5 of the content, this can be used to detect regressions between liquid version
|
||||||
$stdout.puts "* rendered template %s, content: %s" % [template_name, Digest::MD5.hexdigest(html)] if profile
|
$stdout.puts "* rendered template %s, content: %s" % [template_name, Digest::MD5.hexdigest(html)]
|
||||||
|
|
||||||
# Uncomment to dump html files to /tmp so that you can inspect for errors
|
# Uncomment to dump html files to /tmp so that you can inspect for errors
|
||||||
# File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp <<html}
|
# File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp <<html}
|
||||||
end
|
end
|
||||||
|
|
||||||
RubyProf.stop if profile
|
RubyProf.stop
|
||||||
end
|
end
|
||||||
|
|
||||||
def compile_and_render(template, layout, assigns, page_template)
|
def compile_and_render(template, layout, assigns, page_template)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class VariableTest < Test::Unit::TestCase
|
class BlockTest < Test::Unit::TestCase
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def test_blankspace
|
def test_blankspace
|
||||||
|
|||||||
@@ -189,10 +189,10 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
context = Context.new
|
context = Context.new
|
||||||
methods_before = context.strainer.methods.map { |method| method.to_s }
|
assert_equal "Wookie", context.invoke("hi", "Wookie")
|
||||||
|
|
||||||
context.add_filters(filter)
|
context.add_filters(filter)
|
||||||
methods_after = context.strainer.methods.map { |method| method.to_s }
|
assert_equal "Wookie hi!", context.invoke("hi", "Wookie")
|
||||||
assert_equal (methods_before + ["hi"]).sort, methods_after.sort
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_item_in_outer_scope
|
def test_add_item_in_outer_scope
|
||||||
|
|||||||
@@ -115,6 +115,13 @@ class DropsTest < Test::Unit::TestCase
|
|||||||
assert_equal ' ', output
|
assert_equal ' ', output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_object_methods_not_allowed
|
||||||
|
[:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
|
||||||
|
output = Liquid::Template.parse(" {{ product.#{method} }} ").render('product' => ProductDrop.new)
|
||||||
|
assert_equal ' ', output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_scope
|
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 '2', Liquid::Template.parse( '{%for i in dummy%}{{ context.scopes }}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ module CanadianMoneyFilter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module SubstituteFilter
|
||||||
|
def substitute(input, params={})
|
||||||
|
input.gsub(/%\{(\w+)\}/) { |match| params[$1] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class FiltersTest < Test::Unit::TestCase
|
class FiltersTest < Test::Unit::TestCase
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
@@ -92,6 +98,13 @@ class FiltersTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
assert_equal 1000, Variable.new("var | xyzzy").render(@context)
|
assert_equal 1000, Variable.new("var | xyzzy").render(@context)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_filter_with_keyword_arguments
|
||||||
|
@context['surname'] = 'john'
|
||||||
|
@context.add_filters(SubstituteFilter)
|
||||||
|
output = Variable.new(%! 'hello %{first_name}, %{last_name}' | substitute: first_name: surname, last_name: 'doe' !).render(@context)
|
||||||
|
assert_equal 'hello john, doe', output
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class FiltersInTemplate < Test::Unit::TestCase
|
class FiltersInTemplate < Test::Unit::TestCase
|
||||||
|
|||||||
@@ -38,4 +38,27 @@ class SecurityTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter)
|
assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_does_not_add_filters_to_symbol_table
|
||||||
|
current_symbols = Symbol.all_symbols
|
||||||
|
|
||||||
|
test = %( {{ "some_string" | a_bad_filter }} )
|
||||||
|
|
||||||
|
template = Template.parse(test)
|
||||||
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||||
|
|
||||||
|
template.render
|
||||||
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||||
|
end
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||||
|
end
|
||||||
end # SecurityTest
|
end # SecurityTest
|
||||||
|
|||||||
@@ -3,23 +3,50 @@ require 'test_helper'
|
|||||||
class StrainerTest < Test::Unit::TestCase
|
class StrainerTest < Test::Unit::TestCase
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
|
module AccessScopeFilters
|
||||||
|
def public_filter
|
||||||
|
"public"
|
||||||
|
end
|
||||||
|
|
||||||
|
def private_filter
|
||||||
|
"private"
|
||||||
|
end
|
||||||
|
private :private_filter
|
||||||
|
end
|
||||||
|
|
||||||
|
Strainer.global_filter(AccessScopeFilters)
|
||||||
|
|
||||||
def test_strainer
|
def test_strainer
|
||||||
strainer = Strainer.create(nil)
|
strainer = Strainer.create(nil)
|
||||||
assert_equal false, strainer.respond_to?('__test__')
|
assert_equal 5, strainer.invoke('size', 'input')
|
||||||
assert_equal false, strainer.respond_to?('test')
|
assert_equal "public", strainer.invoke("public_filter")
|
||||||
assert_equal false, strainer.respond_to?('instance_eval')
|
|
||||||
assert_equal false, strainer.respond_to?('__send__')
|
|
||||||
assert_equal true, strainer.respond_to?('size') # from the standard lib
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_should_respond_to_two_parameters
|
def test_strainer_only_invokes_public_filter_methods
|
||||||
strainer = Strainer.create(nil)
|
strainer = Strainer.create(nil)
|
||||||
assert_equal true, strainer.respond_to?('size', false)
|
assert_equal false, strainer.invokable?('__test__')
|
||||||
|
assert_equal false, strainer.invokable?('test')
|
||||||
|
assert_equal false, strainer.invokable?('instance_eval')
|
||||||
|
assert_equal false, strainer.invokable?('__send__')
|
||||||
|
assert_equal true, strainer.invokable?('size') # from the standard lib
|
||||||
end
|
end
|
||||||
|
|
||||||
# Asserts that Object#respond_to_missing? is not being undefined in Ruby versions where it has been implemented
|
def test_strainer_returns_nil_if_no_filter_method_found
|
||||||
# Currently this method is only present in Ruby v1.9.2, or higher
|
strainer = Strainer.create(nil)
|
||||||
def test_object_respond_to_missing
|
assert_nil strainer.invoke("private_filter")
|
||||||
assert_equal Object.respond_to?(:respond_to_missing?), Strainer.create(nil).respond_to?(:respond_to_missing?)
|
assert_nil strainer.invoke("undef_the_filter")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_strainer_returns_first_argument_if_no_method_and_arguments_given
|
||||||
|
strainer = Strainer.create(nil)
|
||||||
|
assert_equal "password", strainer.invoke("undef_the_method", "password")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_strainer_only_allows_methods_defined_in_filters
|
||||||
|
strainer = Strainer.create(nil)
|
||||||
|
assert_equal "1 + 1", strainer.invoke("instance_eval", "1 + 1")
|
||||||
|
assert_equal "puts", strainer.invoke("__send__", "puts", "Hi Mom")
|
||||||
|
assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke")
|
||||||
|
end
|
||||||
|
|
||||||
end # StrainerTest
|
end # StrainerTest
|
||||||
|
|||||||
16
test/liquid/tags/break_tag_test.rb
Normal file
16
test/liquid/tags/break_tag_test.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class BreakTagTest < Test::Unit::TestCase
|
||||||
|
include Liquid
|
||||||
|
|
||||||
|
# tests that no weird errors are raised if break is called outside of a
|
||||||
|
# block
|
||||||
|
def test_break_with_no_block
|
||||||
|
assigns = {'i' => 1}
|
||||||
|
markup = '{% break %}'
|
||||||
|
expected = ''
|
||||||
|
|
||||||
|
assert_template_result(expected, markup, assigns)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
16
test/liquid/tags/continue_tag_test.rb
Normal file
16
test/liquid/tags/continue_tag_test.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class ContinueTagTest < Test::Unit::TestCase
|
||||||
|
include Liquid
|
||||||
|
|
||||||
|
# tests that no weird errors are raised if continue is called outside of a
|
||||||
|
# block
|
||||||
|
def test_continue_with_no_block
|
||||||
|
assigns = {}
|
||||||
|
markup = '{% continue %}'
|
||||||
|
expected = ''
|
||||||
|
|
||||||
|
assert_template_result(expected, markup, assigns)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -168,6 +168,88 @@ HERE
|
|||||||
assert_template_result(expected,markup,assigns)
|
assert_template_result(expected,markup,assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_for_with_break
|
||||||
|
assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,10]}}
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{% break %}{% endfor %}'
|
||||||
|
expected = ""
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{{ i }}{% break %}{% endfor %}'
|
||||||
|
expected = "1"
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{% break %}{{ i }}{% endfor %}'
|
||||||
|
expected = ""
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{{ i }}{% if i > 3 %}{% break %}{% endif %}{% endfor %}'
|
||||||
|
expected = "1234"
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
# tests to ensure it only breaks out of the local for loop
|
||||||
|
# and not all of them.
|
||||||
|
assigns = {'array' => [[1,2],[3,4],[5,6]] }
|
||||||
|
markup = '{% for item in array %}' +
|
||||||
|
'{% for i in item %}' +
|
||||||
|
'{% if i == 1 %}' +
|
||||||
|
'{% break %}' +
|
||||||
|
'{% endif %}' +
|
||||||
|
'{{ i }}' +
|
||||||
|
'{% endfor %}' +
|
||||||
|
'{% endfor %}'
|
||||||
|
expected = '3456'
|
||||||
|
assert_template_result(expected, markup, assigns)
|
||||||
|
|
||||||
|
# test break does nothing when unreached
|
||||||
|
assigns = {'array' => {'items' => [1,2,3,4,5]}}
|
||||||
|
markup = '{% for i in array.items %}{% if i == 9999 %}{% break %}{% endif %}{{ i }}{% endfor %}'
|
||||||
|
expected = '12345'
|
||||||
|
assert_template_result(expected, markup, assigns)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_for_with_continue
|
||||||
|
assigns = {'array' => {'items' => [1,2,3,4,5]}}
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{% continue %}{% endfor %}'
|
||||||
|
expected = ""
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{{ i }}{% continue %}{% endfor %}'
|
||||||
|
expected = "12345"
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{% continue %}{{ i }}{% endfor %}'
|
||||||
|
expected = ""
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{% if i > 3 %}{% continue %}{% endif %}{{ i }}{% endfor %}'
|
||||||
|
expected = "123"
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
markup = '{% for i in array.items %}{% if i == 3 %}{% continue %}{% else %}{{ i }}{% endif %}{% endfor %}'
|
||||||
|
expected = "1245"
|
||||||
|
assert_template_result(expected,markup,assigns)
|
||||||
|
|
||||||
|
# tests to ensure it only continues the local for loop and not all of them.
|
||||||
|
assigns = {'array' => [[1,2],[3,4],[5,6]] }
|
||||||
|
markup = '{% for item in array %}' +
|
||||||
|
'{% for i in item %}' +
|
||||||
|
'{% if i == 1 %}' +
|
||||||
|
'{% continue %}' +
|
||||||
|
'{% endif %}' +
|
||||||
|
'{{ i }}' +
|
||||||
|
'{% endfor %}' +
|
||||||
|
'{% endfor %}'
|
||||||
|
expected = '23456'
|
||||||
|
assert_template_result(expected, markup, assigns)
|
||||||
|
|
||||||
|
# test continue does nothing when unreached
|
||||||
|
assigns = {'array' => {'items' => [1,2,3,4,5]}}
|
||||||
|
markup = '{% for i in array.items %}{% if i == 9999 %}{% continue %}{% endif %}{{ i }}{% endfor %}'
|
||||||
|
expected = '12345'
|
||||||
|
assert_template_result(expected, markup, assigns)
|
||||||
|
end
|
||||||
|
|
||||||
def test_for_tag_string
|
def test_for_tag_string
|
||||||
# ruby 1.8.7 "String".each => Enumerator with single "String" element.
|
# ruby 1.8.7 "String".each => Enumerator with single "String" element.
|
||||||
|
|||||||
@@ -11,67 +11,71 @@ class VariableTest < Test::Unit::TestCase
|
|||||||
def test_filters
|
def test_filters
|
||||||
var = Variable.new('hello | textileze')
|
var = Variable.new('hello | textileze')
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:textileze,[]]], var.filters
|
assert_equal [["textileze",[]]], var.filters
|
||||||
|
|
||||||
var = Variable.new('hello | textileze | paragraph')
|
var = Variable.new('hello | textileze | paragraph')
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
|
assert_equal [["textileze",[]], ["paragraph",[]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! hello | strftime: '%Y'!)
|
var = Variable.new(%! hello | strftime: '%Y'!)
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:strftime,["'%Y'"]]], var.filters
|
assert_equal [["strftime",["'%Y'"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! 'typo' | link_to: 'Typo', true !)
|
var = Variable.new(%! 'typo' | link_to: 'Typo', true !)
|
||||||
assert_equal %!'typo'!, var.name
|
assert_equal %!'typo'!, var.name
|
||||||
assert_equal [[:link_to,["'Typo'", "true"]]], var.filters
|
assert_equal [["link_to",["'Typo'", "true"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! 'typo' | link_to: 'Typo', false !)
|
var = Variable.new(%! 'typo' | link_to: 'Typo', false !)
|
||||||
assert_equal %!'typo'!, var.name
|
assert_equal %!'typo'!, var.name
|
||||||
assert_equal [[:link_to,["'Typo'", "false"]]], var.filters
|
assert_equal [["link_to",["'Typo'", "false"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! 'foo' | repeat: 3 !)
|
var = Variable.new(%! 'foo' | repeat: 3 !)
|
||||||
assert_equal %!'foo'!, var.name
|
assert_equal %!'foo'!, var.name
|
||||||
assert_equal [[:repeat,["3"]]], var.filters
|
assert_equal [["repeat",["3"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! 'foo' | repeat: 3, 3 !)
|
var = Variable.new(%! 'foo' | repeat: 3, 3 !)
|
||||||
assert_equal %!'foo'!, var.name
|
assert_equal %!'foo'!, var.name
|
||||||
assert_equal [[:repeat,["3","3"]]], var.filters
|
assert_equal [["repeat",["3","3"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! 'foo' | repeat: 3, 3, 3 !)
|
var = Variable.new(%! 'foo' | repeat: 3, 3, 3 !)
|
||||||
assert_equal %!'foo'!, var.name
|
assert_equal %!'foo'!, var.name
|
||||||
assert_equal [[:repeat,["3","3","3"]]], var.filters
|
assert_equal [["repeat",["3","3","3"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! hello | strftime: '%Y, okay?'!)
|
var = Variable.new(%! hello | strftime: '%Y, okay?'!)
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:strftime,["'%Y, okay?'"]]], var.filters
|
assert_equal [["strftime",["'%Y, okay?'"]]], var.filters
|
||||||
|
|
||||||
var = Variable.new(%! hello | things: "%Y, okay?", 'the other one'!)
|
var = Variable.new(%! hello | things: "%Y, okay?", 'the other one'!)
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:things,["\"%Y, okay?\"","'the other one'"]]], var.filters
|
assert_equal [["things",["\"%Y, okay?\"","'the other one'"]]], var.filters
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_filter_with_date_parameter
|
def test_filter_with_date_parameter
|
||||||
|
|
||||||
var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!)
|
var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!)
|
||||||
assert_equal "'2006-06-06'", var.name
|
assert_equal "'2006-06-06'", var.name
|
||||||
assert_equal [[:date,["\"%m/%d/%Y\""]]], var.filters
|
assert_equal [["date",["\"%m/%d/%Y\""]]], var.filters
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_filters_without_whitespace
|
def test_filters_without_whitespace
|
||||||
var = Variable.new('hello | textileze | paragraph')
|
var = Variable.new('hello | textileze | paragraph')
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
|
assert_equal [["textileze",[]], ["paragraph",[]]], var.filters
|
||||||
|
|
||||||
var = Variable.new('hello|textileze|paragraph')
|
var = Variable.new('hello|textileze|paragraph')
|
||||||
assert_equal 'hello', var.name
|
assert_equal 'hello', var.name
|
||||||
assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
|
assert_equal [["textileze",[]], ["paragraph",[]]], var.filters
|
||||||
|
|
||||||
|
var = Variable.new("hello|replace:'foo','bar'|textileze")
|
||||||
|
assert_equal 'hello', var.name
|
||||||
|
assert_equal [["replace", ["'foo'", "'bar'"]], ["textileze", []]], var.filters
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_symbol
|
def test_symbol
|
||||||
var = Variable.new("http://disney.com/logo.gif | image: 'med' ")
|
var = Variable.new("http://disney.com/logo.gif | image: 'med' ")
|
||||||
assert_equal 'http://disney.com/logo.gif', var.name
|
assert_equal 'http://disney.com/logo.gif', var.name
|
||||||
assert_equal [[:image,["'med'"]]], var.filters
|
assert_equal [["image",["'med'"]]], var.filters
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_string_single_quoted
|
def test_string_single_quoted
|
||||||
@@ -103,6 +107,12 @@ class VariableTest < Test::Unit::TestCase
|
|||||||
var = Variable.new(%| test.test |)
|
var = Variable.new(%| test.test |)
|
||||||
assert_equal 'test.test', var.name
|
assert_equal 'test.test', var.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_filter_with_keyword_arguments
|
||||||
|
var = Variable.new(%! hello | things: greeting: "world", farewell: 'goodbye'!)
|
||||||
|
assert_equal 'hello', var.name
|
||||||
|
assert_equal [['things',["greeting: \"world\"","farewell: 'goodbye'"]]], var.filters
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user