omg-actionpack 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +129 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller/asset_paths.rb +14 -0
- data/lib/abstract_controller/base.rb +299 -0
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +265 -0
- data/lib/abstract_controller/collector.rb +44 -0
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +243 -0
- data/lib/abstract_controller/logger.rb +16 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
- data/lib/abstract_controller/rendering.rb +126 -0
- data/lib/abstract_controller/translation.rb +42 -0
- data/lib/abstract_controller/url_for.rb +37 -0
- data/lib/abstract_controller.rb +36 -0
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +155 -0
- data/lib/action_controller/base.rb +332 -0
- data/lib/action_controller/caching.rb +49 -0
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +96 -0
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +341 -0
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +20 -0
- data/lib/action_controller/metal/data_streaming.rb +154 -0
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
- data/lib/action_controller/metal/exceptions.rb +106 -0
- data/lib/action_controller/metal/flash.rb +67 -0
- data/lib/action_controller/metal/head.rb +67 -0
- data/lib/action_controller/metal/helpers.rb +129 -0
- data/lib/action_controller/metal/http_authentication.rb +565 -0
- data/lib/action_controller/metal/implicit_render.rb +67 -0
- data/lib/action_controller/metal/instrumentation.rb +120 -0
- data/lib/action_controller/metal/live.rb +398 -0
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +337 -0
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +312 -0
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +251 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +260 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
- data/lib/action_controller/metal/rescue.rb +33 -0
- data/lib/action_controller/metal/streaming.rb +183 -0
- data/lib/action_controller/metal/strong_parameters.rb +1546 -0
- data/lib/action_controller/metal/testing.rb +25 -0
- data/lib/action_controller/metal/url_for.rb +65 -0
- data/lib/action_controller/metal.rb +339 -0
- data/lib/action_controller/railtie.rb +149 -0
- data/lib/action_controller/railties/helpers.rb +26 -0
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +691 -0
- data/lib/action_controller.rb +80 -0
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +249 -0
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +365 -0
- data/lib/action_dispatch/http/filter_parameters.rb +80 -0
- data/lib/action_dispatch/http/filter_redirect.rb +50 -0
- data/lib/action_dispatch/http/headers.rb +134 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
- data/lib/action_dispatch/http/mime_type.rb +389 -0
- data/lib/action_dispatch/http/mime_types.rb +54 -0
- data/lib/action_dispatch/http/parameters.rb +119 -0
- data/lib/action_dispatch/http/permissions_policy.rb +189 -0
- data/lib/action_dispatch/http/rack_cache.rb +67 -0
- data/lib/action_dispatch/http/request.rb +498 -0
- data/lib/action_dispatch/http/response.rb +556 -0
- data/lib/action_dispatch/http/upload.rb +107 -0
- data/lib/action_dispatch/http/url.rb +344 -0
- data/lib/action_dispatch/journey/formatter.rb +226 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
- data/lib/action_dispatch/journey/nodes/node.rb +208 -0
- data/lib/action_dispatch/journey/parser.rb +103 -0
- data/lib/action_dispatch/journey/path/pattern.rb +209 -0
- data/lib/action_dispatch/journey/route.rb +189 -0
- data/lib/action_dispatch/journey/router/utils.rb +105 -0
- data/lib/action_dispatch/journey/router.rb +151 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +70 -0
- data/lib/action_dispatch/journey/visitors.rb +267 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +38 -0
- data/lib/action_dispatch/middleware/cookies.rb +719 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +318 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
- data/lib/action_dispatch/middleware/reloader.rb +16 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
- data/lib/action_dispatch/middleware/request_id.rb +50 -0
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
- data/lib/action_dispatch/middleware/ssl.rb +180 -0
- data/lib/action_dispatch/middleware/stack.rb +194 -0
- data/lib/action_dispatch/middleware/static.rb +192 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
- data/lib/action_dispatch/railtie.rb +77 -0
- data/lib/action_dispatch/request/session.rb +283 -0
- data/lib/action_dispatch/request/utils.rb +109 -0
- data/lib/action_dispatch/routing/endpoint.rb +19 -0
- data/lib/action_dispatch/routing/inspector.rb +323 -0
- data/lib/action_dispatch/routing/mapper.rb +2372 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
- data/lib/action_dispatch/routing/redirection.rb +218 -0
- data/lib/action_dispatch/routing/route_set.rb +958 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
- data/lib/action_dispatch/routing/url_for.rb +244 -0
- data/lib/action_dispatch/routing.rb +262 -0
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +75 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +114 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
- data/lib/action_dispatch/testing/assertions.rb +25 -0
- data/lib/action_dispatch/testing/integration.rb +694 -0
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +57 -0
- data/lib/action_dispatch/testing/test_request.rb +73 -0
- data/lib/action_dispatch/testing/test_response.rb +58 -0
- data/lib/action_dispatch.rb +147 -0
- data/lib/action_pack/gem_version.rb +19 -0
- data/lib/action_pack/version.rb +12 -0
- data/lib/action_pack.rb +27 -0
- metadata +375 -0
@@ -0,0 +1,332 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "action_view"
|
6
|
+
require "action_controller/log_subscriber"
|
7
|
+
require "action_controller/metal/params_wrapper"
|
8
|
+
|
9
|
+
module ActionController
|
10
|
+
# # Action Controller Base
|
11
|
+
#
|
12
|
+
# Action Controllers are the core of a web request in Rails. They are made up of
|
13
|
+
# one or more actions that are executed on request and then either it renders a
|
14
|
+
# template or redirects to another action. An action is defined as a public
|
15
|
+
# method on the controller, which will automatically be made accessible to the
|
16
|
+
# web-server through Rails Routes.
|
17
|
+
#
|
18
|
+
# By default, only the ApplicationController in a Rails application inherits
|
19
|
+
# from `ActionController::Base`. All other controllers inherit from
|
20
|
+
# ApplicationController. This gives you one class to configure things such as
|
21
|
+
# request forgery protection and filtering of sensitive request parameters.
|
22
|
+
#
|
23
|
+
# A sample controller could look like this:
|
24
|
+
#
|
25
|
+
# class PostsController < ApplicationController
|
26
|
+
# def index
|
27
|
+
# @posts = Post.all
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# def create
|
31
|
+
# @post = Post.create params[:post]
|
32
|
+
# redirect_to posts_path
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Actions, by default, render a template in the `app/views` directory
|
37
|
+
# corresponding to the name of the controller and action after executing code in
|
38
|
+
# the action. For example, the `index` action of the PostsController would
|
39
|
+
# render the template `app/views/posts/index.html.erb` by default after
|
40
|
+
# populating the `@posts` instance variable.
|
41
|
+
#
|
42
|
+
# Unlike index, the create action will not render a template. After performing
|
43
|
+
# its main purpose (creating a new post), it initiates a redirect instead. This
|
44
|
+
# redirect works by returning an external `302 Moved` HTTP response that takes
|
45
|
+
# the user to the index action.
|
46
|
+
#
|
47
|
+
# These two methods represent the two basic action archetypes used in Action
|
48
|
+
# Controllers: Get-and-show and do-and-redirect. Most actions are variations on
|
49
|
+
# these themes.
|
50
|
+
#
|
51
|
+
# ## Requests
|
52
|
+
#
|
53
|
+
# For every request, the router determines the value of the `controller` and
|
54
|
+
# `action` keys. These determine which controller and action are called. The
|
55
|
+
# remaining request parameters, the session (if one is available), and the full
|
56
|
+
# request with all the HTTP headers are made available to the action through
|
57
|
+
# accessor methods. Then the action is performed.
|
58
|
+
#
|
59
|
+
# The full request object is available via the request accessor and is primarily
|
60
|
+
# used to query for HTTP headers:
|
61
|
+
#
|
62
|
+
# def server_ip
|
63
|
+
# location = request.env["REMOTE_ADDR"]
|
64
|
+
# render plain: "This server hosted at #{location}"
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# ## Parameters
|
68
|
+
#
|
69
|
+
# All request parameters, whether they come from a query string in the URL or
|
70
|
+
# form data submitted through a POST request are available through the `params`
|
71
|
+
# method which returns a hash. For example, an action that was performed through
|
72
|
+
# `/posts?category=All&limit=5` will include `{ "category" => "All", "limit" =>
|
73
|
+
# "5" }` in `params`.
|
74
|
+
#
|
75
|
+
# It's also possible to construct multi-dimensional parameter hashes by
|
76
|
+
# specifying keys using brackets, such as:
|
77
|
+
#
|
78
|
+
# <input type="text" name="post[name]" value="david">
|
79
|
+
# <input type="text" name="post[address]" value="hyacintvej">
|
80
|
+
#
|
81
|
+
# A request coming from a form holding these inputs will include `{ "post" => {
|
82
|
+
# "name" => "david", "address" => "hyacintvej" } }`. If the address input had
|
83
|
+
# been named `post[address][street]`, the `params` would have included `{ "post"
|
84
|
+
# => { "address" => { "street" => "hyacintvej" } } }`. There's no limit to the
|
85
|
+
# depth of the nesting.
|
86
|
+
#
|
87
|
+
# ## Sessions
|
88
|
+
#
|
89
|
+
# Sessions allow you to store objects in between requests. This is useful for
|
90
|
+
# objects that are not yet ready to be persisted, such as a Signup object
|
91
|
+
# constructed in a multi-paged process, or objects that don't change much and
|
92
|
+
# are needed all the time, such as a User object for a system that requires
|
93
|
+
# login. The session should not be used, however, as a cache for objects where
|
94
|
+
# it's likely they could be changed unknowingly. It's usually too much work to
|
95
|
+
# keep it all synchronized -- something databases already excel at.
|
96
|
+
#
|
97
|
+
# You can place objects in the session by using the `session` method, which
|
98
|
+
# accesses a hash:
|
99
|
+
#
|
100
|
+
# session[:person] = Person.authenticate(user_name, password)
|
101
|
+
#
|
102
|
+
# You can retrieve it again through the same hash:
|
103
|
+
#
|
104
|
+
# "Hello #{session[:person]}"
|
105
|
+
#
|
106
|
+
# For removing objects from the session, you can either assign a single key to
|
107
|
+
# `nil`:
|
108
|
+
#
|
109
|
+
# # removes :person from session
|
110
|
+
# session[:person] = nil
|
111
|
+
#
|
112
|
+
# or you can remove the entire session with `reset_session`.
|
113
|
+
#
|
114
|
+
# By default, sessions are stored in an encrypted browser cookie (see
|
115
|
+
# ActionDispatch::Session::CookieStore). Thus the user will not be able to read
|
116
|
+
# or edit the session data. However, the user can keep a copy of the cookie even
|
117
|
+
# after it has expired, so you should avoid storing sensitive information in
|
118
|
+
# cookie-based sessions.
|
119
|
+
#
|
120
|
+
# ## Responses
|
121
|
+
#
|
122
|
+
# Each action results in a response, which holds the headers and document to be
|
123
|
+
# sent to the user's browser. The actual response object is generated
|
124
|
+
# automatically through the use of renders and redirects and requires no user
|
125
|
+
# intervention.
|
126
|
+
#
|
127
|
+
# ## Renders
|
128
|
+
#
|
129
|
+
# Action Controller sends content to the user by using one of five rendering
|
130
|
+
# methods. The most versatile and common is the rendering of a template.
|
131
|
+
# Included in the Action Pack is the Action View, which enables rendering of ERB
|
132
|
+
# templates. It's automatically configured. The controller passes objects to the
|
133
|
+
# view by assigning instance variables:
|
134
|
+
#
|
135
|
+
# def show
|
136
|
+
# @post = Post.find(params[:id])
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# Which are then automatically available to the view:
|
140
|
+
#
|
141
|
+
# Title: <%= @post.title %>
|
142
|
+
#
|
143
|
+
# You don't have to rely on the automated rendering. For example, actions that
|
144
|
+
# could result in the rendering of different templates will use the manual
|
145
|
+
# rendering methods:
|
146
|
+
#
|
147
|
+
# def search
|
148
|
+
# @results = Search.find(params[:query])
|
149
|
+
# case @results.count
|
150
|
+
# when 0 then render action: "no_results"
|
151
|
+
# when 1 then render action: "show"
|
152
|
+
# when 2..10 then render action: "show_many"
|
153
|
+
# end
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# Read more about writing ERB and Builder templates in ActionView::Base.
|
157
|
+
#
|
158
|
+
# ## Redirects
|
159
|
+
#
|
160
|
+
# Redirects are used to move from one action to another. For example, after a
|
161
|
+
# `create` action, which stores a blog entry to the database, we might like to
|
162
|
+
# show the user the new entry. Because we're following good DRY principles
|
163
|
+
# (Don't Repeat Yourself), we're going to reuse (and redirect to) a `show`
|
164
|
+
# action that we'll assume has already been created. The code might look like
|
165
|
+
# this:
|
166
|
+
#
|
167
|
+
# def create
|
168
|
+
# @entry = Entry.new(params[:entry])
|
169
|
+
# if @entry.save
|
170
|
+
# # The entry was saved correctly, redirect to show
|
171
|
+
# redirect_to action: 'show', id: @entry.id
|
172
|
+
# else
|
173
|
+
# # things didn't go so well, do something else
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# In this case, after saving our new entry to the database, the user is
|
178
|
+
# redirected to the `show` method, which is then executed. Note that this is an
|
179
|
+
# external HTTP-level redirection which will cause the browser to make a second
|
180
|
+
# request (a GET to the show action), and not some internal re-routing which
|
181
|
+
# calls both "create" and then "show" within one request.
|
182
|
+
#
|
183
|
+
# Learn more about `redirect_to` and what options you have in
|
184
|
+
# ActionController::Redirecting.
|
185
|
+
#
|
186
|
+
# ## Calling multiple redirects or renders
|
187
|
+
#
|
188
|
+
# An action may perform only a single render or a single redirect. Attempting to
|
189
|
+
# do either again will result in a DoubleRenderError:
|
190
|
+
#
|
191
|
+
# def do_something
|
192
|
+
# redirect_to action: "elsewhere"
|
193
|
+
# render action: "overthere" # raises DoubleRenderError
|
194
|
+
# end
|
195
|
+
#
|
196
|
+
# If you need to redirect on the condition of something, then be sure to add
|
197
|
+
# "return" to halt execution.
|
198
|
+
#
|
199
|
+
# def do_something
|
200
|
+
# if monkeys.nil?
|
201
|
+
# redirect_to(action: "elsewhere")
|
202
|
+
# return
|
203
|
+
# end
|
204
|
+
# render action: "overthere" # won't be called if monkeys is nil
|
205
|
+
# end
|
206
|
+
#
|
207
|
+
class Base < Metal
|
208
|
+
abstract!
|
209
|
+
|
210
|
+
# Shortcut helper that returns all the modules included in
|
211
|
+
# ActionController::Base except the ones passed as arguments:
|
212
|
+
#
|
213
|
+
# class MyBaseController < ActionController::Metal
|
214
|
+
# ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
|
215
|
+
# include left
|
216
|
+
# end
|
217
|
+
# end
|
218
|
+
#
|
219
|
+
# This gives better control over what you want to exclude and makes it easier to
|
220
|
+
# create a bare controller class, instead of listing the modules required
|
221
|
+
# manually.
|
222
|
+
def self.without_modules(*modules)
|
223
|
+
modules = modules.map do |m|
|
224
|
+
m.is_a?(Symbol) ? ActionController.const_get(m) : m
|
225
|
+
end
|
226
|
+
|
227
|
+
MODULES - modules
|
228
|
+
end
|
229
|
+
|
230
|
+
MODULES = [
|
231
|
+
AbstractController::Rendering,
|
232
|
+
AbstractController::Translation,
|
233
|
+
AbstractController::AssetPaths,
|
234
|
+
Helpers,
|
235
|
+
UrlFor,
|
236
|
+
Redirecting,
|
237
|
+
ActionView::Layouts,
|
238
|
+
Rendering,
|
239
|
+
Renderers::All,
|
240
|
+
ConditionalGet,
|
241
|
+
EtagWithTemplateDigest,
|
242
|
+
EtagWithFlash,
|
243
|
+
Caching,
|
244
|
+
MimeResponds,
|
245
|
+
ImplicitRender,
|
246
|
+
StrongParameters,
|
247
|
+
ParameterEncoding,
|
248
|
+
Cookies,
|
249
|
+
Flash,
|
250
|
+
FormBuilder,
|
251
|
+
RequestForgeryProtection,
|
252
|
+
ContentSecurityPolicy,
|
253
|
+
PermissionsPolicy,
|
254
|
+
RateLimiting,
|
255
|
+
AllowBrowser,
|
256
|
+
Streaming,
|
257
|
+
DataStreaming,
|
258
|
+
HttpAuthentication::Basic::ControllerMethods,
|
259
|
+
HttpAuthentication::Digest::ControllerMethods,
|
260
|
+
HttpAuthentication::Token::ControllerMethods,
|
261
|
+
DefaultHeaders,
|
262
|
+
Logging,
|
263
|
+
AbstractController::Callbacks,
|
264
|
+
Rescue,
|
265
|
+
Instrumentation,
|
266
|
+
ParamsWrapper
|
267
|
+
]
|
268
|
+
|
269
|
+
# Note: Documenting these severely degrates the performance of rdoc
|
270
|
+
# :stopdoc:
|
271
|
+
include AbstractController::Rendering
|
272
|
+
include AbstractController::Translation
|
273
|
+
include AbstractController::AssetPaths
|
274
|
+
include Helpers
|
275
|
+
include UrlFor
|
276
|
+
include Redirecting
|
277
|
+
include ActionView::Layouts
|
278
|
+
include Rendering
|
279
|
+
include Renderers::All
|
280
|
+
include ConditionalGet
|
281
|
+
include EtagWithTemplateDigest
|
282
|
+
include EtagWithFlash
|
283
|
+
include Caching
|
284
|
+
include MimeResponds
|
285
|
+
include ImplicitRender
|
286
|
+
include StrongParameters
|
287
|
+
include ParameterEncoding
|
288
|
+
include Cookies
|
289
|
+
include Flash
|
290
|
+
include FormBuilder
|
291
|
+
include RequestForgeryProtection
|
292
|
+
include ContentSecurityPolicy
|
293
|
+
include PermissionsPolicy
|
294
|
+
include RateLimiting
|
295
|
+
include AllowBrowser
|
296
|
+
include Streaming
|
297
|
+
include DataStreaming
|
298
|
+
include HttpAuthentication::Basic::ControllerMethods
|
299
|
+
include HttpAuthentication::Digest::ControllerMethods
|
300
|
+
include HttpAuthentication::Token::ControllerMethods
|
301
|
+
include DefaultHeaders
|
302
|
+
include Logging
|
303
|
+
# Before callbacks should also be executed as early as possible, so also include
|
304
|
+
# them at the bottom.
|
305
|
+
include AbstractController::Callbacks
|
306
|
+
# Append rescue at the bottom to wrap as much as possible.
|
307
|
+
include Rescue
|
308
|
+
# Add instrumentations hooks at the bottom, to ensure they instrument all the
|
309
|
+
# methods properly.
|
310
|
+
include Instrumentation
|
311
|
+
# Params wrapper should come before instrumentation so they are properly showed
|
312
|
+
# in logs
|
313
|
+
include ParamsWrapper
|
314
|
+
# :startdoc:
|
315
|
+
setup_renderer!
|
316
|
+
|
317
|
+
# Define some internal variables that should not be propagated to the view.
|
318
|
+
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i(
|
319
|
+
@_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class
|
320
|
+
@_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy
|
321
|
+
@_marked_for_same_origin_verification @_rendered_format
|
322
|
+
)
|
323
|
+
|
324
|
+
def _protected_ivars
|
325
|
+
PROTECTED_IVARS
|
326
|
+
end
|
327
|
+
private :_protected_ivars
|
328
|
+
|
329
|
+
ActiveSupport.run_load_hooks(:action_controller_base, self)
|
330
|
+
ActiveSupport.run_load_hooks(:action_controller, self)
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
# # Action Controller Caching
|
7
|
+
#
|
8
|
+
# Caching is a cheap way of speeding up slow applications by keeping the result
|
9
|
+
# of calculations, renderings, and database calls around for subsequent
|
10
|
+
# requests.
|
11
|
+
#
|
12
|
+
# You can read more about each approach by clicking the modules below.
|
13
|
+
#
|
14
|
+
# Note: To turn off all caching provided by Action Controller, set
|
15
|
+
# config.action_controller.perform_caching = false
|
16
|
+
#
|
17
|
+
# ## Caching stores
|
18
|
+
#
|
19
|
+
# All the caching stores from ActiveSupport::Cache are available to be used as
|
20
|
+
# backends for Action Controller caching.
|
21
|
+
#
|
22
|
+
# Configuration examples (FileStore is the default):
|
23
|
+
#
|
24
|
+
# config.action_controller.cache_store = :memory_store
|
25
|
+
# config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
|
26
|
+
# config.action_controller.cache_store = :mem_cache_store, 'localhost'
|
27
|
+
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
|
28
|
+
# config.action_controller.cache_store = MyOwnStore.new('parameter')
|
29
|
+
module Caching
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
|
32
|
+
included do
|
33
|
+
include AbstractController::Caching
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def instrument_payload(key)
|
38
|
+
{
|
39
|
+
controller: controller_name,
|
40
|
+
action: action_name,
|
41
|
+
key: key
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def instrument_name
|
46
|
+
"action_controller"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
# # Action Controller Form Builder
|
7
|
+
#
|
8
|
+
# Override the default form builder for all views rendered by this controller
|
9
|
+
# and any of its descendants. Accepts a subclass of
|
10
|
+
# ActionView::Helpers::FormBuilder.
|
11
|
+
#
|
12
|
+
# For example, given a form builder:
|
13
|
+
#
|
14
|
+
# class AdminFormBuilder < ActionView::Helpers::FormBuilder
|
15
|
+
# def special_field(name)
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# The controller specifies a form builder as its default:
|
20
|
+
#
|
21
|
+
# class AdminAreaController < ApplicationController
|
22
|
+
# default_form_builder AdminFormBuilder
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Then in the view any form using `form_for` will be an instance of the
|
26
|
+
# specified form builder:
|
27
|
+
#
|
28
|
+
# <%= form_for(@instance) do |builder| %>
|
29
|
+
# <%= builder.special_field(:name) %>
|
30
|
+
# <% end %>
|
31
|
+
module FormBuilder
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
included do
|
35
|
+
class_attribute :_default_form_builder, instance_accessor: false
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
# Set the form builder to be used as the default for all forms in the views
|
40
|
+
# rendered by this controller and its subclasses.
|
41
|
+
#
|
42
|
+
# #### Parameters
|
43
|
+
# * `builder` - Default form builder, an instance of
|
44
|
+
# ActionView::Helpers::FormBuilder
|
45
|
+
def default_form_builder(builder)
|
46
|
+
self._default_form_builder = builder
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Default form builder for the controller
|
51
|
+
def default_form_builder
|
52
|
+
self.class._default_form_builder
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
7
|
+
INTERNAL_PARAMS = %w(controller action format _method only_path)
|
8
|
+
|
9
|
+
def start_processing(event)
|
10
|
+
return unless logger.info?
|
11
|
+
|
12
|
+
payload = event.payload
|
13
|
+
params = {}
|
14
|
+
payload[:params].each_pair do |k, v|
|
15
|
+
params[k] = v unless INTERNAL_PARAMS.include?(k)
|
16
|
+
end
|
17
|
+
format = payload[:format]
|
18
|
+
format = format.to_s.upcase if format.is_a?(Symbol)
|
19
|
+
format = "*/*" if format.nil?
|
20
|
+
|
21
|
+
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
|
22
|
+
info " Parameters: #{params.inspect}" unless params.empty?
|
23
|
+
end
|
24
|
+
subscribe_log_level :start_processing, :info
|
25
|
+
|
26
|
+
def process_action(event)
|
27
|
+
info do
|
28
|
+
payload = event.payload
|
29
|
+
additions = ActionController::Base.log_process_action(payload)
|
30
|
+
status = payload[:status]
|
31
|
+
|
32
|
+
if status.nil? && (exception_class_name = payload[:exception]&.first)
|
33
|
+
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
additions << "GC: #{event.gc_time.round(1)}ms"
|
37
|
+
|
38
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms" \
|
39
|
+
" (#{additions.join(" | ")})"
|
40
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
41
|
+
|
42
|
+
message
|
43
|
+
end
|
44
|
+
end
|
45
|
+
subscribe_log_level :process_action, :info
|
46
|
+
|
47
|
+
def halted_callback(event)
|
48
|
+
info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
|
49
|
+
end
|
50
|
+
subscribe_log_level :halted_callback, :info
|
51
|
+
|
52
|
+
def send_file(event)
|
53
|
+
info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
|
54
|
+
end
|
55
|
+
subscribe_log_level :send_file, :info
|
56
|
+
|
57
|
+
def redirect_to(event)
|
58
|
+
info { "Redirected to #{event.payload[:location]}" }
|
59
|
+
end
|
60
|
+
subscribe_log_level :redirect_to, :info
|
61
|
+
|
62
|
+
def send_data(event)
|
63
|
+
info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
|
64
|
+
end
|
65
|
+
subscribe_log_level :send_data, :info
|
66
|
+
|
67
|
+
def unpermitted_parameters(event)
|
68
|
+
debug do
|
69
|
+
unpermitted_keys = event.payload[:keys]
|
70
|
+
display_unpermitted_keys = unpermitted_keys.map { |e| ":#{e}" }.join(", ")
|
71
|
+
context = event.payload[:context].map { |k, v| "#{k}: #{v}" }.join(", ")
|
72
|
+
color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{display_unpermitted_keys}. Context: { #{context} }", RED)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
subscribe_log_level :unpermitted_parameters, :debug
|
76
|
+
|
77
|
+
%w(write_fragment read_fragment exist_fragment? expire_fragment).each do |method|
|
78
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
79
|
+
# frozen_string_literal: true
|
80
|
+
def #{method}(event)
|
81
|
+
return unless ActionController::Base.enable_fragment_cache_logging
|
82
|
+
key = ActiveSupport::Cache.expand_cache_key(event.payload[:key] || event.payload[:path])
|
83
|
+
human_name = #{method.to_s.humanize.inspect}
|
84
|
+
info("\#{human_name} \#{key} (\#{event.duration.round(1)}ms)")
|
85
|
+
end
|
86
|
+
subscribe_log_level :#{method}, :info
|
87
|
+
METHOD
|
88
|
+
end
|
89
|
+
|
90
|
+
def logger
|
91
|
+
ActionController::Base.logger
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
ActionController::LogSubscriber.attach_to :action_controller
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController # :nodoc:
|
6
|
+
module AllowBrowser
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# Specify the browser versions that will be allowed to access all actions (or
|
11
|
+
# some, as limited by `only:` or `except:`). Only browsers matched in the hash
|
12
|
+
# or named set passed to `versions:` will be blocked if they're below the
|
13
|
+
# versions specified. This means that all other browsers, as well as agents that
|
14
|
+
# aren't reporting a user-agent header, will be allowed access.
|
15
|
+
#
|
16
|
+
# A browser that's blocked will by default be served the file in
|
17
|
+
# public/406-unsupported-browser.html with a HTTP status code of "406 Not
|
18
|
+
# Acceptable".
|
19
|
+
#
|
20
|
+
# In addition to specifically named browser versions, you can also pass
|
21
|
+
# `:modern` as the set to restrict support to browsers natively supporting webp
|
22
|
+
# images, web push, badges, import maps, CSS nesting, and CSS :has. This
|
23
|
+
# includes Safari 17.2+, Chrome 120+, Firefox 121+, Opera 106+.
|
24
|
+
#
|
25
|
+
# You can use https://caniuse.com to check for browser versions supporting the
|
26
|
+
# features you use.
|
27
|
+
#
|
28
|
+
# You can use `ActiveSupport::Notifications` to subscribe to events of browsers
|
29
|
+
# being blocked using the `browser_block.action_controller` event name.
|
30
|
+
#
|
31
|
+
# Examples:
|
32
|
+
#
|
33
|
+
# class ApplicationController < ActionController::Base
|
34
|
+
# # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has
|
35
|
+
# allow_browser versions: :modern
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# class ApplicationController < ActionController::Base
|
39
|
+
# # All versions of Chrome and Opera will be allowed, but no versions of "internet explorer" (ie). Safari needs to be 16.4+ and Firefox 121+.
|
40
|
+
# allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# class MessagesController < ApplicationController
|
44
|
+
# # In addition to the browsers blocked by ApplicationController, also block Opera below 104 and Chrome below 119 for the show action.
|
45
|
+
# allow_browser versions: { opera: 104, chrome: 119 }, only: :show
|
46
|
+
# end
|
47
|
+
def allow_browser(versions:, block: -> { render file: Rails.root.join("public/406-unsupported-browser.html"), layout: false, status: :not_acceptable }, **options)
|
48
|
+
before_action -> { allow_browser(versions: versions, block: block) }, **options
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def allow_browser(versions:, block:)
|
54
|
+
require "useragent"
|
55
|
+
|
56
|
+
if BrowserBlocker.new(request, versions: versions).blocked?
|
57
|
+
ActiveSupport::Notifications.instrument("browser_block.action_controller", request: request, versions: versions) do
|
58
|
+
instance_exec(&block)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class BrowserBlocker
|
64
|
+
SETS = {
|
65
|
+
modern: { safari: 17.2, chrome: 120, firefox: 121, opera: 106, ie: false }
|
66
|
+
}
|
67
|
+
|
68
|
+
attr_reader :request, :versions
|
69
|
+
|
70
|
+
def initialize(request, versions:)
|
71
|
+
@request, @versions = request, versions
|
72
|
+
end
|
73
|
+
|
74
|
+
def blocked?
|
75
|
+
user_agent_version_reported? && unsupported_browser?
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def parsed_user_agent
|
80
|
+
@parsed_user_agent ||= UserAgent.parse(request.user_agent)
|
81
|
+
end
|
82
|
+
|
83
|
+
def user_agent_version_reported?
|
84
|
+
request.user_agent.present? && parsed_user_agent.version.to_s.present?
|
85
|
+
end
|
86
|
+
|
87
|
+
def unsupported_browser?
|
88
|
+
version_guarded_browser? && version_below_minimum_required? && !bot?
|
89
|
+
end
|
90
|
+
|
91
|
+
def version_guarded_browser?
|
92
|
+
minimum_browser_version_for_browser != nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def bot?
|
96
|
+
parsed_user_agent.bot?
|
97
|
+
end
|
98
|
+
|
99
|
+
def version_below_minimum_required?
|
100
|
+
if minimum_browser_version_for_browser
|
101
|
+
parsed_user_agent.version < UserAgent::Version.new(minimum_browser_version_for_browser.to_s)
|
102
|
+
else
|
103
|
+
true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def minimum_browser_version_for_browser
|
108
|
+
expanded_versions[normalized_browser_name]
|
109
|
+
end
|
110
|
+
|
111
|
+
def expanded_versions
|
112
|
+
@expanded_versions ||= (SETS[versions] || versions).with_indifferent_access
|
113
|
+
end
|
114
|
+
|
115
|
+
def normalized_browser_name
|
116
|
+
case name = parsed_user_agent.browser.downcase
|
117
|
+
when "internet explorer" then "ie"
|
118
|
+
else name
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
module BasicImplicitRender # :nodoc:
|
7
|
+
def send_action(method, *args)
|
8
|
+
ret = super
|
9
|
+
default_render unless performed?
|
10
|
+
ret
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_render
|
14
|
+
head :no_content
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|