lib/roda/plugins/precompile_templates.rb



# frozen-string-literal: true

#
class Roda
  module RodaPlugins
    # The precompile_templates plugin adds support for precompiling template code.
    # This can result in a large memory savings for applications that have large
    # templates or a large number of small templates if the application uses a
    # forking webserver.  By default, template compilation is lazy, so all the
    # child processes in a forking webserver will have their own copy of the
    # compiled template.  By using the precompile_templates plugin, you can
    # precompile the templates in the parent process before forking, and then
    # all of the child processes can use the same precompiled templates, which
    # saves memory.
    #
    # After loading the plugin, you can call +precompile_templates+ with
    # the pattern of templates you would like to precompile:
    #
    #   plugin :precompile_templates
    #   precompile_templates "views/\*\*/*.erb"
    #
    # That will precompile all erb template files in the views directory or
    # any subdirectory.
    #
    # If the templates use local variables, you need to specify which local
    # variables to precompile, which should be an array of symbols:
    #
    #   precompile_templates 'views/users/_*.erb', :locals=>[:user]
    #
    # Note that if you have multiple local variables and are not using a Tilt
    # version greater than 2.0.1, you should specify the :locals option in the
    # same order as the keys in the :locals hash you pass to render/view.  Since
    # hashes are not ordered in ruby 1.8, you should not attempt to precompile
    # templates that use :locals on ruby 1.8 unless you are using a Tilt version
    # greater than 2.0.1.  If you are running the Tilt master branch, you can
    # force sorting of locals using the +:sort_locals+ option when loading the
    # plugin.
    #
    # You can specify other render options when calling +precompile_templates+,
    # including +:cache_key+, +:template_class+, and +:template_opts+.  If you
    # are passing any of those options to render/view for the template, you
    # should pass the same options when precompiling the template.
    #
    # To compile inline templates, just pass a single hash containing an :inline
    # to +precompile_templates+:
    #
    #   precompile_templates :inline=>some_template_string
    module PrecompileTemplates
      OPTS = {}.freeze

      # Load the render plugin as precompile_templates depends on it.
      # Default to sorting the locals if the Tilt version is greater than 2.0.1.
      def self.load_dependencies(app, opts=OPTS)
        app.plugin :render
        app.opts[:precompile_templates_sort] = opts.fetch(:sort_locals, Tilt::VERSION > '2.0.1')
      end

      module ClassMethods
        # Precompile the templates using the given options.  See PrecompileTemplates
        # for details.
        def precompile_templates(pattern, opts=OPTS)
          if pattern.is_a?(Hash)
            opts = pattern.merge(opts)
          end

          locals = opts[:locals] || []
          if locals && self.opts[:precompile_templates_sort]
            locals = locals.sort{|x,y| x.to_s <=> y.to_s}
          end

          compile_opts = if pattern.is_a?(Hash)
            [opts]
          else
            Dir[pattern].map{|file| opts.merge(:path=>File.expand_path(file, nil))}
          end

          instance = allocate
          compile_opts.each do |compile_opt|
            template = instance.send(:retrieve_template, compile_opt)
            template.send(:compiled_method, locals)
          end

          nil
        end
      end
    end

    register_plugin(:precompile_templates, PrecompileTemplates)
  end
end