lib/roda/plugins/multi_public.rb



# frozen-string-literal: true

begin
  require 'rack/files'
rescue LoadError
  require 'rack/file'
end

#
class Roda
  module RodaPlugins
    # The multi_public plugin adds an +r.multi_public+ method that accepts an argument specifying
    # a directory from which to serve static files.  It is similar to the public plugin, but
    # allows for multiple separate directories.
    #
    # Here's an example of using the multi_public plugin to serve 3 different types of files
    # from 3 different directories:
    #
    #   plugin :multi_public,
    #     img:  'static/images',
    #     font: 'assets/fonts',
    #     form: 'static/forms/pdfs'
    #
    #   route do
    #     r.on "images" do
    #       r.multi_public(:img)
    #     end
    #
    #     r.on "fonts" do
    #       r.multi_public(:font)
    #     end
    #
    #     r.on "forms" do
    #       r.multi_public(:form)
    #     end
    #   end
    #
    # It is possible to simplify the routing tree for this using string keys and an array
    # matcher:
    # 
    #   plugin :multi_public,
    #     'images' => 'static/images',
    #     'fonts'  => 'assets/fonts',
    #     'forms'  => 'static/forms/pdfs'
    #
    #   route do
    #     r.on %w"images fonts forms" do |dir|
    #       r.multi_public(dir)
    #     end
    #   end
    #
    # You can provide custom headers and default mime type for each directory using an array
    # of three elements as the value, with the first element being the path, the second
    # being the custom headers, and the third being the default mime type:
    #
    #   plugin :multi_public,
    #     'images' => ['static/images', {'Cache-Control'=>'max-age=86400'}, nil],
    #     'fonts'  => ['assets/fonts', {'Cache-Control'=>'max-age=31536000'}, 'font/ttf'],
    #     'forms'  => ['static/forms/pdfs', nil, 'application/pdf']
    #
    #   route do
    #     r.on %w"images fonts forms" do |dir|
    #       r.multi_public(dir)
    #     end
    #   end
    module MultiPublic
      RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File

      def self.load_dependencies(app, _, opts=OPTS)
        app.plugin(:public, opts)
      end

      # Use the given directories to setup servers.  Any opts are passed to the public plugin.
      def self.configure(app, directories, _=OPTS)
        roots = app.opts[:multi_public_servers] = (app.opts[:multi_public_servers] || {}).dup
        directories.each do |key, path|
          path, headers, mime = path
          roots[key] = RACK_FILES.new(app.expand_path(path), headers||{}, mime||'text/plain')
        end
        roots.freeze
      end

      module RequestMethods
        # Serve files from the directory corresponding to the given key if the file exists and
        # this is a GET request.
        def multi_public(key)
          public_serve_with(roda_class.opts[:multi_public_servers].fetch(key))
        end
      end
    end

    register_plugin(:multi_public, MultiPublic)
  end
end