diff --git a/gems/actionpack/7.2/_test/test.rb b/gems/actionpack/7.2/_test/test.rb index edda1d0d..377cc99f 100644 --- a/gems/actionpack/7.2/_test/test.rb +++ b/gems/actionpack/7.2/_test/test.rb @@ -31,3 +31,54 @@ def show request_http_digest_authentication("Admin", "Denied").length end end + +# Routing DSL forms from the Rails routing guide +class AdminConstraint + def matches?(request) + request.path.start_with?("/admin") + end +end + +ActionDispatch::Routing::RouteSet.new.draw do + root "pages#home" + root(to: "pages#home", as: :home).has_named_route?(:home) + + get "profile", to: "users#show", as: :profile + get "photos/search" => "photos#search" + get "health", to: ->(_env) { [200, { "content-type" => "text/plain" }, ["OK"]] } + get "old", to: redirect("/new") + post "uploads", controller: :uploads, action: :create, defaults: { format: "json" }, format: false + match("photos", "pictures", to: "photos#show", via: [:get, :post]).has_named_route?(:photos) + match "exports/:id", to: "exports#show", via: :all, constraints: { id: /\d+/ }, anchor: true + + resources :photos, only: [:index, :show], param: :slug, path: "pictures", as: :images do + member { get :preview }.has_named_route?(:preview_image) + collection { get :search }.has_named_route?(:search_images) + new { get :draft }.has_named_route?(:draft_new_image) + get :recent, on: :collection + nested { resources :comments, except: :destroy }.has_named_route?(:image_comments) + end.has_named_route?(:images) + resource :geocoder, controller: "geocoders", module: "maps" + resources :articles, concerns: [:commentable], path_names: { new: "make" }, shallow: true + shallow { resources(:posts) { resources :drafts } }.has_named_route?(:posts) + + namespace(:admin, path: "control", as: :ctl) { resources :articles }.has_named_route?(:ctl_articles) + scope(module: "admin", path: "manage", as: :manage) { resources :users }.has_named_route?(:manage_users) + scope("(:locale)", locale: /en|nl/) { resources :books } + scope(only: :index) { resources :tags } + + controller(:pages) { get "about", action: :about }.has_named_route?(:about) + defaults(format: :json) { get "status", to: "status#show" }.has_named_route?(:status) + + constraints(subdomain: "api") { resources :keys } + constraints(id: /\d+/) { get "items/:id", to: "items#show" } + constraints(->(request) { request.xhr? }) { get "async", to: "async#show" } + constraints(AdminConstraint.new) { get "admin", to: "admin#index" } + get "secure", to: "secure#show", constraints: ->(request) { request.ssl? } + + concern(:commentable) { |options| resources :comments, **options } + concerns(:commentable, only: :index).first&.to_proc + + mount Rack::Head.new(->(_env) { [200, { "content-type" => "text/plain" }, ["OK"]] }), at: "/head", as: :head + with_default_scope(module: "v1") { get "ping", to: "ping#show" }.has_named_route?(:ping) +end diff --git a/gems/actionpack/7.2/_test/test.rbs b/gems/actionpack/7.2/_test/test.rbs index 18bcbc7c..b1a08e95 100644 --- a/gems/actionpack/7.2/_test/test.rbs +++ b/gems/actionpack/7.2/_test/test.rbs @@ -10,3 +10,7 @@ end class AuthController < ActionController::Base def show: () -> void end + +class AdminConstraint + def matches?: (ActionDispatch::Request request) -> bool +end diff --git a/gems/actionpack/7.2/actiondispatch.rbs b/gems/actionpack/7.2/actiondispatch.rbs index f0c6a05e..1786cb68 100644 --- a/gems/actionpack/7.2/actiondispatch.rbs +++ b/gems/actionpack/7.2/actiondispatch.rbs @@ -81,42 +81,332 @@ module ActionDispatch end class Mapper + # A route path, resource name, controller, or action. + type path = String | Symbol + + # HTTP verbs the route recognizes, such as `:get`, `[:get, :post]`, or `:all`. + type via = Symbol | Array[Symbol] + + # `true` requires a format segment, `false` drops it, and a String or Regexp constrains it. + type format = bool | String | Regexp | nil + + # Values accepted by the `:only` and `:except` resource options. + type actions = path | Array[path] + + # Values accepted by the `:to` option: `"controller#action"`, an action name, a Rack application, or a controller class. + type endpoint = String | ^(Rack::env env) -> Rack::response | Rack::_Application | singleton(ActionController::Metal) + + # Request constraints are objects responding to `matches?`, or callables taking the request. + interface _RequestMatcher + def matches?: (ActionDispatch::Request request) -> boolish + end + + type request_constraint = ^(ActionDispatch::Request request) -> boolish | _RequestMatcher + + # Segment constraints (`{ id: /\d+/ }`) and request conditions (`{ subdomain: "api" }`), or a request constraint. + type constraints = Hash[Symbol, untyped] | request_constraint + + # The scope a member, collection, or new route is added to. + type on = :member | :collection | :new + + # A concern defined with a callable receives the mapper and the options given to `concerns`. + interface _Concern + def call: (ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void + end + + module Base + # Mount a Rack-based application to be used within the application. + # + # mount SomeRackApp, at: "some_route" + # + # Alternatively: + # + # mount(SomeRackApp => "some_route") + # + # For options, see +match+, as +mount+ uses it internally. + # + # All mounted applications come with routing helpers to access them. + # These are named after the class specified, so for the above example + # the helper is either +some_rack_app_path+ or +some_rack_app_url+. + # To customize this helper's name, use the +:as+ option: + # + # mount(SomeRackApp => "some_route", as: "exciting") + # + # This will generate the +exciting_path+ and +exciting_url+ helpers + # which can be used to navigate to this mounted app. + def mount: (untyped app, at: String, ?as: path?, ?via: via, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[untyped, untyped] app_to_path_and_options) -> self + + def with_default_scope: (Hash[Symbol, untyped] scope) { () [self: ActionDispatch::Routing::Mapper] -> void } -> self + + # Query if the following named route was already defined. + def has_named_route?: (path name) -> bool + end + module HttpHelpers # Define a route that only recognizes HTTP GET. # For supported arguments, see match[rdoc-ref:Base#match] # # get 'bacon', to: 'food#bacon' - def get: (*untyped args) ?{ () -> untyped } -> untyped + def get: (path path, *path paths, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP POST. # For supported arguments, see match[rdoc-ref:Base#match] # # post 'bacon', to: 'food#bacon' - def post: (*untyped args) ?{ () -> untyped } -> untyped + def post: (path path, *path paths, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP PATCH. # For supported arguments, see match[rdoc-ref:Base#match] # # patch 'bacon', to: 'food#bacon' - def patch: (*untyped args) ?{ () -> untyped } -> untyped + def patch: (path path, *path paths, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP PUT. # For supported arguments, see match[rdoc-ref:Base#match] # # put 'bacon', to: 'food#bacon' - def put: (*untyped args) ?{ () -> untyped } -> untyped + def put: (path path, *path paths, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP DELETE. # For supported arguments, see match[rdoc-ref:Base#match] # # delete 'broccoli', to: 'food#broccoli' - def delete: (*untyped args) ?{ () -> untyped } -> untyped + def delete: (path path, *path paths, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self + + def options: (path path, *path paths, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self private def map_method: (untyped method, untyped args) ?{ () -> untyped } -> untyped end + module Scoping + # Scopes a set of routes to the given default options. + # + # Take the following route definition as an example: + # + # scope path: ":account_id", as: "account" do + # resources :projects + # end + # + # This generates helpers such as +account_projects_path+, just like +resources+ does. + # The difference here being that the routes generated are like /:account_id/projects, + # rather than /accounts/:account_id/projects. + # + # === Options + # + # Takes same options as Base#match and Resources#resources. + # + # # route /posts (without the prefix /admin) to Admin::PostsController + # scope module: "admin" do + # resources :posts + # end + # + # # prefix the posts resource's requests with '/admin' + # scope path: "/admin" do + # resources :posts + # end + # + # # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+ + # scope as: "sekret" do + # resources :posts + # end + def scope: (*(path | Array[path]) paths, ?only: actions, ?except: actions, ?module: path, ?path: path, ?as: path, ?controller: path, ?action: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?shallow: bool, ?shallow_path: path, ?shallow_prefix: path, ?path_names: Hash[Symbol, String], ?format: format, ?via: via, ?to: endpoint, **untyped) { () -> void } -> self + + # Scopes routes to a specific controller + # + # controller "food" do + # match "bacon", action: :bacon, via: :get + # end + def controller: [T] (path controller) { () -> T } -> T + + # Scopes routes to a specific namespace. For example: + # + # namespace :admin do + # resources :posts + # end + # + # This generates the following routes: + # + # admin_posts GET /admin/posts(.:format) admin/posts#index + # admin_posts POST /admin/posts(.:format) admin/posts#create + # new_admin_post GET /admin/posts/new(.:format) admin/posts#new + # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit + # admin_post GET /admin/posts/:id(.:format) admin/posts#show + # admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update + # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy + # + # === Options + # + # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ + # options all default to the name of the namespace. + # + # For options, see Base#match. For +:shallow_path+ option, see + # Resources#resources. + # + # # accessible through /sekret/posts rather than /admin/posts + # namespace :admin, path: "sekret" do + # resources :posts + # end + # + # # maps to Sekret::PostsController rather than Admin::PostsController + # namespace :admin, module: "sekret" do + # resources :posts + # end + # + # # generates +sekret_posts_path+ rather than +admin_posts_path+ + # namespace :admin, as: "sekret" do + # resources :posts + # end + def namespace: (path name, ?as: path, ?path: path, ?shallow_path: path, ?shallow_prefix: path, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], **untyped) { () -> void } -> self + + # === Parameter Restriction + # Allows you to constrain the nested routes based on a set of rules. + # For instance, in order to change the routes to allow for a dot character in the +id+ parameter: + # + # constraints(id: /\d+\.\d+/) do + # resources :posts + # end + # + # Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be. + # The +id+ parameter must match the constraint passed in for this example. + # + # You may use this to also restrict other parameters: + # + # resources :posts do + # constraints(post_id: /\d+\.\d+/) do + # resources :comments + # end + # end + # + # === Restricting based on IP + # + # Routes can also be constrained to an IP or a certain range of IP addresses: + # + # constraints(ip: /192\.168\.\d+\.\d+/) do + # resources :posts + # end + # + # Any user connecting from the 192.168.* range will be able to see this resource, + # where as any user connecting outside of this range will be told there is no such route. + # + # === Dynamic request matching + # + # Requests to routes can be constrained based on specific criteria: + # + # constraints(-> (req) { req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do + # resources :iphones + # end + # + # You are able to move this logic out into a class if it is too complex for routes. + # This class must have a +matches?+ method defined on it which either returns +true+ + # if the user should be given access to that route, or +false+ if the user should not. + # + # class Iphone + # def self.matches?(request) + # request.env["HTTP_USER_AGENT"] =~ /iPhone/ + # end + # end + # + # An expected place for this code would be +lib/constraints+. + # + # This class is then used like this: + # + # constraints(Iphone) do + # resources :iphones + # end + def constraints: (?constraints constraints) { () -> void } -> self + + # Allows you to set default parameters for a route, such as this: + # defaults id: 'home' do + # match 'scoped_pages/(:id)', to: 'pages#show' + # end + # Using this, the +:id+ parameter here will default to 'home'. + def defaults: [T] (?Hash[Symbol, untyped] defaults) { () -> T } -> T + end + + module Concerns + # Define a routing concern using a name. + # + # Concerns may be defined inline, using a block, or handled by + # another object, by passing that object as the second parameter. + # + # The concern object, if supplied, should respond to call, + # which will receive two parameters: + # + # * The current mapper + # * A hash of options which the concern object may use + # + # Options may also be used by concerns defined in a block by accepting + # a block parameter. So, using a block, you might do something as + # simple as limit the actions available on certain resources, passing + # standard resource options through the concern: + # + # concern :commentable do |options| + # resources :comments, options + # end + # + # resources :posts, concerns: :commentable + # resources :archived_posts do + # # Don't allow comments on archived posts + # concerns :commentable, only: [:index, :show] + # end + # + # Or, using a callable object, you might implement something more + # specific to your application, which would be out of place in your + # routes file. + # + # # purchasable.rb + # class Purchasable + # def initialize(defaults = {}) + # @defaults = defaults + # end + # + # def call(mapper, options = {}) + # options = @defaults.merge(options) + # mapper.resources :purchases + # mapper.resources :receipts + # mapper.resources :returns if options[:returnable] + # end + # end + # + # # routes.rb + # concern :purchasable, Purchasable.new(returnable: true) + # + # resources :toys, concerns: :purchasable + # resources :electronics, concerns: :purchasable + # resources :pets do + # concerns :purchasable, returnable: false + # end + # + # Any routing helpers can be used inside a concern. If using a + # callable, they're accessible from the Mapper that's passed to + # call. + def concern: (Symbol name, (^(ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void) callable) -> ^(ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void + | (Symbol name, _Concern callable) -> _Concern + | (Symbol name) { (Hash[Symbol, untyped] options) [self: ActionDispatch::Routing::Mapper] -> void } -> ^(ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void + + # Use the named concerns + # + # resources :posts do + # concerns :commentable + # end + # + # Concerns also work in any routes helper that you want to use: + # + # namespace :posts do + # concerns :commentable + # end + def concerns: (*(Symbol | Array[Symbol]) names, **untyped options) -> Array[Symbol] + end + module Resources # Sometimes, you have a resource that clients always look up without # referencing an ID. A common example, /profile always shows the @@ -139,7 +429,7 @@ module ActionDispatch # # === Options # Takes same options as resources[rdoc-ref:#resources] - def resource: (*untyped resources) ?{ () -> untyped } -> untyped + def resource: (*path resources, ?as: path, ?controller: path, ?path: path, ?only: actions, ?except: actions, ?param: path, ?concerns: Symbol | Array[Symbol], ?shallow: bool, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?path_names: Hash[Symbol, String], ?shallow_path: path, ?shallow_prefix: path, ?format: format, **untyped) ?{ () -> void } -> self # In Rails, a resourceful route provides a mapping between HTTP verbs # and URLs and controller actions. By convention, each action also maps @@ -278,7 +568,67 @@ module ActionDispatch # # # resource actions are at /admin/posts. # resources :posts, path: "admin/posts" - def resources: (*untyped resources) ?{ () -> untyped } -> untyped + def resources: (*path resources, ?as: path, ?controller: path, ?path: path, ?only: actions, ?except: actions, ?param: path, ?concerns: Symbol | Array[Symbol], ?shallow: bool, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?path_names: Hash[Symbol, String], ?shallow_path: path, ?shallow_prefix: path, ?format: format, **untyped) ?{ () -> void } -> self + + # To add a route to the collection: + # + # resources :photos do + # collection do + # get 'search' + # end + # end + # + # This will enable Rails to recognize paths such as /photos/search + # with GET, and route to the search action of +PhotosController+. It will also + # create the search_photos_url and search_photos_path + # route helpers. + def collection: [T] () { () -> T } -> T + + # To add a member route, add a member block into the resource block: + # + # resources :photos do + # member do + # get 'preview' + # end + # end + # + # This will recognize /photos/1/preview with GET, and route to the + # preview action of +PhotosController+. It will also create the + # preview_photo_url and preview_photo_path helpers. + def member: [T] () { () -> T } -> T + + def new: [T] () { () -> T } -> T + + def nested: () { () -> void } -> self + + def namespace: (path name, ?as: path, ?path: path, ?shallow_path: path, ?shallow_prefix: path, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], **untyped) { () -> void } -> self + + def shallow: [T] () { () -> T } -> T + + # Matches a URL pattern to one or more routes. + # For more information, see match[rdoc-ref:Base#match]. + # + # match 'path' => 'controller#action', via: :patch + # match 'path', to: 'controller#action', via: :post + # match 'path', 'otherpath', on: :member, via: :get + def match: (path path, *path paths, ?as: path?, ?via: via, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self + + # You can specify what Rails should route "/" to with the root method: + # + # root to: 'pages#main' + # + # For options, see +match+, as +root+ uses it internally. + # + # You can also pass a string which will expand + # + # root 'pages#main' + # + # You should put the root route at the top of config/routes.rb, + # because this means it will be matched first. As this is the most popular route + # of most Rails applications, this is beneficial. + def root: (String to, ?as: path?, ?via: via, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?format: format, **untyped) -> self + | (to: endpoint, ?as: path?, ?via: via, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?format: format, **untyped) -> self end end end diff --git a/gems/actionpack/7.2/actionpack-generated.rbs b/gems/actionpack/7.2/actionpack-generated.rbs index 7136f58c..7bb05ee0 100644 --- a/gems/actionpack/7.2/actionpack-generated.rbs +++ b/gems/actionpack/7.2/actionpack-generated.rbs @@ -8857,36 +8857,10 @@ module ActionDispatch # segment or disable it by supplying +false+. def match: (untyped path, ?untyped? options) -> nil - # Mount a Rack-based application to be used within the application. - # - # mount SomeRackApp, at: "some_route" - # - # Alternatively: - # - # mount(SomeRackApp => "some_route") - # - # For options, see +match+, as +mount+ uses it internally. - # - # All mounted applications come with routing helpers to access them. - # These are named after the class specified, so for the above example - # the helper is either +some_rack_app_path+ or +some_rack_app_url+. - # To customize this helper's name, use the +:as+ option: - # - # mount(SomeRackApp => "some_route", as: "exciting") - # - # This will generate the +exciting_path+ and +exciting_url+ helpers - # which can be used to navigate to this mounted app. - def mount: (untyped app, ?untyped? options) -> untyped - def default_url_options=: (untyped options) -> untyped alias default_url_options default_url_options= - def with_default_scope: (untyped scope) { () -> untyped } -> untyped - - # Query if the following named route was already defined. - def has_named_route?: (untyped name) -> untyped - private def rails_app?: (untyped app) -> untyped @@ -8951,151 +8925,8 @@ module ActionDispatch # PATCH/PUT /admin/posts/1 # DELETE /admin/posts/1 module Scoping - # Scopes a set of routes to the given default options. - # - # Take the following route definition as an example: - # - # scope path: ":account_id", as: "account" do - # resources :projects - # end - # - # This generates helpers such as +account_projects_path+, just like +resources+ does. - # The difference here being that the routes generated are like /:account_id/projects, - # rather than /accounts/:account_id/projects. - # - # === Options - # - # Takes same options as Base#match and Resources#resources. - # - # # route /posts (without the prefix /admin) to Admin::PostsController - # scope module: "admin" do - # resources :posts - # end - # - # # prefix the posts resource's requests with '/admin' - # scope path: "/admin" do - # resources :posts - # end - # - # # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+ - # scope as: "sekret" do - # resources :posts - # end - def scope: (*untyped args) { () -> untyped } -> untyped - POISON: untyped - # Scopes routes to a specific controller - # - # controller "food" do - # match "bacon", action: :bacon, via: :get - # end - def controller: (untyped controller) { () -> untyped } -> untyped - - # Scopes routes to a specific namespace. For example: - # - # namespace :admin do - # resources :posts - # end - # - # This generates the following routes: - # - # admin_posts GET /admin/posts(.:format) admin/posts#index - # admin_posts POST /admin/posts(.:format) admin/posts#create - # new_admin_post GET /admin/posts/new(.:format) admin/posts#new - # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit - # admin_post GET /admin/posts/:id(.:format) admin/posts#show - # admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update - # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy - # - # === Options - # - # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ - # options all default to the name of the namespace. - # - # For options, see Base#match. For +:shallow_path+ option, see - # Resources#resources. - # - # # accessible through /sekret/posts rather than /admin/posts - # namespace :admin, path: "sekret" do - # resources :posts - # end - # - # # maps to Sekret::PostsController rather than Admin::PostsController - # namespace :admin, module: "sekret" do - # resources :posts - # end - # - # # generates +sekret_posts_path+ rather than +admin_posts_path+ - # namespace :admin, as: "sekret" do - # resources :posts - # end - def namespace: (untyped path, ?::Hash[untyped, untyped] options) { () -> untyped } -> untyped - - # === Parameter Restriction - # Allows you to constrain the nested routes based on a set of rules. - # For instance, in order to change the routes to allow for a dot character in the +id+ parameter: - # - # constraints(id: /\d+\.\d+/) do - # resources :posts - # end - # - # Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be. - # The +id+ parameter must match the constraint passed in for this example. - # - # You may use this to also restrict other parameters: - # - # resources :posts do - # constraints(post_id: /\d+\.\d+/) do - # resources :comments - # end - # end - # - # === Restricting based on IP - # - # Routes can also be constrained to an IP or a certain range of IP addresses: - # - # constraints(ip: /192\.168\.\d+\.\d+/) do - # resources :posts - # end - # - # Any user connecting from the 192.168.* range will be able to see this resource, - # where as any user connecting outside of this range will be told there is no such route. - # - # === Dynamic request matching - # - # Requests to routes can be constrained based on specific criteria: - # - # constraints(-> (req) { req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do - # resources :iphones - # end - # - # You are able to move this logic out into a class if it is too complex for routes. - # This class must have a +matches?+ method defined on it which either returns +true+ - # if the user should be given access to that route, or +false+ if the user should not. - # - # class Iphone - # def self.matches?(request) - # request.env["HTTP_USER_AGENT"] =~ /iPhone/ - # end - # end - # - # An expected place for this code would be +lib/constraints+. - # - # This class is then used like this: - # - # constraints(Iphone) do - # resources :iphones - # end - def constraints: (?::Hash[untyped, untyped] constraints) { () -> untyped } -> untyped - - # Allows you to set default parameters for a route, such as this: - # defaults id: 'home' do - # match 'scoped_pages/(:id)', to: 'pages#show' - # end - # Using this, the +:id+ parameter here will default to 'home'. - def defaults: (?::Hash[untyped, untyped] defaults) { () -> untyped } -> untyped - private def merge_path_scope: (untyped parent, untyped child) -> untyped @@ -9251,64 +9082,8 @@ module ActionDispatch def resources_path_names: (untyped options) -> untyped - # To add a route to the collection: - # - # resources :photos do - # collection do - # get 'search' - # end - # end - # - # This will enable Rails to recognize paths such as /photos/search - # with GET, and route to the search action of +PhotosController+. It will also - # create the search_photos_url and search_photos_path - # route helpers. - def collection: () { () -> untyped } -> untyped - - # To add a member route, add a member block into the resource block: - # - # resources :photos do - # member do - # get 'preview' - # end - # end - # - # This will recognize /photos/1/preview with GET, and route to the - # preview action of +PhotosController+. It will also create the - # preview_photo_url and preview_photo_path helpers. - def member: () { () -> untyped } -> untyped - - def new: () { () -> untyped } -> untyped - - def nested: () { () -> untyped } -> untyped - - def shallow: () { () -> untyped } -> untyped - def shallow?: () -> untyped - # Matches a URL pattern to one or more routes. - # For more information, see match[rdoc-ref:Base#match]. - # - # match 'path' => 'controller#action', via: :patch - # match 'path', to: 'controller#action', via: :post - # match 'path', 'otherpath', on: :member, via: :get - def match: (untyped path, *untyped rest) { () -> untyped } -> untyped - - # You can specify what Rails should route "/" to with the root method: - # - # root to: 'pages#main' - # - # For options, see +match+, as +root+ uses it internally. - # - # You can also pass a string which will expand - # - # root 'pages#main' - # - # You should put the root route at the top of config/routes.rb, - # because this means it will be matched first. As this is the most popular route - # of most Rails applications, this is beneficial. - def root: (untyped path, ?::Hash[untyped, untyped] options) -> untyped - private def parent_resource: () -> untyped @@ -9391,76 +9166,6 @@ module ActionDispatch # concerns :commentable # end module Concerns - # Define a routing concern using a name. - # - # Concerns may be defined inline, using a block, or handled by - # another object, by passing that object as the second parameter. - # - # The concern object, if supplied, should respond to call, - # which will receive two parameters: - # - # * The current mapper - # * A hash of options which the concern object may use - # - # Options may also be used by concerns defined in a block by accepting - # a block parameter. So, using a block, you might do something as - # simple as limit the actions available on certain resources, passing - # standard resource options through the concern: - # - # concern :commentable do |options| - # resources :comments, options - # end - # - # resources :posts, concerns: :commentable - # resources :archived_posts do - # # Don't allow comments on archived posts - # concerns :commentable, only: [:index, :show] - # end - # - # Or, using a callable object, you might implement something more - # specific to your application, which would be out of place in your - # routes file. - # - # # purchasable.rb - # class Purchasable - # def initialize(defaults = {}) - # @defaults = defaults - # end - # - # def call(mapper, options = {}) - # options = @defaults.merge(options) - # mapper.resources :purchases - # mapper.resources :receipts - # mapper.resources :returns if options[:returnable] - # end - # end - # - # # routes.rb - # concern :purchasable, Purchasable.new(returnable: true) - # - # resources :toys, concerns: :purchasable - # resources :electronics, concerns: :purchasable - # resources :pets do - # concerns :purchasable, returnable: false - # end - # - # Any routing helpers can be used inside a concern. If using a - # callable, they're accessible from the Mapper that's passed to - # call. - def concern: (untyped name, ?untyped? callable) { () -> untyped } -> untyped - - # Use the named concerns - # - # resources :posts do - # concerns :commentable - # end - # - # Concerns also work in any routes helper that you want to use: - # - # namespace :posts do - # concerns :commentable - # end - def concerns: (*untyped args) -> untyped end module CustomUrls diff --git a/gems/actionpack/8.1/_test/test.rb b/gems/actionpack/8.1/_test/test.rb index db5a44c6..eb1b683c 100644 --- a/gems/actionpack/8.1/_test/test.rb +++ b/gems/actionpack/8.1/_test/test.rb @@ -117,3 +117,54 @@ def show request_http_digest_authentication("Admin", "Denied").length end end + +# Routing DSL forms from the Rails routing guide +class AdminConstraint + def matches?(request) + request.path.start_with?("/admin") + end +end + +ActionDispatch::Routing::RouteSet.new.draw do + root "pages#home" + root(to: "pages#home", as: :home).first&.length + + get "profile", to: "users#show", as: :profile + get "photos/search" => "photos#search" + get "health", to: ->(_env) { [200, { "content-type" => "text/plain" }, ["OK"]] } + get "old", to: redirect("/new") + post "uploads", controller: :uploads, action: :create, defaults: { format: "json" }, format: false + match("photos", to: "photos#show", via: [:get, :post]).size + match "exports/:id", to: "exports#show", via: :all, constraints: { id: /\d+/ }, anchor: true + + resources :photos, only: [:index, :show], param: :slug, path: "pictures", as: :images do + member { get :preview }.has_named_route?(:preview_image) + collection { get :search }.has_named_route?(:search_images) + new { get :draft }.has_named_route?(:draft_new_image) + get :recent, on: :collection + nested { resources :comments, except: :destroy }.has_named_route?(:image_comments) + end.has_named_route?(:images) + resource :geocoder, controller: "geocoders", module: "maps" + resources :articles, concerns: [:commentable], path_names: { new: "make" }, shallow: true + shallow { resources(:posts) { resources :drafts } }.has_named_route?(:posts) + + namespace(:admin, path: "control", as: :ctl) { resources :articles }.has_named_route?(:ctl_articles) + scope(module: "admin", path: "manage", as: :manage) { resources :users }.has_named_route?(:manage_users) + scope("(:locale)", locale: /en|nl/) { resources :books } + scope(only: :index) { resources :tags } + + controller(:pages) { get "about", action: :about }.has_named_route?(:about) + defaults(format: :json) { get "status", to: "status#show" }.has_named_route?(:status) + + constraints(subdomain: "api") { resources :keys } + constraints(id: /\d+/) { get "items/:id", to: "items#show" } + constraints(->(request) { request.xhr? }) { get "async", to: "async#show" } + constraints(AdminConstraint.new) { get "admin", to: "admin#index" } + get "secure", to: "secure#show", constraints: ->(request) { request.ssl? } + + concern(:commentable) { |options| resources :comments, **options } + concerns(:commentable, only: :index).first&.to_proc + + mount Rack::Head.new(->(_env) { [200, { "content-type" => "text/plain" }, ["OK"]] }), at: "/head", as: :head + with_default_scope(module: "v1") { get "ping", to: "ping#show" }.has_named_route?(:ping) +end diff --git a/gems/actionpack/8.1/_test/test.rbs b/gems/actionpack/8.1/_test/test.rbs index d568eb0a..1b4162bb 100644 --- a/gems/actionpack/8.1/_test/test.rbs +++ b/gems/actionpack/8.1/_test/test.rbs @@ -24,3 +24,7 @@ end class AuthController < ActionController::Base def show: () -> void end + +class AdminConstraint + def matches?: (ActionDispatch::Request request) -> bool +end diff --git a/gems/actionpack/8.1/actiondispatch.rbs b/gems/actionpack/8.1/actiondispatch.rbs index 5edd8f20..aa2beb84 100644 --- a/gems/actionpack/8.1/actiondispatch.rbs +++ b/gems/actionpack/8.1/actiondispatch.rbs @@ -81,36 +81,338 @@ module ActionDispatch end class Mapper + # A route path, resource name, controller, or action. + type path = String | Symbol + + # HTTP verbs the route recognizes, such as `:get`, `[:get, :post]`, or `:all`. + type via = Symbol | Array[Symbol] + + # `true` requires a format segment, `false` drops it, and a String or Regexp constrains it. + type format = bool | String | Regexp | nil + + # Values accepted by the `:only` and `:except` resource options. + type actions = path | Array[path] + + # Values accepted by the `:to` option: `"controller#action"`, an action name, a Rack application, or a controller class. + type endpoint = String | ^(Rack::env env) -> Rack::response | Rack::_Application | singleton(ActionController::Metal) + + # Request constraints are objects responding to `matches?`, or callables taking the request. + interface _RequestMatcher + def matches?: (ActionDispatch::Request request) -> boolish + end + + type request_constraint = ^(ActionDispatch::Request request) -> boolish | _RequestMatcher + + # Segment constraints (`{ id: /\d+/ }`) and request conditions (`{ subdomain: "api" }`), or a request constraint. + type constraints = Hash[Symbol, untyped] | request_constraint + + # The scope a member, collection, or new route is added to. + type on = :member | :collection | :new + + # A concern defined with a callable receives the mapper and the options given to `concerns`. + interface _Concern + def call: (ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void + end + + module Base + # Mount a Rack-based application to be used within the application. + # + # mount SomeRackApp, at: "some_route" + # + # For options, see `match`, as `mount` uses it internally. + # + # All mounted applications come with routing helpers to access them. These are + # named after the class specified, so for the above example the helper is either + # `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name, + # use the `:as` option: + # + # mount(SomeRackApp, at: "some_route", as: "exciting") + # + # This will generate the `exciting_path` and `exciting_url` helpers which can be + # used to navigate to this mounted app. + def mount: (untyped app, at: String, ?as: path?, ?via: via, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[untyped, untyped] app_to_path_and_options) -> self + + def with_default_scope: (Hash[Symbol, untyped] scope) { () [self: ActionDispatch::Routing::Mapper] -> void } -> self + + # Query if the following named route was already defined. + def has_named_route?: (path name) -> bool + end + module HttpHelpers # Define a route that only recognizes HTTP GET. # For supported arguments, see match[rdoc-ref:Base#match] # # get 'bacon', to: 'food#bacon' - def get: (*untyped args) ?{ () -> untyped } -> untyped + def get: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP POST. # For supported arguments, see match[rdoc-ref:Base#match] # # post 'bacon', to: 'food#bacon' - def post: (*untyped args) ?{ () -> untyped } -> untyped + def post: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP PATCH. # For supported arguments, see match[rdoc-ref:Base#match] # # patch 'bacon', to: 'food#bacon' - def patch: (*untyped args) ?{ () -> untyped } -> untyped + def patch: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP PUT. # For supported arguments, see match[rdoc-ref:Base#match] # # put 'bacon', to: 'food#bacon' - def put: (*untyped args) ?{ () -> untyped } -> untyped + def put: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self # Define a route that only recognizes HTTP DELETE. # For supported arguments, see match[rdoc-ref:Base#match] # # delete 'broccoli', to: 'food#broccoli' - def delete: (*untyped args) ?{ () -> untyped } -> untyped + def delete: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self + + # Define a route that only recognizes HTTP OPTIONS. For supported arguments, see + # [match](rdoc-ref:Base#match) + # + # options 'carrots', to: 'food#carrots' + def options: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self + + # Define a route that recognizes HTTP CONNECT (and GET) requests. More + # specifically this recognizes HTTP/1 protocol upgrade requests and HTTP/2 + # CONNECT requests with the protocol pseudo header. For supported arguments, + # see [match](rdoc-ref:Base#match) + # + # connect 'live', to: 'live#index' + def connect: (path path, ?as: path?, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> self + | (Hash[path, untyped] path_to_endpoint_and_options) -> self + end + + module Scoping + # Scopes a set of routes to the given default options. + # + # Take the following route definition as an example: + # + # scope path: ":account_id", as: "account" do + # resources :projects + # end + # + # This generates helpers such as `account_projects_path`, just like `resources` + # does. The difference here being that the routes generated are like + # /:account_id/projects, rather than /accounts/:account_id/projects. + # + # ### Options + # + # Takes same options as `Base#match` and `Resources#resources`. + # + # # route /posts (without the prefix /admin) to Admin::PostsController + # scope module: "admin" do + # resources :posts + # end + # + # # prefix the posts resource's requests with '/admin' + # scope path: "/admin" do + # resources :posts + # end + # + # # prefix the routing helper name: sekret_posts_path instead of posts_path + # scope as: "sekret" do + # resources :posts + # end + def scope: (*(path | Array[path]) paths, ?only: actions, ?except: actions, ?module: path, ?path: path, ?as: path, ?controller: path, ?action: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?shallow: bool, ?shallow_path: path, ?shallow_prefix: path, ?path_names: Hash[Symbol, String], ?format: format, ?via: via, ?to: endpoint, **untyped) { () -> void } -> self + + # Scopes routes to a specific controller + # + # controller "food" do + # match "bacon", action: :bacon, via: :get + # end + def controller: [T] (path controller) { () -> T } -> T + + # Scopes routes to a specific namespace. For example: + # + # namespace :admin do + # resources :posts + # end + # + # This generates the following routes: + # + # admin_posts GET /admin/posts(.:format) admin/posts#index + # admin_posts POST /admin/posts(.:format) admin/posts#create + # new_admin_post GET /admin/posts/new(.:format) admin/posts#new + # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit + # admin_post GET /admin/posts/:id(.:format) admin/posts#show + # admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update + # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy + # + # ### Options + # + # The `:path`, `:as`, `:module`, `:shallow_path`, and `:shallow_prefix` options + # all default to the name of the namespace. + # + # For options, see `Base#match`. For `:shallow_path` option, see + # `Resources#resources`. + # + # # accessible through /sekret/posts rather than /admin/posts + # namespace :admin, path: "sekret" do + # resources :posts + # end + # + # # maps to Sekret::PostsController rather than Admin::PostsController + # namespace :admin, module: "sekret" do + # resources :posts + # end + # + # # generates sekret_posts_path rather than admin_posts_path + # namespace :admin, as: "sekret" do + # resources :posts + # end + def namespace: (path name, ?as: path, ?path: path, ?shallow_path: path, ?shallow_prefix: path, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], **untyped) { () -> void } -> self + + # ### Parameter Restriction + # Allows you to constrain the nested routes based on a set of rules. For + # instance, in order to change the routes to allow for a dot character in the + # `id` parameter: + # + # constraints(id: /\d+\.\d+/) do + # resources :posts + # end + # + # Now routes such as `/posts/1` will no longer be valid, but `/posts/1.1` will + # be. The `id` parameter must match the constraint passed in for this example. + # + # You may use this to also restrict other parameters: + # + # resources :posts do + # constraints(post_id: /\d+\.\d+/) do + # resources :comments + # end + # end + # + # ### Restricting based on IP + # + # Routes can also be constrained to an IP or a certain range of IP addresses: + # + # constraints(ip: /192\.168\.\d+\.\d+/) do + # resources :posts + # end + # + # Any user connecting from the 192.168.* range will be able to see this + # resource, where as any user connecting outside of this range will be told + # there is no such route. + # + # ### Dynamic request matching + # + # Requests to routes can be constrained based on specific criteria: + # + # constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do + # resources :iphones + # end + # + # You are able to move this logic out into a class if it is too complex for + # routes. This class must have a `matches?` method defined on it which either + # returns `true` if the user should be given access to that route, or `false` if + # the user should not. + # + # class Iphone + # def self.matches?(request) + # /iPhone/.match?(request.env["HTTP_USER_AGENT"]) + # end + # end + # + # An expected place for this code would be `lib/constraints`. + # + # This class is then used like this: + # + # constraints(Iphone) do + # resources :iphones + # end + def constraints: (?constraints constraints) { () -> void } -> self + + # Allows you to set default parameters for a route, such as this: + # + # defaults id: 'home' do + # match 'scoped_pages/(:id)', to: 'pages#show' + # end + # + # Using this, the `:id` parameter here will default to 'home'. + def defaults: [T] (?Hash[Symbol, untyped] defaults) { () -> T } -> T + end + + module Concerns + # Define a routing concern using a name. + # + # Concerns may be defined inline, using a block, or handled by another object, + # by passing that object as the second parameter. + # + # The concern object, if supplied, should respond to `call`, which will receive + # two parameters: + # + # * The current mapper + # * A hash of options which the concern object may use + # + # Options may also be used by concerns defined in a block by accepting a block + # parameter. So, using a block, you might do something as simple as limit the + # actions available on certain resources, passing standard resource options + # through the concern: + # + # concern :commentable do |options| + # resources :comments, options + # end + # + # resources :posts, concerns: :commentable + # resources :archived_posts do + # # Don't allow comments on archived posts + # concerns :commentable, only: [:index, :show] + # end + # + # Or, using a callable object, you might implement something more specific to + # your application, which would be out of place in your routes file. + # + # # purchasable.rb + # class Purchasable + # def initialize(defaults = {}) + # @defaults = defaults + # end + # + # def call(mapper, options = {}) + # options = @defaults.merge(options) + # mapper.resources :purchases + # mapper.resources :receipts + # mapper.resources :returns if options[:returnable] + # end + # end + # + # # routes.rb + # concern :purchasable, Purchasable.new(returnable: true) + # + # resources :toys, concerns: :purchasable + # resources :electronics, concerns: :purchasable + # resources :pets do + # concerns :purchasable, returnable: false + # end + # + # Any routing helpers can be used inside a concern. If using a callable, they're + # accessible from the Mapper that's passed to `call`. + def concern: (Symbol name, (^(ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void) callable) -> ^(ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void + | (Symbol name, _Concern callable) -> _Concern + | (Symbol name) { (Hash[Symbol, untyped] options) [self: ActionDispatch::Routing::Mapper] -> void } -> ^(ActionDispatch::Routing::Mapper mapper, Hash[Symbol, untyped] options) -> void + + # Use the named concerns + # + # resources :posts do + # concerns :commentable + # end + # + # Concerns also work in any routes helper that you want to use: + # + # namespace :posts do + # concerns :commentable + # end + def concerns: (*(Symbol | Array[Symbol]) names, **untyped options) -> Array[Symbol] end module Resources @@ -135,7 +437,7 @@ module ActionDispatch # # === Options # Takes same options as resources[rdoc-ref:#resources] - def resource: (*untyped resources) ?{ () -> untyped } -> untyped + def resource: (*path resources, ?as: path, ?controller: path, ?path: path, ?only: actions, ?except: actions, ?param: path, ?concerns: Symbol | Array[Symbol], ?shallow: bool, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?path_names: Hash[Symbol, String], ?shallow_path: path, ?shallow_prefix: path, ?format: format, **untyped) ?{ () -> void } -> self # In Rails, a resourceful route provides a mapping between HTTP verbs # and URLs and controller actions. By convention, each action also maps @@ -274,7 +576,66 @@ module ActionDispatch # # # resource actions are at /admin/posts. # resources :posts, path: "admin/posts" - def resources: (*untyped resources) ?{ () -> untyped } -> untyped + def resources: (*path resources, ?as: path, ?controller: path, ?path: path, ?only: actions, ?except: actions, ?param: path, ?concerns: Symbol | Array[Symbol], ?shallow: bool, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?path_names: Hash[Symbol, String], ?shallow_path: path, ?shallow_prefix: path, ?format: format, **untyped) ?{ () -> void } -> self + + # To add a route to the collection: + # + # resources :photos do + # collection do + # get 'search' + # end + # end + # + # This will enable Rails to recognize paths such as `/photos/search` with GET, + # and route to the search action of `PhotosController`. It will also create the + # `search_photos_url` and `search_photos_path` route helpers. + def collection: [T] () { () -> T } -> T + + # To add a member route, add a member block into the resource block: + # + # resources :photos do + # member do + # get 'preview' + # end + # end + # + # This will recognize `/photos/1/preview` with GET, and route to the preview + # action of `PhotosController`. It will also create the `preview_photo_url` and + # `preview_photo_path` helpers. + def member: [T] () { () -> T } -> T + + def new: [T] () { () -> T } -> T + + def nested: () { () -> void } -> self + + # See ActionDispatch::Routing::Mapper::Scoping#namespace. + def namespace: (path name, ?as: path, ?path: path, ?shallow_path: path, ?shallow_prefix: path, ?module: path, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], **untyped) { () -> void } -> self + + def shallow: [T] () { () -> T } -> T + + # Matches a URL pattern to one or more routes. For more information, see + # [match](rdoc-ref:Base#match). + # + # match 'path', to: 'controller#action', via: :post + # match 'otherpath', on: :member, via: :get + def match: (path path, ?as: path?, ?via: via, ?to: endpoint, ?controller: path, ?action: path, ?on: on, ?defaults: Hash[Symbol, untyped], ?constraints: constraints, ?anchor: bool, ?format: format, ?path: path, ?internal: bool, **untyped) -> Array[path] + | (Hash[path, untyped] path_to_endpoint_and_options) -> Array[path] + + # You can specify what Rails should route "/" to with the root method: + # + # root to: 'pages#main' + # + # For options, see `match`, as `root` uses it internally. + # + # You can also pass a string which will expand + # + # root 'pages#main' + # + # You should put the root route at the top of `config/routes.rb`, because this + # means it will be matched first. As this is the most popular route of most + # Rails applications, this is beneficial. + def root: (String to, ?as: path?, ?via: via, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?format: format, **untyped) -> Array[String] + | (to: endpoint, ?as: path?, ?via: via, ?constraints: constraints, ?defaults: Hash[Symbol, untyped], ?format: format, **untyped) -> Array[String] end end end diff --git a/gems/actionpack/8.1/actionpack-generated.rbs b/gems/actionpack/8.1/actionpack-generated.rbs index c1aa5d0e..b4434d68 100644 --- a/gems/actionpack/8.1/actionpack-generated.rbs +++ b/gems/actionpack/8.1/actionpack-generated.rbs @@ -10663,32 +10663,10 @@ module ActionDispatch # def match: (untyped path, ?untyped? options) -> nil - # Mount a Rack-based application to be used within the application. - # - # mount SomeRackApp, at: "some_route" - # - # For options, see `match`, as `mount` uses it internally. - # - # All mounted applications come with routing helpers to access them. These are - # named after the class specified, so for the above example the helper is either - # `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name, - # use the `:as` option: - # - # mount(SomeRackApp, at: "some_route", as: "exciting") - # - # This will generate the `exciting_path` and `exciting_url` helpers which can be - # used to navigate to this mounted app. - def mount: (?untyped? app, ?untyped? deprecated_options, ?as: untyped, ?via: untyped?, ?at: untyped?, ?defaults: untyped?, ?constraints: untyped?, ?anchor: bool, ?format: bool, ?path: untyped?, ?internal: untyped?, **untyped mapping) ?{ (?) -> untyped } -> self - def default_url_options=: (untyped options) -> untyped alias default_url_options default_url_options= - def with_default_scope: (untyped scope) { (?) -> untyped } -> untyped - - # Query if the following named route was already defined. - def has_named_route?: (untyped name) -> untyped - private def assign_deprecated_option: (untyped deprecated_options, untyped key, untyped method_name) -> (untyped | nil) @@ -10703,19 +10681,6 @@ module ActionDispatch end module HttpHelpers - # Define a route that only recognizes HTTP OPTIONS. For supported arguments, see - # [match](rdoc-ref:Base#match) - # - # options 'carrots', to: 'food#carrots' - def options: (*untyped path_or_actions, ?as: untyped, ?to: untyped?, ?controller: untyped?, ?action: untyped?, ?on: untyped?, ?defaults: untyped?, ?constraints: untyped?, ?anchor: bool, ?format: bool, ?path: untyped?, ?internal: untyped?, **untyped mapping) ?{ (?) -> untyped } -> self - - # Define a route that recognizes HTTP CONNECT (and GET) requests. More - # specifically this recognizes HTTP/1 protocol upgrade requests and HTTP/2 - # CONNECT requests with the protocol pseudo header. For supported arguments, - # see [match](rdoc-ref:Base#match) - # - # connect 'live', to: 'live#index' - def connect: (*untyped path_or_actions, ?as: untyped, ?to: untyped?, ?controller: untyped?, ?action: untyped?, ?on: untyped?, ?defaults: untyped?, ?constraints: untyped?, ?anchor: bool, ?format: bool, ?path: untyped?, ?internal: untyped?, **untyped mapping) ?{ (?) -> untyped } -> self end # You may wish to organize groups of controllers under a namespace. Most @@ -10772,156 +10737,8 @@ module ActionDispatch # PATCH/PUT /admin/posts/1 # DELETE /admin/posts/1 module Scoping - # Scopes a set of routes to the given default options. - # - # Take the following route definition as an example: - # - # scope path: ":account_id", as: "account" do - # resources :projects - # end - # - # This generates helpers such as `account_projects_path`, just like `resources` - # does. The difference here being that the routes generated are like - # /:account_id/projects, rather than /accounts/:account_id/projects. - # - # ### Options - # - # Takes same options as `Base#match` and `Resources#resources`. - # - # # route /posts (without the prefix /admin) to Admin::PostsController - # scope module: "admin" do - # resources :posts - # end - # - # # prefix the posts resource's requests with '/admin' - # scope path: "/admin" do - # resources :posts - # end - # - # # prefix the routing helper name: sekret_posts_path instead of posts_path - # scope as: "sekret" do - # resources :posts - # end - def scope: (*untyped args, ?only: untyped?, ?except: untyped?, **untyped options) { () -> untyped } -> untyped - POISON: untyped - # Scopes routes to a specific controller - # - # controller "food" do - # match "bacon", action: :bacon, via: :get - # end - def controller: (untyped controller) { () -> untyped } -> untyped - - # Scopes routes to a specific namespace. For example: - # - # namespace :admin do - # resources :posts - # end - # - # This generates the following routes: - # - # admin_posts GET /admin/posts(.:format) admin/posts#index - # admin_posts POST /admin/posts(.:format) admin/posts#create - # new_admin_post GET /admin/posts/new(.:format) admin/posts#new - # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit - # admin_post GET /admin/posts/:id(.:format) admin/posts#show - # admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update - # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy - # - # ### Options - # - # The `:path`, `:as`, `:module`, `:shallow_path`, and `:shallow_prefix` options - # all default to the name of the namespace. - # - # For options, see `Base#match`. For `:shallow_path` option, see - # `Resources#resources`. - # - # # accessible through /sekret/posts rather than /admin/posts - # namespace :admin, path: "sekret" do - # resources :posts - # end - # - # # maps to Sekret::PostsController rather than Admin::PostsController - # namespace :admin, module: "sekret" do - # resources :posts - # end - # - # # generates sekret_posts_path rather than admin_posts_path - # namespace :admin, as: "sekret" do - # resources :posts - # end - def namespace: (untyped name, ?untyped? deprecated_options, ?as: untyped, ?path: untyped, ?shallow_path: untyped, ?shallow_prefix: untyped, **untyped options) { (?) -> untyped } -> untyped - - # ### Parameter Restriction - # Allows you to constrain the nested routes based on a set of rules. For - # instance, in order to change the routes to allow for a dot character in the - # `id` parameter: - # - # constraints(id: /\d+\.\d+/) do - # resources :posts - # end - # - # Now routes such as `/posts/1` will no longer be valid, but `/posts/1.1` will - # be. The `id` parameter must match the constraint passed in for this example. - # - # You may use this to also restrict other parameters: - # - # resources :posts do - # constraints(post_id: /\d+\.\d+/) do - # resources :comments - # end - # end - # - # ### Restricting based on IP - # - # Routes can also be constrained to an IP or a certain range of IP addresses: - # - # constraints(ip: /192\.168\.\d+\.\d+/) do - # resources :posts - # end - # - # Any user connecting from the 192.168.* range will be able to see this - # resource, where as any user connecting outside of this range will be told - # there is no such route. - # - # ### Dynamic request matching - # - # Requests to routes can be constrained based on specific criteria: - # - # constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do - # resources :iphones - # end - # - # You are able to move this logic out into a class if it is too complex for - # routes. This class must have a `matches?` method defined on it which either - # returns `true` if the user should be given access to that route, or `false` if - # the user should not. - # - # class Iphone - # def self.matches?(request) - # /iPhone/.match?(request.env["HTTP_USER_AGENT"]) - # end - # end - # - # An expected place for this code would be `lib/constraints`. - # - # This class is then used like this: - # - # constraints(Iphone) do - # resources :iphones - # end - def constraints: (?::Hash[untyped, untyped] constraints) { (?) -> untyped } -> untyped - - # Allows you to set default parameters for a route, such as this: - # - # defaults id: 'home' do - # match 'scoped_pages/(:id)', to: 'pages#show' - # end - # - # Using this, the `:id` parameter here will default to 'home'. - def defaults: (?::Hash[untyped, untyped] defaults) { () -> untyped } -> untyped - private def merge_path_scope: (untyped parent, untyped child) -> untyped @@ -11079,41 +10896,6 @@ module ActionDispatch def resources_path_names: (untyped options) -> untyped - # To add a route to the collection: - # - # resources :photos do - # collection do - # get 'search' - # end - # end - # - # This will enable Rails to recognize paths such as `/photos/search` with GET, - # and route to the search action of `PhotosController`. It will also create the - # `search_photos_url` and `search_photos_path` route helpers. - def collection: () { (?) -> untyped } -> untyped - - # To add a member route, add a member block into the resource block: - # - # resources :photos do - # member do - # get 'preview' - # end - # end - # - # This will recognize `/photos/1/preview` with GET, and route to the preview - # action of `PhotosController`. It will also create the `preview_photo_url` and - # `preview_photo_path` helpers. - def member: () { (?) -> untyped } -> untyped - - def new: () { (?) -> untyped } -> untyped - - def nested: () { (?) -> untyped } -> untyped - - # See ActionDispatch::Routing::Mapper::Scoping#namespace. - def namespace: (untyped name, ?untyped? deprecated_options, ?as: untyped, ?path: untyped, ?shallow_path: untyped, ?shallow_prefix: untyped, **untyped options) { (?) -> untyped } -> untyped - - def shallow: () { () -> untyped } -> untyped - def shallow?: () -> untyped # Loads another routes file with the given `name` located inside the @@ -11140,28 +10922,6 @@ module ActionDispatch # single routes file. def draw: (untyped name) -> untyped - # Matches a URL pattern to one or more routes. For more information, see - # [match](rdoc-ref:Base#match). - # - # match 'path', to: 'controller#action', via: :post - # match 'otherpath', on: :member, via: :get - def match: (*untyped path_or_actions, ?as: untyped, ?via: untyped?, ?to: untyped?, ?controller: untyped?, ?action: untyped?, ?on: untyped?, ?defaults: untyped?, ?constraints: untyped?, ?anchor: untyped?, ?format: untyped?, ?path: untyped?, ?internal: untyped?, **untyped mapping) ?{ (?) -> untyped } -> untyped - - # You can specify what Rails should route "/" to with the root method: - # - # root to: 'pages#main' - # - # For options, see `match`, as `root` uses it internally. - # - # You can also pass a string which will expand - # - # root 'pages#main' - # - # You should put the root route at the top of `config/routes.rb`, because this - # means it will be matched first. As this is the most popular route of most - # Rails applications, this is beneficial. - def root: (untyped path, ?::Hash[untyped, untyped] options) -> untyped - private def parent_resource: () -> untyped @@ -11246,74 +11006,6 @@ module ActionDispatch # concerns :commentable # end module Concerns - # Define a routing concern using a name. - # - # Concerns may be defined inline, using a block, or handled by another object, - # by passing that object as the second parameter. - # - # The concern object, if supplied, should respond to `call`, which will receive - # two parameters: - # - # * The current mapper - # * A hash of options which the concern object may use - # - # Options may also be used by concerns defined in a block by accepting a block - # parameter. So, using a block, you might do something as simple as limit the - # actions available on certain resources, passing standard resource options - # through the concern: - # - # concern :commentable do |options| - # resources :comments, options - # end - # - # resources :posts, concerns: :commentable - # resources :archived_posts do - # # Don't allow comments on archived posts - # concerns :commentable, only: [:index, :show] - # end - # - # Or, using a callable object, you might implement something more specific to - # your application, which would be out of place in your routes file. - # - # # purchasable.rb - # class Purchasable - # def initialize(defaults = {}) - # @defaults = defaults - # end - # - # def call(mapper, options = {}) - # options = @defaults.merge(options) - # mapper.resources :purchases - # mapper.resources :receipts - # mapper.resources :returns if options[:returnable] - # end - # end - # - # # routes.rb - # concern :purchasable, Purchasable.new(returnable: true) - # - # resources :toys, concerns: :purchasable - # resources :electronics, concerns: :purchasable - # resources :pets do - # concerns :purchasable, returnable: false - # end - # - # Any routing helpers can be used inside a concern. If using a callable, they're - # accessible from the Mapper that's passed to `call`. - def concern: (untyped name, ?untyped? callable) ?{ (?) -> untyped } -> untyped - - # Use the named concerns - # - # resources :posts do - # concerns :commentable - # end - # - # Concerns also work in any routes helper that you want to use: - # - # namespace :posts do - # concerns :commentable - # end - def concerns: (*untyped args, **untyped options) -> untyped end module CustomUrls