mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
Add a class cache to avoid runtime extend calls
* Strainer has a class cache that creates Strainer subclasses for each filter set that is used on .create calls. * Context now creates a list of filters and passes this to Strainer.create to utilize the class cache in almost all use cases. * If add_filter was called after a render, then the method cache may still be invalidated. Conflicts: lib/liquid/strainer.rb
This commit is contained in:
+13
-3
@@ -25,6 +25,7 @@ module Liquid
|
|||||||
squash_instance_assigns_with_environments
|
squash_instance_assigns_with_environments
|
||||||
|
|
||||||
@interrupts = []
|
@interrupts = []
|
||||||
|
@filters = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_limits_reached?
|
def resource_limits_reached?
|
||||||
@@ -34,7 +35,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def strainer
|
def strainer
|
||||||
@strainer ||= Strainer.create(self)
|
@strainer ||= Strainer.create(self, @filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adds filters to this context.
|
# Adds filters to this context.
|
||||||
@@ -43,11 +44,20 @@ module Liquid
|
|||||||
# for that
|
# for that
|
||||||
def add_filters(filters)
|
def add_filters(filters)
|
||||||
filters = [filters].flatten.compact
|
filters = [filters].flatten.compact
|
||||||
|
|
||||||
filters.each do |f|
|
filters.each do |f|
|
||||||
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
|
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
|
||||||
Strainer.add_known_filter(f)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+12
-4
@@ -11,6 +11,11 @@ module Liquid
|
|||||||
@@filters = []
|
@@filters = []
|
||||||
@@known_filters = Set.new
|
@@known_filters = Set.new
|
||||||
@@known_methods = 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)
|
def initialize(context)
|
||||||
@context = context
|
@context = context
|
||||||
@@ -32,10 +37,13 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create(context)
|
def self.strainer_class_cache
|
||||||
strainer = Strainer.new(context)
|
@@strainer_class_cache
|
||||||
@@filters.each { |m| strainer.extend(m) }
|
end
|
||||||
strainer
|
|
||||||
|
def self.create(context, filters = nil)
|
||||||
|
filters = @@filters.values + (filters || [])
|
||||||
|
strainer_class_cache[filters].new(context)
|
||||||
end
|
end
|
||||||
|
|
||||||
def invoke(method, *args)
|
def invoke(method, *args)
|
||||||
|
|||||||
@@ -49,4 +49,15 @@ class StrainerTest < Test::Unit::TestCase
|
|||||||
assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke")
|
assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
|
||||||
|
a, b = Module.new, Module.new
|
||||||
|
strainer = Strainer.create(nil, [a,b])
|
||||||
|
assert_kind_of Strainer, strainer
|
||||||
|
assert_kind_of a, strainer
|
||||||
|
assert_kind_of b, strainer
|
||||||
|
Strainer.class_variable_get(:@@filters).values.each do |m|
|
||||||
|
assert_kind_of m, strainer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end # StrainerTest
|
end # StrainerTest
|
||||||
|
|||||||
Reference in New Issue
Block a user