liquid-2.6.1/0000755000004100000410000000000012271557765013060 5ustar www-datawww-dataliquid-2.6.1/MIT-LICENSE0000644000004100000410000000204712271557765014517 0ustar www-datawww-dataCopyright (c) 2005, 2006 Tobias Luetke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. liquid-2.6.1/lib/0000755000004100000410000000000012271557765013626 5ustar www-datawww-dataliquid-2.6.1/lib/extras/0000755000004100000410000000000012271557765015134 5ustar www-datawww-dataliquid-2.6.1/lib/extras/liquid_view.rb0000644000004100000410000000405712271557765020010 0ustar www-datawww-data# 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 liquid-2.6.1/lib/liquid/0000755000004100000410000000000012271557765015115 5ustar www-datawww-dataliquid-2.6.1/lib/liquid/condition.rb0000644000004100000410000000546012271557765017435 0ustar www-datawww-datamodule Liquid # Container for liquid nodes which conveniently wraps decision making logic # # Example: # # c = Condition.new('1', '==', '1') # c.evaluate #=> true # class Condition #:nodoc: @@operators = { '==' => lambda { |cond, left, right| cond.send(:equal_variables, left, right) }, '!=' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) }, '<>' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) }, '<' => :<, '>' => :>, '>=' => :>=, '<=' => :<=, 'contains' => lambda { |cond, left, right| left && right ? left.include?(right) : false } } def self.operators @@operators end attr_reader :attachment attr_accessor :left, :operator, :right def initialize(left = nil, operator = nil, right = nil) @left, @operator, @right = left, operator, right @child_relation = nil @child_condition = nil end def evaluate(context = Context.new) result = interpret_condition(left, right, operator, context) case @child_relation when :or result || @child_condition.evaluate(context) when :and result && @child_condition.evaluate(context) else result end end def or(condition) @child_relation, @child_condition = :or, condition end def and(condition) @child_relation, @child_condition = :and, condition end def attach(attachment) @attachment = attachment end def else? false end def inspect "#" end private def equal_variables(left, right) if left.is_a?(Symbol) if right.respond_to?(left) return right.send(left.to_s) else return nil end end if right.is_a?(Symbol) if left.respond_to?(right) return left.send(right.to_s) else return nil end end left == right end def interpret_condition(left, right, op, context) # If the operator is empty this means that the decision statement is just # a single variable. We can just poll this variable from the context and # return this as the result. return context[left] if op == nil left, right = context[left], context[right] 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) left.send(operation, right) else nil end end end class ElseCondition < Condition def else? true end def evaluate(context) true end end end liquid-2.6.1/lib/liquid/tags/0000755000004100000410000000000012271557765016053 5ustar www-datawww-dataliquid-2.6.1/lib/liquid/tags/case.rb0000644000004100000410000000352412271557765017317 0ustar www-datawww-datamodule Liquid class Case < Block Syntax = /(#{QuotedFragment})/o WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/o def initialize(tag_name, markup, tokens) @blocks = [] if markup =~ Syntax @left = $1 else raise SyntaxError.new("Syntax Error in tag 'case' - Valid syntax: case [condition]") end super end def unknown_tag(tag, markup, tokens) @nodelist = [] case tag when 'when' record_when_condition(markup) when 'else' record_else_condition(markup) else super end end def render(context) context.stack do execute_else_block = true output = '' @blocks.each do |block| if block.else? return render_all(block.attachment, context) if execute_else_block elsif block.evaluate(context) execute_else_block = false output << render_all(block.attachment, context) end end output end end private def record_when_condition(markup) while markup # Create a new nodelist and assign it to the new block if not markup =~ WhenSyntax raise SyntaxError.new("Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %} ") end markup = $2 block = Condition.new(@left, '==', $1) block.attach(@nodelist) @blocks.push(block) end end def record_else_condition(markup) if not markup.strip.empty? raise SyntaxError.new("Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) ") end block = ElseCondition.new block.attach(@nodelist) @blocks << block end end Template.register_tag('case', Case) end liquid-2.6.1/lib/liquid/tags/comment.rb0000644000004100000410000000020612271557765020040 0ustar www-datawww-datamodule Liquid class Comment < Block def render(context) '' end end Template.register_tag('comment', Comment) end liquid-2.6.1/lib/liquid/tags/capture.rb0000644000004100000410000000153312271557765020045 0ustar www-datawww-datamodule Liquid # Capture stores the result of a block into a variable without rendering it inplace. # # {% capture heading %} # Monkeys! # {% endcapture %} # ... #

{{ heading }}

