All tests pass on Ruby 1.9.1

Signed-off-by: Tobias Lütke <[email protected]>
This commit is contained in:
Jakub Kuźma
2009-04-17 06:33:28 +08:00
committed by Tobias Lütke
parent 8d27864845
commit ed1b542abf
8 changed files with 130 additions and 130 deletions
+23 -23
View File
@@ -1,19 +1,19 @@
module Liquid module Liquid
class Block < Tag class Block < Tag
def parse(tokens) def parse(tokens)
@nodelist ||= [] @nodelist ||= []
@nodelist.clear @nodelist.clear
while token = tokens.shift while token = tokens.shift
case token case token
when /^#{TagStart}/ when /^#{TagStart}/
if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/ if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
# if we found the proper block delimitor just end parsing here and let the outer block # if we found the proper block delimitor just end parsing here and let the outer block
# proceed # proceed
if block_delimiter == $1 if block_delimiter == $1
end_tag end_tag
return return
@@ -23,10 +23,10 @@ module Liquid
if tag = Template.tags[$1] if tag = Template.tags[$1]
@nodelist << tag.new($1, $2, tokens) @nodelist << tag.new($1, $2, tokens)
else else
# this tag is not registered with the system # this tag is not registered with the system
# pass it to the current block for special handling or error reporting # pass it to the current block for special handling or error reporting
unknown_tag($1, $2, tokens) unknown_tag($1, $2, tokens)
end end
else else
raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} " raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
end end
@@ -37,19 +37,19 @@ module Liquid
else else
@nodelist << token @nodelist << token
end end
end end
# Make sure that its ok to end parsing in the current block. # Make sure that its ok to end parsing in the current block.
# Effectively this method will throw and exception unless the current block is # Effectively this method will throw and exception unless the current block is
# of type Document # of type Document
assert_missing_delimitation! assert_missing_delimitation!
end end
def end_tag def end_tag
end end
def unknown_tag(tag, params, tokens) def unknown_tag(tag, params, tokens)
case tag case tag
when 'else' when 'else'
raise SyntaxError, "#{block_name} tag does not expect else tag" raise SyntaxError, "#{block_name} tag does not expect else tag"
when 'end' when 'end'
@@ -61,7 +61,7 @@ module Liquid
def block_delimiter def block_delimiter
"end#{block_name}" "end#{block_name}"
end end
def block_name def block_name
@tag_name @tag_name
@@ -77,7 +77,7 @@ module Liquid
def render(context) def render(context)
render_all(@nodelist, context) render_all(@nodelist, context)
end end
protected protected
def assert_missing_delimitation! def assert_missing_delimitation!
@@ -86,12 +86,12 @@ module Liquid
def render_all(list, context) def render_all(list, context)
list.collect do |token| list.collect do |token|
begin begin
token.respond_to?(:render) ? token.render(context) : token token.respond_to?(:render) ? token.render(context) : token
rescue Exception => e rescue Exception => e
context.handle_error(e) context.handle_error(e)
end end
end end
end end
end end
end end
+7 -7
View File
@@ -1,17 +1,17 @@
module Liquid module Liquid
class Document < Block class Document < Block
# we don't need markup to open this block # we don't need markup to open this block
def initialize(tokens) def initialize(tokens)
parse(tokens) parse(tokens)
end end
# There isn't a real delimter # There isn't a real delimter
def block_delimiter def block_delimiter
[] []
end end
# Document blocks don't need to be terminated since they are not actually opened # Document blocks don't need to be terminated since they are not actually opened
def assert_missing_delimitation! def assert_missing_delimitation!
end end
end end
end end
+27 -27
View File
@@ -1,7 +1,7 @@
module Liquid module Liquid
class TableRow < Block class TableRow < Block
Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/ Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens)
if markup =~ Syntax if markup =~ Syntax
@variable_name = $1 @variable_name = $1
@@ -13,62 +13,62 @@ module Liquid
else else
raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3") raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3")
end end
super super
end end
def render(context) def render(context)
collection = context[@collection_name] or return '' collection = context[@collection_name] or return ''
if @attributes['limit'] or @attributes['offset'] if @attributes['limit'] or @attributes['offset']
limit = context[@attributes['limit']] || -1 limit = context[@attributes['limit']] || -1
offset = context[@attributes['offset']] || 0 offset = context[@attributes['offset']] || 0
collection = collection[offset.to_i..(limit.to_i + offset.to_i - 1)] collection = collection[offset.to_i..(limit.to_i + offset.to_i - 1)]
end end
length = collection.length length = collection.length
cols = context[@attributes['cols']].to_i cols = context[@attributes['cols']].to_i
row = 1 row = 1
col = 0 col = 0
result = ["<tr class=\"row1\">\n"] result = ["<tr class=\"row1\">\n"]
context.stack do context.stack do
collection.each_with_index do |item, index| collection.each_with_index do |item, index|
context[@variable_name] = item context[@variable_name] = item
context['tablerowloop'] = { context['tablerowloop'] = {
'length' => length, 'length' => length,
'index' => index + 1, 'index' => index + 1,
'index0' => index, 'index0' => index,
'col' => col + 1, 'col' => col + 1,
'col0' => col, 'col0' => col,
'index0' => index, 'index0' => index,
'rindex' => length - index, 'rindex' => length - index,
'rindex0' => length - index -1, 'rindex0' => length - index -1,
'first' => (index == 0), 'first' => (index == 0),
'last' => (index == length - 1), 'last' => (index == length - 1),
'col_first' => (col == 0), 'col_first' => (col == 0),
'col_last' => (col == cols - 1) 'col_last' => (col == cols - 1)
} }
col += 1 col += 1
result << ["<td class=\"col#{col}\">"] + render_all(@nodelist, context) + ['</td>'] result << ["<td class=\"col#{col}\">"] + render_all(@nodelist, context) + ['</td>']
if col == cols and not (index == length - 1) if col == cols and not (index == length - 1)
col = 0 col = 0
row += 1 row += 1
result << ["</tr>\n<tr class=\"row#{row}\">"] result << ["</tr>\n<tr class=\"row#{row}\">"]
end end
end end
end end
result + ["</tr>\n"] result + ["</tr>\n"]
end end
end end
Template.register_tag('tablerow', TableRow) Template.register_tag('tablerow', TableRow)
end end
+4 -4
View File
@@ -18,7 +18,7 @@
# end # end
# end # end
# #
# if you want to extend the drop to other methods you can defines more methods # if you want to extend the drop to other methods you can defines more methods
# in the class <YourClass>::LiquidDropClass # in the class <YourClass>::LiquidDropClass
# #
# class SomeClass::LiquidDropClass # class SomeClass::LiquidDropClass
@@ -37,11 +37,11 @@
# output: # output:
# 'this comes from an allowed method and this from another allowed method' # 'this comes from an allowed method and this from another allowed method'
# #
# You can also chain associations, by adding the liquid_method call in the # You can also chain associations, by adding the liquid_method call in the
# association models. # association models.
# #
class Module class Module
def liquid_methods(*allowed_methods) def liquid_methods(*allowed_methods)
drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end" drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end"
define_method :to_liquid do define_method :to_liquid do
@@ -58,5 +58,5 @@ class Module
end end
end end
end end
end end
+1 -1
View File
@@ -14,7 +14,7 @@ module Liquid
# One of the strainer's responsibilities is to keep malicious method calls out # One of the strainer's responsibilities is to keep malicious method calls out
class Strainer < parent_object #:nodoc: class Strainer < parent_object #:nodoc:
INTERNAL_METHOD = /^__/ INTERNAL_METHOD = /^__/
@@required_methods = Set.new([:__send__, :respond_to?, :extend, :methods, :class, :object_id]) @@required_methods = Set.new([:__id__, :__send__, :respond_to?, :extend, :methods, :class, :object_id])
@@filters = {} @@filters = {}
+8 -8
View File
@@ -1,26 +1,26 @@
module Liquid module Liquid
class Tag class Tag
attr_accessor :nodelist attr_accessor :nodelist
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens)
@tag_name = tag_name @tag_name = tag_name
@markup = markup @markup = markup
parse(tokens) parse(tokens)
end end
def parse(tokens) def parse(tokens)
end end
def name def name
self.class.name.downcase self.class.name.downcase
end end
def render(context) def render(context)
'' ''
end end
end end
end end
+9 -9
View File
@@ -1,5 +1,5 @@
module Liquid module Liquid
# Capture stores the result of a block into a variable without rendering it inplace. # Capture stores the result of a block into a variable without rendering it inplace.
# #
# {% capture heading %} # {% capture heading %}
@@ -8,28 +8,28 @@ module Liquid
# ... # ...
# <h1>{{ monkeys }}</h1> # <h1>{{ monkeys }}</h1>
# #
# Capture is useful for saving content for use later in your template, such as # Capture is useful for saving content for use later in your template, such as
# in a sidebar or footer. # in a sidebar or footer.
# #
class Capture < Block class Capture < Block
Syntax = /(\w+)/ Syntax = /(\w+)/
def initialize(tag_name, markup, tokens) def initialize(tag_name, markup, tokens)
if markup =~ Syntax if markup =~ Syntax
@to = $1 @to = $1
else else
raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]") raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]")
end end
super super
end end
def render(context) def render(context)
output = super output = super
context[@to] = output.to_s context[@to] = output.join
'' ''
end end
end end
Template.register_tag('capture', Capture) Template.register_tag('capture', Capture)
end end
+51 -51
View File
@@ -1,120 +1,120 @@
module Liquid module Liquid
# Templates are central to liquid. # Templates are central to liquid.
# Interpretating templates is a two step process. First you compile the # Interpretating templates is a two step process. First you compile the
# source code you got. During compile time some extensive error checking is performed. # source code you got. During compile time some extensive error checking is performed.
# your code should expect to get some SyntaxErrors. # your code should expect to get some SyntaxErrors.
# #
# After you have a compiled template you can then <tt>render</tt> it. # After you have a compiled template you can then <tt>render</tt> it.
# You can use a compiled template over and over again and keep it cached. # You can use a compiled template over and over again and keep it cached.
#
# Example:
# #
# Example:
#
# template = Liquid::Template.parse(source) # template = Liquid::Template.parse(source)
# template.render('user_name' => 'bob') # template.render('user_name' => 'bob')
# #
class Template class Template
attr_accessor :root attr_accessor :root
@@file_system = BlankFileSystem.new @@file_system = BlankFileSystem.new
class <<self class << self
def file_system def file_system
@@file_system @@file_system
end end
def file_system=(obj) def file_system=(obj)
@@file_system = obj @@file_system = obj
end end
def register_tag(name, klass) def register_tag(name, klass)
tags[name.to_s] = klass tags[name.to_s] = klass
end end
def tags def tags
@tags ||= {} @tags ||= {}
end end
# Pass a module with filter methods which should be available # Pass a module with filter methods which should be available
# to all liquid views. Good for registering the standard library # to all liquid views. Good for registering the standard library
def register_filter(mod) def register_filter(mod)
Strainer.global_filter(mod) Strainer.global_filter(mod)
end end
# creates a new <tt>Template</tt> object from liquid source code # creates a new <tt>Template</tt> object from liquid source code
def parse(source) def parse(source)
template = Template.new template = Template.new
template.parse(source) template.parse(source)
template template
end end
end end
# creates a new <tt>Template</tt> from an array of tokens. Use <tt>Template.parse</tt> instead # creates a new <tt>Template</tt> from an array of tokens. Use <tt>Template.parse</tt> instead
def initialize def initialize
end end
# Parse source code. # Parse source code.
# Returns self for easy chaining # Returns self for easy chaining
def parse(source) def parse(source)
@root = Document.new(tokenize(source)) @root = Document.new(tokenize(source))
self self
end end
def registers def registers
@registers ||= {} @registers ||= {}
end end
def assigns def assigns
@assigns ||= {} @assigns ||= {}
end end
def errors def errors
@errors ||= [] @errors ||= []
end end
# Render takes a hash with local variables. # Render takes a hash with local variables.
# #
# if you use the same filters over and over again consider registering them globally # if you use the same filters over and over again consider registering them globally
# with <tt>Template.register_filter</tt> # with <tt>Template.register_filter</tt>
# #
# Following options can be passed: # Following options can be passed:
# #
# * <tt>filters</tt> : array with local filters # * <tt>filters</tt> : array with local filters
# * <tt>registers</tt> : hash with register variables. Those can be accessed from # * <tt>registers</tt> : hash with register variables. Those can be accessed from
# filters and tags and might be useful to integrate liquid more with its host application # filters and tags and might be useful to integrate liquid more with its host application
# #
def render(*args) def render(*args)
return '' if @root.nil? return '' if @root.nil?
context = case args.first context = case args.first
when Liquid::Context when Liquid::Context
args.shift args.shift
when Hash when Hash
self.assigns.merge!(args.shift) self.assigns.merge!(args.shift)
Context.new(assigns, registers, @rethrow_errors) Context.new(assigns, registers, @rethrow_errors)
when nil when nil
Context.new(assigns, registers, @rethrow_errors) Context.new(assigns, registers, @rethrow_errors)
else else
raise ArgumentError, "Expect Hash or Liquid::Context as parameter" raise ArgumentError, "Expect Hash or Liquid::Context as parameter"
end end
case args.last case args.last
when Hash when Hash
options = args.pop options = args.pop
if options[:registers].is_a?(Hash) if options[:registers].is_a?(Hash)
self.registers.merge!(options[:registers]) self.registers.merge!(options[:registers])
end end
if options[:filters] if options[:filters]
context.add_filters(options[:filters]) context.add_filters(options[:filters])
end end
when Module when Module
context.add_filters(args.pop) context.add_filters(args.pop)
when Array when Array
context.add_filters(args.pop) context.add_filters(args.pop)
end end
begin begin
# render the nodelist. # render the nodelist.
# for performance reasons we get a array back here. join will make a string out of it # for performance reasons we get a array back here. join will make a string out of it
@@ -123,24 +123,24 @@ module Liquid
@errors = context.errors @errors = context.errors
end end
end end
def render!(*args) def render!(*args)
@rethrow_errors = true; render(*args) @rethrow_errors = true; render(*args)
end end
private private
# Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize the passed source # Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize the passed source
def tokenize(source) def tokenize(source)
source = source.source if source.respond_to?(:source) source = source.source if source.respond_to?(:source)
return [] if source.to_s.empty? return [] if source.to_s.empty?
tokens = source.split(TemplateParser) tokens = source.split(TemplateParser)
# removes the rogue empty element at the beginning of the array # removes the rogue empty element at the beginning of the array
tokens.shift if tokens[0] and tokens[0].empty? tokens.shift if tokens[0] and tokens[0].empty?
tokens tokens
end end
end end
end end