lib/roda/plugins/autoload_named_routes.rb



# frozen-string-literal: true

#
class Roda
  module RodaPlugins
    # The autoload_named_routes plugin builds on the named_routes plugin and allows for
    # delaying loading of a file containing a named route for an application until there
    # is a request that uses the named route. This can be useful in development
    # to improvement startup time by not loading all named routes up front.  It can also be
    # useful in testing subsets of an application by only loading the named routes being
    # tested.
    #
    # You can specify a single hash branch for autoloading:
    #
    #   plugin :autoload_named_route
    #   autoload_named_route(:route_name, '/absolute/path/to/file')
    #   autoload_named_route(:namespace, :route_name, 'relative/path/to/file')
    #
    # Note that unlike the +route+ method defined by the named_routes plugin, when providing
    # a namespace, the namespace comes before the route name and not after.
    #
    # When the autoloaded file is required, it should redefine the same
    # named route.  If it does not, requests to the named route will be ignored (as if the
    # related named route block was empty).
    #
    # When freezing an application, all named routes are automatically loaded, because
    # autoloading named routes does not work for frozen applications.
    module AutoloadNamedRoutes
      def self.load_dependencies(app)
        app.plugin :named_routes
      end

      def self.configure(app)
        app.opts[:autoload_named_route_files] ||= []
      end

      module ClassMethods
        # Autoload the given file when there is request for the named route.
        # The given file should configure the named route specified.
        def autoload_named_route(namespace=nil, name, file)
          file = File.expand_path(file)
          opts[:autoload_named_route_files] << file
          routes = opts[:namespaced_routes][namespace] ||= {}
          meth = routes[name] = define_roda_method(routes[name] || "named_routes_#{namespace}_#{name}", 1) do |r|
            loc = method(routes[name]).source_location
            require file
            # Avoid infinite loop in case method is not overridden
            if method(meth).source_location != loc
              send(meth, r)
            end
          end
          nil
        end

        # Eagerly load all autoloaded named routes when freezing the application.
        def freeze
          opts.delete(:autoload_named_route_files).each{|file| require file}
          super
        end
      end
    end

    register_plugin(:autoload_named_routes, AutoloadNamedRoutes)
  end
end