# # Capture is useful for saving content for use later in your template, such as # in a sidebar or footer. # class Capture < Block Syntax = /(\w+)/ def initialize(tag_name, markup, tokens) if markup =~ Syntax @to = $1 else raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]") end super end def render(context) output = super context.scopes.last[@to] = output context.resource_limits[:assign_score_current] += (output.respond_to?(:length) ? output.length : 1) '' end end Template.register_tag('capture', Capture) end liquid-2.6.1/lib/liquid/tags/continue.rb0000644000004100000410000000056312271557765020230 0ustar www-datawww-datamodule 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 liquid-2.6.1/lib/liquid/tags/ifchanged.rb0000644000004100000410000000056212271557765020313 0ustar www-datawww-datamodule Liquid class Ifchanged < Block def render(context) context.stack do output = render_all(@nodelist, context) if output != context.registers[:ifchanged] context.registers[:ifchanged] = output output else '' end end end end Template.register_tag('ifchanged', Ifchanged) end liquid-2.6.1/lib/liquid/tags/include.rb0000644000004100000410000000502712271557765020027 0ustar www-datawww-datamodule Liquid # Include allows templates to relate with other templates # # Simply include another template: # # {% include 'product' %} # # Include a template with a local variable: # # {% include 'product' with products[0] %} # # Include a template for a collection: # # {% include 'product' for products %} # class Include < Tag Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o def initialize(tag_name, markup, tokens) if markup =~ Syntax @template_name = $1 @variable_name = $3 @attributes = {} markup.scan(TagAttributes) do |key, value| @attributes[key] = value end else raise SyntaxError.new("Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]") end super end def parse(tokens) end def render(context) partial = load_cached_partial(context) variable = context[@variable_name || @template_name[1..-2]] context.stack do @attributes.each do |key, value| context[key] = context[value] end if variable.is_a?(Array) variable.collect do |var| context[@template_name[1..-2]] = var partial.render(context) end else context[@template_name[1..-2]] = variable partial.render(context) end end end private def load_cached_partial(context) cached_partials = context.registers[:cached_partials] || {} template_name = context[@template_name] if cached = cached_partials[template_name] return cached end source = read_template_from_file_system(context) partial = Liquid::Template.parse(source) cached_partials[template_name] = partial context.registers[:cached_partials] = cached_partials partial end def read_template_from_file_system(context) file_system = context.registers[:file_system] || Liquid::Template.file_system # make read_template_file call backwards-compatible. case file_system.method(:read_template_file).arity when 1 file_system.read_template_file(context[@template_name]) when 2 file_system.read_template_file(context[@template_name], context) else raise ArgumentError, "file_system.read_template_file expects two parameters: (template_name, context)" end end end Template.register_tag('include', Include) end liquid-2.6.1/lib/liquid/tags/cycle.rb0000644000004100000410000000312312271557765017476 0ustar www-datawww-datamodule Liquid # Cycle is usually used within a loop to alternate between values, like colors or DOM classes. # # {% for item in items %} #
{{ item }}
# {% end %} # #
Item one
#
Item two
#
Item three
#
Item four
#
Item five
# class Cycle < Tag SimpleSyntax = /^#{QuotedFragment}+/o NamedSyntax = /^(#{QuotedFragment})\s*\:\s*(.*)/o def initialize(tag_name, markup, tokens) case markup when NamedSyntax @variables = variables_from_string($2) @name = $1 when SimpleSyntax @variables = variables_from_string(markup) @name = "'#{@variables.to_s}'" else raise SyntaxError.new("Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]") end super end def render(context) context.registers[:cycle] ||= Hash.new(0) context.stack do key = context[@name] iteration = context.registers[:cycle][key] result = context[@variables[iteration]] iteration += 1 iteration = 0 if iteration >= @variables.size context.registers[:cycle][key] = iteration result end end private def variables_from_string(markup) markup.split(',').collect do |var| var =~ /\s*(#{QuotedFragment})\s*/o $1 ? $1 : nil end.compact end end Template.register_tag('cycle', Cycle) end liquid-2.6.1/lib/liquid/tags/assign.rb0000644000004100000410000000141612271557765017666 0ustar www-datawww-datamodule Liquid # Assign sets a variable in your template. # # {% assign foo = 'monkey' %} # # You can then use the variable later in the page. # # {{ foo }} # class Assign < Tag Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/o def initialize(tag_name, markup, tokens) if markup =~ Syntax @to = $1 @from = Variable.new($2) else raise SyntaxError.new("Syntax Error in 'assign' - Valid syntax: assign [var] = [source]") end super end def render(context) val = @from.render(context) context.scopes.last[@to] = val context.resource_limits[:assign_score_current] += (val.respond_to?(:length) ? val.length : 1) '' end end Template.register_tag('assign', Assign) end liquid-2.6.1/lib/liquid/tags/decrement.rb0000644000004100000410000000165312271557765020353 0ustar www-datawww-datamodule Liquid # decrement is used in a place where one needs to insert a counter # into a template, and needs the counter to survive across # multiple instantiations of the template. # NOTE: decrement is a pre-decrement, --i, # while increment is post: i++. # # (To achieve the survival, the application must keep the context) # # if the variable does not exist, it is created with value 0. # Hello: {% decrement variable %} # # gives you: # # Hello: -1 # Hello: -2 # Hello: -3 # class Decrement < Tag def initialize(tag_name, markup, tokens) @variable = markup.strip super end def render(context) value = context.environments.first[@variable] ||= 0 value = value - 1 context.environments.first[@variable] = value value.to_s end private end Template.register_tag('decrement', Decrement) end liquid-2.6.1/lib/liquid/tags/for.rb0000644000004100000410000001041012271557765017162 0ustar www-datawww-datamodule Liquid # "For" iterates over an array or collection. # Several useful variables are available to you within the loop. # # == Basic usage: # {% for item in collection %} # {{ forloop.index }}: {{ item.name }} # {% endfor %} # # == Advanced usage: # {% for item in collection %} #
# Item {{ forloop.index }}: {{ item.name }} #
# {% else %} # There is nothing in the collection. # {% endfor %} # # You can also define a limit and offset much like SQL. Remember # that offset starts at 0 for the first item. # # {% for item in collection limit:5 offset:10 %} # {{ item.name }} # {% end %} # # To reverse the for loop simply use {% for item in collection reversed %} # # == Available variables: # # forloop.name:: 'item-collection' # forloop.length:: Length of the loop # forloop.index:: The current item's position in the collection; # forloop.index starts at 1. # This is helpful for non-programmers who start believe # the first item in an array is 1, not 0. # forloop.index0:: The current item's position in the collection # where the first item is 0 # forloop.rindex:: Number of items remaining in the loop # (length - index) where 1 is the last item. # forloop.rindex0:: Number of items remaining in the loop # where 0 is the last item. # forloop.first:: Returns true if the item is the first item. # forloop.last:: Returns true if the item is the last item. # class For < Block Syntax = /\A(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o def initialize(tag_name, markup, tokens) if markup =~ Syntax @variable_name = $1 @collection_name = $2 @name = "#{$1}-#{$2}" @reversed = $3 @attributes = {} markup.scan(TagAttributes) do |key, value| @attributes[key] = value end else raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]") end @nodelist = @for_block = [] super end def unknown_tag(tag, markup, tokens) return super unless tag == 'else' @nodelist = @else_block = [] end def render(context) context.registers[:for] ||= Hash.new(0) collection = context[@collection_name] collection = collection.to_a if collection.is_a?(Range) # Maintains Ruby 1.8.7 String#each behaviour on 1.9 return render_else(context) unless iterable?(collection) from = if @attributes['offset'] == 'continue' context.registers[:for][@name].to_i else context[@attributes['offset']].to_i end limit = context[@attributes['limit']] to = limit ? limit.to_i + from : nil segment = Utils.slice_collection_using_each(collection, from, to) return render_else(context) if segment.empty? segment.reverse! if @reversed result = '' length = segment.length # Store our progress through the collection for the continue flag context.registers[:for][@name] = from + segment.length context.stack do segment.each_with_index do |item, index| context[@variable_name] = item context['forloop'] = { 'name' => @name, 'length' => length, 'index' => index + 1, 'index0' => index, 'rindex' => length - index, 'rindex0' => length - index - 1, 'first' => (index == 0), 'last' => (index == length - 1) } 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 result end private def render_else(context) return @else_block ? [render_all(@else_block, context)] : '' end def iterable?(collection) collection.respond_to?(:each) || Utils.non_blank_string?(collection) end end Template.register_tag('for', For) end liquid-2.6.1/lib/liquid/tags/raw.rb0000644000004100000410000000077312271557765017200 0ustar www-datawww-datamodule Liquid class Raw < Block FullTokenPossiblyInvalid = /^(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/o def parse(tokens) @nodelist ||= [] @nodelist.clear while token = tokens.shift if token =~ FullTokenPossiblyInvalid @nodelist << $1 if $1 != "" if block_delimiter == $2 end_tag return end end @nodelist << token if not token.empty? end end end Template.register_tag('raw', Raw) end liquid-2.6.1/lib/liquid/tags/unless.rb0000644000004100000410000000142212271557765017710 0ustar www-datawww-datarequire File.dirname(__FILE__) + '/if' module Liquid # Unless is a conditional just like 'if' but works on the inverse logic. # # {% unless x < 0 %} x is greater than zero {% end %} # class Unless < If def render(context) context.stack do # First condition is interpreted backwards ( if not ) first_block = @blocks.first unless first_block.evaluate(context) return render_all(first_block.attachment, context) end # After the first condition unless works just like if @blocks[1..-1].each do |block| if block.evaluate(context) return render_all(block.attachment, context) end end '' end end end Template.register_tag('unless', Unless) end liquid-2.6.1/lib/liquid/tags/increment.rb0000644000004100000410000000145612271557765020372 0ustar www-datawww-datamodule Liquid # increment is used in a place where one needs to insert a counter # into a template, and needs the counter to survive across # multiple instantiations of the template. # (To achieve the survival, the application must keep the context) # # if the variable does not exist, it is created with value 0. # Hello: {% increment variable %} # # gives you: # # Hello: 0 # Hello: 1 # Hello: 2 # class Increment < Tag def initialize(tag_name, markup, tokens) @variable = markup.strip super end def render(context) value = context.environments.first[@variable] ||= 0 context.environments.first[@variable] = value + 1 value.to_s end private end Template.register_tag('increment', Increment) end liquid-2.6.1/lib/liquid/tags/if.rb0000644000004100000410000000374012271557765017002 0ustar www-datawww-datamodule Liquid # If is the conditional block # # {% if user.admin %} # Admin user! # {% else %} # Not admin user # {% endif %} # # There are {% if count < 5 %} less {% else %} more {% endif %} items than you need. # # class If < Block SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]" Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o BOOLEAN_OPERATORS = %w(and or) def initialize(tag_name, markup, tokens) @blocks = [] push_block('if', markup) super end def unknown_tag(tag, markup, tokens) if ['elsif', 'else'].include?(tag) push_block(tag, markup) else super end end def render(context) context.stack do @blocks.each do |block| if block.evaluate(context) return render_all(block.attachment, context) end end '' end end private def push_block(tag, markup) block = if tag == 'else' ElseCondition.new else expressions = markup.scan(ExpressionsAndOperators).reverse raise(SyntaxError, SyntaxHelp) unless expressions.shift =~ Syntax condition = Condition.new($1, $2, $3) while not expressions.empty? operator = (expressions.shift).to_s.strip raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax new_condition = Condition.new($1, $2, $3) raise SyntaxError, "invalid boolean operator" unless BOOLEAN_OPERATORS.include?(operator) new_condition.send(operator, condition) condition = new_condition end condition end @blocks.push(block) @nodelist = block.attach(Array.new) end end Template.register_tag('if', If) end liquid-2.6.1/lib/liquid/tags/break.rb0000644000004100000410000000054112271557765017464 0ustar www-datawww-datamodule 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 liquid-2.6.1/lib/liquid/utils.rb0000644000004100000410000000107112271557765016601 0ustar www-datawww-datamodule Liquid module Utils def self.slice_collection_using_each(collection, from, to) segments = [] index = 0 # Maintains Ruby 1.8.7 String#each behaviour on 1.9 return [collection] if non_blank_string?(collection) collection.each do |item| if to && to <= index break end if from <= index segments << item end index += 1 end segments end def self.non_blank_string?(collection) collection.is_a?(String) && collection != '' end end end liquid-2.6.1/lib/liquid/errors.rb0000644000004100000410000000052012271557765016753 0ustar www-datawww-datamodule Liquid class Error < ::StandardError; end class ArgumentError < Error; end class ContextError < Error; end class FilterNotFound < Error; end class FileSystemError < Error; end class StandardError < Error; end class SyntaxError < Error; end class StackLevelError < Error; end class MemoryError < Error; end end liquid-2.6.1/lib/liquid/context.rb0000644000004100000410000001770712271557765017142 0ustar www-datawww-datamodule Liquid # Context keeps the variable stack and resolves variables, as well as keywords # # context['variable'] = 'testing' # context['variable'] #=> 'testing' # context['true'] #=> true # context['10.2232'] #=> 10.2232 # # context.stack do # context['bob'] = 'bobsen' # end # # context['bob'] #=> nil class Context class Context attr_reader :scopes, :errors, :registers, :environments, :resource_limits 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 @interrupts = [] end def resource_limits_reached? (@resource_limits[:render_length_limit] && @resource_limits[:render_length_current] > @resource_limits[:render_length_limit]) || (@resource_limits[:render_score_limit] && @resource_limits[:render_score_current] > @resource_limits[:render_score_limit] ) || (@resource_limits[:assign_score_limit] && @resource_limits[:assign_score_current] > @resource_limits[:assign_score_limit] ) end def strainer @strainer ||= Strainer.create(self) end # Adds filters to this context. # # Note that this does not register the filters with the main Template object. see Template.register_filter # for that def add_filters(filters) filters = [filters].flatten.compact filters.each do |f| raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module) Strainer.add_known_filter(f) strainer.extend(f) end end # are there any not handled interrupts? def has_interrupt? @interrupts.any? 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) errors.push(e) raise if @rethrow_errors case e when SyntaxError "Liquid syntax error: #{e.message}" else "Liquid error: #{e.message}" end end def invoke(method, *args) strainer.invoke(method, *args) end # Push new local scope on the stack. use Context#stack instead def push(new_scope={}) @scopes.unshift(new_scope) raise StackLevelError, "Nesting too deep" if @scopes.length > 100 end # Merge a hash of variables in the current local scope def merge(new_scopes) @scopes[0].merge!(new_scopes) end # Pop from the stack. use Context#stack instead def pop raise ContextError if @scopes.size == 1 @scopes.shift end # Pushes a new local scope on the stack, pops it at the end of the block # # Example: # context.stack do # context['var'] = 'hi' # end # # context['var] #=> nil def stack(new_scope={}) push(new_scope) yield ensure pop end def clear_instance_assigns @scopes[0] = {} end # Only allow String, Numeric, Hash, Array, Proc, Boolean or Liquid::Drop def []=(key, value) @scopes[0][key] = value end def [](key) resolve(key) end def has_key?(key) resolve(key) != nil end private LITERALS = { nil => nil, 'nil' => nil, 'null' => nil, '' => nil, 'true' => true, 'false' => false, 'blank' => :blank?, 'empty' => :empty? } # Look up variable, either resolve directly after considering the name. We can directly handle # Strings, digits, floats and booleans (true,false). # If no match is made we lookup the variable in the current scope and # later move up to the parent blocks to see if we can resolve the variable somewhere up the tree. # Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions # # Example: # products == empty #=> products.empty? def resolve(key) if LITERALS.key?(key) LITERALS[key] else case key when /^'(.*)'$/ # Single quoted strings $1 when /^"(.*)"$/ # Double quoted strings $1 when /^(-?\d+)$/ # Integer and floats $1.to_i when /^\((\S+)\.\.(\S+)\)$/ # Ranges (resolve($1).to_i..resolve($2).to_i) when /^(-?\d[\d\.]+)$/ # Floats $1.to_f else variable(key) end end end # Fetches an object starting at the local scope and then moving up the hierachy def find_variable(key) scope = @scopes.find { |s| s.has_key?(key) } variable = nil if scope.nil? @environments.each do |e| if variable = lookup_and_evaluate(e, key) scope = e break end end end scope ||= @environments.last || @scopes.last variable ||= lookup_and_evaluate(scope, key) variable = variable.to_liquid variable.context = self if variable.respond_to?(:context=) return variable end # Resolves namespaced queries gracefully. # # Example # @context['hash'] = {"name" => 'tobi'} # assert_equal 'tobi', @context['hash.name'] # assert_equal 'tobi', @context['hash["name"]'] def variable(markup) parts = markup.scan(VariableParser) square_bracketed = /^\[(.*)\]$/ first_part = parts.shift if first_part =~ square_bracketed first_part = resolve($1) end if object = find_variable(first_part) parts.each do |part| part = resolve($1) if part_resolved = (part =~ square_bracketed) # If object is a hash- or array-like object we look for the # presence of the key and if its available we return it if object.respond_to?(:[]) and ((object.respond_to?(:has_key?) and object.has_key?(part)) or (object.respond_to?(:fetch) and part.is_a?(Integer))) # if its a proc we will replace the entry with the proc res = lookup_and_evaluate(object, part) object = res.to_liquid # Some special cases. If the part wasn't in square brackets and # no key with the same name was found we interpret following calls # as commands and call them on the current object elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part) object = object.send(part.intern).to_liquid # No key was present with the desired value and it wasn't one of the directly supported # keywords either. The only thing we got left is to return nil else return nil end # If we are dealing with a drop here we have to object.context = self if object.respond_to?(:context=) end end object end # variable def lookup_and_evaluate(obj, key) if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=) obj[key] = (value.arity == 0) ? value.call : value.call(self) else value end end # lookup_and_evaluate def squash_instance_assigns_with_environments @scopes.last.each_key do |k| @environments.each do |env| if env.has_key?(k) scopes.last[k] = lookup_and_evaluate(env, k) break end end end end # squash_instance_assigns_with_environments end # Context end # Liquid liquid-2.6.1/lib/liquid/interrupts.rb0000644000004100000410000000067712271557765017673 0ustar www-datawww-datamodule 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 liquid-2.6.1/lib/liquid/document.rb0000644000004100000410000000054612271557765017265 0ustar www-datawww-datamodule Liquid class Document < Block # we don't need markup to open this block def initialize(tokens) parse(tokens) end # There isn't a real delimiter def block_delimiter [] end # Document blocks don't need to be terminated since they are not actually opened def assert_missing_delimitation! end end end liquid-2.6.1/lib/liquid/module_ex.rb0000644000004100000410000000316712271557765017432 0ustar www-datawww-data# Copyright 2007 by Domizio Demichelis # This library is free software. It may be used, redistributed and/or modified # under the same terms as Ruby itself # # This extension is used in order to expose the object of the implementing class # to liquid as it were a Drop. It also limits the liquid-callable methods of the instance # to the allowed method passed with the liquid_methods call # Example: # # class SomeClass # liquid_methods :an_allowed_method # # def an_allowed_method # 'this comes from an allowed method' # end # def unallowed_method # 'this will never be an output' # end # end # # if you want to extend the drop to other methods you can defines more methods # in the class ::LiquidDropClass # # class SomeClass::LiquidDropClass # def another_allowed_method # 'and this from another allowed method' # end # end # end # # usage: # @something = SomeClass.new # # template: # {{something.an_allowed_method}}{{something.unallowed_method}} {{something.another_allowed_method}} # # output: # '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 # association models. # class Module def liquid_methods(*allowed_methods) drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end" define_method :to_liquid do drop_class.new(self) end drop_class.class_eval do def initialize(object) @object = object end allowed_methods.each do |sym| define_method sym do @object.send sym end end end end end liquid-2.6.1/lib/liquid/standardfilters.rb0000644000004100000410000001572612271557765020646 0ustar www-datawww-datarequire 'cgi' require 'bigdecimal' module Liquid module StandardFilters # Return the size of an array or of an string def size(input) input.respond_to?(:size) ? input.size : 0 end # convert an input string to DOWNCASE def downcase(input) input.to_s.downcase end # convert an input string to UPCASE def upcase(input) input.to_s.upcase end # capitalize words in the input centence def capitalize(input) input.to_s.capitalize end def escape(input) CGI.escapeHTML(input) rescue input end def escape_once(input) ActionView::Helpers::TagHelper.escape_once(input) rescue NameError input end alias_method :h, :escape # Truncate a string down to x characters def truncate(input, length = 50, truncate_string = "...") if input.nil? then return end l = length.to_i - truncate_string.length l = 0 if l < 0 truncated = RUBY_VERSION[0,3] == "1.8" ? input.scan(/./mu)[0...l].to_s : input[0...l] input.length > length.to_i ? truncated + truncate_string : input end def truncatewords(input, words = 15, truncate_string = "...") if input.nil? then return end wordlist = input.to_s.split l = words.to_i - 1 l = 0 if l < 0 wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input end # Split input string into an array of substrings separated by given pattern. # # Example: #
{{ post | split '//' | first }}
# def split(input, pattern) input.split(pattern) end def strip_html(input) input.to_s.gsub(//m, '').gsub(//m, '').gsub(//m, '').gsub(/<.*?>/m, '') end # Remove all newlines from the string def strip_newlines(input) input.to_s.gsub(/\r?\n/, '') end # Join elements of the array with certain character between them def join(input, 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 = [input].flatten if property.nil? ary.sort elsif ary.first.respond_to?('[]') 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) } end end # Reverse the elements of an array def reverse(input) ary = [input].flatten ary.reverse end # map/collect on a given property def map(input, property) ary = [input].flatten ary.map do |e| e = e.call if e.is_a?(Proc) e = e.to_liquid if e.respond_to?(:to_liquid) if property == "to_liquid" e elsif e.respond_to?(:[]) e[property] end end end # Replace occurrences of a string with another def replace(input, string, replacement = '') input.to_s.gsub(string, replacement.to_s) end # Replace the first occurrences of a string with another def replace_first(input, string, replacement = '') input.to_s.sub(string, replacement.to_s) end # remove a substring def remove(input, string) input.to_s.gsub(string, '') end # remove the first occurrences of a substring def remove_first(input, string) input.to_s.sub(string, '') end # add one string to another def append(input, string) input.to_s + string.to_s end # prepend a string to another def prepend(input, string) string.to_s + input.to_s end # Add
tags in front of all newlines in input string def newline_to_br(input) input.to_s.gsub(/\n/, "
\n") end # Reformat a date # # %a - The abbreviated weekday name (``Sun'') # %A - The full weekday name (``Sunday'') # %b - The abbreviated month name (``Jan'') # %B - The full month name (``January'') # %c - The preferred local date and time representation # %d - Day of the month (01..31) # %H - Hour of the day, 24-hour clock (00..23) # %I - Hour of the day, 12-hour clock (01..12) # %j - Day of the year (001..366) # %m - Month of the year (01..12) # %M - Minute of the hour (00..59) # %p - Meridian indicator (``AM'' or ``PM'') # %S - Second of the minute (00..60) # %U - Week number of the current year, # starting with the first Sunday as the first # day of the first week (00..53) # %W - Week number of the current year, # starting with the first Monday as the first # day of the first week (00..53) # %w - Day of the week (Sunday is 0, 0..6) # %x - Preferred representation for the date alone, no time # %X - Preferred representation for the time alone, no date # %y - Year without a century (00..99) # %Y - Year with century # %Z - Time zone name # %% - Literal ``%'' character def date(input, format) if format.to_s.empty? return input.to_s end if ((input.is_a?(String) && !/^\d+$/.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', 'today' 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 end # Get the first element of the passed in array # # Example: # {{ product.images | first | to_img }} # def first(array) array.first if array.respond_to?(:first) end # Get the last element of the passed in array # # Example: # {{ product.images | last | to_img }} # def last(array) array.last if array.respond_to?(:last) end # addition def plus(input, operand) apply_operation(input, operand, :+) end # subtraction def minus(input, operand) apply_operation(input, operand, :-) end # multiplication def times(input, operand) apply_operation(input, operand, :*) end # division def divided_by(input, operand) apply_operation(input, operand, :/) end def modulo(input, operand) apply_operation(input, operand, :%) end private def to_number(obj) case obj when Float BigDecimal.new(obj.to_s) when Numeric obj when String (obj.strip =~ /^\d+\.\d+$/) ? BigDecimal.new(obj) : obj.to_i else 0 end end def apply_operation(input, operand, operation) result = to_number(input).send(operation, to_number(operand)) result.is_a?(BigDecimal) ? result.to_f : result end end Template.register_filter(StandardFilters) end liquid-2.6.1/lib/liquid/version.rb0000644000004100000410000000007012271557765017124 0ustar www-datawww-data# encoding: utf-8 module Liquid VERSION = "2.6.1" end liquid-2.6.1/lib/liquid/block.rb0000644000004100000410000000720612271557765016541 0ustar www-datawww-datamodule Liquid class Block < Tag IsTag = /^#{TagStart}/o IsVariable = /^#{VariableStart}/o FullToken = /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/o ContentOfVariable = /^#{VariableStart}(.*)#{VariableEnd}$/o def parse(tokens) @nodelist ||= [] @nodelist.clear while token = tokens.shift 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] @nodelist << tag.new($1, $2, tokens) 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, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} " end when IsVariable @nodelist << create_variable(token) when '' # pass else @nodelist << token end end # Make sure that it's ok to end parsing in the current block. # Effectively this method will throw an exception unless the current block is # of type Document assert_missing_delimitation! end def end_tag end def unknown_tag(tag, params, tokens) case tag when 'else' raise SyntaxError, "#{block_name} tag does not expect else tag" when 'end' raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}" else raise SyntaxError, "Unknown tag '#{tag}'" end end def block_delimiter "end#{block_name}" end def block_name @tag_name end def create_variable(token) token.scan(ContentOfVariable) do |content| return Variable.new(content.first) end raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ") end def render(context) render_all(@nodelist, context) end protected def assert_missing_delimitation! raise SyntaxError.new("#{block_name} tag was never closed") end def render_all(list, context) output = [] context.resource_limits[:render_length_current] = 0 context.resource_limits[:render_score_current] += list.length list.each do |token| # Break out if we have any unhanded interrupts. break if context.has_interrupt? begin # 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 token_output = (token.respond_to?(:render) ? token.render(context) : token) context.resource_limits[:render_length_current] += (token_output.respond_to?(:length) ? token_output.length : 1) if context.resource_limits_reached? context.resource_limits[:reached] = true raise MemoryError.new("Memory limits exceeded") end output << token_output rescue MemoryError => e raise e rescue ::StandardError => e output << (context.handle_error(e)) end end output.join end end end liquid-2.6.1/lib/liquid/tag.rb0000644000004100000410000000052312271557765016215 0ustar www-datawww-datamodule Liquid class Tag attr_accessor :nodelist def initialize(tag_name, markup, tokens) @tag_name = tag_name @markup = markup parse(tokens) end def parse(tokens) end def name self.class.name.downcase end def render(context) '' end end # Tag end # Liquid liquid-2.6.1/lib/liquid/extensions.rb0000644000004100000410000000117712271557765017647 0ustar www-datawww-datarequire 'time' require 'date' class String # :nodoc: def to_liquid self end end class Array # :nodoc: def to_liquid self end end class Hash # :nodoc: def to_liquid self end end class Numeric # :nodoc: def to_liquid self end end class Time # :nodoc: def to_liquid self end end class DateTime < Date # :nodoc: def to_liquid self end end class Date # :nodoc: def to_liquid self end end class TrueClass def to_liquid # :nodoc: self end end class FalseClass def to_liquid # :nodoc: self end end class NilClass def to_liquid # :nodoc: self end end liquid-2.6.1/lib/liquid/file_system.rb0000644000004100000410000000450212271557765017766 0ustar www-datawww-datamodule Liquid # A Liquid file system is a way to let your templates retrieve other templates for use with the include tag. # # You can implement subclasses that retrieve templates from the database, from the file system using a different # path structure, you can provide them as hard-coded inline strings, or any manner that you see fit. # # You can add additional instance variables, arguments, or methods as needed. # # Example: # # Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_path) # liquid = Liquid::Template.parse(template) # # This will parse the template with a LocalFileSystem implementation rooted at 'template_path'. class BlankFileSystem # Called by Liquid to retrieve a template file def read_template_file(template_path, context) raise FileSystemError, "This liquid context does not allow includes." end end # This implements an abstract file system which retrieves template files named in a manner similar to Rails partials, # ie. with the template name prefixed with an underscore. The extension ".liquid" is also added. # # For security reasons, template paths are only allowed to contain letters, numbers, and underscore. # # Example: # # file_system = Liquid::LocalFileSystem.new("/some/path") # # file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid" # file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid" # class LocalFileSystem attr_accessor :root def initialize(root) @root = root end def read_template_file(template_path, context) full_path = full_path(template_path) raise FileSystemError, "No such template '#{template_path}'" unless File.exists?(full_path) File.read(full_path) end def full_path(template_path) raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /^[^.\/][a-zA-Z0-9_\/]+$/ full_path = if template_path.include?('/') File.join(root, File.dirname(template_path), "_#{File.basename(template_path)}.liquid") else File.join(root, "_#{template_path}.liquid") end raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/ full_path end end end liquid-2.6.1/lib/liquid/variable.rb0000644000004100000410000000335212271557765017232 0ustar www-datawww-datamodule Liquid # Holds variables. Variables are only loaded "just in time" # and are not evaluated as part of the render stage # # {{ monkey }} # {{ user.name }} # # Variables can be combined with filters: # # {{ user | link }} # class Variable FilterParser = /(?:#{FilterSeparator}|(?:\s*(?:#{QuotedFragment}|#{ArgumentSeparator})\s*)+)/o attr_accessor :filters, :name def initialize(markup) @markup = markup @name = nil @filters = [] if match = markup.match(/\s*(#{QuotedFragment})(.*)/o) @name = match[1] if match[2].match(/#{FilterSeparator}\s*(.*)/o) filters = Regexp.last_match(1).scan(FilterParser) filters.each do |f| 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 end end end end def render(context) return '' if @name.nil? @filters.inject(context[@name]) do |output, filter| filterargs = [] 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 filterargs << keyword_args unless keyword_args.empty? begin output = context.invoke(filter[0], output, *filterargs) rescue FilterNotFound raise FilterNotFound, "Error - filter '#{filter[0]}' in '#{@markup.strip}' could not be found." end end end end end liquid-2.6.1/lib/liquid/strainer.rb0000644000004100000410000000275712271557765017304 0ustar www-datawww-datarequire 'set' module Liquid # Strainer is the parent class for the filters system. # New filters are mixed into the strainer class which is then instantiated for each liquid template render run. # # The Strainer only allows method calls defined in filters given to it via Strainer.global_filter, # Context#add_filters or Template.register_filter class Strainer #:nodoc: @@filters = [] @@known_filters = Set.new @@known_methods = Set.new def initialize(context) @context = context end def self.global_filter(filter) raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module) add_known_filter(filter) @@filters << filter unless @@filters.include?(filter) 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) strainer = Strainer.new(context) @@filters.each { |m| strainer.extend(m) } strainer end def invoke(method, *args) if invokable?(method) send(method, *args) else args.first end end def invokable?(method) @@known_methods.include?(method.to_s) && respond_to?(method) end end end liquid-2.6.1/lib/liquid/drop.rb0000644000004100000410000000333012271557765016405 0ustar www-datawww-datarequire 'set' module Liquid # A drop in liquid is a class which allows you to export DOM like things to liquid. # Methods of drops are callable. # The main use for liquid drops is to implement lazy loaded objects. # If you would like to make data available to the web designers which you don't want loaded unless needed then # a drop is a great way to do that. # # Example: # # class ProductDrop < Liquid::Drop # def top_sales # Shop.current.products.find(:all, :order => 'sales', :limit => 10 ) # end # end # # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' ) # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query. # # Your drop can either implement the methods sans any parameters or implement the before_method(name) method which is a # catch all. class Drop attr_writer :context EMPTY_STRING = ''.freeze # Catch all for the method def before_method(method) nil end # called by liquid to invoke a drop def invoke_drop(method_or_key) if method_or_key && method_or_key != EMPTY_STRING && self.class.invokable?(method_or_key) send(method_or_key) else before_method(method_or_key) end end def has_key?(name) true end def to_liquid self end alias :[] :invoke_drop private # Check for method existence without invoking respond_to?, which creates symbols def self.invokable?(method_name) @invokable_methods ||= Set.new(["to_liquid"] + (public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s)) @invokable_methods.include?(method_name.to_s) end end end liquid-2.6.1/lib/liquid/htmltags.rb0000644000004100000410000000372212271557765017271 0ustar www-datawww-datamodule Liquid class TableRow < Block Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o def initialize(tag_name, markup, tokens) if markup =~ Syntax @variable_name = $1 @collection_name = $2 @attributes = {} markup.scan(TagAttributes) do |key, value| @attributes[key] = value end else raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3") end super end def render(context) collection = context[@collection_name] or return '' from = @attributes['offset'] ? context[@attributes['offset']].to_i : 0 to = @attributes['limit'] ? from + context[@attributes['limit']].to_i : nil collection = Utils.slice_collection_using_each(collection, from, to) length = collection.length cols = context[@attributes['cols']].to_i row = 1 col = 0 result = "\n" context.stack do collection.each_with_index do |item, index| context[@variable_name] = item context['tablerowloop'] = { 'length' => length, 'index' => index + 1, 'index0' => index, 'col' => col + 1, 'col0' => col, 'index0' => index, 'rindex' => length - index, 'rindex0' => length - index - 1, 'first' => (index == 0), 'last' => (index == length - 1), 'col_first' => (col == 0), 'col_last' => (col == cols - 1) } col += 1 result << "" << render_all(@nodelist, context) << '' if col == cols and (index != length - 1) col = 0 row += 1 result << "\n" end end end result << "\n" result end end Template.register_tag('tablerow', TableRow) end liquid-2.6.1/lib/liquid/template.rb0000644000004100000410000001013412271557765017254 0ustar www-datawww-datamodule Liquid # Templates are central to liquid. # Interpretating templates is a two step process. First you compile the # source code you got. During compile time some extensive error checking is performed. # your code should expect to get some SyntaxErrors. # # After you have a compiled template you can then render it. # You can use a compiled template over and over again and keep it cached. # # Example: # # template = Liquid::Template.parse(source) # template.render('user_name' => 'bob') # class Template attr_accessor :root, :resource_limits @@file_system = BlankFileSystem.new class << self def file_system @@file_system end def file_system=(obj) @@file_system = obj end def register_tag(name, klass) tags[name.to_s] = klass end def tags @tags ||= {} end # Pass a module with filter methods which should be available # to all liquid views. Good for registering the standard library def register_filter(mod) Strainer.global_filter(mod) end # creates a new Template object from liquid source code def parse(source) template = Template.new template.parse(source) template end end # creates a new Template from an array of tokens. Use Template.parse instead def initialize @resource_limits = {} end # Parse source code. # Returns self for easy chaining def parse(source) @root = Document.new(tokenize(source)) self end def registers @registers ||= {} end def assigns @assigns ||= {} end def instance_assigns @instance_assigns ||= {} end def errors @errors ||= [] end # Render takes a hash with local variables. # # if you use the same filters over and over again consider registering them globally # with Template.register_filter # # Following options can be passed: # # * filters : array with local filters # * registers : hash with register variables. Those can be accessed from # filters and tags and might be useful to integrate liquid more with its host application # def render(*args) return '' if @root.nil? context = case args.first when Liquid::Context args.shift when Liquid::Drop drop = args.shift drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits) when Hash Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits) when nil Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits) else raise ArgumentError, "Expect Hash or Liquid::Context as parameter" end case args.last when Hash options = args.pop if options[:registers].is_a?(Hash) self.registers.merge!(options[:registers]) end if options[:filters] context.add_filters(options[:filters]) end when Module context.add_filters(args.pop) when Array context.add_filters(args.pop) end begin # render the nodelist. # for performance reasons we get an array back here. join will make a string out of it. result = @root.render(context) result.respond_to?(:join) ? result.join : result rescue Liquid::MemoryError => e context.handle_error(e) ensure @errors = context.errors end end def render!(*args) @rethrow_errors = true; render(*args) end private # Uses the Liquid::TemplateParser regexp to tokenize the passed source def tokenize(source) source = source.source if source.respond_to?(:source) return [] if source.to_s.empty? tokens = source.split(TemplateParser) # removes the rogue empty element at the beginning of the array tokens.shift if tokens[0] and tokens[0].empty? tokens end end end liquid-2.6.1/lib/liquid.rb0000644000004100000410000000604112271557765015443 0ustar www-datawww-data# Copyright (c) 2005 Tobias Luetke # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. module Liquid FilterSeparator = /\|/ ArgumentSeparator = ',' FilterArgumentSeparator = ':' VariableAttributeSeparator = '.' TagStart = /\{\%/ TagEnd = /\%\}/ VariableSignature = /\(?[\w\-\.\[\]]\)?/ VariableSegment = /[\w\-]/ VariableStart = /\{\{/ VariableEnd = /\}\}/ VariableIncompleteEnd = /\}\}?/ QuotedString = /"[^"]*"|'[^']*'/ QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s|:,]+/ FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/o OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/o SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/o Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/o TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o AnyStartingTag = /\{\{|\{\%/ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/o TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/o VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o end require "liquid/version" require 'liquid/drop' require 'liquid/extensions' require 'liquid/errors' require 'liquid/interrupts' require 'liquid/strainer' require 'liquid/context' require 'liquid/tag' require 'liquid/block' require 'liquid/document' require 'liquid/variable' require 'liquid/file_system' require 'liquid/template' require 'liquid/htmltags' require 'liquid/standardfilters' require 'liquid/condition' require 'liquid/module_ex' require 'liquid/utils' # Load all the tags of the standard library # Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f } liquid-2.6.1/metadata.yml0000644000004100000410000000762112271557765015371 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: liquid version: !ruby/object:Gem::Version version: 2.6.1 platform: ruby authors: - Tobias Luetke autorequire: bindir: bin cert_chain: [] date: 2014-01-10 00:00:00.000000000 Z dependencies: [] description: email: - tobi@leetsoft.com executables: [] extensions: [] extra_rdoc_files: - History.md - README.md files: - lib/extras/liquid_view.rb - lib/liquid/block.rb - lib/liquid/condition.rb - lib/liquid/context.rb - lib/liquid/document.rb - lib/liquid/drop.rb - lib/liquid/errors.rb - lib/liquid/extensions.rb - lib/liquid/file_system.rb - lib/liquid/htmltags.rb - lib/liquid/interrupts.rb - lib/liquid/module_ex.rb - lib/liquid/standardfilters.rb - lib/liquid/strainer.rb - lib/liquid/tag.rb - lib/liquid/tags/assign.rb - lib/liquid/tags/break.rb - lib/liquid/tags/capture.rb - lib/liquid/tags/case.rb - lib/liquid/tags/comment.rb - lib/liquid/tags/continue.rb - lib/liquid/tags/cycle.rb - lib/liquid/tags/decrement.rb - lib/liquid/tags/for.rb - lib/liquid/tags/if.rb - lib/liquid/tags/ifchanged.rb - lib/liquid/tags/include.rb - lib/liquid/tags/increment.rb - lib/liquid/tags/raw.rb - lib/liquid/tags/unless.rb - lib/liquid/template.rb - lib/liquid/utils.rb - lib/liquid/variable.rb - lib/liquid/version.rb - lib/liquid.rb - MIT-LICENSE - README.md - History.md - test/liquid/assign_test.rb - test/liquid/block_test.rb - test/liquid/capture_test.rb - test/liquid/condition_test.rb - test/liquid/context_test.rb - test/liquid/drop_test.rb - test/liquid/error_handling_test.rb - test/liquid/file_system_test.rb - test/liquid/filter_test.rb - test/liquid/hash_ordering_test.rb - test/liquid/module_ex_test.rb - test/liquid/output_test.rb - test/liquid/parsing_quirks_test.rb - test/liquid/regexp_test.rb - test/liquid/security_test.rb - test/liquid/standard_filter_test.rb - test/liquid/strainer_test.rb - test/liquid/tags/break_tag_test.rb - test/liquid/tags/continue_tag_test.rb - test/liquid/tags/for_tag_test.rb - test/liquid/tags/html_tag_test.rb - test/liquid/tags/if_else_tag_test.rb - test/liquid/tags/include_tag_test.rb - test/liquid/tags/increment_tag_test.rb - test/liquid/tags/raw_tag_test.rb - test/liquid/tags/standard_tag_test.rb - test/liquid/tags/statements_test.rb - test/liquid/tags/unless_else_tag_test.rb - test/liquid/template_test.rb - test/liquid/variable_test.rb - test/test_helper.rb homepage: http://www.liquidmarkup.org licenses: [] metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 1.3.7 requirements: [] rubyforge_project: rubygems_version: 2.0.3 signing_key: specification_version: 4 summary: A secure, non-evaling end user template engine with aesthetic markup. test_files: - test/liquid/assign_test.rb - test/liquid/block_test.rb - test/liquid/capture_test.rb - test/liquid/condition_test.rb - test/liquid/context_test.rb - test/liquid/drop_test.rb - test/liquid/error_handling_test.rb - test/liquid/file_system_test.rb - test/liquid/filter_test.rb - test/liquid/hash_ordering_test.rb - test/liquid/module_ex_test.rb - test/liquid/output_test.rb - test/liquid/parsing_quirks_test.rb - test/liquid/regexp_test.rb - test/liquid/security_test.rb - test/liquid/standard_filter_test.rb - test/liquid/strainer_test.rb - test/liquid/tags/break_tag_test.rb - test/liquid/tags/continue_tag_test.rb - test/liquid/tags/for_tag_test.rb - test/liquid/tags/html_tag_test.rb - test/liquid/tags/if_else_tag_test.rb - test/liquid/tags/include_tag_test.rb - test/liquid/tags/increment_tag_test.rb - test/liquid/tags/raw_tag_test.rb - test/liquid/tags/standard_tag_test.rb - test/liquid/tags/statements_test.rb - test/liquid/tags/unless_else_tag_test.rb - test/liquid/template_test.rb - test/liquid/variable_test.rb - test/test_helper.rb liquid-2.6.1/test/0000755000004100000410000000000012271557765014037 5ustar www-datawww-dataliquid-2.6.1/test/liquid/0000755000004100000410000000000012271557765015326 5ustar www-datawww-dataliquid-2.6.1/test/liquid/variable_test.rb0000644000004100000410000001400012271557765020472 0ustar www-datawww-datarequire 'test_helper' class VariableTest < Test::Unit::TestCase include Liquid def test_variable var = Variable.new('hello') assert_equal 'hello', var.name end def test_filters var = Variable.new('hello | textileze') assert_equal 'hello', var.name assert_equal [["textileze",[]]], var.filters var = Variable.new('hello | textileze | paragraph') assert_equal 'hello', var.name assert_equal [["textileze",[]], ["paragraph",[]]], var.filters var = Variable.new(%! hello | strftime: '%Y'!) assert_equal 'hello', var.name assert_equal [["strftime",["'%Y'"]]], var.filters var = Variable.new(%! 'typo' | link_to: 'Typo', true !) assert_equal %!'typo'!, var.name assert_equal [["link_to",["'Typo'", "true"]]], var.filters var = Variable.new(%! 'typo' | link_to: 'Typo', false !) assert_equal %!'typo'!, var.name assert_equal [["link_to",["'Typo'", "false"]]], var.filters var = Variable.new(%! 'foo' | repeat: 3 !) assert_equal %!'foo'!, var.name assert_equal [["repeat",["3"]]], var.filters var = Variable.new(%! 'foo' | repeat: 3, 3 !) assert_equal %!'foo'!, var.name assert_equal [["repeat",["3","3"]]], var.filters var = Variable.new(%! 'foo' | repeat: 3, 3, 3 !) assert_equal %!'foo'!, var.name assert_equal [["repeat",["3","3","3"]]], var.filters var = Variable.new(%! hello | strftime: '%Y, okay?'!) assert_equal 'hello', var.name assert_equal [["strftime",["'%Y, okay?'"]]], var.filters var = Variable.new(%! hello | things: "%Y, okay?", 'the other one'!) assert_equal 'hello', var.name assert_equal [["things",["\"%Y, okay?\"","'the other one'"]]], var.filters end def test_filter_with_date_parameter var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!) assert_equal "'2006-06-06'", var.name assert_equal [["date",["\"%m/%d/%Y\""]]], var.filters end def test_filters_without_whitespace var = Variable.new('hello | textileze | paragraph') assert_equal 'hello', var.name assert_equal [["textileze",[]], ["paragraph",[]]], var.filters var = Variable.new('hello|textileze|paragraph') assert_equal 'hello', var.name 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 def test_symbol var = Variable.new("http://disney.com/logo.gif | image: 'med' ") assert_equal 'http://disney.com/logo.gif', var.name assert_equal [["image",["'med'"]]], var.filters end def test_string_single_quoted var = Variable.new(%| "hello" |) assert_equal '"hello"', var.name end def test_string_double_quoted var = Variable.new(%| 'hello' |) assert_equal "'hello'", var.name end def test_integer var = Variable.new(%| 1000 |) assert_equal "1000", var.name end def test_float var = Variable.new(%| 1000.01 |) assert_equal "1000.01", var.name end def test_string_with_special_chars var = Variable.new(%| 'hello! $!@.;"ddasd" ' |) assert_equal %|'hello! $!@.;"ddasd" '|, var.name end def test_string_dot var = Variable.new(%| test.test |) assert_equal 'test.test', var.name 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 def test_lax_filter_argument_parsing var = Variable.new(%! number_of_comments | pluralize: 'comment': 'comments' !) assert_equal 'number_of_comments', var.name assert_equal [['pluralize',["'comment'","'comments'"]]], var.filters 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 end # VariableTest liquid-2.6.1/test/liquid/error_handling_test.rb0000644000004100000410000000435012271557765021711 0ustar www-datawww-datarequire 'test_helper' class ErrorDrop < Liquid::Drop def standard_error raise Liquid::StandardError, 'standard error' end def argument_error raise Liquid::ArgumentError, 'argument error' end def syntax_error raise Liquid::SyntaxError, 'syntax error' end def exception raise Exception, 'exception' end end class ErrorHandlingTest < Test::Unit::TestCase include Liquid def test_standard_error assert_nothing_raised do template = Liquid::Template.parse( ' {{ errors.standard_error }} ' ) assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new) assert_equal 1, template.errors.size assert_equal StandardError, template.errors.first.class end end def test_syntax assert_nothing_raised do template = Liquid::Template.parse( ' {{ errors.syntax_error }} ' ) assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new) assert_equal 1, template.errors.size assert_equal SyntaxError, template.errors.first.class end end def test_argument assert_nothing_raised do template = Liquid::Template.parse( ' {{ errors.argument_error }} ' ) assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new) assert_equal 1, template.errors.size assert_equal ArgumentError, template.errors.first.class end end def test_missing_endtag_parse_time_error assert_raise(Liquid::SyntaxError) do template = Liquid::Template.parse(' {% for a in b %} ... ') end end def test_unrecognized_operator assert_nothing_raised do template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ') assert_equal ' Liquid error: Unknown operator =! ', template.render assert_equal 1, template.errors.size assert_equal Liquid::ArgumentError, template.errors.first.class end end # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError def test_exceptions_propagate assert_raise Exception do template = Liquid::Template.parse( ' {{ errors.exception }} ' ) template.render('errors' => ErrorDrop.new) end end end # ErrorHandlingTest liquid-2.6.1/test/liquid/tags/0000755000004100000410000000000012271557765016264 5ustar www-datawww-dataliquid-2.6.1/test/liquid/tags/standard_tag_test.rb0000644000004100000410000003160512271557765022310 0ustar www-datawww-datarequire 'test_helper' class StandardTagTest < Test::Unit::TestCase include Liquid def test_tag tag = Tag.new('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...') assert_template_result('blah','blah') assert_template_result('','') assert_template_result('|,.:','|,.:') assert_template_result('','') text = %|this shouldnt see any transformation either but has multiple lines as you can clearly see here ...| assert_template_result(text,text) end def test_has_a_block_which_does_nothing assert_template_result(%|the comment block should be removed .. right?|, %|the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?|) assert_template_result('','{%comment%}{%endcomment%}') assert_template_result('','{%comment%}{% endcomment %}') assert_template_result('','{% comment %}{%endcomment%}') assert_template_result('','{% comment %}{% endcomment %}') assert_template_result('','{%comment%}comment{%endcomment%}') assert_template_result('','{% comment %}comment{% endcomment %}') assert_template_result('foobar','foo{%comment%}comment{%endcomment%}bar') assert_template_result('foobar','foo{% comment %}comment{% endcomment %}bar') assert_template_result('foobar','foo{%comment%} comment {%endcomment%}bar') assert_template_result('foobar','foo{% comment %} comment {% endcomment %}bar') assert_template_result('foo bar','foo {%comment%} {%endcomment%} bar') assert_template_result('foo bar','foo {%comment%}comment{%endcomment%} bar') assert_template_result('foo bar','foo {%comment%} comment {%endcomment%} bar') assert_template_result('foobar','foo{%comment%} {%endcomment%}bar') end def test_assign assigns = {'var' => 'content' } assert_template_result('var2: var2:content', 'var2:{{var2}} {%assign var2 = var%} var2:{{var2}}', assigns) end def test_hyphenated_assign assigns = {'a-b' => '1' } assert_template_result('a-b:1 a-b:2', 'a-b:{{a-b}} {%assign a-b = 2 %}a-b:{{a-b}}', assigns) end def test_assign_with_colon_and_spaces assigns = {'var' => {'a:b c' => {'paged' => '1' }}} assert_template_result('var2: 1', '{%assign var2 = var["a:b c"].paged %}var2: {{var2}}', assigns) end def test_capture assigns = {'var' => 'content' } assert_template_result('content foo content foo ', '{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}', assigns) end def test_capture_detects_bad_syntax assert_raise(SyntaxError) do assert_template_result('content foo content foo ', '{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}', {'var' => 'content' }) end end def test_case assigns = {'condition' => 2 } assert_template_result(' its 2 ', '{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns) assigns = {'condition' => 1 } assert_template_result(' its 1 ', '{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns) assigns = {'condition' => 3 } assert_template_result('', '{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns) assigns = {'condition' => "string here" } assert_template_result(' hit ', '{% case condition %}{% when "string here" %} hit {% endcase %}', assigns) assigns = {'condition' => "bad string here" } assert_template_result('', '{% case condition %}{% when "string here" %} hit {% endcase %}',\ assigns) end def test_case_with_else assigns = {'condition' => 5 } assert_template_result(' hit ', '{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}', assigns) assigns = {'condition' => 6 } assert_template_result(' else ', '{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}', assigns) assigns = {'condition' => 6 } assert_template_result(' else ', '{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}', assigns) end def test_case_on_size assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => []) assert_template_result('1', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1]) assert_template_result('2', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1]) assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1]) assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1]) assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1, 1]) end def test_case_on_size_with_else assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => []) assert_template_result('1', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1]) assert_template_result('2', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1]) assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1]) assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1, 1]) assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1, 1, 1]) end def test_case_on_length_with_else assert_template_result('else', '{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {}) assert_template_result('false', '{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {}) assert_template_result('true', '{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {}) assert_template_result('else', '{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {}) end def test_assign_from_case # Example from the shopify forums code = %q({% case collection.handle %}{% when 'menswear-jackets' %}{% assign ptitle = 'menswear' %}{% when 'menswear-t-shirts' %}{% assign ptitle = 'menswear' %}{% else %}{% assign ptitle = 'womenswear' %}{% endcase %}{{ ptitle }}) template = Liquid::Template.parse(code) assert_equal "menswear", template.render("collection" => {'handle' => 'menswear-jackets'}) assert_equal "menswear", template.render("collection" => {'handle' => 'menswear-t-shirts'}) assert_equal "womenswear", template.render("collection" => {'handle' => 'x'}) assert_equal "womenswear", template.render("collection" => {'handle' => 'y'}) assert_equal "womenswear", template.render("collection" => {'handle' => 'z'}) end def test_case_when_or code = '{% case condition %}{% when 1 or 2 or 3 %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}' assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 2 }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 3 }) assert_template_result(' its 4 ', code, {'condition' => 4 }) assert_template_result('', code, {'condition' => 5 }) code = '{% case condition %}{% when 1 or "string" or null %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}' assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 'string' }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => nil }) assert_template_result('', code, {'condition' => 'something else' }) end def test_case_when_comma code = '{% case condition %}{% when 1, 2, 3 %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}' assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 2 }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 3 }) assert_template_result(' its 4 ', code, {'condition' => 4 }) assert_template_result('', code, {'condition' => 5 }) code = '{% case condition %}{% when 1, "string", null %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}' assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 'string' }) assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => nil }) assert_template_result('', code, {'condition' => 'something else' }) end def test_assign assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render end def test_assign_an_empty_string assert_equal '', Liquid::Template.parse( '{% assign a = ""%}{{a}}' ).render end def test_assign_is_global assert_equal 'variable', Liquid::Template.parse( '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' ).render end def test_case_detects_bad_syntax assert_raise(SyntaxError) do assert_template_result('', '{% case false %}{% when %}true{% endcase %}', {}) end assert_raise(SyntaxError) do assert_template_result('', '{% case false %}{% huh %}true{% endcase %}', {}) end end def test_cycle assert_template_result('one','{%cycle "one", "two"%}') assert_template_result('one two','{%cycle "one", "two"%} {%cycle "one", "two"%}') assert_template_result(' two','{%cycle "", "two"%} {%cycle "", "two"%}') assert_template_result('one two one','{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}') assert_template_result('text-align: left text-align: right', '{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}') end def test_multiple_cycles assert_template_result('1 2 1 1 2 3 1', '{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}') end def test_multiple_named_cycles assert_template_result('one one two two one one', '{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}') end def test_multiple_named_cycles_with_names_from_context assigns = {"var1" => 1, "var2" => 2 } assert_template_result('one one two two one one', '{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}', assigns) end def test_size_of_array assigns = {"array" => [1,2,3,4]} assert_template_result('array has 4 elements', "array has {{ array.size }} elements", assigns) end def test_size_of_hash assigns = {"hash" => {:a => 1, :b => 2, :c=> 3, :d => 4}} assert_template_result('hash has 4 elements', "hash has {{ hash.size }} elements", assigns) end def test_illegal_symbols assert_template_result('', '{% if true == empty %}?{% endif %}', {}) assert_template_result('', '{% if true == null %}?{% endif %}', {}) assert_template_result('', '{% if empty == true %}?{% endif %}', {}) assert_template_result('', '{% if null == true %}?{% endif %}', {}) end def test_ifchanged assigns = {'array' => [ 1, 1, 2, 2, 3, 3] } assert_template_result('123','{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}',assigns) assigns = {'array' => [ 1, 1, 1, 1] } assert_template_result('1','{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}',assigns) end end # StandardTagTest liquid-2.6.1/test/liquid/tags/raw_tag_test.rb0000644000004100000410000000211112271557765021267 0ustar www-datawww-datarequire 'test_helper' class RawTagTest < Test::Unit::TestCase include Liquid def test_tag_in_raw assert_template_result '{% comment %} test {% endcomment %}', '{% raw %}{% comment %} test {% endcomment %}{% endraw %}' end def test_output_in_raw assert_template_result '{{ test }}', '{% raw %}{{ test }}{% endraw %}' end def test_open_tag_in_raw assert_template_result ' Foobar {% invalid ', '{% raw %} Foobar {% invalid {% endraw %}' assert_template_result ' Foobar invalid %} ', '{% raw %} Foobar invalid %} {% endraw %}' assert_template_result ' Foobar {{ invalid ', '{% raw %} Foobar {{ invalid {% endraw %}' assert_template_result ' Foobar invalid }} ', '{% raw %} Foobar invalid }} {% endraw %}' assert_template_result ' Foobar {% invalid {% {% endraw ', '{% raw %} Foobar {% invalid {% {% endraw {% endraw %}' assert_template_result ' Foobar {% {% {% ', '{% raw %} Foobar {% {% {% {% endraw %}' assert_template_result ' test {% raw %} {% endraw %}', '{% raw %} test {% raw %} {% {% endraw %}endraw %}' end end liquid-2.6.1/test/liquid/tags/statements_test.rb0000644000004100000410000001052312271557765022040 0ustar www-datawww-datarequire 'test_helper' class StatementsTest < Test::Unit::TestCase include Liquid def test_true_eql_true text = %| {% if true == true %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render end def test_true_not_eql_true text = %| {% if true != true %} true {% else %} false {% endif %} | expected = %| false | assert_equal expected, Template.parse(text).render end def test_true_lq_true text = %| {% if 0 > 0 %} true {% else %} false {% endif %} | expected = %| false | assert_equal expected, Template.parse(text).render end def test_one_lq_zero text = %| {% if 1 > 0 %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render end def test_zero_lq_one text = %| {% if 0 < 1 %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render end def test_zero_lq_or_equal_one text = %| {% if 0 <= 0 %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render end def test_zero_lq_or_equal_one_involving_nil text = %| {% if null <= 0 %} true {% else %} false {% endif %} | expected = %| false | assert_equal expected, Template.parse(text).render text = %| {% if 0 <= null %} true {% else %} false {% endif %} | expected = %| false | assert_equal expected, Template.parse(text).render end def test_zero_lqq_or_equal_one text = %| {% if 0 >= 0 %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render end def test_strings text = %| {% if 'test' == 'test' %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render end def test_strings_not_equal text = %| {% if 'test' != 'test' %} true {% else %} false {% endif %} | expected = %| false | assert_equal expected, Template.parse(text).render end def test_var_strings_equal text = %| {% if var == "hello there!" %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => 'hello there!') end def test_var_strings_are_not_equal text = %| {% if "hello there!" == var %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => 'hello there!') end def test_var_and_long_string_are_equal text = %| {% if var == 'hello there!' %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => 'hello there!') end def test_var_and_long_string_are_equal_backwards text = %| {% if 'hello there!' == var %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => 'hello there!') end #def test_is_nil # text = %| {% if var != nil %} true {% else %} false {% end %} | # @template.assigns = { 'var' => 'hello there!'} # expected = %| true | # assert_equal expected, @template.parse(text) #end def test_is_collection_empty text = %| {% if array == empty %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('array' => []) end def test_is_not_collection_empty text = %| {% if array == empty %} true {% else %} false {% endif %} | expected = %| false | assert_equal expected, Template.parse(text).render('array' => [1,2,3]) end def test_nil text = %| {% if var == nil %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => nil) text = %| {% if var == null %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => nil) end def test_not_nil text = %| {% if var != nil %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => 1 ) text = %| {% if var != null %} true {% else %} false {% endif %} | expected = %| true | assert_equal expected, Template.parse(text).render('var' => 1 ) end end # StatementsTest liquid-2.6.1/test/liquid/tags/unless_else_tag_test.rb0000644000004100000410000000233712271557765023031 0ustar www-datawww-datarequire 'test_helper' class UnlessElseTagTest < Test::Unit::TestCase include Liquid def test_unless assert_template_result(' ',' {% unless true %} this text should not go into the output {% endunless %} ') assert_template_result(' this text should go into the output ', ' {% unless false %} this text should go into the output {% endunless %} ') assert_template_result(' you rock ?','{% unless true %} you suck {% endunless %} {% unless false %} you rock {% endunless %}?') end def test_unless_else assert_template_result(' YES ','{% unless true %} NO {% else %} YES {% endunless %}') assert_template_result(' YES ','{% unless false %} YES {% else %} NO {% endunless %}') assert_template_result(' YES ','{% unless "foo" %} NO {% else %} YES {% endunless %}') end def test_unless_in_loop assert_template_result '23', '{% for i in choices %}{% unless i %}{{ forloop.index }}{% endunless %}{% endfor %}', 'choices' => [1, nil, false] end def test_unless_else_in_loop assert_template_result ' TRUE 2 3 ', '{% for i in choices %}{% unless i %} {{ forloop.index }} {% else %} TRUE {% endunless %}{% endfor %}', 'choices' => [1, nil, false] end end # UnlessElseTest liquid-2.6.1/test/liquid/tags/include_tag_test.rb0000644000004100000410000001252312271557765022131 0ustar www-datawww-datarequire 'test_helper' class TestFileSystem def read_template_file(template_path, context) case template_path when "product" "Product: {{ product.title }} " when "locale_variables" "Locale: {{echo1}} {{echo2}}" when "variant" "Variant: {{ variant.title }}" when "nested_template" "{% include 'header' %} {% include 'body' %} {% include 'footer' %}" when "body" "body {% include 'body_detail' %}" when "nested_product_template" "Product: {{ nested_product_template.title }} {%include 'details'%} " when "recursively_nested_template" "-{% include 'recursively_nested_template' %}" when "pick_a_source" "from TestFileSystem" else template_path end end end class OtherFileSystem def read_template_file(template_path, context) 'from OtherFileSystem' end end class CountingFileSystem attr_reader :count def read_template_file(template_path, context) @count ||= 0 @count += 1 'from CountingFileSystem' end end class IncludeTagTest < Test::Unit::TestCase include Liquid def setup Liquid::Template.file_system = TestFileSystem.new end def test_include_tag_looks_for_file_system_in_registers_first assert_equal 'from OtherFileSystem', Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => OtherFileSystem.new}) end def test_include_tag_with assert_equal "Product: Draft 151cm ", Template.parse("{% include 'product' with products[0] %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] ) end def test_include_tag_with_default_name assert_equal "Product: Draft 151cm ", Template.parse("{% include 'product' %}").render( "product" => {'title' => 'Draft 151cm'} ) end def test_include_tag_for assert_equal "Product: Draft 151cm Product: Element 155cm ", Template.parse("{% include 'product' for products %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] ) end def test_include_tag_with_local_variables assert_equal "Locale: test123 ", Template.parse("{% include 'locale_variables' echo1: 'test123' %}").render end def test_include_tag_with_multiple_local_variables assert_equal "Locale: test123 test321", Template.parse("{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}").render end def test_include_tag_with_multiple_local_variables_from_context assert_equal "Locale: test123 test321", Template.parse("{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}").render('echo1' => 'test123', 'more_echos' => { "echo2" => 'test321'}) end def test_nested_include_tag assert_equal "body body_detail", Template.parse("{% include 'body' %}").render assert_equal "header body body_detail footer", Template.parse("{% include 'nested_template' %}").render end def test_nested_include_with_variable assert_equal "Product: Draft 151cm details ", Template.parse("{% include 'nested_product_template' with product %}").render("product" => {"title" => 'Draft 151cm'}) assert_equal "Product: Draft 151cm details Product: Element 155cm details ", Template.parse("{% include 'nested_product_template' for products %}").render("products" => [{"title" => 'Draft 151cm'}, {"title" => 'Element 155cm'}]) end def test_recursively_included_template_does_not_produce_endless_loop infinite_file_system = Class.new do def read_template_file(template_path, context) "-{% include 'loop' %}" end end Liquid::Template.file_system = infinite_file_system.new assert_raise(Liquid::StackLevelError) do Template.parse("{% include 'loop' %}").render! end end def test_backwards_compatability_support_for_overridden_read_template_file infinite_file_system = Class.new do def read_template_file(template_path) # testing only one argument here. "- hi mom" end end Liquid::Template.file_system = infinite_file_system.new Template.parse("{% include 'hi_mom' %}").render! end def test_dynamically_choosen_template assert_equal "Test123", Template.parse("{% include template %}").render("template" => 'Test123') assert_equal "Test321", Template.parse("{% include template %}").render("template" => 'Test321') assert_equal "Product: Draft 151cm ", Template.parse("{% include template for product %}").render("template" => 'product', 'product' => { 'title' => 'Draft 151cm'}) end def test_include_tag_caches_second_read_of_same_partial file_system = CountingFileSystem.new assert_equal 'from CountingFileSystemfrom CountingFileSystem', Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render({}, :registers => {:file_system => file_system}) assert_equal 1, file_system.count end def test_include_tag_doesnt_cache_partials_across_renders file_system = CountingFileSystem.new assert_equal 'from CountingFileSystem', Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => file_system}) assert_equal 1, file_system.count assert_equal 'from CountingFileSystem', Template.parse("{% include 'pick_a_source' %}").render({}, :registers => {:file_system => file_system}) assert_equal 2, file_system.count end end # IncludeTagTest liquid-2.6.1/test/liquid/tags/continue_tag_test.rb0000644000004100000410000000052212271557765022326 0ustar www-datawww-datarequire '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 liquid-2.6.1/test/liquid/tags/if_else_tag_test.rb0000644000004100000410000002042112271557765022110 0ustar www-datawww-datarequire 'test_helper' class IfElseTagTest < Test::Unit::TestCase include Liquid def test_if assert_template_result(' ',' {% if false %} this text should not go into the output {% endif %} ') assert_template_result(' this text should go into the output ', ' {% if true %} this text should go into the output {% endif %} ') assert_template_result(' you rock ?','{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?') end def test_if_else assert_template_result(' YES ','{% if false %} NO {% else %} YES {% endif %}') assert_template_result(' YES ','{% if true %} YES {% else %} NO {% endif %}') assert_template_result(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}') end def test_if_boolean assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true) end def test_if_or assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => true) assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => false) assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => false, 'b' => true) assert_template_result('', '{% if a or b %} YES {% endif %}', 'a' => false, 'b' => false) assert_template_result(' YES ','{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => true) assert_template_result('', '{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => false) end def test_if_or_with_operators assert_template_result(' YES ','{% if a == true or b == true %} YES {% endif %}', 'a' => true, 'b' => true) assert_template_result(' YES ','{% if a == true or b == false %} YES {% endif %}', 'a' => true, 'b' => true) assert_template_result('','{% if a == false or b == false %} YES {% endif %}', 'a' => true, 'b' => true) end def test_comparison_of_strings_containing_and_or_or assert_nothing_raised do awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar" assigns = {'a' => 'and', 'b' => 'or', 'c' => 'foo and bar', 'd' => 'bar or baz', 'e' => 'foo', 'foo' => true, 'bar' => true} assert_template_result(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns) end end def test_comparison_of_expressions_starting_with_and_or_or assigns = {'order' => {'items_count' => 0}, 'android' => {'name' => 'Roy'}} assert_nothing_raised do assert_template_result( "YES", "{% if android.name == 'Roy' %}YES{% endif %}", assigns) end assert_nothing_raised do assert_template_result( "YES", "{% if order.items_count == 0 %}YES{% endif %}", assigns) end end def test_if_and assert_template_result(' YES ','{% if true and true %} YES {% endif %}') assert_template_result('','{% if false and true %} YES {% endif %}') assert_template_result('','{% if false and true %} YES {% endif %}') end def test_hash_miss_generates_false assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {}) end def test_if_from_variable assert_template_result('','{% if var %} NO {% endif %}', 'var' => false) assert_template_result('','{% if var %} NO {% endif %}', 'var' => nil) assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {'bar' => false}) assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {}) assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => nil) assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => true) assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => "text") assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true) assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => 1) assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => {}) assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => []) assert_template_result(' YES ','{% if "foo" %} YES {% endif %}') assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => true}) assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => "text"}) assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => 1 }) assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => {} }) assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => [] }) assert_template_result(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var' => false) assert_template_result(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var' => nil) assert_template_result(' YES ','{% if var %} YES {% else %} NO {% endif %}', 'var' => true) assert_template_result(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}', 'var' => "text") assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {'bar' => false}) assert_template_result(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => {'bar' => true}) assert_template_result(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => {'bar' => "text"}) assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {'notbar' => true}) assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {}) assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'notfoo' => {'bar' => true}) end def test_nested_if assert_template_result('', '{% if false %}{% if false %} NO {% endif %}{% endif %}') assert_template_result('', '{% if false %}{% if true %} NO {% endif %}{% endif %}') assert_template_result('', '{% if true %}{% if false %} NO {% endif %}{% endif %}') assert_template_result(' YES ', '{% if true %}{% if true %} YES {% endif %}{% endif %}') assert_template_result(' YES ', '{% if true %}{% if true %} YES {% else %} NO {% endif %}{% else %} NO {% endif %}') assert_template_result(' YES ', '{% if true %}{% if false %} NO {% else %} YES {% endif %}{% else %} NO {% endif %}') assert_template_result(' YES ', '{% if false %}{% if true %} NO {% else %} NONO {% endif %}{% else %} YES {% endif %}') end def test_comparisons_on_null assert_template_result('','{% if null < 10 %} NO {% endif %}') assert_template_result('','{% if null <= 10 %} NO {% endif %}') assert_template_result('','{% if null >= 10 %} NO {% endif %}') assert_template_result('','{% if null > 10 %} NO {% endif %}') assert_template_result('','{% if 10 < null %} NO {% endif %}') assert_template_result('','{% if 10 <= null %} NO {% endif %}') assert_template_result('','{% if 10 >= null %} NO {% endif %}') assert_template_result('','{% if 10 > null %} NO {% endif %}') end def test_else_if assert_template_result('0','{% if 0 == 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}') assert_template_result('1','{% if 0 != 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}') assert_template_result('2','{% if 0 != 0 %}0{% elsif 1 != 1%}1{% else %}2{% endif %}') assert_template_result('elsif','{% if false %}if{% elsif true %}elsif{% endif %}') end def test_syntax_error_no_variable assert_raise(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}')} end def test_syntax_error_no_expression assert_raise(SyntaxError) { assert_template_result('', '{% if %}') } end def test_if_with_custom_condition Condition.operators['contains'] = :[] assert_template_result('yes', %({% if 'bob' contains 'o' %}yes{% endif %})) assert_template_result('no', %({% if 'bob' contains 'f' %}yes{% else %}no{% endif %})) ensure Condition.operators.delete 'contains' end def test_operators_are_ignored_unless_isolated Condition.operators['contains'] = :[] assert_template_result('yes', %({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %})) end 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 liquid-2.6.1/test/liquid/tags/increment_tag_test.rb0000644000004100000410000000166312271557765022475 0ustar www-datawww-datarequire 'test_helper' class IncrementTagTest < Test::Unit::TestCase include Liquid def test_inc assert_template_result('0','{%increment port %}', {}) assert_template_result('0 1','{%increment port %} {%increment port%}', {}) assert_template_result('0 0 1 2 1', '{%increment port %} {%increment starboard%} ' + '{%increment port %} {%increment port%} ' + '{%increment starboard %}', {}) end def test_dec assert_template_result('9','{%decrement port %}', { 'port' => 10}) assert_template_result('-1 -2','{%decrement port %} {%decrement port%}', {}) assert_template_result('1 5 2 2 5', '{%increment port %} {%increment starboard%} ' + '{%increment port %} {%decrement port%} ' + '{%decrement starboard %}', { 'port' => 1, 'starboard' => 5 }) end end liquid-2.6.1/test/liquid/tags/for_tag_test.rb0000644000004100000410000002500412271557765021272 0ustar www-datawww-datarequire 'test_helper' class ForTagTest < Test::Unit::TestCase include Liquid def test_for assert_template_result(' yo yo yo yo ','{%for item in array%} yo {%endfor%}','array' => [1,2,3,4]) assert_template_result('yoyo','{%for item in array%}yo{%endfor%}','array' => [1,2]) assert_template_result(' yo ','{%for item in array%} yo {%endfor%}','array' => [1]) assert_template_result('','{%for item in array%}{%endfor%}','array' => [1,2]) expected = < [1,2,3]) end def test_for_reversed assigns = {'array' => [ 1, 2, 3] } assert_template_result('321','{%for item in array reversed %}{{item}}{%endfor%}',assigns) end def test_for_with_range assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}') 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]) assert_template_result('123','{% for item in array %}{{item}}{% endfor %}','array' => [1,2,3]) assert_template_result('abcd','{%for item in array%}{{item}}{%endfor%}','array' => ['a','b','c','d']) assert_template_result('a b c','{%for item in array%}{{item}}{%endfor%}','array' => ['a',' ','b',' ','c']) assert_template_result('abc','{%for item in array%}{{item}}{%endfor%}','array' => ['a','','b','','c']) end def test_for_helpers assigns = {'array' => [1,2,3] } assert_template_result(' 1/3 2/3 3/3 ', '{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}', assigns) assert_template_result(' 1 2 3 ', '{%for item in array%} {{forloop.index}} {%endfor%}', assigns) assert_template_result(' 0 1 2 ', '{%for item in array%} {{forloop.index0}} {%endfor%}', assigns) assert_template_result(' 2 1 0 ', '{%for item in array%} {{forloop.rindex0}} {%endfor%}', assigns) assert_template_result(' 3 2 1 ', '{%for item in array%} {{forloop.rindex}} {%endfor%}', assigns) assert_template_result(' true false false ', '{%for item in array%} {{forloop.first}} {%endfor%}', assigns) assert_template_result(' false false true ', '{%for item in array%} {{forloop.last}} {%endfor%}', assigns) end def test_for_and_if assigns = {'array' => [1,2,3] } assert_template_result('+--', '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}', assigns) end def test_for_else assert_template_result('+++', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>[1,2,3]) assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>[]) assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>nil) end def test_limiting assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]} assert_template_result('12', '{%for i in array limit:2 %}{{ i }}{%endfor%}', assigns) assert_template_result('1234', '{%for i in array limit:4 %}{{ i }}{%endfor%}', assigns) assert_template_result('3456', '{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}', assigns) assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns) end def test_dynamic_variable_limiting assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]} assigns['limit'] = 2 assigns['offset'] = 2 assert_template_result('34', '{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}', assigns) end def test_nested_for assigns = {'array' => [[1,2],[3,4],[5,6]] } assert_template_result('123456', '{%for item in array%}{%for i in item%}{{ i }}{%endfor%}{%endfor%}', assigns) end def test_offset_only assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]} assert_template_result('890', '{%for i in array offset:7 %}{{ i }}{%endfor%}', assigns) end def test_pause_resume assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}} markup = <<-MKUP {%for i in array.items limit: 3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit: 3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit: 3 %}{{i}}{%endfor%} MKUP expected = <<-XPCTD 123 next 456 next 789 XPCTD assert_template_result(expected,markup,assigns) end def test_pause_resume_limit assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}} markup = <<-MKUP {%for i in array.items limit:3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit:1 %}{{i}}{%endfor%} MKUP expected = <<-XPCTD 123 next 456 next 7 XPCTD assert_template_result(expected,markup,assigns) end def test_pause_resume_BIG_limit assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}} markup = <<-MKUP {%for i in array.items limit:3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit:1000 %}{{i}}{%endfor%} MKUP expected = <<-XPCTD 123 next 456 next 7890 XPCTD assert_template_result(expected,markup,assigns) end def test_pause_resume_BIG_offset assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}} markup = %q({%for i in array.items limit:3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%} next {%for i in array.items offset:continue limit:3 offset:1000 %}{{i}}{%endfor%}) expected = %q(123 next 456 next ) assert_template_result(expected,markup,assigns) 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 # ruby 1.8.7 "String".each => Enumerator with single "String" element. # ruby 1.9.3 no longer supports .each on String though we mimic # the functionality for backwards compatibility assert_template_result('test string', '{%for val in string%}{{val}}{%endfor%}', 'string' => "test string") assert_template_result('test string', '{%for val in string limit:1%}{{val}}{%endfor%}', 'string' => "test string") assert_template_result('val-string-1-1-0-1-0-true-true-test string', '{%for val in string%}' + '{{forloop.name}}-' + '{{forloop.index}}-' + '{{forloop.length}}-' + '{{forloop.index0}}-' + '{{forloop.rindex}}-' + '{{forloop.rindex0}}-' + '{{forloop.first}}-' + '{{forloop.last}}-' + '{{val}}{%endfor%}', 'string' => "test string") end def test_blank_string_not_iterable assert_template_result('', "{% for char in characters %}I WILL NOT BE OUTPUT{% endfor %}", 'characters' => '') end def test_bad_variable_naming_in_for_loop assert_raise(Liquid::SyntaxError) do Liquid::Template.parse('{% for a/b in x %}{% endfor %}') end end def test_spacing_with_variable_naming_in_for_loop expected = '12345' template = '{% for item in items %}{{item}}{% endfor %}' assigns = {'items' => [1,2,3,4,5]} assert_template_result(expected, template, assigns) end end liquid-2.6.1/test/liquid/tags/break_tag_test.rb0000644000004100000410000000051612271557765021571 0ustar www-datawww-datarequire '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 liquid-2.6.1/test/liquid/tags/html_tag_test.rb0000644000004100000410000000673712271557765021464 0ustar www-datawww-datarequire 'test_helper' class HtmlTagTest < Test::Unit::TestCase include Liquid class ArrayDrop < Liquid::Drop include Enumerable def initialize(array) @array = array end def each(&block) @array.each(&block) end end def test_html_table assert_template_result("\n 1 2 3 \n 4 5 6 \n", '{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}', 'numbers' => [1,2,3,4,5,6]) assert_template_result("\n\n", '{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}', 'numbers' => []) end def test_html_table_with_different_cols assert_template_result("\n 1 2 3 4 5 \n 6 \n", '{% tablerow n in numbers cols:5%} {{n}} {% endtablerow %}', 'numbers' => [1,2,3,4,5,6]) end def test_html_col_counter assert_template_result("\n12\n12\n12\n", '{% tablerow n in numbers cols:2%}{{tablerowloop.col}}{% endtablerow %}', 'numbers' => [1,2,3,4,5,6]) end def test_quoted_fragment assert_template_result("\n 1 2 3 \n 4 5 6 \n", "{% tablerow n in collections.frontpage cols:3%} {{n}} {% endtablerow %}", 'collections' => {'frontpage' => [1,2,3,4,5,6]}) assert_template_result("\n 1 2 3 \n 4 5 6 \n", "{% tablerow n in collections['frontpage'] cols:3%} {{n}} {% endtablerow %}", 'collections' => {'frontpage' => [1,2,3,4,5,6]}) end def test_enumerable_drop assert_template_result("\n 1 2 3 \n 4 5 6 \n", '{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}', 'numbers' => ArrayDrop.new([1,2,3,4,5,6])) end def test_offset_and_limit assert_template_result("\n 1 2 3 \n 4 5 6 \n", '{% tablerow n in numbers cols:3 offset:1 limit:6%} {{n}} {% endtablerow %}', 'numbers' => [0,1,2,3,4,5,6,7]) end end # HtmlTagTest liquid-2.6.1/test/liquid/context_test.rb0000644000004100000410000002752612271557765020412 0ustar www-datawww-datarequire 'test_helper' class HundredCentes def to_liquid 100 end end class CentsDrop < Liquid::Drop def amount HundredCentes.new end def non_zero? true end end class ContextSensitiveDrop < Liquid::Drop def test @context['test'] end end class Category < Liquid::Drop attr_accessor :name def initialize(name) @name = name end def to_liquid CategoryDrop.new(self) end end class CategoryDrop attr_accessor :category, :context def initialize(category) @category = category end end class CounterDrop < Liquid::Drop def count @count ||= 0 @count += 1 end end class ArrayLike def fetch(index) end def [](index) @counts ||= [] @counts[index] ||= 0 @counts[index] += 1 end def to_liquid self end end class ContextTest < Test::Unit::TestCase include Liquid def setup @context = Liquid::Context.new end def test_variables @context['string'] = 'string' assert_equal 'string', @context['string'] @context['num'] = 5 assert_equal 5, @context['num'] @context['time'] = Time.parse('2006-06-06 12:00:00') assert_equal Time.parse('2006-06-06 12:00:00'), @context['time'] @context['date'] = Date.today assert_equal Date.today, @context['date'] now = DateTime.now @context['datetime'] = now assert_equal now, @context['datetime'] @context['bool'] = true assert_equal true, @context['bool'] @context['bool'] = false assert_equal false, @context['bool'] @context['nil'] = nil assert_equal nil, @context['nil'] assert_equal nil, @context['nil'] end def test_variables_not_existing assert_equal nil, @context['does_not_exist'] end def test_scoping assert_nothing_raised do @context.push @context.pop end assert_raise(Liquid::ContextError) do @context.pop end assert_raise(Liquid::ContextError) do @context.push @context.pop @context.pop end end def test_length_query @context['numbers'] = [1,2,3,4] assert_equal 4, @context['numbers.size'] @context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4} assert_equal 4, @context['numbers.size'] @context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4, 'size' => 1000} assert_equal 1000, @context['numbers.size'] end def test_hyphenated_variable @context['oh-my'] = 'godz' assert_equal 'godz', @context['oh-my'] end def test_add_filter filter = Module.new do def hi(output) output + ' hi!' end end context = Context.new context.add_filters(filter) assert_equal 'hi? hi!', context.invoke(:hi, 'hi?') context = Context.new assert_equal 'hi?', context.invoke(:hi, 'hi?') context.add_filters(filter) assert_equal 'hi? hi!', context.invoke(:hi, 'hi?') 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 def hi(output) output + ' hi!' end end context = Context.new assert_equal "Wookie", context.invoke("hi", "Wookie") context.add_filters(filter) assert_equal "Wookie hi!", context.invoke("hi", "Wookie") end def test_add_item_in_outer_scope @context['test'] = 'test' @context.push assert_equal 'test', @context['test'] @context.pop assert_equal 'test', @context['test'] end def test_add_item_in_inner_scope @context.push @context['test'] = 'test' assert_equal 'test', @context['test'] @context.pop assert_equal nil, @context['test'] end def test_hierachical_data @context['hash'] = {"name" => 'tobi'} assert_equal 'tobi', @context['hash.name'] assert_equal 'tobi', @context['hash["name"]'] end def test_keywords assert_equal true, @context['true'] assert_equal false, @context['false'] end def test_digits assert_equal 100, @context['100'] assert_equal 100.00, @context['100.00'] end def test_strings assert_equal "hello!", @context['"hello!"'] assert_equal "hello!", @context["'hello!'"] end def test_merge @context.merge({ "test" => "test" }) assert_equal 'test', @context['test'] @context.merge({ "test" => "newvalue", "foo" => "bar" }) assert_equal 'newvalue', @context['test'] assert_equal 'bar', @context['foo'] end def test_array_notation @context['test'] = [1,2,3,4,5] assert_equal 1, @context['test[0]'] assert_equal 2, @context['test[1]'] assert_equal 3, @context['test[2]'] assert_equal 4, @context['test[3]'] assert_equal 5, @context['test[4]'] end def test_recoursive_array_notation @context['test'] = {'test' => [1,2,3,4,5]} assert_equal 1, @context['test.test[0]'] @context['test'] = [{'test' => 'worked'}] assert_equal 'worked', @context['test[0].test'] end def test_hash_to_array_transition @context['colors'] = { 'Blue' => ['003366','336699', '6699CC', '99CCFF'], 'Green' => ['003300','336633', '669966', '99CC99'], 'Yellow' => ['CC9900','FFCC00', 'FFFF99', 'FFFFCC'], 'Red' => ['660000','993333', 'CC6666', 'FF9999'] } assert_equal '003366', @context['colors.Blue[0]'] assert_equal 'FF9999', @context['colors.Red[3]'] end def test_try_first @context['test'] = [1,2,3,4,5] assert_equal 1, @context['test.first'] assert_equal 5, @context['test.last'] @context['test'] = {'test' => [1,2,3,4,5]} assert_equal 1, @context['test.test.first'] assert_equal 5, @context['test.test.last'] @context['test'] = [1] assert_equal 1, @context['test.first'] assert_equal 1, @context['test.last'] end def test_access_hashes_with_hash_notation @context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] } @context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]} assert_equal 5, @context['products["count"]'] assert_equal 'deepsnow', @context['products["tags"][0]'] assert_equal 'deepsnow', @context['products["tags"].first'] assert_equal 'draft151cm', @context['product["variants"][0]["title"]'] assert_equal 'element151cm', @context['product["variants"][1]["title"]'] assert_equal 'draft151cm', @context['product["variants"][0]["title"]'] assert_equal 'element151cm', @context['product["variants"].last["title"]'] end def test_access_variable_with_hash_notation @context['foo'] = 'baz' @context['bar'] = 'foo' assert_equal 'baz', @context['["foo"]'] assert_equal 'baz', @context['[bar]'] end def test_access_hashes_with_hash_access_variables @context['var'] = 'tags' @context['nested'] = {'var' => 'tags'} @context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] } assert_equal 'deepsnow', @context['products[var].first'] assert_equal 'freestyle', @context['products[nested.var].last'] end def test_hash_notation_only_for_hash_access @context['array'] = [1,2,3,4,5] @context['hash'] = {'first' => 'Hello'} assert_equal 1, @context['array.first'] assert_equal nil, @context['array["first"]'] assert_equal 'Hello', @context['hash["first"]'] end def test_first_can_appear_in_middle_of_callchain @context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]} assert_equal 'draft151cm', @context['product.variants[0].title'] assert_equal 'element151cm', @context['product.variants[1].title'] assert_equal 'draft151cm', @context['product.variants.first.title'] assert_equal 'element151cm', @context['product.variants.last.title'] end def test_cents @context.merge( "cents" => HundredCentes.new ) assert_equal 100, @context['cents'] end def test_nested_cents @context.merge( "cents" => { 'amount' => HundredCentes.new} ) assert_equal 100, @context['cents.amount'] @context.merge( "cents" => { 'cents' => { 'amount' => HundredCentes.new} } ) assert_equal 100, @context['cents.cents.amount'] end def test_cents_through_drop @context.merge( "cents" => CentsDrop.new ) assert_equal 100, @context['cents.amount'] end def test_nested_cents_through_drop @context.merge( "vars" => {"cents" => CentsDrop.new} ) assert_equal 100, @context['vars.cents.amount'] end def test_drop_methods_with_question_marks @context.merge( "cents" => CentsDrop.new ) assert @context['cents.non_zero?'] end def test_context_from_within_drop @context.merge( "test" => '123', "vars" => ContextSensitiveDrop.new ) assert_equal '123', @context['vars.test'] end def test_nested_context_from_within_drop @context.merge( "test" => '123', "vars" => {"local" => ContextSensitiveDrop.new } ) assert_equal '123', @context['vars.local.test'] end def test_ranges @context.merge( "test" => '5' ) assert_equal (1..5), @context['(1..5)'] assert_equal (1..5), @context['(1..test)'] assert_equal (5..5), @context['(test..test)'] end def test_cents_through_drop_nestedly @context.merge( "cents" => {"cents" => CentsDrop.new} ) assert_equal 100, @context['cents.cents.amount'] @context.merge( "cents" => { "cents" => {"cents" => CentsDrop.new}} ) assert_equal 100, @context['cents.cents.cents.amount'] end def test_drop_with_variable_called_only_once @context['counter'] = CounterDrop.new assert_equal 1, @context['counter.count'] assert_equal 2, @context['counter.count'] assert_equal 3, @context['counter.count'] end def test_drop_with_key_called_only_once @context['counter'] = CounterDrop.new assert_equal 1, @context['counter["count"]'] assert_equal 2, @context['counter["count"]'] assert_equal 3, @context['counter["count"]'] end def test_proc_as_variable @context['dynamic'] = Proc.new { 'Hello' } assert_equal 'Hello', @context['dynamic'] end def test_lambda_as_variable @context['dynamic'] = proc { 'Hello' } assert_equal 'Hello', @context['dynamic'] end def test_nested_lambda_as_variable @context['dynamic'] = { "lambda" => proc { 'Hello' } } assert_equal 'Hello', @context['dynamic.lambda'] end def test_array_containing_lambda_as_variable @context['dynamic'] = [1,2, proc { 'Hello' } ,4,5] assert_equal 'Hello', @context['dynamic[2]'] end def test_lambda_is_called_once @context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s } assert_equal '1', @context['callcount'] assert_equal '1', @context['callcount'] assert_equal '1', @context['callcount'] @global = nil end def test_nested_lambda_is_called_once @context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } } assert_equal '1', @context['callcount.lambda'] assert_equal '1', @context['callcount.lambda'] assert_equal '1', @context['callcount.lambda'] @global = nil end def test_lambda_in_array_is_called_once @context['callcount'] = [1,2, proc { @global ||= 0; @global += 1; @global.to_s } ,4,5] assert_equal '1', @context['callcount[2]'] assert_equal '1', @context['callcount[2]'] assert_equal '1', @context['callcount[2]'] @global = nil end def test_access_to_context_from_proc @context.registers[:magic] = 345392 @context['magic'] = proc { @context.registers[:magic] } assert_equal 345392, @context['magic'] end def test_to_liquid_and_context_at_first_level @context['category'] = Category.new("foobar") assert_kind_of CategoryDrop, @context['category'] assert_equal @context, @context['category'].context end end # ContextTest liquid-2.6.1/test/liquid/block_test.rb0000644000004100000410000000350212271557765020004 0ustar www-datawww-datarequire 'test_helper' class BlockTest < Test::Unit::TestCase include Liquid def test_blankspace template = Liquid::Template.parse(" ") assert_equal [" "], template.root.nodelist end def test_variable_beginning template = Liquid::Template.parse("{{funk}} ") assert_equal 2, template.root.nodelist.size assert_equal Variable, template.root.nodelist[0].class assert_equal String, template.root.nodelist[1].class end def test_variable_end template = Liquid::Template.parse(" {{funk}}") assert_equal 2, template.root.nodelist.size assert_equal String, template.root.nodelist[0].class assert_equal Variable, template.root.nodelist[1].class end def test_variable_middle template = Liquid::Template.parse(" {{funk}} ") assert_equal 3, template.root.nodelist.size assert_equal String, template.root.nodelist[0].class assert_equal Variable, template.root.nodelist[1].class assert_equal String, template.root.nodelist[2].class end def test_variable_many_embedded_fragments template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ") assert_equal 7, template.root.nodelist.size assert_equal [String, Variable, String, Variable, String, Variable, String], block_types(template.root.nodelist) end def test_with_block template = Liquid::Template.parse(" {% comment %} {% endcomment %} ") assert_equal [String, Comment, String], block_types(template.root.nodelist) assert_equal 3, template.root.nodelist.size end def test_with_custom_tag Liquid::Template.register_tag("testtag", Block) assert_nothing_thrown do template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}") end end private def block_types(nodelist) nodelist.collect { |node| node.class } end end # VariableTest liquid-2.6.1/test/liquid/security_test.rb0000644000004100000410000000305212271557765020561 0ustar www-datawww-datarequire 'test_helper' module SecurityFilter def add_one(input) "#{input} + 1" end end class SecurityTest < Test::Unit::TestCase include Liquid def test_no_instance_eval text = %( {{ '1+1' | instance_eval }} ) expected = %| 1+1 | assert_equal expected, Template.parse(text).render(@assigns) end def test_no_existing_instance_eval text = %( {{ '1+1' | __instance_eval__ }} ) expected = %| 1+1 | assert_equal expected, Template.parse(text).render(@assigns) end def test_no_instance_eval_after_mixing_in_new_filter text = %( {{ '1+1' | instance_eval }} ) expected = %| 1+1 | assert_equal expected, Template.parse(text).render(@assigns) end def test_no_instance_eval_later_in_chain text = %( {{ '1+1' | add_one | instance_eval }} ) expected = %| 1+1 + 1 | assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter) end 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 liquid-2.6.1/test/liquid/capture_test.rb0000644000004100000410000000221012271557765020350 0ustar www-datawww-datarequire 'test_helper' class CaptureTest < Test::Unit::TestCase include Liquid def test_captures_block_content_in_variable assert_template_result("test string", "{% capture 'var' %}test string{% endcapture %}{{var}}", {}) end def test_capture_to_variable_from_outer_scope_if_existing template_source = <<-END_TEMPLATE {% assign var = '' %} {% if true %} {% capture var %}first-block-string{% endcapture %} {% endif %} {% if true %} {% capture var %}test-string{% endcapture %} {% endif %} {{var}} END_TEMPLATE template = Template.parse(template_source) rendered = template.render assert_equal "test-string", rendered.gsub(/\s/, '') end def test_assigning_from_capture template_source = <<-END_TEMPLATE {% assign first = '' %} {% assign second = '' %} {% for number in (1..3) %} {% capture first %}{{number}}{% endcapture %} {% assign second = first %} {% endfor %} {{ first }}-{{ second }} END_TEMPLATE template = Template.parse(template_source) rendered = template.render assert_equal "3-3", rendered.gsub(/\s/, '') end end # CaptureTest liquid-2.6.1/test/liquid/parsing_quirks_test.rb0000644000004100000410000000257412271557765021763 0ustar www-datawww-datarequire '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} end def test_raise_on_single_close_bracet assert_raise(SyntaxError) do Template.parse("text {{method} oh nos!") end end def test_raise_on_label_and_no_close_bracets assert_raise(SyntaxError) do Template.parse("TEST {{ ") end end def test_raise_on_label_and_no_close_bracets_percent assert_raise(SyntaxError) do Template.parse("TEST {% ") end end def test_error_on_empty_filter assert_nothing_raised do Template.parse("{{test |a|b|}}") Template.parse("{{test}}") Template.parse("{{|test|}}") end end def test_meaningless_parens assigns = {'b' => 'bar', 'c' => 'baz'} markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false" assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns) end def test_unexpected_characters_silently_eat_logic markup = "true && false" assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}") markup = "false || true" assert_template_result('',"{% if #{markup} %} YES {% endif %}") end end # ParsingQuirksTest liquid-2.6.1/test/liquid/condition_test.rb0000644000004100000410000000770612271557765020712 0ustar www-datawww-datarequire 'test_helper' class ConditionTest < Test::Unit::TestCase include Liquid def test_basic_condition assert_equal false, Condition.new('1', '==', '2').evaluate assert_equal true, Condition.new('1', '==', '1').evaluate end def test_default_operators_evalute_true assert_evalutes_true '1', '==', '1' assert_evalutes_true '1', '!=', '2' assert_evalutes_true '1', '<>', '2' assert_evalutes_true '1', '<', '2' assert_evalutes_true '2', '>', '1' assert_evalutes_true '1', '>=', '1' assert_evalutes_true '2', '>=', '1' assert_evalutes_true '1', '<=', '2' assert_evalutes_true '1', '<=', '1' # negative numbers assert_evalutes_true '1', '>', '-1' assert_evalutes_true '-1', '<', '1' assert_evalutes_true '1.0', '>', '-1.0' assert_evalutes_true '-1.0', '<', '1.0' end def test_default_operators_evalute_false assert_evalutes_false '1', '==', '2' assert_evalutes_false '1', '!=', '1' assert_evalutes_false '1', '<>', '1' assert_evalutes_false '1', '<', '0' assert_evalutes_false '2', '>', '4' assert_evalutes_false '1', '>=', '3' assert_evalutes_false '2', '>=', '4' assert_evalutes_false '1', '<=', '0' assert_evalutes_false '1', '<=', '0' end def test_contains_works_on_strings assert_evalutes_true "'bob'", 'contains', "'o'" assert_evalutes_true "'bob'", 'contains', "'b'" assert_evalutes_true "'bob'", 'contains', "'bo'" assert_evalutes_true "'bob'", 'contains', "'ob'" assert_evalutes_true "'bob'", 'contains', "'bob'" assert_evalutes_false "'bob'", 'contains', "'bob2'" assert_evalutes_false "'bob'", 'contains', "'a'" assert_evalutes_false "'bob'", 'contains', "'---'" end def test_contains_works_on_arrays @context = Liquid::Context.new @context['array'] = [1,2,3,4,5] assert_evalutes_false "array", 'contains', '0' assert_evalutes_true "array", 'contains', '1' assert_evalutes_true "array", 'contains', '2' assert_evalutes_true "array", 'contains', '3' assert_evalutes_true "array", 'contains', '4' assert_evalutes_true "array", 'contains', '5' assert_evalutes_false "array", 'contains', '6' assert_evalutes_false "array", 'contains', '"1"' end def test_contains_returns_false_for_nil_operands @context = Liquid::Context.new assert_evalutes_false "not_assigned", 'contains', '0' assert_evalutes_false "0", 'contains', 'not_assigned' end def test_or_condition condition = Condition.new('1', '==', '2') assert_equal false, condition.evaluate condition.or Condition.new('2', '==', '1') assert_equal false, condition.evaluate condition.or Condition.new('1', '==', '1') assert_equal true, condition.evaluate end def test_and_condition condition = Condition.new('1', '==', '1') assert_equal true, condition.evaluate condition.and Condition.new('2', '==', '2') assert_equal true, condition.evaluate condition.and Condition.new('2', '==', '1') assert_equal false, condition.evaluate end def test_should_allow_custom_proc_operator Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}} } assert_evalutes_true "'bob'", 'starts_with', "'b'" assert_evalutes_false "'bob'", 'starts_with', "'o'" ensure Condition.operators.delete 'starts_with' end def test_left_or_right_may_contain_operators @context = Liquid::Context.new @context['one'] = @context['another'] = "gnomeslab-and-or-liquid" assert_evalutes_true "one", '==', "another" end private def assert_evalutes_true(left, op, right) assert Condition.new(left, op, right).evaluate(@context || Liquid::Context.new), "Evaluated false: #{left} #{op} #{right}" end def assert_evalutes_false(left, op, right) assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new), "Evaluated true: #{left} #{op} #{right}" end end # ConditionTest liquid-2.6.1/test/liquid/drop_test.rb0000644000004100000410000001526312271557765017665 0ustar www-datawww-datarequire 'test_helper' class ContextDrop < Liquid::Drop def scopes @context.scopes.size end def scopes_as_array (1..@context.scopes.size).to_a end def loop_pos @context['forloop.index'] end def before_method(method) return @context[method] end end class ProductDrop < Liquid::Drop class TextDrop < Liquid::Drop def array ['text1', 'text2'] end def text 'text1' end end class CatchallDrop < Liquid::Drop def before_method(method) return 'method: ' << method.to_s end end def texts TextDrop.new end def catchall CatchallDrop.new end def context ContextDrop.new end protected def callmenot "protected" end end class EnumerableDrop < Liquid::Drop def size 3 end def each yield 1 yield 2 yield 3 end end class DropsTest < Test::Unit::TestCase include Liquid def test_product_drop assert_nothing_raised do tpl = Liquid::Template.parse( ' ' ) tpl.render('product' => ProductDrop.new) end end def test_drop_does_only_respond_to_whitelisted_methods assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render('product' => ProductDrop.new) assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render('product' => ProductDrop.new) assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render('product' => ProductDrop.new) assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render('product' => ProductDrop.new) assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render('product' => ProductDrop.new) assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render('product' => ProductDrop.new) end def test_drops_respond_to_to_liquid assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render('product' => ProductDrop.new) assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render('product' => ProductDrop.new) end def test_text_drop output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render('product' => ProductDrop.new) assert_equal ' text1 ', output end def test_unknown_method output = Liquid::Template.parse( ' {{ product.catchall.unknown }} ' ).render('product' => ProductDrop.new) assert_equal ' method: unknown ', output end def test_integer_argument_drop output = Liquid::Template.parse( ' {{ product.catchall[8] }} ' ).render('product' => ProductDrop.new) assert_equal ' method: 8 ', output end def test_text_array_drop output = Liquid::Template.parse( '{% for text in product.texts.array %} {{text}} {% endfor %}' ).render('product' => ProductDrop.new) assert_equal ' text1 text2 ', output end def test_context_drop output = Liquid::Template.parse( ' {{ context.bar }} ' ).render('context' => ContextDrop.new, 'bar' => "carrot") assert_equal ' carrot ', output end def test_nested_context_drop output = Liquid::Template.parse( ' {{ product.context.foo }} ' ).render('product' => ProductDrop.new, 'foo' => "monkey") assert_equal ' monkey ', output end def test_protected output = Liquid::Template.parse( ' {{ product.callmenot }} ' ).render('product' => ProductDrop.new) assert_equal ' ', output 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 assert_equal '1', Liquid::Template.parse( '{{ context.scopes }}' ).render('context' => ContextDrop.new) assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ context.scopes }}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1]) assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1]) end def test_scope_though_proc assert_equal '1', Liquid::Template.parse( '{{ s }}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }) assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ s }}{%endfor%}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1]) assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1]) end def test_scope_with_assigns assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render('context' => ContextDrop.new) assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1]) assert_equal 'test', Liquid::Template.parse( '{% assign header_gif = "test"%}{{header_gif}}' ).render('context' => ContextDrop.new) assert_equal 'test', Liquid::Template.parse( "{% assign header_gif = 'test'%}{{header_gif}}" ).render('context' => ContextDrop.new) end def test_scope_from_tags assert_equal '1', Liquid::Template.parse( '{% for i in context.scopes_as_array %}{{i}}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1]) assert_equal '12', Liquid::Template.parse( '{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1]) assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1]) end def test_access_context_from_drop assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3]) end def test_enumerable_drop assert_equal '123', Liquid::Template.parse( '{% for c in collection %}{{c}}{% endfor %}').render('collection' => EnumerableDrop.new) end def test_enumerable_drop_size assert_equal '3', Liquid::Template.parse( '{{collection.size}}').render('collection' => EnumerableDrop.new) end def test_empty_string_value_access assert_equal '', Liquid::Template.parse('{{ product[value] }}').render('product' => ProductDrop.new, 'value' => '') end def test_nil_value_access assert_equal '', Liquid::Template.parse('{{ product[value] }}').render('product' => ProductDrop.new, 'value' => nil) end end # DropsTest liquid-2.6.1/test/liquid/standard_filter_test.rb0000644000004100000410000002065212271557765022064 0ustar www-datawww-data# encoding: utf-8 require 'test_helper' class Filters include Liquid::StandardFilters end class TestThing def initialize @foo = 0 end def to_s "woot: #{@foo}" end def to_liquid @foo += 1 self end end class TestDrop < Liquid::Drop def test "testfoo" end end class StandardFiltersTest < Test::Unit::TestCase include Liquid def setup @filters = Filters.new end def test_size assert_equal 3, @filters.size([1,2,3]) assert_equal 0, @filters.size([]) assert_equal 0, @filters.size(nil) end def test_downcase assert_equal 'testing', @filters.downcase("Testing") assert_equal '', @filters.downcase(nil) end def test_upcase assert_equal 'TESTING', @filters.upcase("Testing") assert_equal '', @filters.upcase(nil) end def test_upcase assert_equal 'TESTING', @filters.upcase("Testing") assert_equal '', @filters.upcase(nil) end def test_truncate assert_equal '1234...', @filters.truncate('1234567890', 7) assert_equal '1234567890', @filters.truncate('1234567890', 20) assert_equal '...', @filters.truncate('1234567890', 0) assert_equal '1234567890', @filters.truncate('1234567890') assert_equal "测试...", @filters.truncate("测试测试测试测试", 5) end def test_strip assert_equal ['12','34'], @filters.split('12~34', '~') assert_equal ['A? ',' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~') assert_equal ['A?Z'], @filters.split('A?Z', '~') # Regexp works although Liquid does not support. assert_equal ['A','Z'], @filters.split('AxZ', /x/) end def test_escape assert_equal '<strong>', @filters.escape('') assert_equal '<strong>', @filters.h('') end def test_escape_once assert_equal '<strong>', @filters.escape_once(@filters.escape('')) end def test_truncatewords assert_equal 'one two three', @filters.truncatewords('one two three', 4) assert_equal 'one two...', @filters.truncatewords('one two three', 2) assert_equal 'one two three', @filters.truncatewords('one two three') assert_equal 'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...', @filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15) assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5) end def test_strip_html assert_equal 'test', @filters.strip_html("
test
") assert_equal 'test', @filters.strip_html("
test
") assert_equal '', @filters.strip_html("") assert_equal '', @filters.strip_html("") assert_equal 'test', @filters.strip_html("test") assert_equal 'test', @filters.strip_html("test") assert_equal '', @filters.strip_html(nil) end def test_join assert_equal '1 2 3 4', @filters.join([1,2,3,4]) assert_equal '1 - 2 - 3 - 4', @filters.join([1,2,3,4], ' - ') end def test_sort assert_equal [1,2,3,4], @filters.sort([4,3,2,1]) assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a") end def test_reverse assert_equal [4,3,2,1], @filters.reverse([1,2,3,4]) end def test_map assert_equal [1,2,3,4], @filters.map([{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], 'a') assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}", 'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}] end def test_map_doesnt_call_arbitrary_stuff assert_equal "", Liquid::Template.parse('{{ "foo" | map: "__id__" }}').render assert_equal "", Liquid::Template.parse('{{ "foo" | map: "inspect" }}').render end def test_map_calls_to_liquid t = TestThing.new assert_equal "woot: 1", Liquid::Template.parse('{{ foo }}').render("foo" => t) end def test_map_over_proc drop = TestDrop.new p = Proc.new{ drop } templ = '{{ procs | map: "test" }}' assert_equal "testfoo", Liquid::Template.parse(templ).render("procs" => [p]) end def test_date assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B") assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B") assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B") assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B") assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B") assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B") assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "") assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "") assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "") assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil) assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y") assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y") assert_equal "#{Date.today.year}", @filters.date('now', '%Y') assert_equal "#{Date.today.year}", @filters.date('today', '%Y') assert_equal nil, @filters.date(nil, "%B") assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y") 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]) assert_equal nil, @filters.first([]) assert_equal nil, @filters.last([]) end def test_replace assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2) assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2) assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}" end def test_remove assert_equal ' ', @filters.remove("a a a a", 'a') assert_equal 'a a a', @filters.remove_first("a a a a", 'a ') assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}" end def test_pipes_in_string_arguments assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}" end def test_strip_newlines assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc" assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\r\nb\nc" end def test_newlines_to_br assert_template_result "a
\nb
\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc" end def test_plus assert_template_result "2", "{{ 1 | plus:1 }}" assert_template_result "2.0", "{{ '1' | plus:'1.0' }}" end def test_minus assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1 assert_template_result "2.3", "{{ '4.3' | minus:'2' }}" end def test_times assert_template_result "12", "{{ 3 | times:4 }}" assert_template_result "0", "{{ 'foo' | times:4 }}" # Ruby v1.9.2-rc1, or higher, backwards compatible Float test assert_match(/(6\.3)|(6\.(0{13})1)/, Template.parse("{{ '2.1' | times:3 }}").render) assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}" assert_template_result "7.25", "{{ 0.0725 | times:100 }}" end def test_divided_by assert_template_result "4", "{{ 12 | divided_by:3 }}" assert_template_result "4", "{{ 14 | divided_by:3 }}" # Ruby v1.9.2-rc1, or higher, backwards compatible Float test assert_match(/4\.(6{13,14})7/, Template.parse("{{ 14 | divided_by:'3.0' }}").render) assert_template_result "5", "{{ 15 | divided_by:3 }}" assert_template_result "Liquid error: divided by 0", "{{ 5 | divided_by:0 }}" assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}" end def test_modulo assert_template_result "1", "{{ 3 | modulo:2 }}" end def test_append assigns = {'a' => 'bc', 'b' => 'd' } assert_template_result('bcd',"{{ a | append: 'd'}}",assigns) assert_template_result('bcd',"{{ a | append: b}}",assigns) end def test_prepend assigns = {'a' => 'bc', 'b' => 'a' } assert_template_result('abc',"{{ a | prepend: 'a'}}",assigns) assert_template_result('abc',"{{ a | prepend: b}}",assigns) end def test_cannot_access_private_methods assert_template_result('a',"{{ 'a' | to_number }}") end end # StandardFiltersTest liquid-2.6.1/test/liquid/file_system_test.rb0000644000004100000410000000144012271557765021234 0ustar www-datawww-datarequire 'test_helper' class FileSystemTest < Test::Unit::TestCase include Liquid def test_default assert_raise(FileSystemError) do BlankFileSystem.new.read_template_file("dummy", {'dummy'=>'smarty'}) end end def test_local file_system = Liquid::LocalFileSystem.new("/some/path") assert_equal "/some/path/_mypartial.liquid" , file_system.full_path("mypartial") assert_equal "/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial") assert_raise(FileSystemError) do file_system.full_path("../dir/mypartial") end assert_raise(FileSystemError) do file_system.full_path("/dir/../../dir/mypartial") end assert_raise(FileSystemError) do file_system.full_path("/etc/passwd") end end end # FileSystemTest liquid-2.6.1/test/liquid/output_test.rb0000644000004100000410000000565412271557765020264 0ustar www-datawww-datarequire 'test_helper' module FunnyFilter def make_funny(input) 'LOL' end def cite_funny(input) "LOL: #{input}" end def add_smiley(input, smiley = ":-)") "#{input} #{smiley}" end def add_tag(input, tag = "p", id = "foo") %|<#{tag} id="#{id}">#{input}| end def paragraph(input) "

