lib/liquid/template.rb in liquid-3.0.0.rc1 vs lib/liquid/template.rb in liquid-3.0.0

- old
+ new

@@ -49,17 +49,25 @@ def lookup_class(name) name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) } end end + attr_reader :profiler + class << self # Sets how strict the parser should be. # :lax acts like liquid 2.5 and silently ignores malformed tags in most cases. # :warn is the default and will give deprecation warnings when invalid syntax is used. # :strict will enforce correct syntax. attr_writer :error_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 + attr_writer :taint_mode + def file_system @@file_system end def file_system=(obj) @@ -76,31 +84,43 @@ def error_mode @error_mode || :lax end + def taint_mode + @taint_mode || :lax + 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) Strainer.global_filter(mod) end + def default_resource_limits + @default_resource_limits ||= {} + end + # creates a new <tt>Template</tt> object from liquid source code + # 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) end end - # creates a new <tt>Template</tt> from an array of tokens. Use <tt>Template.parse</tt> instead def initialize - @resource_limits = {} + @resource_limits = self.class.default_resource_limits.dup end # Parse source code. # Returns self for easy chaining def parse(source, options = {}) + @options = options + @profiling = options[:profile] + @line_numbers = options[:line_numbers] || @profiling @root = Document.parse(tokenize(source), DEFAULT_OPTIONS.merge(options)) @warnings = nil self end @@ -128,10 +148,13 @@ # Render takes a hash with local variables. # # if you use the same filters over and over again consider registering them globally # with <tt>Template.register_filter</tt> # + # if profiling was enabled in <tt>Template#parse</tt> then the resulting profiling information + # will be available via <tt>Template#profiler</tt> + # # Following options can be passed: # # * <tt>filters</tt> : array with local filters # * <tt>registers</tt> : hash with register variables. Those can be accessed from # filters and tags and might be useful to integrate liquid more with its host application @@ -181,11 +204,13 @@ end begin # render the nodelist. # for performance reasons we get an array back here. join will make a string out of it. - result = @root.render(context) + result = with_profiling do + @root.render(context) + end result.respond_to?(:join) ? result.join : result rescue Liquid::MemoryError => e context.handle_error(e) ensure @errors = context.errors @@ -201,15 +226,41 @@ # Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize the passed source def tokenize(source) source = source.source if source.respond_to?(:source) return [] if source.to_s.empty? - tokens = source.split(TemplateParser) + tokens = calculate_line_numbers(source.split(TemplateParser)) + # removes the rogue empty element at the beginning of the array tokens.shift if tokens[0] and tokens[0].empty? tokens end + def calculate_line_numbers(raw_tokens) + return raw_tokens unless @line_numbers + + current_line = 1 + raw_tokens.map do |token| + Token.new(token, current_line).tap do + current_line += token.count("\n") + end + end + end + + def with_profiling + if @profiling && !@options[:included] + @profiler = Profiler.new + @profiler.start + + begin + yield + ensure + @profiler.stop + end + else + yield + end + end end end