Skip to content

Commit

Permalink
Merge pull request #90 from rage-rb/router-compatibility
Browse files Browse the repository at this point in the history
Improve Router compatibility
  • Loading branch information
rsamoilov authored Jul 15, 2024
2 parents 41203f2 + 5176d8d commit e1dd8e7
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 24 deletions.
3 changes: 3 additions & 0 deletions lib/rage-rb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ def self.patch_active_record_connection_pool
module Router
module Strategies
end

module DSLPlugins
end
end

module Ext
Expand Down
72 changes: 48 additions & 24 deletions lib/rage/router/dsl.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# frozen_string_literal: true

require_relative "dsl_plugins/legacy_hash_notation"
require_relative "dsl_plugins/legacy_root_notation"
require_relative "dsl_plugins/named_route_helpers"
require_relative "dsl_plugins/controller_action_options"

class Rage::Router::DSL
def initialize(router)
@router = router
Expand Down Expand Up @@ -47,6 +52,11 @@ def draw(&block)
# resources :posts
# end
class Handler
prepend Rage::Router::DSLPlugins::ControllerActionOptions
prepend Rage::Router::DSLPlugins::NamedRouteHelpers
prepend Rage::Router::DSLPlugins::LegacyHashNotation
prepend Rage::Router::DSLPlugins::LegacyRootNotation

# @private
def initialize(router)
@router = router
Expand All @@ -67,12 +77,13 @@ def initialize(router)
# @param to [String] the route handler in the format of "controller#action"
# @param constraints [Hash] a hash of constraints for the route
# @param defaults [Hash] a hash of default parameters for the route
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
# @example
# get "/photos/:id", to: "photos#show", constraints: { host: /myhost/ }
# @example
# get "/photos(/:id)", to: "photos#show", defaults: { id: "-1" }
def get(path, to: nil, constraints: nil, defaults: nil)
__on("GET", path, to, constraints, defaults)
def get(path, to: nil, constraints: nil, defaults: nil, on: nil)
__with_on_scope(on) { __on("GET", path, to, constraints, defaults) }
end

# Register a new POST route.
Expand All @@ -81,12 +92,13 @@ def get(path, to: nil, constraints: nil, defaults: nil)
# @param to [String] the route handler in the format of "controller#action"
# @param constraints [Hash] a hash of constraints for the route
# @param defaults [Hash] a hash of default parameters for the route
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
# @example
# post "/photos", to: "photos#create", constraints: { host: /myhost/ }
# @example
# post "/photos", to: "photos#create", defaults: { format: "jpg" }
def post(path, to: nil, constraints: nil, defaults: nil)
__on("POST", path, to, constraints, defaults)
def post(path, to: nil, constraints: nil, defaults: nil, on: nil)
__with_on_scope(on) { __on("POST", path, to, constraints, defaults) }
end

# Register a new PUT route.
Expand All @@ -95,12 +107,13 @@ def post(path, to: nil, constraints: nil, defaults: nil)
# @param to [String] the route handler in the format of "controller#action"
# @param constraints [Hash] a hash of constraints for the route
# @param defaults [Hash] a hash of default parameters for the route
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
# @example
# put "/photos/:id", to: "photos#update", constraints: { host: /myhost/ }
# @example
# put "/photos(/:id)", to: "photos#update", defaults: { id: "-1" }
def put(path, to: nil, constraints: nil, defaults: nil)
__on("PUT", path, to, constraints, defaults)
def put(path, to: nil, constraints: nil, defaults: nil, on: nil)
__with_on_scope(on) { __on("PUT", path, to, constraints, defaults) }
end

# Register a new PATCH route.
Expand All @@ -109,12 +122,13 @@ def put(path, to: nil, constraints: nil, defaults: nil)
# @param to [String] the route handler in the format of "controller#action"
# @param constraints [Hash] a hash of constraints for the route
# @param defaults [Hash] a hash of default parameters for the route
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
# @example
# patch "/photos/:id", to: "photos#update", constraints: { host: /myhost/ }
# @example
# patch "/photos(/:id)", to: "photos#update", defaults: { id: "-1" }
def patch(path, to: nil, constraints: nil, defaults: nil)
__on("PATCH", path, to, constraints, defaults)
def patch(path, to: nil, constraints: nil, defaults: nil, on: nil)
__with_on_scope(on) { __on("PATCH", path, to, constraints, defaults) }
end

# Register a new DELETE route.
Expand All @@ -123,12 +137,13 @@ def patch(path, to: nil, constraints: nil, defaults: nil)
# @param to [String] the route handler in the format of "controller#action"
# @param constraints [Hash] a hash of constraints for the route
# @param defaults [Hash] a hash of default parameters for the route
# @param on [nil, :member, :collection] a shorthand for wrapping routes in a specific RESTful context
# @example
# delete "/photos/:id", to: "photos#destroy", constraints: { host: /myhost/ }
# @example
# delete "/photos(/:id)", to: "photos#destroy", defaults: { id: "-1" }
def delete(path, to: nil, constraints: nil, defaults: nil)
__on("DELETE", path, to, constraints, defaults)
def delete(path, to: nil, constraints: nil, defaults: nil, on: nil)
__with_on_scope(on) { __on("DELETE", path, to, constraints, defaults) }
end