#{input}

" end def link_to(name, url) %|#{name}| end end class OutputTest < Test::Unit::TestCase include Liquid def setup @assigns = { 'best_cars' => 'bmw', 'car' => {'bmw' => 'good', 'gm' => 'bad'} } end def test_variable text = %| {{best_cars}} | expected = %| bmw | assert_equal expected, Template.parse(text).render(@assigns) end def test_variable_traversing text = %| {{car.bmw}} {{car.gm}} {{car.bmw}} | expected = %| good bad good | assert_equal expected, Template.parse(text).render(@assigns) end def test_variable_piping text = %( {{ car.gm | make_funny }} ) expected = %| LOL | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_variable_piping_with_input text = %( {{ car.gm | cite_funny }} ) expected = %| LOL: bad | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_variable_piping_with_args text = %! {{ car.gm | add_smiley : ':-(' }} ! expected = %| bad :-( | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_variable_piping_with_no_args text = %! {{ car.gm | add_smiley }} ! expected = %| bad :-) | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_multiple_variable_piping_with_args text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} ! expected = %| bad :-( :-( | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_variable_piping_with_args text = %! {{ car.gm | add_tag : 'span', 'bar'}} ! expected = %| bad | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_variable_piping_with_variable_args text = %! {{ car.gm | add_tag : 'span', car.bmw}} ! expected = %| bad | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_multiple_pipings text = %( {{ best_cars | cite_funny | paragraph }} ) expected = %|

