mirror of
https://github.com/kemko/liquid.git
synced 2026-01-06 10:15:40 +03:00
[Strainer] Separate factory from template
This commit is contained in:
@@ -55,19 +55,21 @@ module Minitest
|
||||
end
|
||||
|
||||
def with_global_filter(*globals)
|
||||
original_global_strainer = Liquid::Strainer.class_variable_get(:@@global_strainer)
|
||||
Liquid::Strainer.class_variable_set(:@@global_strainer, Class.new(Liquid::Strainer) do
|
||||
@filter_methods = Set.new
|
||||
end)
|
||||
Liquid::Strainer.class_variable_get(:@@strainer_class_cache).clear
|
||||
original_global_filters = Liquid::StrainerFactory.instance_variable_get(:@global_filters)
|
||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, [])
|
||||
globals.each do |global|
|
||||
Liquid::StrainerFactory.add_global_filter(global)
|
||||
end
|
||||
|
||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||
|
||||
globals.each do |global|
|
||||
Liquid::Template.register_filter(global)
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
Liquid::Strainer.class_variable_get(:@@strainer_class_cache).clear
|
||||
Liquid::Strainer.class_variable_set(:@@global_strainer, original_global_strainer)
|
||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, original_global_filters)
|
||||
end
|
||||
|
||||
def with_taint_mode(mode)
|
||||
|
||||
96
test/unit/strainer_factory_unit_test.rb
Normal file
96
test/unit/strainer_factory_unit_test.rb
Normal file
@@ -0,0 +1,96 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StrainerFactoryUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
module AccessScopeFilters
|
||||
def public_filter
|
||||
"public"
|
||||
end
|
||||
|
||||
def private_filter
|
||||
"private"
|
||||
end
|
||||
private :private_filter
|
||||
end
|
||||
|
||||
StrainerFactory.add_global_filter(AccessScopeFilters)
|
||||
|
||||
module LateAddedFilter
|
||||
def late_added_filter(_input)
|
||||
"filtered"
|
||||
end
|
||||
end
|
||||
|
||||
def test_strainer
|
||||
strainer = StrainerFactory.create(nil)
|
||||
assert_equal(5, strainer.invoke('size', 'input'))
|
||||
assert_equal("public", strainer.invoke("public_filter"))
|
||||
end
|
||||
|
||||
def test_stainer_raises_argument_error
|
||||
strainer = StrainerFactory.create(nil)
|
||||
assert_raises(Liquid::ArgumentError) do
|
||||
strainer.invoke("public_filter", 1)
|
||||
end
|
||||
end
|
||||
|
||||
def test_stainer_argument_error_contains_backtrace
|
||||
strainer = StrainerFactory.create(nil)
|
||||
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
strainer.invoke("public_filter", 1)
|
||||
end
|
||||
|
||||
assert_match(
|
||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
||||
exception.message
|
||||
)
|
||||
assert_equal(exception.backtrace[0].split(':')[0], __FILE__)
|
||||
end
|
||||
|
||||
def test_strainer_only_invokes_public_filter_methods
|
||||
strainer = StrainerFactory.create(nil)
|
||||
assert_equal(false, strainer.class.invokable?('__test__'))
|
||||
assert_equal(false, strainer.class.invokable?('test'))
|
||||
assert_equal(false, strainer.class.invokable?('instance_eval'))
|
||||
assert_equal(false, strainer.class.invokable?('__send__'))
|
||||
assert_equal(true, strainer.class.invokable?('size')) # from the standard lib
|
||||
end
|
||||
|
||||
def test_strainer_returns_nil_if_no_filter_method_found
|
||||
strainer = StrainerFactory.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 = StrainerFactory.create(nil)
|
||||
assert_equal("password", strainer.invoke("undef_the_method", "password"))
|
||||
end
|
||||
|
||||
def test_strainer_only_allows_methods_defined_in_filters
|
||||
strainer = StrainerFactory.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
|
||||
|
||||
def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
|
||||
a = Module.new
|
||||
b = Module.new
|
||||
strainer = StrainerFactory.create(nil, [a, b])
|
||||
assert_kind_of(StrainerTemplate, strainer)
|
||||
assert_kind_of(a, strainer)
|
||||
assert_kind_of(b, strainer)
|
||||
assert_kind_of(Liquid::StandardFilters, strainer)
|
||||
end
|
||||
|
||||
def test_add_global_filter_clears_cache
|
||||
assert_equal('input', StrainerFactory.create(nil).invoke('late_added_filter', 'input'))
|
||||
StrainerFactory.add_global_filter(LateAddedFilter)
|
||||
assert_equal('filtered', StrainerFactory.create(nil).invoke('late_added_filter', 'input'))
|
||||
end
|
||||
end
|
||||
82
test/unit/strainer_template_unit_test.rb
Normal file
82
test/unit/strainer_template_unit_test.rb
Normal file
@@ -0,0 +1,82 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StrainerTemplateUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_add_filter_when_wrong_filter_class
|
||||
c = Context.new
|
||||
s = c.strainer
|
||||
wrong_filter = ->(v) { v.reverse }
|
||||
|
||||
exception = assert_raises(TypeError) do
|
||||
s.class.add_filter(wrong_filter)
|
||||
end
|
||||
assert_equal(exception.message, "wrong argument type Proc (expected Module)")
|
||||
end
|
||||
|
||||
module PrivateMethodOverrideFilter
|
||||
private
|
||||
|
||||
def public_filter
|
||||
"overriden as private"
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_raises_when_module_privately_overrides_registered_public_methods
|
||||
strainer = Context.new.strainer
|
||||
|
||||
error = assert_raises(Liquid::MethodOverrideError) do
|
||||
strainer.class.add_filter(PrivateMethodOverrideFilter)
|
||||
end
|
||||
assert_equal('Liquid error: Filter overrides registered public methods as non public: public_filter', error.message)
|
||||
end
|
||||
|
||||
module ProtectedMethodOverrideFilter
|
||||
protected
|
||||
|
||||
def public_filter
|
||||
"overriden as protected"
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_raises_when_module_overrides_registered_public_method_as_protected
|
||||
strainer = Context.new.strainer
|
||||
|
||||
error = assert_raises(Liquid::MethodOverrideError) do
|
||||
strainer.class.add_filter(ProtectedMethodOverrideFilter)
|
||||
end
|
||||
assert_equal('Liquid error: Filter overrides registered public methods as non public: public_filter', error.message)
|
||||
end
|
||||
|
||||
module PublicMethodOverrideFilter
|
||||
def public_filter
|
||||
"public"
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
||||
strainer = Context.new.strainer
|
||||
with_global_filter do
|
||||
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_does_not_include_already_included_module
|
||||
mod = Module.new do
|
||||
class << self
|
||||
attr_accessor :include_count
|
||||
def included(_mod)
|
||||
self.include_count += 1
|
||||
end
|
||||
end
|
||||
self.include_count = 0
|
||||
end
|
||||
strainer = Context.new.strainer
|
||||
strainer.class.add_filter(mod)
|
||||
strainer.class.add_filter(mod)
|
||||
assert_equal(1, mod.include_count)
|
||||
end
|
||||
end
|
||||
@@ -1,167 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class StrainerUnitTest < Minitest::Test
|
||||
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_stainer_raises_argument_error
|
||||
strainer = Strainer.create(nil)
|
||||
assert_raises(Liquid::ArgumentError) do
|
||||
strainer.invoke("public_filter", 1)
|
||||
end
|
||||
end
|
||||
|
||||
def test_stainer_argument_error_contains_backtrace
|
||||
strainer = Strainer.create(nil)
|
||||
begin
|
||||
strainer.invoke("public_filter", 1)
|
||||
rescue Liquid::ArgumentError => e
|
||||
assert_match(
|
||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
||||
e.message
|
||||
)
|
||||
assert_equal(e.backtrace[0].split(':')[0], __FILE__)
|
||||
end
|
||||
end
|
||||
|
||||
def test_strainer_only_invokes_public_filter_methods
|
||||
strainer = Strainer.create(nil)
|
||||
assert_equal(false, strainer.class.invokable?('__test__'))
|
||||
assert_equal(false, strainer.class.invokable?('test'))
|
||||
assert_equal(false, strainer.class.invokable?('instance_eval'))
|
||||
assert_equal(false, strainer.class.invokable?('__send__'))
|
||||
assert_equal(true, strainer.class.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
|
||||
|
||||
def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
|
||||
a = Module.new
|
||||
b = Module.new
|
||||
strainer = Strainer.create(nil, [a, b])
|
||||
assert_kind_of(Strainer, strainer)
|
||||
assert_kind_of(a, strainer)
|
||||
assert_kind_of(b, strainer)
|
||||
assert_kind_of(Liquid::StandardFilters, strainer)
|
||||
end
|
||||
|
||||
def test_add_filter_when_wrong_filter_class
|
||||
c = Context.new
|
||||
s = c.strainer
|
||||
wrong_filter = ->(v) { v.reverse }
|
||||
|
||||
assert_raises ArgumentError do
|
||||
s.class.add_filter(wrong_filter)
|
||||
end
|
||||
end
|
||||
|
||||
module PrivateMethodOverrideFilter
|
||||
private
|
||||
|
||||
def public_filter
|
||||
"overriden as private"
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_raises_when_module_privately_overrides_registered_public_methods
|
||||
strainer = Context.new.strainer
|
||||
|
||||
error = assert_raises(Liquid::MethodOverrideError) do
|
||||
strainer.class.add_filter(PrivateMethodOverrideFilter)
|
||||
end
|
||||
assert_equal('Liquid error: Filter overrides registered public methods as non public: public_filter', error.message)
|
||||
end
|
||||
|
||||
module ProtectedMethodOverrideFilter
|
||||
protected
|
||||
|
||||
def public_filter
|
||||
"overriden as protected"
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_raises_when_module_overrides_registered_public_method_as_protected
|
||||
strainer = Context.new.strainer
|
||||
|
||||
error = assert_raises(Liquid::MethodOverrideError) do
|
||||
strainer.class.add_filter(ProtectedMethodOverrideFilter)
|
||||
end
|
||||
assert_equal('Liquid error: Filter overrides registered public methods as non public: public_filter', error.message)
|
||||
end
|
||||
|
||||
module PublicMethodOverrideFilter
|
||||
def public_filter
|
||||
"public"
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
||||
strainer = Context.new.strainer
|
||||
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||
assert(strainer.class.filter_methods.include?('public_filter'))
|
||||
end
|
||||
|
||||
module LateAddedFilter
|
||||
def late_added_filter(_input)
|
||||
"filtered"
|
||||
end
|
||||
end
|
||||
|
||||
def test_global_filter_clears_cache
|
||||
assert_equal('input', Strainer.create(nil).invoke('late_added_filter', 'input'))
|
||||
Strainer.global_filter(LateAddedFilter)
|
||||
assert_equal('filtered', Strainer.create(nil).invoke('late_added_filter', 'input'))
|
||||
end
|
||||
|
||||
def test_add_filter_does_not_include_already_included_module
|
||||
mod = Module.new do
|
||||
class << self
|
||||
attr_accessor :include_count
|
||||
def included(_mod)
|
||||
self.include_count += 1
|
||||
end
|
||||
end
|
||||
self.include_count = 0
|
||||
end
|
||||
strainer = Context.new.strainer
|
||||
strainer.class.add_filter(mod)
|
||||
strainer.class.add_filter(mod)
|
||||
assert_equal(1, mod.include_count)
|
||||
end
|
||||
end # StrainerTest
|
||||
Reference in New Issue
Block a user