# Register a new route pointing to '/'.
Expand Down Expand Up @@ -207,9 +222,9 @@ def namespace(path, **options, &block)
# Scopes a set of routes to the given default options.
#
# @param [Hash] opts scope options.
# @option opts [String] :module module option
# @option opts [String] :path path option
# @option opts [String] :controller controller option
# @option opts [String] :module the namespace for the controller
# @option opts [String] :path the path prefix for the routes
# @option opts [String] :controller scopes routes to a specific controller
# @example Route `/photos` to `Api::PhotosController`
# scope module: "api" do
# get "photos", to: "photos#index"
Expand Down Expand Up @@ -309,6 +324,12 @@ def member(&block)

# Automatically create REST routes for a resource.
#
# @param [Hash] opts resource options
# @option opts [String] :module the namespace for the controller
# @option opts [String] :path the path prefix for the routes
# @option opts [Symbol, Array<Symbol>] :only only generate routes for the given actions
# @option opts [Symbol, Array<Symbol>] :except generate all routes except for the given actions
# @option opts [String] :param overrides the default param name of `:id` in the URL
# @example Create five REST routes, all mapping to the `Photos` controller:
# resources :photos
# # GET /photos => photos#index
Expand Down Expand Up @@ -358,17 +379,7 @@ def resources(*_resources, **opts, &block)
# mount Sidekiq::Web => "/sidekiq"
# @example
# mount Sidekiq::Web, at: "/sidekiq", via: :get
def mount(*args)
if args.first.is_a?(Hash)
app = args.first.keys.first
at = args.first.values.first
via = args[0][:via]
else
app = args.first
at = args[1][:at]
via = args[1][:via]
end

def mount(app, at:, via: :all)
at = "/#{at}" unless at.start_with?("/")
at = at.delete_suffix("/") if at.end_with?("/")

Expand Down Expand Up @@ -421,6 +432,19 @@ def __on(method, path, to, constraints, defaults)
end
end

def __with_on_scope(on, &block)
case on
when nil
block.call
when :member
member(&block)
when :collection
collection(&block)
else
raise ArgumentError, "Unknown scope :#{on} given to :on"
end
end

def to_singular(str)
@active_support_loaded ||= str.respond_to?(:singularize) || :false
return str.singularize if @active_support_loaded != :false
Expand Down
25 changes: 25 additions & 0 deletions lib/rage/router/dsl_plugins/controller_action_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
##
# Support the `:controller` and `:action` options.
#
# @example
# get :admins, controller: :users
# @example
# post :search, action: :index
module Rage::Router::DSLPlugins::ControllerActionOptions
%i(get post put patch delete).each do |action_name|
define_method(action_name) do |*args, **kwargs|
if args.length == 1 && !kwargs.has_key?(:to) && (kwargs.has_key?(:controller) || kwargs.has_key?(:action))
path = args[0]
controller = kwargs.delete(:controller) || @controllers.last || raise(ArgumentError, "Could not derive the controller value from the route definitions")
action = kwargs.delete(:action) || path.split("/").last
end

if controller && action
kwargs[:to] = "#{controller}##{action}"
super(path, **kwargs)
else
super(*args, **kwargs)
end
end
end
end
46 changes: 46 additions & 0 deletions lib/rage/router/dsl_plugins/legacy_hash_notation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
##
# Support legacy URL helpers that use hashes instead of the `:to` keyword argument.
#
# @example
# get "/photos/:id" => "photos#show"
# @example
# mount Sidekiq::Web => "/sidekiq"
# @example
# get "search" => :index
# @example
# get "admin_users" => "users"
module Rage::Router::DSLPlugins::LegacyHashNotation
%i(get post put patch delete).each do |action_name|
define_method(action_name) do |*args, **kwargs|
if args.empty? && !kwargs.empty?
path, handler = kwargs.first

to = if handler.is_a?(Symbol)
raise ArgumentError, "Could not derive the controller value from the route definitions" if @controllers.empty?
"#{@controllers.last}##{handler}"
elsif handler.is_a?(String) && !handler.include?("#")
"#{handler}##{path.split("/").last}"
elsif handler.is_a?(String)
handler
end
end

if path && to
options = kwargs.except(path).merge(to: to)
super(path, **options)
else
super(*args, **kwargs)
end
end
end

def mount(*args, **kwargs)
if args.empty? && !kwargs.empty?
app, at = kwargs.first
options = kwargs.except(app).merge(at: at)
super(app, **options)
else
super(*args, **kwargs)
end
end
end
14 changes: 14 additions & 0 deletions lib/rage/router/dsl_plugins/legacy_root_notation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
##
# Support legacy root helpers that don't use the `:to` keyword argument.
#
# @example
# root "photos#index"
module Rage::Router::DSLPlugins::LegacyRootNotation
def root(*args, **kwargs)
if args.length == 1 && args[0].is_a?(String) && kwargs.empty?
super(to: args[0])
else
super(*args, **kwargs)
end
end
end
13 changes: 13 additions & 0 deletions lib/rage/router/dsl_plugins/named_route_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
##
# Support the `as` option. As Rage currently doesn't generate named route helpers, we simply ignore it.
#
# @example
# get "/photos/:id", to: "photos#show", as: :user_photos
module Rage::Router::DSLPlugins::NamedRouteHelpers
%i(get post put patch delete).each do |action_name|
define_method(action_name) do |*args, **kwargs|
kwargs.delete(:as)
super(*args, **kwargs)
end
end
end
Loading

0 comments on commit e1dd8e7

Please sign in to comment.