LOL: bmw

| assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end def test_link_to text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} ) expected = %| Typo | assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter]) end end # OutputTest liquid-2.6.1/test/liquid/hash_ordering_test.rb0000644000004100000410000000073012271557765021526 0ustar www-datawww-datarequire 'test_helper' module MoneyFilter def money(input) sprintf(' %d$ ', input) end end module CanadianMoneyFilter def money(input) sprintf(' %d$ CAD ', input) end end class HashOrderingTest < Test::Unit::TestCase include Liquid def test_global_register_order Template.register_filter(MoneyFilter) Template.register_filter(CanadianMoneyFilter) assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil) end end liquid-2.6.1/test/liquid/template_test.rb0000644000004100000410000001342512271557765020532 0ustar www-datawww-datarequire 'test_helper' class TemplateContextDrop < Liquid::Drop def before_method(method) method end def foo 'fizzbuzz' end def baz @context.registers['lulz'] 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 assert_equal 'from instance assigns', t.parse("{{ foo }}").render end def test_instance_assigns_persist_on_same_template_parsing_between_renders t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}") assert_equal 'foo', t.render assert_equal 'foofoo', t.render end def test_custom_assigns_do_not_persist_on_same_template t = Template.new assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns') assert_equal '', t.parse("{{ foo }}").render end def test_custom_assigns_squash_instance_assigns t = Template.new assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns') end def test_persistent_assigns_squash_instance_assigns t = Template.new assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render t.assigns['foo'] = 'from persistent assigns' assert_equal 'from persistent assigns', t.parse("{{ foo }}").render end def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders t = Template.new t.assigns['number'] = lambda { @global ||= 0; @global += 1 } assert_equal '1', t.parse("{{number}}").render assert_equal '1', t.parse("{{number}}").render assert_equal '1', t.render @global = nil end def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders t = Template.new assigns = {'number' => lambda { @global ||= 0; @global += 1 }} assert_equal '1', t.parse("{{number}}").render(assigns) assert_equal '1', t.parse("{{number}}").render(assigns) assert_equal '1', t.render(assigns) @global = nil end def test_resource_limits_render_length t = Template.parse("0123456789") t.resource_limits = { :render_length_limit => 5 } assert_equal "Liquid error: Memory limits exceeded", t.render() assert t.resource_limits[:reached] t.resource_limits = { :render_length_limit => 10 } assert_equal "0123456789", t.render() assert_not_nil t.resource_limits[:render_length_current] end def test_resource_limits_render_score t = Template.parse("{% for a in (1..10) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}") t.resource_limits = { :render_score_limit => 50 } assert_equal "Liquid error: Memory limits exceeded", t.render() assert t.resource_limits[:reached] t = Template.parse("{% for a in (1..100) %} foo {% endfor %}") t.resource_limits = { :render_score_limit => 50 } assert_equal "Liquid error: Memory limits exceeded", t.render() assert t.resource_limits[:reached] t.resource_limits = { :render_score_limit => 200 } assert_equal (" foo " * 100), t.render() assert_not_nil t.resource_limits[:render_score_current] end def test_resource_limits_assign_score t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}") t.resource_limits = { :assign_score_limit => 1 } assert_equal "Liquid error: Memory limits exceeded", t.render() assert t.resource_limits[:reached] t.resource_limits = { :assign_score_limit => 2 } assert_equal "", t.render() assert_not_nil t.resource_limits[:assign_score_current] end def test_resource_limits_aborts_rendering_after_first_error t = Template.parse("{% for a in (1..100) %} foo1 {% endfor %} bar {% for a in (1..100) %} foo2 {% endfor %}") t.resource_limits = { :render_score_limit => 50 } assert_equal "Liquid error: Memory limits exceeded", t.render() assert t.resource_limits[:reached] end def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}") t.render() assert t.resource_limits[:assign_score_current] > 0 assert t.resource_limits[:render_score_current] > 0 assert t.resource_limits[:render_length_current] > 0 end def test_can_use_drop_as_context t = Template.new t.registers['lulz'] = 'haha' drop = TemplateContextDrop.new assert_equal 'fizzbuzz', t.parse('{{foo}}').render(drop) assert_equal 'bar', t.parse('{{bar}}').render(drop) assert_equal 'haha', t.parse("{{baz}}").render(drop) end end # TemplateTest liquid-2.6.1/test/liquid/regexp_test.rb0000644000004100000410000000306712271557765020212 0ustar www-datawww-datarequire 'test_helper' class RegexpTest < Test::Unit::TestCase include Liquid def test_empty assert_equal [], ''.scan(QuotedFragment) end def test_quote assert_equal ['"arg 1"'], '"arg 1"'.scan(QuotedFragment) end def test_words assert_equal ['arg1', 'arg2'], 'arg1 arg2'.scan(QuotedFragment) end def test_tags assert_equal ['', ''], ' '.scan(QuotedFragment) assert_equal [''], ''.scan(QuotedFragment) assert_equal ['', ''], %||.scan(QuotedFragment) end def test_quoted_words assert_equal ['arg1', 'arg2', '"arg 3"'], 'arg1 arg2 "arg 3"'.scan(QuotedFragment) end def test_quoted_words assert_equal ['arg1', 'arg2', "'arg 3'"], 'arg1 arg2 \'arg 3\''.scan(QuotedFragment) end def test_quoted_words_in_the_middle assert_equal ['arg1', 'arg2', '"arg 3"', 'arg4'], 'arg1 arg2 "arg 3" arg4 '.scan(QuotedFragment) end def test_variable_parser assert_equal ['var'], 'var'.scan(VariableParser) assert_equal ['var', 'method'], 'var.method'.scan(VariableParser) assert_equal ['var', '[method]'], 'var[method]'.scan(VariableParser) assert_equal ['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser) assert_equal ['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser) assert_equal ['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser) end end # RegexpTest liquid-2.6.1/test/liquid/assign_test.rb0000644000004100000410000000123412271557765020176 0ustar www-datawww-datarequire 'test_helper' class AssignTest < Test::Unit::TestCase include Liquid def test_assigned_variable assert_template_result('.foo.', '{% assign foo = values %}.{{ foo[0] }}.', 'values' => %w{foo bar baz}) assert_template_result('.bar.', '{% assign foo = values %}.{{ foo[1] }}.', 'values' => %w{foo bar baz}) end def test_assign_with_filter assert_template_result('.bar.', '{% assign foo = values | split: "," %}.{{ foo[1] }}.', 'values' => "foo,bar,baz") end end # AssignTest liquid-2.6.1/test/liquid/strainer_test.rb0000644000004100000410000000310012271557765020533 0ustar www-datawww-datarequire 'test_helper' class StrainerTest < Test::Unit::TestCase 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 strainer = Strainer.create(nil) assert_equal 5, strainer.invoke('size', 'input') assert_equal "public", strainer.invoke("public_filter") end def test_strainer_only_invokes_public_filter_methods strainer = Strainer.create(nil) 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 def test_strainer_returns_nil_if_no_filter_method_found strainer = Strainer.create(nil) assert_nil strainer.invoke("private_filter") assert_nil strainer.invoke("undef_the_filter") 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 liquid-2.6.1/test/liquid/filter_test.rb0000644000004100000410000000674312271557765020211 0ustar www-datawww-datarequire 'test_helper' module MoneyFilter def money(input) sprintf(' %d$ ', input) end def money_with_underscore(input) sprintf(' %d$ ', input) end end module CanadianMoneyFilter def money(input) sprintf(' %d$ CAD ', input) end end module SubstituteFilter def substitute(input, params={}) input.gsub(/%\{(\w+)\}/) { |match| params[$1] } end end class FiltersTest < Test::Unit::TestCase include Liquid def setup @context = Context.new end def test_local_filter @context['var'] = 1000 @context.add_filters(MoneyFilter) assert_equal ' 1000$ ', Variable.new("var | money").render(@context) end def test_underscore_in_filter_name @context['var'] = 1000 @context.add_filters(MoneyFilter) assert_equal ' 1000$ ', Variable.new("var | money_with_underscore").render(@context) end def test_second_filter_overwrites_first @context['var'] = 1000 @context.add_filters(MoneyFilter) @context.add_filters(CanadianMoneyFilter) assert_equal ' 1000$ CAD ', Variable.new("var | money").render(@context) end def test_size @context['var'] = 'abcd' @context.add_filters(MoneyFilter) assert_equal 4, Variable.new("var | size").render(@context) end def test_join @context['var'] = [1,2,3,4] assert_equal "1 2 3 4", Variable.new("var | join").render(@context) end def test_sort @context['value'] = 3 @context['numbers'] = [2,1,4,3] @context['words'] = ['expected', 'as', 'alphabetic'] @context['arrays'] = [['flattened'], ['are']] assert_equal [1,2,3,4], Variable.new("numbers | sort").render(@context) assert_equal ['alphabetic', 'as', 'expected'], Variable.new("words | sort").render(@context) assert_equal [3], Variable.new("value | sort").render(@context) assert_equal ['are', 'flattened'], Variable.new("arrays | sort").render(@context) end def test_strip_html @context['var'] = "bla blub" assert_equal "bla blub", Variable.new("var | strip_html").render(@context) end def test_strip_html_ignore_comments_with_html @context['var'] = "bla blub" assert_equal "bla blub", Variable.new("var | strip_html").render(@context) end def test_capitalize @context['var'] = "blub" assert_equal "Blub", Variable.new("var | capitalize").render(@context) end def test_nonexistent_filter_is_ignored @context['var'] = 1000 assert_equal 1000, Variable.new("var | xyzzy").render(@context) end 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 class FiltersInTemplate < Test::Unit::TestCase include Liquid def test_local_global Template.register_filter(MoneyFilter) assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render(nil, nil) assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, :filters => CanadianMoneyFilter) assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, :filters => [CanadianMoneyFilter]) end def test_local_filter_with_deprecated_syntax assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, CanadianMoneyFilter) assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, [CanadianMoneyFilter]) end end # FiltersTest liquid-2.6.1/test/liquid/module_ex_test.rb0000644000004100000410000000441312271557765020675 0ustar www-datawww-datarequire 'test_helper' class TestClassA liquid_methods :allowedA, :chainedB def allowedA 'allowedA' end def restrictedA 'restrictedA' end def chainedB TestClassB.new end end class TestClassB liquid_methods :allowedB, :chainedC def allowedB 'allowedB' end def chainedC TestClassC.new end end class TestClassC liquid_methods :allowedC def allowedC 'allowedC' end end class TestClassC::LiquidDropClass def another_allowedC 'another_allowedC' end end class ModuleExTest < Test::Unit::TestCase include Liquid def setup @a = TestClassA.new @b = TestClassB.new @c = TestClassC.new end def test_should_create_LiquidDropClass assert TestClassA::LiquidDropClass assert TestClassB::LiquidDropClass assert TestClassC::LiquidDropClass end def test_should_respond_to_liquid assert @a.respond_to?(:to_liquid) assert @b.respond_to?(:to_liquid) assert @c.respond_to?(:to_liquid) end def test_should_return_LiquidDropClass_object assert @a.to_liquid.is_a?(TestClassA::LiquidDropClass) assert @b.to_liquid.is_a?(TestClassB::LiquidDropClass) assert @c.to_liquid.is_a?(TestClassC::LiquidDropClass) end def test_should_respond_to_liquid_methods assert @a.to_liquid.respond_to?(:allowedA) assert @a.to_liquid.respond_to?(:chainedB) assert @b.to_liquid.respond_to?(:allowedB) assert @b.to_liquid.respond_to?(:chainedC) assert @c.to_liquid.respond_to?(:allowedC) assert @c.to_liquid.respond_to?(:another_allowedC) end def test_should_not_respond_to_restricted_methods assert ! @a.to_liquid.respond_to?(:restricted) end def test_should_use_regular_objects_as_drops assert_equal 'allowedA', Liquid::Template.parse("{{ a.allowedA }}").render('a'=>@a) assert_equal 'allowedB', Liquid::Template.parse("{{ a.chainedB.allowedB }}").render('a'=>@a) assert_equal 'allowedC', Liquid::Template.parse("{{ a.chainedB.chainedC.allowedC }}").render('a'=>@a) assert_equal 'another_allowedC', Liquid::Template.parse("{{ a.chainedB.chainedC.another_allowedC }}").render('a'=>@a) assert_equal '', Liquid::Template.parse("{{ a.restricted }}").render('a'=>@a) assert_equal '', Liquid::Template.parse("{{ a.unknown }}").render('a'=>@a) end end # ModuleExTest liquid-2.6.1/test/test_helper.rb0000644000004100000410000000146512271557765016710 0ustar www-datawww-data#!/usr/bin/env ruby require 'test/unit' require 'test/unit/assertions' begin require 'ruby-debug' rescue LoadError puts "Couldn't load ruby-debug. gem install ruby-debug if you need it." end require File.join(File.dirname(__FILE__), '..', 'lib', 'liquid') module Test module Unit module Assertions include Liquid def assert_template_result(expected, template, assigns = {}, message = nil) assert_equal expected, Template.parse(template).render(assigns) end def assert_template_result_matches(expected, template, assigns = {}, message = nil) return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp assert_match expected, Template.parse(template).render(assigns) end end # Assertions end # Unit end # Test liquid-2.6.1/History.md0000644000004100000410000001242412271557765015046 0ustar www-datawww-data# Liquid Version History 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. ## 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 * ... * Bugfix for #106: fix example servlet [gnowoel] * Bugfix for #97: strip_html filter supports multi-line tags [Jo Liss, joliss] * Bugfix for #114: strip_html filter supports style tags [James Allardice, jamesallardice] * Bugfix for #117: 'now' support for date filter in Ruby 1.9 [Notre Dame Webgroup, ndwebgroup] * Bugfix for #166: truncate filter on UTF-8 strings with Ruby 1.8 [Florian Weingarten, fw42] * Bugfix for #204: 'raw' parsing bug [Florian Weingarten, fw42] * Bugfix for #150: 'for' parsing bug [Peter Schröder, phoet] * Bugfix for #126: Strip CRLF in strip_newline [Peter Schröder, phoet] * Bugfix for #174, "can't convert Fixnum into String" for "replace" [wǒ_is神仙, jsw0528] * Allow a Liquid::Drop to be passed into Template#render [Daniel Huckstep, darkhelmet] * Resource limits [Florian Weingarten, fw42] * Add reverse filter [Jay Strybis, unreal] * Add utf-8 support * Use array instead of Hash to keep the registered filters [Tasos Stathopoulos, astathopoulos] * Cache tokenized partial templates [Tom Burns, boourns] * Avoid warnings in Ruby 1.9.3 [Marcus Stollsteimer, stomar] * 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.4 / 2013-11-11 / branch "2.5-stable" * Fix "can't convert Fixnum into String" for "replace", see #173, [wǒ_is神仙, jsw0528] ## 2.5.3 / 2013-10-09 * #232, #234, #237: Fix map filter bugs [Florian Weingarten, fw42] ## 2.5.2 / 2013-09-03 / deleted Yanked from rubygems, as it contained too many changes that broke compatibility. Those changes will be on following major releases. ## 2.5.1 / 2013-07-24 * #230: Fix security issue with map filter, Use invoke_drop in map filter [Florian Weingarten, fw42] ## 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 * Performance improvements * Allow filters in `assign` * Add `modulo` filter * Ruby 1.8, 1.9, and Rubinius compatibility fixes * Add support for `quoted['references']` in `tablerow` * Add support for Enumerable to `tablerow` * `strip_html` filter removes html comments ## 2.3.0 / 2011-10-16 * Several speed/memory improvements * Numerous bug fixes * Added support for MRI 1.9, Rubinius, and JRuby * Added support for integer drop parameters * Added epoch support to `date` filter * New `raw` tag that suppresses parsing * Added `else` option to `for` tag * New `increment` tag * New `split` filter ## 2.2.1 / 2010-08-23 * Added support for literal tags ## 2.2.0 / 2010-08-22 * Compatible with Ruby 1.8.7, 1.9.1 and 1.9.2-p0 * Merged some changed made by the community ## 1.9.0 / 2008-03-04 * Fixed gem install rake task * Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins ## Before 1.9.0 * Added If with or / and expressions * Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans. To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods. * Added more tags to standard library * Added include tag ( like partials in rails ) * [...] Gazillion of detail improvements * Added strainers as filter hosts for better security [Tobias Luetke] * Fixed that rails integration would call filter with the wrong "self" [Michael Geary] * Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the filter which was obviously misleading [Tobias Luetke] * Removed count helper from standard lib. use size [Tobias Luetke] * Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond] * Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond] {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }} * Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke] class ProductDrop < Liquid::Drop def top_sales Shop.current.products.find(:all, :order => 'sales', :limit => 10 ) end end t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' ) t.render('product' => ProductDrop.new ) * Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond] liquid-2.6.1/checksums.yaml.gz0000444000004100000410000000041412271557765016345 0ustar www-datawww-data3:Re=V@"eY{XƗӻTfnW_zx_>׀b<䠽BaF~r{v4׶,:6/AZꬶmIBR} vͩi24}~@5bّ`&m٬,~!K,'> I¨c?Ǚb|.>i-mx.Sv6gR3*xʈE1|liquid-2.6.1/README.md0000644000004100000410000000374712271557765014352 0ustar www-datawww-data# Liquid template engine * [Contributing guidelines](CONTRIBUTING.md) * [Version history](History.md) * [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics) * [Liquid Wiki from Shopify](http://wiki.shopify.com/Liquid) * [Website](http://liquidmarkup.org/) ## Introduction Liquid is a template engine which was written with very specific requirements: * It has to have beautiful and simple markup. Template engines which don't produce good looking markup are no fun to use. * It needs to be non evaling and secure. Liquid templates are made so that users can edit them. You don't want to run code on your server which your users wrote. * It has to be stateless. Compile and render steps have to be separate so that the expensive parsing and compiling can be done once and later on you can just render it passing in a hash with local variables and objects. ## Why you should use Liquid * You want to allow your users to edit the appearance of your application but don't want them to run **insecure code on your server**. * You want to render templates directly from the database. * You like smarty (PHP) style template engines. * You need a template engine which does HTML just as well as emails. * You don't like the markup of your current templating engine. ## What does it look like? ```html
    {% for product in products %}
  • {{ product.name }}

    Only {{ product.price | price }} {{ product.description | prettyprint | paragraph }}
  • {% endfor %}
``` ## How to use Liquid Liquid supports a very simple API based around the Liquid::Template class. For standard use you can just pass it the content of a file and call render with a parameters hash. ```ruby @template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template @template.render('name' => 'tobi') # => "hi tobi" ``` [![Build Status](https://secure.travis-ci.org/Shopify/liquid.png)](http://travis-ci.org/Shopify/liquid)