lib/roda/plugins/capture_erb.rb



# frozen-string-literal: true

#
class Roda
  module RodaPlugins
    # The capture_erb plugin allows you to capture the content of a block
    # in an ERB template, and return it as a value, instead of
    # injecting the template block into the template output.
    #
    #   <% value = capture_erb do %>
    #     Some content here.
    #   <% end %>
    #
    # +capture_erb+ can be used inside other methods that are called
    # inside templates.  It can be combined with the inject_erb plugin
    # to wrap template blocks with arbitrary output and then inject the
    # wrapped output into the template.
    #
    # If the output buffer object responds to +capture+ and is not
    # an instance of String (e.g. when +erubi/capture_block+ is being
    # used as the template engine), this will call +capture+ on the
    # output buffer object, instead of setting the output buffer object
    # temporarily to a new object.
    #
    # By default, capture_erb returns the value of the block, converted
    # to a string.  However, that can cause issues with code such as:
    #
    #   <% value = capture_erb do %>
    #     Some content here.
    #     <% if something %>
    #       Some more content here.
    #     <% end %>
    #   <% end %>
    #
    # In this case, the block may return nil, instead of the content of
    # the template.  To handle this case, you can provide the
    # <tt>returns: :buffer</tt> option when calling the method (to handle
    # that specific call, or when loading the plugin (to default to that
    # behavior). Note that if the output buffer object responds to
    # +capture+ and is not an instance of String, the <tt>returns: :buffer</tt>
    # behavior is the default and cannot be changed.
    module CaptureERB
      def self.load_dependencies(app, opts=OPTS)
        app.plugin :render
      end

      # Support <tt>returns: :buffer</tt> to default to returning buffer
      # object.
      def self.configure(app, opts=OPTS)
        # RODA4: make returns: :buffer the default behavior
        app.opts[:capture_erb_returns] = opts[:returns] if opts.has_key?(:returns)
      end

      module InstanceMethods
        # Temporarily replace the ERB output buffer
        # with an empty string, and then yield to the block.
        # Return the value of the block, converted to a string.
        # Restore the previous ERB output buffer before returning.
        #
        # Options:
        # :returns :: If set to :buffer, returns the value of the
        #             template output variable, instead of the return
        #             value of the block converted to a string. This
        #             is the default behavior if the template output
        #             variable supports the +capture+ method and is not
        #             a String instance.
        def capture_erb(opts=OPTS, &block)
          outvar = render_opts[:template_opts][:outvar]
          buf_was = instance_variable_get(outvar)

          if buf_was.respond_to?(:capture) && !buf_was.instance_of?(String)
            buf_was.capture(&block)
          else
            returns = opts.fetch(:returns) { self.opts[:capture_erb_returns] }

            begin
              instance_variable_set(outvar, String.new)
              if returns == :buffer
                yield
                instance_variable_get(outvar).to_s
              else
                yield.to_s
              end
            ensure
              instance_variable_set(outvar, buf_was) if outvar && buf_was
            end
          end
        end
      end
    end

    register_plugin(:capture_erb, CaptureERB)
  end
end