- Check arity of proc before calling, to prevent error when using ruby 1.9.1+

Code beautifer
  - context.rb
This commit is contained in:
DBA
2010-08-23 01:30:03 +08:00
committed by Tobias Lütke
parent c72c84ea9b
commit 8304a046c9
+135 -140
View File
@@ -28,8 +28,9 @@ module Liquid
@strainer ||= Strainer.create(self) @strainer ||= Strainer.create(self)
end end
# adds filters to this context. # Adds filters to this context.
# this does not register the filters with the main Template object. see <tt>Template.register_filter</tt> #
# Note that this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
# for that # for that
def add_filters(filters) def add_filters(filters)
filters = [filters].flatten.compact filters = [filters].flatten.compact
@@ -52,7 +53,6 @@ module Liquid
end end
end end
def invoke(method, *args) def invoke(method, *args)
if strainer.respond_to?(method) if strainer.respond_to?(method)
strainer.__send__(method, *args) strainer.__send__(method, *args)
@@ -61,43 +61,44 @@ module Liquid
end end
end end
# push new local scope on the stack. use <tt>Context#stack</tt> instead # Push new local scope on the stack. use <tt>Context#stack</tt> instead
def push(new_scope={}) def push(new_scope={})
raise StackLevelError, "Nesting too deep" if @scopes.length > 100 raise StackLevelError, "Nesting too deep" if @scopes.length > 100
@scopes.unshift(new_scope) @scopes.unshift(new_scope)
end end
# merge a hash of variables in the current local scope # Merge a hash of variables in the current local scope
def merge(new_scopes) def merge(new_scopes)
@scopes[0].merge!(new_scopes) @scopes[0].merge!(new_scopes)
end end
# pop from the stack. use <tt>Context#stack</tt> instead # Pop from the stack. use <tt>Context#stack</tt> instead
def pop def pop
raise ContextError if @scopes.size == 1 raise ContextError if @scopes.size == 1
@scopes.shift @scopes.shift
end end
# pushes a new local scope on the stack, pops it at the end of the block # Pushes a new local scope on the stack, pops it at the end of the block
# #
# Example: # Example:
#
# context.stack do # context.stack do
# context['var'] = 'hi' # context['var'] = 'hi'
# end # end
# context['var] #=> nil
# #
# context['var] #=> nil
def stack(new_scope={},&block) def stack(new_scope={},&block)
result = nil result = nil
push(new_scope) push(new_scope)
begin begin
result = yield result = yield
ensure ensure
pop pop
end end
result result
end end
def clear_instance_assigns def clear_instance_assigns
@scopes[0] = {} @scopes[0] = {}
end end
@@ -116,139 +117,133 @@ module Liquid
end end
private private
# Look up variable, either resolve directly after considering the name. We can directly handle
# Look up variable, either resolve directly after considering the name. We can directly handle # Strings, digits, floats and booleans (true,false).
# Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and # 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. # 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 # Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
# #
# Example: # Example:
# # products == empty #=> products.empty?
# products == empty #=> products.empty? def resolve(key)
# case key
def resolve(key) when nil, 'nil', 'null', ''
case key nil
when nil, 'nil', 'null', '' when 'true'
nil true
when 'true' when 'false'
true false
when 'false' when 'blank'
false :blank?
when 'blank' when 'empty' # Single quoted strings
:blank? :empty?
when 'empty' when /^'(.*)'$/ # Double quoted strings
:empty? $1.to_s
# Single quoted strings when /^"(.*)"$/ # Integer and floats
when /^'(.*)'$/ $1.to_s
$1.to_s when /^(\d+)$/ # Ranges
# Double quoted strings $1.to_i
when /^"(.*)"$/ when /^\((\S+)\.\.(\S+)\)$/ # Floats
$1.to_s (resolve($1).to_i..resolve($2).to_i)
# Integer and floats when /^(\d[\d\.]+)$/
when /^(\d+)$/ $1.to_f
$1.to_i else
# Ranges variable(key)
when /^\((\S+)\.\.(\S+)\)$/
(resolve($1).to_i..resolve($2).to_i)
# Floats
when /^(\d[\d\.]+)$/
$1.to_f
else
variable(key)
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) }
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
end end
object # Fetches an object starting at the local scope and then moving up the hierachy
end def find_variable(key)
scope = @scopes.find { |s| s.has_key?(key) }
def lookup_and_evaluate(obj, key)
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=) if scope.nil?
obj[key] = value.call(self) @environments.each do |e|
else if variable = lookup_and_evaluate(e, key)
value scope = e
end break
end end
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
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 end
end
# Resolves namespaced queries gracefully.
end #
end # 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