mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
Compare commits
25 Commits
StrainerTe
...
symbol-fil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
391a6eb92b | ||
|
|
89d206d8c6 | ||
|
|
5532df880f | ||
|
|
2b11efc3ae | ||
|
|
a1d982ca76 | ||
|
|
03be7f1ee3 | ||
|
|
1ced4eaf10 | ||
|
|
4970167726 | ||
|
|
065ccbc4aa | ||
|
|
1feaa63813 | ||
|
|
8541c6be35 | ||
|
|
18654526c8 | ||
|
|
bd1f7f9492 | ||
|
|
40d75dd283 | ||
|
|
f5011365f1 | ||
|
|
ebbd046c92 | ||
|
|
b9979088ec | ||
|
|
bd0e53bd2e | ||
|
|
4b586f4105 | ||
|
|
0410119d5f | ||
|
|
c2f67398d0 | ||
|
|
81149344a5 | ||
|
|
e9b649b345 | ||
|
|
9c538f4237 | ||
|
|
c08a358a2b |
@@ -13,6 +13,8 @@ matrix:
|
||||
- rvm: *latest_ruby
|
||||
script: bundle exec rake memory_profile:run
|
||||
name: Profiling Memory Usage
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -22,6 +22,6 @@ group :test do
|
||||
gem 'rubocop-performance', require: false
|
||||
|
||||
platform :mri, :truffleruby do
|
||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'master'
|
||||
gem 'liquid-c', github: 'Shopify/liquid-c', branch: 'symbol-filter-names'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
* Split Strainer class as a factory and a template (#1208) [Thierry Joyal]
|
||||
* Remove handling of a nil context in the Strainer class (#1218) [Thierry Joyal]
|
||||
* StaticRegisters#fetch to raise on missing key (#1250) [Thierry Joyal]
|
||||
|
||||
## 4.0.3 / 2019-03-12
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ require 'liquid/expression'
|
||||
require 'liquid/context'
|
||||
require 'liquid/parser_switching'
|
||||
require 'liquid/tag'
|
||||
require 'liquid/tag/disabler'
|
||||
require 'liquid/tag/disableable'
|
||||
require 'liquid/block'
|
||||
require 'liquid/block_body'
|
||||
require 'liquid/document'
|
||||
@@ -86,4 +88,3 @@ require 'liquid/template_factory'
|
||||
# Load all the tags of the standard library
|
||||
#
|
||||
Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
|
||||
Dir["#{__dir__}/liquid/registers/*.rb"].each { |f| require f }
|
||||
|
||||
@@ -171,13 +171,7 @@ module Liquid
|
||||
private
|
||||
|
||||
def render_node(context, output, node)
|
||||
if node.disabled?(context)
|
||||
output << node.disabled_error_message
|
||||
return
|
||||
end
|
||||
disable_tags(context, node.disabled_tags) do
|
||||
node.render_to_output_buffer(context, output)
|
||||
end
|
||||
node.render_to_output_buffer(context, output)
|
||||
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
||||
context.handle_error(e, node.line_number)
|
||||
rescue ::StandardError => e
|
||||
@@ -185,11 +179,6 @@ module Liquid
|
||||
output << context.handle_error(e, line_number)
|
||||
end
|
||||
|
||||
def disable_tags(context, tags, &block)
|
||||
return yield if tags.empty?
|
||||
context.registers[:disabled_tags].disable(tags, &block)
|
||||
end
|
||||
|
||||
def raise_if_resource_limits_reached(context, length)
|
||||
context.resource_limits.render_length += length
|
||||
return unless context.resource_limits.reached?
|
||||
|
||||
@@ -44,6 +44,7 @@ module Liquid
|
||||
@interrupts = []
|
||||
@filters = []
|
||||
@global_filter = nil
|
||||
@disabled_tags = {}
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
|
||||
@@ -144,6 +145,7 @@ module Liquid
|
||||
subcontext.strainer = nil
|
||||
subcontext.errors = errors
|
||||
subcontext.warnings = warnings
|
||||
subcontext.disabled_tags = @disabled_tags
|
||||
end
|
||||
end
|
||||
|
||||
@@ -208,9 +210,24 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def with_disabled_tags(tag_names)
|
||||
tag_names.each do |name|
|
||||
@disabled_tags[name] = @disabled_tags.fetch(name, 0) + 1
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
tag_names.each do |name|
|
||||
@disabled_tags[name] -= 1
|
||||
end
|
||||
end
|
||||
|
||||
def tag_disabled?(tag_name)
|
||||
@disabled_tags.fetch(tag_name, 0) > 0
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :base_scope_depth, :warnings, :errors, :strainer, :filters
|
||||
attr_writer :base_scope_depth, :warnings, :errors, :strainer, :filters, :disabled_tags
|
||||
|
||||
private
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ module Liquid
|
||||
StandardError = Class.new(Error)
|
||||
SyntaxError = Class.new(Error)
|
||||
StackLevelError = Class.new(Error)
|
||||
TaintedError = Class.new(Error)
|
||||
MemoryError = Class.new(Error)
|
||||
ZeroDivisionError = Class.new(Error)
|
||||
FloatDomainError = Class.new(Error)
|
||||
@@ -54,5 +53,6 @@ module Liquid
|
||||
UndefinedDropMethod = Class.new(Error)
|
||||
UndefinedFilter = Class.new(Error)
|
||||
MethodOverrideError = Class.new(Error)
|
||||
DisabledError = Class.new(Error)
|
||||
InternalError = Class.new(Error)
|
||||
end
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class BlockBody
|
||||
def render_node_with_profiling(context, output, node)
|
||||
module BlockBodyProfilingHook
|
||||
def render_node(context, output, node)
|
||||
Profiler.profile_node_render(node) do
|
||||
render_node_without_profiling(context, output, node)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :render_node_without_profiling, :render_node
|
||||
alias_method :render_node, :render_node_with_profiling
|
||||
end
|
||||
BlockBody.prepend(BlockBodyProfilingHook)
|
||||
|
||||
class Include < Tag
|
||||
def render_to_output_buffer_with_profiling(context, output)
|
||||
module IncludeProfilingHook
|
||||
def render_to_output_buffer(context, output)
|
||||
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
||||
render_to_output_buffer_without_profiling(context, output)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :render_to_output_buffer_without_profiling, :render_to_output_buffer
|
||||
alias_method :render_to_output_buffer, :render_to_output_buffer_with_profiling
|
||||
end
|
||||
Include.prepend(IncludeProfilingHook)
|
||||
end
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
module Liquid
|
||||
class DisabledTags < Register
|
||||
def initialize
|
||||
@disabled_tags = {}
|
||||
end
|
||||
|
||||
def disabled?(tag)
|
||||
@disabled_tags.key?(tag) && @disabled_tags[tag] > 0
|
||||
end
|
||||
|
||||
def disable(tags)
|
||||
tags.each(&method(:increment))
|
||||
yield
|
||||
ensure
|
||||
tags.each(&method(:decrement))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def increment(tag)
|
||||
@disabled_tags[tag] ||= 0
|
||||
@disabled_tags[tag] += 1
|
||||
end
|
||||
|
||||
def decrement(tag)
|
||||
@disabled_tags[tag] -= 1
|
||||
end
|
||||
end
|
||||
|
||||
Template.add_register(:disabled_tags, DisabledTags.new)
|
||||
end
|
||||
@@ -41,7 +41,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def escape(input)
|
||||
CGI.escapeHTML(input.to_s).untaint unless input.nil?
|
||||
CGI.escapeHTML(input.to_s) unless input.nil?
|
||||
end
|
||||
alias_method :h, :escape
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
module Liquid
|
||||
class StaticRegisters
|
||||
attr_reader :static, :registers
|
||||
attr_reader :static
|
||||
|
||||
def initialize(registers = {})
|
||||
@static = registers.is_a?(StaticRegisters) ? registers.static : registers
|
||||
@@ -25,8 +25,16 @@ module Liquid
|
||||
@registers.delete(key)
|
||||
end
|
||||
|
||||
def fetch(key, default = nil)
|
||||
key?(key) ? self[key] : default
|
||||
UNDEFINED = Object.new
|
||||
|
||||
def fetch(key, default = UNDEFINED, &block)
|
||||
if @registers.key?(key)
|
||||
@registers.fetch(key)
|
||||
elsif default != UNDEFINED
|
||||
@static.fetch(key, default, &block)
|
||||
else
|
||||
@static.fetch(key, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
|
||||
@@ -24,17 +24,17 @@ module Liquid
|
||||
|
||||
include(filter)
|
||||
|
||||
filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
||||
filter.public_instance_methods.each { |name| filter_methods[name] = true }
|
||||
end
|
||||
|
||||
def invokable?(method)
|
||||
filter_methods.include?(method.to_s)
|
||||
filter_methods.key?(method.to_sym)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filter_methods
|
||||
@filter_methods ||= Set.new
|
||||
@filter_methods ||= {}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -13,15 +13,13 @@ module Liquid
|
||||
tag
|
||||
end
|
||||
|
||||
def disable_tags(*tags)
|
||||
disabled_tags.push(*tags)
|
||||
def disable_tags(*tag_names)
|
||||
@disabled_tags ||= []
|
||||
@disabled_tags.concat(tag_names)
|
||||
prepend(Disabler)
|
||||
end
|
||||
|
||||
private :new
|
||||
|
||||
def disabled_tags
|
||||
@disabled_tags ||= []
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
@@ -46,14 +44,6 @@ module Liquid
|
||||
''
|
||||
end
|
||||
|
||||
def disabled?(context)
|
||||
context.registers[:disabled_tags].disabled?(tag_name)
|
||||
end
|
||||
|
||||
def disabled_error_message
|
||||
"#{tag_name} #{options[:locale].t('errors.disabled.tag')}"
|
||||
end
|
||||
|
||||
# For backwards compatibility with custom tags. In a future release, the semantics
|
||||
# of the `render_to_output_buffer` method will become the default and the `render`
|
||||
# method will be removed.
|
||||
@@ -65,9 +55,5 @@ module Liquid
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
|
||||
def disabled_tags
|
||||
self.class.disabled_tags
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
22
lib/liquid/tag/disableable.rb
Normal file
22
lib/liquid/tag/disableable.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Tag
|
||||
module Disableable
|
||||
def render_to_output_buffer(context, output)
|
||||
if context.tag_disabled?(tag_name)
|
||||
output << disabled_error(context)
|
||||
return
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def disabled_error(context)
|
||||
# raise then rescue the exception so that the Context#exception_renderer can re-raise it
|
||||
raise DisabledError, "#{tag_name} #{parse_context[:locale].t('errors.disabled.tag')}"
|
||||
rescue DisabledError => exc
|
||||
context.handle_error(exc, line_number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
21
lib/liquid/tag/disabler.rb
Normal file
21
lib/liquid/tag/disabler.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Tag
|
||||
module Disabler
|
||||
module ClassMethods
|
||||
attr_reader :disabled_tags
|
||||
end
|
||||
|
||||
def self.prepended(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.with_disabled_tags(self.class.disabled_tags) do
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,10 +12,6 @@ module Liquid
|
||||
class Assign < Tag
|
||||
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
||||
|
||||
def self.syntax_error_translation_key
|
||||
"errors.syntax.assign"
|
||||
end
|
||||
|
||||
attr_reader :to, :from
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
@@ -24,7 +20,7 @@ module Liquid
|
||||
@to = Regexp.last_match(1)
|
||||
@from = Variable.new(Regexp.last_match(2), options)
|
||||
else
|
||||
raise SyntaxError, options[:locale].t(self.class.syntax_error_translation_key)
|
||||
raise SyntaxError, options[:locale].t('errors.syntax.assign')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ module Liquid
|
||||
# {% include 'product' for products %}
|
||||
#
|
||||
class Include < Tag
|
||||
prepend Tag::Disableable
|
||||
|
||||
SYNTAX = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||
Syntax = SYNTAX
|
||||
|
||||
|
||||
@@ -64,23 +64,6 @@ module Liquid
|
||||
attr_accessor :error_mode
|
||||
Template.error_mode = :lax
|
||||
|
||||
attr_reader :taint_mode
|
||||
|
||||
# Sets how strict the taint checker should be.
|
||||
# :lax is the default, and ignores the taint flag completely
|
||||
# :warn adds a warning, but does not interrupt the rendering
|
||||
# :error raises an error when tainted output is used
|
||||
# @deprecated Since it is being deprecated in ruby itself.
|
||||
def taint_mode=(mode)
|
||||
taint_supported = Object.new.taint.tainted?
|
||||
if mode != :lax && !taint_supported
|
||||
raise NotImplementedError, "#{RUBY_ENGINE} #{RUBY_VERSION} doesn't support taint checking"
|
||||
end
|
||||
@taint_mode = mode
|
||||
end
|
||||
|
||||
Template.taint_mode = :lax
|
||||
|
||||
attr_accessor :default_exception_renderer
|
||||
Template.default_exception_renderer = lambda do |exception|
|
||||
exception
|
||||
@@ -97,14 +80,6 @@ module Liquid
|
||||
tags[name.to_s] = klass
|
||||
end
|
||||
|
||||
attr_accessor :registers
|
||||
Template.registers = {}
|
||||
private :registers=
|
||||
|
||||
def add_register(name, klass)
|
||||
registers[name.to_sym] = klass
|
||||
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)
|
||||
@@ -119,14 +94,13 @@ module Liquid
|
||||
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
||||
# See Liquid::Profiler for more information
|
||||
def parse(source, options = {})
|
||||
template = Template.new
|
||||
template.parse(source, options)
|
||||
new.parse(source, options)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@rethrow_errors = false
|
||||
@resource_limits = ResourceLimits.new(self.class.default_resource_limits)
|
||||
@resource_limits = ResourceLimits.new(Template.default_resource_limits)
|
||||
end
|
||||
|
||||
# Parse source code.
|
||||
@@ -212,10 +186,6 @@ module Liquid
|
||||
context.add_filters(args.pop)
|
||||
end
|
||||
|
||||
Template.registers.each do |key, register|
|
||||
context_register[key] = register
|
||||
end
|
||||
|
||||
# Retrying a render resets resource usage
|
||||
context.resource_limits.reset
|
||||
|
||||
|
||||
@@ -86,9 +86,7 @@ module Liquid
|
||||
context.invoke(filter_name, output, *filter_args)
|
||||
end
|
||||
|
||||
obj = context.apply_global_filter(obj)
|
||||
taint_check(context, obj)
|
||||
obj
|
||||
context.apply_global_filter(obj)
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
@@ -125,7 +123,7 @@ module Liquid
|
||||
filter_args << Expression.parse(a)
|
||||
end
|
||||
end
|
||||
result = [filter_name, filter_args]
|
||||
result = [filter_name.to_sym, filter_args]
|
||||
result << keyword_args if keyword_args
|
||||
result
|
||||
end
|
||||
@@ -142,25 +140,6 @@ module Liquid
|
||||
parsed_args
|
||||
end
|
||||
|
||||
def taint_check(context, obj)
|
||||
return if Template.taint_mode == :lax
|
||||
return unless obj.tainted?
|
||||
|
||||
@markup =~ QuotedFragment
|
||||
name = Regexp.last_match(0)
|
||||
|
||||
error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
|
||||
error.line_number = line_number
|
||||
error.template_name = context.template_name
|
||||
|
||||
case Template.taint_mode
|
||||
when :warn
|
||||
context.warnings << error
|
||||
when :error
|
||||
raise error
|
||||
end
|
||||
end
|
||||
|
||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||
def children
|
||||
[@node.name] + @node.filters.flatten
|
||||
|
||||
@@ -27,6 +27,6 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.require_path = "lib"
|
||||
|
||||
s.add_development_dependency('rake', '~> 11.3')
|
||||
s.add_development_dependency('rake', '~> 13.0')
|
||||
s.add_development_dependency('minitest')
|
||||
end
|
||||
|
||||
@@ -49,10 +49,6 @@ class ProductDrop < Liquid::Drop
|
||||
ContextDrop.new
|
||||
end
|
||||
|
||||
def user_input
|
||||
(+"foo").taint
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def callmenot
|
||||
@@ -114,34 +110,6 @@ class DropsTest < Minitest::Test
|
||||
assert_equal(' ', tpl.render!('product' => ProductDrop.new))
|
||||
end
|
||||
|
||||
if taint_supported?
|
||||
def test_rendering_raises_on_tainted_attr
|
||||
with_taint_mode(:error) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input }}')
|
||||
assert_raises TaintedError do
|
||||
tpl.render!('product' => ProductDrop.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_rendering_warns_on_tainted_attr
|
||||
with_taint_mode(:warn) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input }}')
|
||||
context = Context.new('product' => ProductDrop.new)
|
||||
tpl.render!(context)
|
||||
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
|
||||
assert_equal "variable 'product.user_input' is tainted and was not escaped", context.warnings.first.to_s(false)
|
||||
end
|
||||
end
|
||||
|
||||
def test_rendering_doesnt_raise_on_escaped_tainted_attr
|
||||
with_taint_mode(:error) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
|
||||
tpl.render!('product' => ProductDrop.new)
|
||||
end
|
||||
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))
|
||||
@@ -281,7 +249,7 @@ class DropsTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_invokable_methods
|
||||
assert_equal(%w(to_liquid catchall user_input context texts).to_set, ProductDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid catchall context texts).to_set, ProductDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid scopes_as_array loop_pos scopes).to_set, ContextDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid size max min first count).to_set, EnumerableDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid max min sort count first).to_set, RealEnumerableDrop.invokable_methods)
|
||||
|
||||
@@ -238,7 +238,7 @@ class ParseTreeVisitorTest < Minitest::Test
|
||||
def traversal(template)
|
||||
ParseTreeVisitor
|
||||
.for(Template.parse(template).root)
|
||||
.add_callback_for(VariableLookup, &:name)
|
||||
.add_callback_for(VariableLookup) { |node| node.name } # rubocop:disable Style/SymbolProc
|
||||
end
|
||||
|
||||
def visit(template)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DisabledTagsTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
class DisableRaw < Block
|
||||
disable_tags "raw"
|
||||
end
|
||||
|
||||
class DisableRawEcho < Block
|
||||
disable_tags "raw", "echo"
|
||||
end
|
||||
|
||||
def test_disables_raw
|
||||
with_custom_tag('disable', DisableRaw) do
|
||||
assert_template_result 'raw usage is not allowed in this contextfoo', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_echo_and_raw
|
||||
with_custom_tag('disable', DisableRawEcho) do
|
||||
assert_template_result 'raw usage is not allowed in this contextecho usage is not allowed in this context', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -43,15 +43,22 @@ class SecurityTest < Minitest::Test
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: SecurityFilter))
|
||||
end
|
||||
|
||||
def test_does_not_add_filters_to_symbol_table
|
||||
def test_does_not_permanently_add_filters_to_symbol_table
|
||||
current_symbols = Symbol.all_symbols
|
||||
|
||||
test = %( {{ "some_string" | a_bad_filter }} )
|
||||
# MRI imprecisely marks objects found on the C stack, which can result
|
||||
# in uninitialized memory being marked. This can even result in the test failing
|
||||
# deterministically for a given compilation of ruby. Using a separate thread will
|
||||
# keep these writes of the symbol pointer on a separate stack that will be garbage
|
||||
# collected after Thread#join.
|
||||
Thread.new do
|
||||
test = %( {{ "some_string" | a_bad_filter }} )
|
||||
Template.parse(test).render!
|
||||
nil
|
||||
end.join
|
||||
|
||||
template = Template.parse(test)
|
||||
assert_equal([], (Symbol.all_symbols - current_symbols))
|
||||
GC.start
|
||||
|
||||
template.render!
|
||||
assert_equal([], (Symbol.all_symbols - current_symbols))
|
||||
end
|
||||
|
||||
|
||||
51
test/integration/tag/disableable_test.rb
Normal file
51
test/integration/tag/disableable_test.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class TagDisableableTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
class DisableRaw < Block
|
||||
disable_tags "raw"
|
||||
end
|
||||
|
||||
class DisableRawEcho < Block
|
||||
disable_tags "raw", "echo"
|
||||
end
|
||||
|
||||
class DisableableRaw < Liquid::Raw
|
||||
prepend Liquid::Tag::Disableable
|
||||
end
|
||||
|
||||
class DisableableEcho < Liquid::Echo
|
||||
prepend Liquid::Tag::Disableable
|
||||
end
|
||||
|
||||
def test_disables_raw
|
||||
with_disableable_tags do
|
||||
with_custom_tag('disable', DisableRaw) do
|
||||
output = Template.parse('{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}').render
|
||||
assert_equal('Liquid error: raw usage is not allowed in this contextfoo', output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_echo_and_raw
|
||||
with_disableable_tags do
|
||||
with_custom_tag('disable', DisableRawEcho) do
|
||||
output = Template.parse('{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}').render
|
||||
assert_equal('Liquid error: raw usage is not allowed in this contextLiquid error: echo usage is not allowed in this context', output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_disableable_tags
|
||||
with_custom_tag('raw', DisableableRaw) do
|
||||
with_custom_tag('echo', DisableableEcho) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -42,34 +42,6 @@ class RenderTagTest < Minitest::Test
|
||||
assert_template_result('', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}")
|
||||
end
|
||||
|
||||
if taint_supported?
|
||||
def test_render_sets_the_correct_template_name_for_errors
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ unsafe }}')
|
||||
|
||||
with_taint_mode :error do
|
||||
template = Liquid::Template.parse('{% render "snippet", unsafe: unsafe %}')
|
||||
context = Context.new('unsafe' => (+'unsafe').tap(&:taint))
|
||||
template.render(context)
|
||||
|
||||
assert_equal [Liquid::TaintedError], template.errors.map(&:class)
|
||||
assert_equal 'snippet', template.errors.first.template_name
|
||||
end
|
||||
end
|
||||
|
||||
def test_render_sets_the_correct_template_name_for_warnings
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ unsafe }}')
|
||||
|
||||
with_taint_mode :warn do
|
||||
template = Liquid::Template.parse('{% render "snippet", unsafe: unsafe %}')
|
||||
context = Context.new('unsafe' => (+'unsafe').tap(&:taint))
|
||||
template.render(context)
|
||||
|
||||
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
|
||||
assert_equal 'snippet', context.warnings.first.template_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_render_does_not_mutate_parent_scope
|
||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{% assign inner = 1 %}')
|
||||
assert_template_result('', "{% render 'snippet' %}{{ inner }}")
|
||||
@@ -155,7 +127,10 @@ class RenderTagTest < Minitest::Test
|
||||
'test_include' => '{% include "foo" %}'
|
||||
)
|
||||
|
||||
assert_template_result('include usage is not allowed in this context', '{% render "test_include" %}')
|
||||
exc = assert_raises(Liquid::DisabledError) do
|
||||
Liquid::Template.parse('{% render "test_include" %}').render!
|
||||
end
|
||||
assert_equal('Liquid error: include usage is not allowed in this context', exc.message)
|
||||
end
|
||||
|
||||
def test_includes_will_not_render_inside_nested_sibling_tags
|
||||
@@ -165,7 +140,8 @@ class RenderTagTest < Minitest::Test
|
||||
'test_include' => '{% include "foo" %}'
|
||||
)
|
||||
|
||||
assert_template_result('include usage is not allowed in this contextinclude usage is not allowed in this context', '{% render "nested_render_with_sibling_include" %}')
|
||||
output = Liquid::Template.parse('{% render "nested_render_with_sibling_include" %}').render
|
||||
assert_equal('Liquid error: include usage is not allowed in this contextLiquid error: include usage is not allowed in this context', output)
|
||||
end
|
||||
|
||||
def test_render_tag_with
|
||||
|
||||
@@ -361,12 +361,4 @@ class TemplateTest < Minitest::Test
|
||||
result = t.render('x' => 1, 'y' => 5)
|
||||
assert_equal('12345', result)
|
||||
end
|
||||
|
||||
unless taint_supported?
|
||||
def test_taint_mode
|
||||
assert_raises(NotImplementedError) do
|
||||
Template.taint_mode = :warn
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,10 +32,6 @@ module Minitest
|
||||
def fixture(name)
|
||||
File.join(File.expand_path(__dir__), "fixtures", name)
|
||||
end
|
||||
|
||||
def self.taint_supported?
|
||||
Object.new.taint.tainted?
|
||||
end
|
||||
end
|
||||
|
||||
module Assertions
|
||||
@@ -93,14 +89,6 @@ module Minitest
|
||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, original_global_filters)
|
||||
end
|
||||
|
||||
def with_taint_mode(mode)
|
||||
old_mode = Liquid::Template.taint_mode
|
||||
Liquid::Template.taint_mode = mode
|
||||
yield
|
||||
ensure
|
||||
Liquid::Template.taint_mode = old_mode
|
||||
end
|
||||
|
||||
def with_error_mode(mode)
|
||||
old_mode = Liquid::Template.error_mode
|
||||
Liquid::Template.error_mode = mode
|
||||
@@ -110,10 +98,17 @@ module Minitest
|
||||
end
|
||||
|
||||
def with_custom_tag(tag_name, tag_class)
|
||||
Liquid::Template.register_tag(tag_name, tag_class)
|
||||
yield
|
||||
ensure
|
||||
Liquid::Template.tags.delete(tag_name)
|
||||
old_tag = Liquid::Template.tags[tag_name]
|
||||
begin
|
||||
Liquid::Template.register_tag(tag_name, tag_class)
|
||||
yield
|
||||
ensure
|
||||
if old_tag
|
||||
Liquid::Template.tags[tag_name] = old_tag
|
||||
else
|
||||
Liquid::Template.tags.delete(tag_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -563,6 +563,35 @@ class ContextUnitTest < Minitest::Test
|
||||
assert_equal('my filter result', template.render(subcontext))
|
||||
end
|
||||
|
||||
def test_disables_tag_specified
|
||||
context = Context.new
|
||||
context.with_disabled_tags(%w(foo bar)) do
|
||||
assert_equal true, context.tag_disabled?("foo")
|
||||
assert_equal true, context.tag_disabled?("bar")
|
||||
assert_equal false, context.tag_disabled?("unknown")
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_nested_tags
|
||||
context = Context.new
|
||||
context.with_disabled_tags(["foo"]) do
|
||||
context.with_disabled_tags(["foo"]) do
|
||||
assert_equal true, context.tag_disabled?("foo")
|
||||
assert_equal false, context.tag_disabled?("bar")
|
||||
end
|
||||
context.with_disabled_tags(["bar"]) do
|
||||
assert_equal true, context.tag_disabled?("foo")
|
||||
assert_equal true, context.tag_disabled?("bar")
|
||||
context.with_disabled_tags(["foo"]) do
|
||||
assert_equal true, context.tag_disabled?("foo")
|
||||
assert_equal true, context.tag_disabled?("bar")
|
||||
end
|
||||
end
|
||||
assert_equal true, context.tag_disabled?("foo")
|
||||
assert_equal false, context.tag_disabled?("bar")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_no_object_allocations
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DisabledTagsUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_disables_tag_specified
|
||||
register = DisabledTags.new
|
||||
register.disable(%w(foo bar)) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal true, register.disabled?("bar")
|
||||
assert_equal false, register.disabled?("unknown")
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_nested_tags
|
||||
register = DisabledTags.new
|
||||
register.disable(["foo"]) do
|
||||
register.disable(["foo"]) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal false, register.disabled?("bar")
|
||||
end
|
||||
register.disable(["bar"]) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal true, register.disabled?("bar")
|
||||
register.disable(["foo"]) do
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal true, register.disabled?("bar")
|
||||
end
|
||||
end
|
||||
assert_equal true, register.disabled?("foo")
|
||||
assert_equal false, register.disabled?("bar")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,244 +5,152 @@ require 'test_helper'
|
||||
class StaticRegistersUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def set
|
||||
static_register = StaticRegisters.new
|
||||
static_register[nil] = true
|
||||
static_register[1] = :one
|
||||
static_register[:one] = "one"
|
||||
static_register["two"] = "three"
|
||||
static_register["two"] = 3
|
||||
static_register[false] = nil
|
||||
def test_set
|
||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register[:b] = 22
|
||||
static_register[:c] = 33
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.registers)
|
||||
|
||||
static_register
|
||||
assert_equal(1, static_register[:a])
|
||||
assert_equal(22, static_register[:b])
|
||||
assert_equal(33, static_register[:c])
|
||||
end
|
||||
|
||||
def test_get
|
||||
static_register = set
|
||||
def test_get_missing_key
|
||||
static_register = StaticRegisters.new
|
||||
|
||||
assert_equal(true, static_register[nil])
|
||||
assert_equal(:one, static_register[1])
|
||||
assert_equal("one", static_register[:one])
|
||||
assert_equal(3, static_register["two"])
|
||||
assert_nil(static_register[false])
|
||||
assert_nil(static_register["unknown"])
|
||||
assert_nil(static_register[:missing])
|
||||
end
|
||||
|
||||
def test_delete
|
||||
static_register = set
|
||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register[:b] = 22
|
||||
static_register[:c] = 33
|
||||
|
||||
assert_equal(true, static_register.delete(nil))
|
||||
assert_equal(:one, static_register.delete(1))
|
||||
assert_equal("one", static_register.delete(:one))
|
||||
assert_equal(3, static_register.delete("two"))
|
||||
assert_nil(static_register.delete(false))
|
||||
assert_nil(static_register.delete("unknown"))
|
||||
assert_nil(static_register.delete(:a))
|
||||
|
||||
assert_equal({}, static_register.registers)
|
||||
assert_equal(22, static_register.delete(:b))
|
||||
|
||||
assert_equal(33, static_register.delete(:c))
|
||||
assert_nil(static_register[:c])
|
||||
|
||||
assert_nil(static_register.delete(:d))
|
||||
end
|
||||
|
||||
def test_fetch
|
||||
static_register = set
|
||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register[:b] = 22
|
||||
static_register[:c] = 33
|
||||
|
||||
assert_equal(true, static_register.fetch(nil))
|
||||
assert_equal(:one, static_register.fetch(1))
|
||||
assert_equal("one", static_register.fetch(:one))
|
||||
assert_equal(3, static_register.fetch("two"))
|
||||
assert_nil(static_register.fetch(false))
|
||||
assert_nil(static_register.fetch("unknown"))
|
||||
end
|
||||
assert_equal(1, static_register.fetch(:a))
|
||||
assert_equal(1, static_register.fetch(:a, "default"))
|
||||
assert_equal(22, static_register.fetch(:b))
|
||||
assert_equal(22, static_register.fetch(:b, "default"))
|
||||
assert_equal(33, static_register.fetch(:c))
|
||||
assert_equal(33, static_register.fetch(:c, "default"))
|
||||
|
||||
def test_fetch_default
|
||||
static_register = StaticRegisters.new
|
||||
assert_raises(KeyError) do
|
||||
static_register.fetch(:d)
|
||||
end
|
||||
assert_equal("default", static_register.fetch(:d, "default"))
|
||||
|
||||
assert_equal(true, static_register.fetch(nil, true))
|
||||
assert_equal(:one, static_register.fetch(1, :one))
|
||||
assert_equal("one", static_register.fetch(:one, "one"))
|
||||
assert_equal(3, static_register.fetch("two", 3))
|
||||
assert_nil(static_register.fetch(false, nil))
|
||||
result = static_register.fetch(:d) { "default" }
|
||||
assert_equal("default", result)
|
||||
|
||||
result = static_register.fetch(:d, "default 1") { "default 2" }
|
||||
assert_equal("default 2", result)
|
||||
end
|
||||
|
||||
def test_key
|
||||
static_register = set
|
||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register[:b] = 22
|
||||
static_register[:c] = 33
|
||||
|
||||
assert_equal(true, static_register.key?(nil))
|
||||
assert_equal(true, static_register.key?(1))
|
||||
assert_equal(true, static_register.key?(:one))
|
||||
assert_equal(true, static_register.key?("two"))
|
||||
assert_equal(true, static_register.key?(false))
|
||||
assert_equal(false, static_register.key?("unknown"))
|
||||
assert_equal(false, static_register.key?(true))
|
||||
end
|
||||
|
||||
def set_with_static
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register[nil] = false
|
||||
static_register["two"] = 4
|
||||
static_register[true] = "foo"
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal({ nil => false, "two" => 4, true => "foo" }, static_register.registers)
|
||||
|
||||
static_register
|
||||
end
|
||||
|
||||
def test_get_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal(false, static_register[nil])
|
||||
assert_equal(:one, static_register[1])
|
||||
assert_equal("one", static_register[:one])
|
||||
assert_equal(4, static_register["two"])
|
||||
assert_equal("foo", static_register[true])
|
||||
assert_nil(static_register[false])
|
||||
end
|
||||
|
||||
def test_delete_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal(false, static_register.delete(nil))
|
||||
assert_equal(4, static_register.delete("two"))
|
||||
assert_equal("foo", static_register.delete(true))
|
||||
assert_nil(static_register.delete("unknown"))
|
||||
assert_nil(static_register.delete(:one))
|
||||
|
||||
assert_equal({}, static_register.registers)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
end
|
||||
|
||||
def test_fetch_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal(false, static_register.fetch(nil))
|
||||
assert_equal(:one, static_register.fetch(1))
|
||||
assert_equal("one", static_register.fetch(:one))
|
||||
assert_equal(4, static_register.fetch("two"))
|
||||
assert_equal("foo", static_register.fetch(true))
|
||||
assert_nil(static_register.fetch(false))
|
||||
end
|
||||
|
||||
def test_key_with_static
|
||||
static_register = set_with_static
|
||||
|
||||
assert_equal(true, static_register.key?(nil))
|
||||
assert_equal(true, static_register.key?(1))
|
||||
assert_equal(true, static_register.key?(:one))
|
||||
assert_equal(true, static_register.key?("two"))
|
||||
assert_equal(true, static_register.key?(false))
|
||||
assert_equal(false, static_register.key?("unknown"))
|
||||
assert_equal(true, static_register.key?(true))
|
||||
assert_equal(true, static_register.key?(:a))
|
||||
assert_equal(true, static_register.key?(:b))
|
||||
assert_equal(true, static_register.key?(:c))
|
||||
assert_equal(false, static_register.key?(:d))
|
||||
end
|
||||
|
||||
def test_static_register_can_be_frozen
|
||||
static_register = set_with_static
|
||||
static_register = StaticRegisters.new(a: 1)
|
||||
|
||||
static = static_register.static.freeze
|
||||
static_register.static.freeze
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static["two"] = "foo"
|
||||
static_register.static[:a] = "foo"
|
||||
end
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static["unknown"] = "foo"
|
||||
static_register.static[:b] = "foo"
|
||||
end
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static.delete("two")
|
||||
static_register.static.delete(:a)
|
||||
end
|
||||
|
||||
assert_raises(RuntimeError) do
|
||||
static_register.static.delete(:c)
|
||||
end
|
||||
end
|
||||
|
||||
def test_new_static_retains_static
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register["one"] = 1
|
||||
static_register["two"] = 2
|
||||
static_register["three"] = 3
|
||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register[:b] = 22
|
||||
static_register[:c] = 33
|
||||
|
||||
new_register = StaticRegisters.new(static_register)
|
||||
assert_equal({}, new_register.registers)
|
||||
new_static_register = StaticRegisters.new(static_register)
|
||||
new_static_register[:b] = 222
|
||||
|
||||
new_register["one"] = 4
|
||||
new_register["two"] = 5
|
||||
new_register["three"] = 6
|
||||
newest_static_register = StaticRegisters.new(new_static_register)
|
||||
newest_static_register[:c] = 333
|
||||
|
||||
newest_register = StaticRegisters.new(new_register)
|
||||
assert_equal({}, newest_register.registers)
|
||||
assert_equal(1, static_register[:a])
|
||||
assert_equal(22, static_register[:b])
|
||||
assert_equal(33, static_register[:c])
|
||||
|
||||
newest_register["one"] = 7
|
||||
newest_register["two"] = 8
|
||||
newest_register["three"] = 9
|
||||
assert_equal(1, new_static_register[:a])
|
||||
assert_equal(222, new_static_register[:b])
|
||||
assert_nil(new_static_register[:c])
|
||||
|
||||
assert_equal({ "one" => 1, "two" => 2, "three" => 3 }, static_register.registers)
|
||||
assert_equal({ "one" => 4, "two" => 5, "three" => 6 }, new_register.registers)
|
||||
assert_equal({ "one" => 7, "two" => 8, "three" => 9 }, newest_register.registers)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, new_register.static)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, newest_register.static)
|
||||
assert_equal(1, newest_static_register[:a])
|
||||
assert_equal(2, newest_static_register[:b])
|
||||
assert_equal(333, newest_static_register[:c])
|
||||
end
|
||||
|
||||
def test_multiple_instances_are_unique
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register["one"] = 1
|
||||
static_register["two"] = 2
|
||||
static_register["three"] = 3
|
||||
static_register_1 = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register_1[:b] = 22
|
||||
static_register_1[:c] = 33
|
||||
|
||||
new_register = StaticRegisters.new(foo: :bar)
|
||||
assert_equal({}, new_register.registers)
|
||||
static_register_2 = StaticRegisters.new(a: 10, b: 20)
|
||||
static_register_2[:b] = 220
|
||||
static_register_2[:c] = 330
|
||||
|
||||
new_register["one"] = 4
|
||||
new_register["two"] = 5
|
||||
new_register["three"] = 6
|
||||
assert_equal({ a: 1, b: 2 }, static_register_1.static)
|
||||
assert_equal(1, static_register_1[:a])
|
||||
assert_equal(22, static_register_1[:b])
|
||||
assert_equal(33, static_register_1[:c])
|
||||
|
||||
newest_register = StaticRegisters.new(bar: :foo)
|
||||
assert_equal({}, newest_register.registers)
|
||||
|
||||
newest_register["one"] = 7
|
||||
newest_register["two"] = 8
|
||||
newest_register["three"] = 9
|
||||
|
||||
assert_equal({ "one" => 1, "two" => 2, "three" => 3 }, static_register.registers)
|
||||
assert_equal({ "one" => 4, "two" => 5, "three" => 6 }, new_register.registers)
|
||||
assert_equal({ "one" => 7, "two" => 8, "three" => 9 }, newest_register.registers)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal({ foo: :bar }, new_register.static)
|
||||
assert_equal({ bar: :foo }, newest_register.static)
|
||||
assert_equal({ a: 10, b: 20 }, static_register_2.static)
|
||||
assert_equal(10, static_register_2[:a])
|
||||
assert_equal(220, static_register_2[:b])
|
||||
assert_equal(330, static_register_2[:c])
|
||||
end
|
||||
|
||||
def test_can_update_static_directly_and_updates_all_instances
|
||||
static_register = StaticRegisters.new(nil => true, 1 => :one, :one => "one", "two" => 3, false => nil)
|
||||
static_register["one"] = 1
|
||||
static_register["two"] = 2
|
||||
static_register["three"] = 3
|
||||
def test_initialization_reused_static_same_memory_object
|
||||
static_register_1 = StaticRegisters.new(a: 1, b: 2)
|
||||
static_register_1[:b] = 22
|
||||
static_register_1[:c] = 33
|
||||
|
||||
new_register = StaticRegisters.new(static_register)
|
||||
assert_equal({}, new_register.registers)
|
||||
static_register_2 = StaticRegisters.new(static_register_1)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil }, static_register.static)
|
||||
assert_equal(1, static_register_2[:a])
|
||||
assert_equal(2, static_register_2[:b])
|
||||
assert_nil(static_register_2[:c])
|
||||
|
||||
new_register["one"] = 4
|
||||
new_register["two"] = 5
|
||||
new_register["three"] = 6
|
||||
new_register.static["four"] = 10
|
||||
static_register_1.static[:b] = 222
|
||||
static_register_1.static[:c] = 333
|
||||
|
||||
newest_register = StaticRegisters.new(new_register)
|
||||
assert_equal({}, newest_register.registers)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 10 }, new_register.static)
|
||||
|
||||
newest_register["one"] = 7
|
||||
newest_register["two"] = 8
|
||||
newest_register["three"] = 9
|
||||
new_register.static["four"] = 5
|
||||
new_register.static["five"] = 15
|
||||
|
||||
assert_equal({ "one" => 1, "two" => 2, "three" => 3 }, static_register.registers)
|
||||
assert_equal({ "one" => 4, "two" => 5, "three" => 6 }, new_register.registers)
|
||||
assert_equal({ "one" => 7, "two" => 8, "three" => 9 }, newest_register.registers)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 5, "five" => 15 }, newest_register.static)
|
||||
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 5, "five" => 15 }, static_register.static)
|
||||
assert_equal({ nil => true, 1 => :one, :one => "one", "two" => 3, false => nil, "four" => 5, "five" => 15 }, new_register.static)
|
||||
assert_same(static_register_1.static, static_register_2.static)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,7 +60,7 @@ class StrainerTemplateUnitTest < Minitest::Test
|
||||
strainer = Context.new.strainer
|
||||
with_global_filter do
|
||||
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
||||
assert(strainer.class.send(:filter_methods).include?(:public_filter))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -77,4 +77,11 @@ class TemplateUnitTest < Minitest::Test
|
||||
ensure
|
||||
Template.tags.delete('fake')
|
||||
end
|
||||
|
||||
class TemplateSubclass < Liquid::Template
|
||||
end
|
||||
|
||||
def test_template_inheritance
|
||||
assert_equal("foo", TemplateSubclass.parse("foo").render)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,75 +13,75 @@ class VariableUnitTest < Minitest::Test
|
||||
def test_filters
|
||||
var = create_variable('hello | textileze')
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['textileze', []]], var.filters)
|
||||
assert_equal([[:textileze, []]], var.filters)
|
||||
|
||||
var = create_variable('hello | textileze | paragraph')
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
||||
assert_equal([[:textileze, []], [:paragraph, []]], var.filters)
|
||||
|
||||
var = create_variable(%( hello | strftime: '%Y'))
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['strftime', ['%Y']]], var.filters)
|
||||
assert_equal([[:strftime, ['%Y']]], var.filters)
|
||||
|
||||
var = create_variable(%( 'typo' | link_to: 'Typo', true ))
|
||||
assert_equal('typo', var.name)
|
||||
assert_equal([['link_to', ['Typo', true]]], var.filters)
|
||||
assert_equal([[:link_to, ['Typo', true]]], var.filters)
|
||||
|
||||
var = create_variable(%( 'typo' | link_to: 'Typo', false ))
|
||||
assert_equal('typo', var.name)
|
||||
assert_equal([['link_to', ['Typo', false]]], var.filters)
|
||||
assert_equal([[:link_to, ['Typo', false]]], var.filters)
|
||||
|
||||
var = create_variable(%( 'foo' | repeat: 3 ))
|
||||
assert_equal('foo', var.name)
|
||||
assert_equal([['repeat', [3]]], var.filters)
|
||||
assert_equal([[:repeat, [3]]], var.filters)
|
||||
|
||||
var = create_variable(%( 'foo' | repeat: 3, 3 ))
|
||||
assert_equal('foo', var.name)
|
||||
assert_equal([['repeat', [3, 3]]], var.filters)
|
||||
assert_equal([[:repeat, [3, 3]]], var.filters)
|
||||
|
||||
var = create_variable(%( 'foo' | repeat: 3, 3, 3 ))
|
||||
assert_equal('foo', var.name)
|
||||
assert_equal([['repeat', [3, 3, 3]]], var.filters)
|
||||
assert_equal([[:repeat, [3, 3, 3]]], var.filters)
|
||||
|
||||
var = create_variable(%( hello | strftime: '%Y, okay?'))
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['strftime', ['%Y, okay?']]], var.filters)
|
||||
assert_equal([[:strftime, ['%Y, okay?']]], var.filters)
|
||||
|
||||
var = create_variable(%( hello | things: "%Y, okay?", 'the other one'))
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['things', ['%Y, okay?', 'the other one']]], var.filters)
|
||||
assert_equal([[:things, ['%Y, okay?', 'the other one']]], var.filters)
|
||||
end
|
||||
|
||||
def test_filter_with_date_parameter
|
||||
var = create_variable(%( '2006-06-06' | date: "%m/%d/%Y"))
|
||||
assert_equal('2006-06-06', var.name)
|
||||
assert_equal([['date', ['%m/%d/%Y']]], var.filters)
|
||||
assert_equal([[:date, ['%m/%d/%Y']]], var.filters)
|
||||
end
|
||||
|
||||
def test_filters_without_whitespace
|
||||
var = create_variable('hello | textileze | paragraph')
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
||||
assert_equal([[:textileze, []], [:paragraph, []]], var.filters)
|
||||
|
||||
var = create_variable('hello|textileze|paragraph')
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
||||
assert_equal([[:textileze, []], [:paragraph, []]], var.filters)
|
||||
|
||||
var = create_variable("hello|replace:'foo','bar'|textileze")
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['replace', ['foo', 'bar']], ['textileze', []]], var.filters)
|
||||
assert_equal([[:replace, ['foo', 'bar']], [:textileze, []]], var.filters)
|
||||
end
|
||||
|
||||
def test_symbol
|
||||
var = create_variable("http://disney.com/logo.gif | image: 'med' ", error_mode: :lax)
|
||||
assert_equal(VariableLookup.new('http://disney.com/logo.gif'), var.name)
|
||||
assert_equal([['image', ['med']]], var.filters)
|
||||
assert_equal([[:image, ['med']]], var.filters)
|
||||
end
|
||||
|
||||
def test_string_to_filter
|
||||
var = create_variable("'http://disney.com/logo.gif' | image: 'med' ")
|
||||
assert_equal('http://disney.com/logo.gif', var.name)
|
||||
assert_equal([['image', ['med']]], var.filters)
|
||||
assert_equal([[:image, ['med']]], var.filters)
|
||||
end
|
||||
|
||||
def test_string_single_quoted
|
||||
@@ -128,13 +128,13 @@ class VariableUnitTest < Minitest::Test
|
||||
def test_filter_with_keyword_arguments
|
||||
var = create_variable(%( hello | things: greeting: "world", farewell: 'goodbye'))
|
||||
assert_equal(VariableLookup.new('hello'), var.name)
|
||||
assert_equal([['things', [], { 'greeting' => 'world', 'farewell' => 'goodbye' }]], var.filters)
|
||||
assert_equal([[:things, [], { 'greeting' => 'world', 'farewell' => 'goodbye' }]], var.filters)
|
||||
end
|
||||
|
||||
def test_lax_filter_argument_parsing
|
||||
var = create_variable(%( number_of_comments | pluralize: 'comment': 'comments' ), error_mode: :lax)
|
||||
assert_equal(VariableLookup.new('number_of_comments'), var.name)
|
||||
assert_equal([['pluralize', ['comment', 'comments']]], var.filters)
|
||||
assert_equal([[:pluralize, ['comment', 'comments']]], var.filters)
|
||||
end
|
||||
|
||||
def test_strict_filter_argument_parsing
|
||||
|
||||
Reference in New Issue
Block a user