Merge remote-tracking branch 'upstream/master' into correct-if-nodelists

Conflicts:
	test/liquid/tags/for_tag_test.rb
	test/liquid/tags/if_else_tag_test.rb
This commit is contained in:
Nicholas Jones
2014-01-13 12:43:43 -08:00
36 changed files with 344 additions and 89 deletions
+1 -1
View File
@@ -135,7 +135,7 @@ module Liquid
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)
context.increment_used_resources(:render_length_current, token_output)
if context.resource_limits_reached?
context.resource_limits[:reached] = true
raise MemoryError.new("Memory limits exceeded")
+21 -3
View File
@@ -25,6 +25,15 @@ module Liquid
squash_instance_assigns_with_environments
@interrupts = []
@filters = []
end
def increment_used_resources(key, obj)
@resource_limits[key] += if obj.class.ancestors & [ String, Array, Hash ] != []
obj.length
else
1
end
end
def resource_limits_reached?
@@ -34,7 +43,7 @@ module Liquid
end
def strainer
@strainer ||= Strainer.create(self)
@strainer ||= Strainer.create(self, @filters)
end
# Adds filters to this context.
@@ -43,11 +52,20 @@ module Liquid
# 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
# If strainer is already setup then there's no choice but to use a runtime
# extend call. If strainer is not yet created, we can utilize strainers
# cached class based API, which avoids busting the method cache.
if @strainer
filters.each do |f|
strainer.extend(f)
end
else
@filters.concat filters
end
end
+4 -5
View File
@@ -59,13 +59,12 @@ module Liquid
# Check for method existence without invoking respond_to?, which creates symbols
def self.invokable?(method_name)
unless @invokable_methods
# Ruby 1.8 compatibility: call to_s on method names (which are strings in 1.8, but already symbols in 1.9)
blacklist = (Liquid::Drop.public_instance_methods + [:each]).map(&:to_s)
blacklist = Liquid::Drop.public_instance_methods + [:each]
if include?(Enumerable)
blacklist += Enumerable.public_instance_methods.map(&:to_s)
blacklist -= [:sort, :count, :first, :min, :max, :include?].map(&:to_s)
blacklist += Enumerable.public_instance_methods
blacklist -= [:sort, :count, :first, :min, :max, :include?]
end
whitelist = [:to_liquid] + (public_instance_methods.map(&:to_s) - blacklist.map(&:to_s))
whitelist = [:to_liquid] + (public_instance_methods - blacklist)
@invokable_methods = Set.new(whitelist.map(&:to_s))
end
@invokable_methods.include?(method_name.to_s)
+14 -3
View File
@@ -31,11 +31,22 @@ module Liquid
# file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid"
# file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid"
#
# Optionally in the second argument you can specify a custom pattern for template filenames.
# The Kernel::sprintf format specification is used.
# Default pattern is "_%s.liquid".
#
# Example:
#
# file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html")
#
# file_system.full_path("index") # => "/some/path/index.html"
#
class LocalFileSystem
attr_accessor :root
def initialize(root)
def initialize(root, pattern = "_%s.liquid")
@root = root
@pattern = pattern
end
def read_template_file(template_path, context)
@@ -49,9 +60,9 @@ module Liquid
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")
File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
else
File.join(root, "_#{template_path}.liquid")
File.join(root, @pattern % template_path)
end
raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/
+1 -1
View File
@@ -23,7 +23,7 @@ module Liquid
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)
collection = Utils.slice_collection(collection, from, to)
length = collection.length
+1 -1
View File
@@ -24,7 +24,7 @@ module Liquid
private
def interpolate(name, vars)
name.gsub(/%{(\w+)}/) {
name.gsub(/%\{(\w+)\}/) {
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
"#{vars[$1.to_sym]}"
}
+2
View File
@@ -15,6 +15,7 @@ module Liquid
SINGLE_STRING_LITERAL = /'[^\']*'/
DOUBLE_STRING_LITERAL = /"[^\"]*"/
NUMBER_LITERAL = /-?\d+(\.\d+)?/
DOTDOT = /\.\./
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains/
def initialize(input)
@@ -32,6 +33,7 @@ module Liquid
when t = @ss.scan(DOUBLE_STRING_LITERAL) then [:string, t]
when t = @ss.scan(NUMBER_LITERAL) then [:number, t]
when t = @ss.scan(IDENTIFIER) then [:id, t]
when t = @ss.scan(DOTDOT) then [:dotdot, t]
else
c = @ss.getch
if s = SPECIALS[c]
+1 -2
View File
@@ -53,8 +53,7 @@ module Liquid
elsif token.first == :open_round
consume
first = expression
consume(:dot)
consume(:dot)
consume(:dotdot)
last = expression
consume(:close_round)
"(#{first}..#{last})"
+10 -6
View File
@@ -4,6 +4,8 @@ require 'bigdecimal'
module Liquid
module StandardFilters
HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
# Return the size of an array or of an string
def size(input)
@@ -31,9 +33,7 @@ module Liquid
end
def escape_once(input)
ActionView::Helpers::TagHelper.escape_once(input)
rescue NameError
input
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
end
alias_method :h, :escape
@@ -43,8 +43,7 @@ module Liquid
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
input.length > length.to_i ? input[0...l] + truncate_string : input
end
def truncatewords(input, words = 15, truncate_string = "...")
@@ -245,12 +244,17 @@ module Liquid
apply_operation(input, operand, :%)
end
def default(input, default_value = "")
is_blank = input.respond_to?(:empty?) ? input.empty? : !input
is_blank ? default_value : input
end
private
def flatten_if_necessary(input)
ary = if input.is_a?(Array)
input.flatten
elsif input.kind_of?(Enumerable)
elsif input.is_a?(Enumerable) && !input.is_a?(Hash)
input
else
[input].flatten
+12 -4
View File
@@ -11,6 +11,11 @@ module Liquid
@@filters = []
@@known_filters = Set.new
@@known_methods = Set.new
@@strainer_class_cache = Hash.new do |hash, filters|
hash[filters] = Class.new(Strainer) do
filters.each { |f| include f }
end
end
def initialize(context)
@context = context
@@ -32,10 +37,13 @@ module Liquid
end
end
def self.create(context)
strainer = Strainer.new(context)
@@filters.each { |m| strainer.extend(m) }
strainer
def self.strainer_class_cache
@@strainer_class_cache
end
def self.create(context, filters = [])
filters = @@filters + filters
strainer_class_cache[filters].new(context)
end
def invoke(method, *args)
+1 -1
View File
@@ -31,7 +31,7 @@ module Liquid
end
def blank?
@blank || true
@blank || false
end
def parse_with_selected_parser(markup)
+4 -1
View File
@@ -25,10 +25,13 @@ module Liquid
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)
context.increment_used_resources(:assign_score_current, val)
''
end
def blank?
true
end
end
Template.register_tag('assign', Assign)
+1 -1
View File
@@ -27,7 +27,7 @@ module Liquid
def render(context)
output = super
context.scopes.last[@to] = output
context.resource_limits[:assign_score_current] += (output.respond_to?(:length) ? output.length : 1)
context.increment_used_resources(:assign_score_current, output)
''
end
+3
View File
@@ -4,6 +4,9 @@ module Liquid
''
end
def unknown_tag(tag, markup, tokens)
end
def blank?
true
end
+1 -2
View File
@@ -83,8 +83,7 @@ module Liquid
limit = context[@attributes['limit']]
to = limit ? limit.to_i + from : nil
segment = Utils.slice_collection_using_each(collection, from, to)
segment = Utils.slice_collection(collection, from, to)
return render_else(context) if segment.empty?
+3 -1
View File
@@ -12,6 +12,7 @@ module Liquid
class If < Block
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 = []
@@ -67,7 +68,8 @@ module Liquid
raise(SyntaxError.new(options[:locale].t("errors.syntax.if"))) unless expressions.shift.to_s =~ Syntax
new_condition = Condition.new($1, $2, $3)
new_condition.send(operator.to_sym, condition)
raise(SyntaxError.new(options[:locale].t("errors.syntax.if"))) unless BOOLEAN_OPERATORS.include?(operator)
new_condition.send(operator, condition)
condition = new_condition
end
+3 -2
View File
@@ -51,13 +51,14 @@ module Liquid
context[key] = context[value]
end
context_variable_name = @template_name[1..-2].split('/').last
if variable.is_a?(Array)
variable.collect do |var|
context[@template_name[1..-2]] = var
context[context_variable_name] = var
partial.render(context)
end
else
context[@template_name[1..-2]] = variable
context[context_variable_name] = variable
partial.render(context)
end
end
+13 -4
View File
@@ -1,5 +1,18 @@
module Liquid
module Utils
def self.slice_collection(collection, from, to)
if (from != 0 || to != nil) && collection.respond_to?(:load_slice)
collection.load_slice(from, to)
else
slice_collection_using_each(collection, from, to)
end
end
def self.non_blank_string?(collection)
collection.is_a?(String) && collection != ''
end
def self.slice_collection_using_each(collection, from, to)
segments = []
index = 0
@@ -22,9 +35,5 @@ module Liquid
segments
end
def self.non_blank_string?(collection)
collection.is_a?(String) && collection != ''
end
end
end
+1 -1
View File
@@ -1,4 +1,4 @@
# encoding: utf-8
module Liquid
VERSION = "2.6.0"
VERSION = "3.0.0"
end