actionpack 3.2.19 → 4.2.11.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +412 -503
- data/MIT-LICENSE +1 -1
- data/README.rdoc +11 -294
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +52 -18
- data/lib/abstract_controller/callbacks.rb +87 -89
- data/lib/abstract_controller/collector.rb +17 -3
- data/lib/abstract_controller/helpers.rb +41 -14
- data/lib/abstract_controller/logger.rb +1 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +65 -118
- data/lib/abstract_controller/translation.rb +16 -1
- data/lib/abstract_controller/url_for.rb +7 -7
- data/lib/abstract_controller.rb +2 -10
- data/lib/action_controller/base.rb +61 -28
- data/lib/action_controller/caching/fragments.rb +30 -54
- data/lib/action_controller/caching.rb +38 -35
- data/lib/action_controller/log_subscriber.rb +35 -18
- data/lib/action_controller/metal/conditional_get.rb +103 -34
- data/lib/action_controller/metal/data_streaming.rb +20 -26
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/exceptions.rb +19 -6
- data/lib/action_controller/metal/flash.rb +41 -9
- data/lib/action_controller/metal/force_ssl.rb +70 -12
- data/lib/action_controller/metal/head.rb +30 -7
- data/lib/action_controller/metal/helpers.rb +11 -11
- data/lib/action_controller/metal/hide_actions.rb +0 -1
- data/lib/action_controller/metal/http_authentication.rb +140 -94
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -7
- data/lib/action_controller/metal/live.rb +328 -0
- data/lib/action_controller/metal/mime_responds.rb +161 -152
- data/lib/action_controller/metal/params_wrapper.rb +126 -81
- data/lib/action_controller/metal/rack_delegation.rb +10 -4
- data/lib/action_controller/metal/redirecting.rb +44 -41
- data/lib/action_controller/metal/renderers.rb +48 -19
- data/lib/action_controller/metal/rendering.rb +46 -11
- data/lib/action_controller/metal/request_forgery_protection.rb +250 -29
- data/lib/action_controller/metal/streaming.rb +30 -38
- data/lib/action_controller/metal/strong_parameters.rb +669 -0
- data/lib/action_controller/metal/testing.rb +12 -18
- data/lib/action_controller/metal/url_for.rb +31 -29
- data/lib/action_controller/metal.rb +31 -40
- data/lib/action_controller/model_naming.rb +12 -0
- data/lib/action_controller/railtie.rb +38 -18
- data/lib/action_controller/railties/helpers.rb +22 -0
- data/lib/action_controller/test_case.rb +359 -173
- data/lib/action_controller.rb +9 -16
- data/lib/action_dispatch/http/cache.rb +64 -11
- data/lib/action_dispatch/http/filter_parameters.rb +20 -10
- data/lib/action_dispatch/http/filter_redirect.rb +38 -0
- data/lib/action_dispatch/http/headers.rb +85 -17
- data/lib/action_dispatch/http/mime_negotiation.rb +55 -5
- data/lib/action_dispatch/http/mime_type.rb +167 -114
- data/lib/action_dispatch/http/mime_types.rb +2 -1
- data/lib/action_dispatch/http/parameter_filter.rb +44 -46
- data/lib/action_dispatch/http/parameters.rb +30 -46
- data/lib/action_dispatch/http/rack_cache.rb +2 -3
- data/lib/action_dispatch/http/request.rb +108 -45
- data/lib/action_dispatch/http/response.rb +247 -48
- data/lib/action_dispatch/http/upload.rb +60 -29
- data/lib/action_dispatch/http/url.rb +135 -45
- data/lib/action_dispatch/journey/backwards.rb +5 -0
- data/lib/action_dispatch/journey/formatter.rb +166 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +47 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +157 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
- data/lib/action_dispatch/journey/nodes/node.rb +128 -0
- data/lib/action_dispatch/journey/parser.rb +198 -0
- data/lib/action_dispatch/journey/parser.y +49 -0
- data/lib/action_dispatch/journey/parser_extras.rb +23 -0
- data/lib/action_dispatch/journey/path/pattern.rb +193 -0
- data/lib/action_dispatch/journey/route.rb +125 -0
- data/lib/action_dispatch/journey/router/strexp.rb +27 -0
- data/lib/action_dispatch/journey/router/utils.rb +93 -0
- data/lib/action_dispatch/journey/router.rb +144 -0
- data/lib/action_dispatch/journey/routes.rb +80 -0
- data/lib/action_dispatch/journey/scanner.rb +61 -0
- data/lib/action_dispatch/journey/visitors.rb +221 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +5 -0
- data/lib/action_dispatch/middleware/callbacks.rb +16 -11
- data/lib/action_dispatch/middleware/cookies.rb +346 -125
- data/lib/action_dispatch/middleware/debug_exceptions.rb +52 -24
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -9
- data/lib/action_dispatch/middleware/flash.rb +85 -72
- data/lib/action_dispatch/middleware/params_parser.rb +16 -31
- data/lib/action_dispatch/middleware/public_exceptions.rb +39 -14
- data/lib/action_dispatch/middleware/reloader.rb +16 -7
- data/lib/action_dispatch/middleware/remote_ip.rb +132 -40
- data/lib/action_dispatch/middleware/request_id.rb +3 -7
- data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
- data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +84 -29
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +15 -44
- data/lib/action_dispatch/middleware/ssl.rb +72 -0
- data/lib/action_dispatch/middleware/stack.rb +6 -1
- data/lib/action_dispatch/middleware/static.rb +80 -23
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +34 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +133 -5
- 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 +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +19 -6
- data/lib/action_dispatch/request/session.rb +193 -0
- data/lib/action_dispatch/request/utils.rb +35 -0
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +234 -0
- data/lib/action_dispatch/routing/mapper.rb +897 -436
- data/lib/action_dispatch/routing/polymorphic_routes.rb +213 -92
- data/lib/action_dispatch/routing/redirection.rb +97 -37
- data/lib/action_dispatch/routing/route_set.rb +432 -239
- data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
- data/lib/action_dispatch/routing/url_for.rb +63 -34
- data/lib/action_dispatch/routing.rb +57 -89
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -36
- data/lib/action_dispatch/testing/assertions/response.rb +24 -38
- data/lib/action_dispatch/testing/assertions/routing.rb +55 -54
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -434
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -137
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/integration.rb +88 -72
- data/lib/action_dispatch/testing/test_process.rb +9 -6
- data/lib/action_dispatch/testing/test_request.rb +13 -9
- data/lib/action_dispatch/testing/test_response.rb +1 -5
- data/lib/action_dispatch.rb +24 -21
- data/lib/action_pack/gem_version.rb +15 -0
- data/lib/action_pack/version.rb +5 -7
- data/lib/action_pack.rb +1 -1
- metadata +181 -292
- data/lib/abstract_controller/layouts.rb +0 -423
- data/lib/abstract_controller/view_paths.rb +0 -96
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/integration_test.rb +0 -2
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/deprecated.rb +0 -3
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/responder.rb +0 -286
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/railties/paths.rb +0 -25
- data/lib/action_controller/record_identifier.rb +0 -85
- data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
- data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
- data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
- data/lib/action_controller/vendor/html-scanner.rb +0 -20
- data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
- data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
- data/lib/action_dispatch/middleware/head.rb +0 -18
- data/lib/action_dispatch/middleware/rescue.rb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/base.rb +0 -220
- data/lib/action_view/buffers.rb +0 -43
- data/lib/action_view/context.rb +0 -36
- data/lib/action_view/flows.rb +0 -79
- data/lib/action_view/helpers/active_model_helper.rb +0 -50
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
- data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
- data/lib/action_view/helpers/cache_helper.rb +0 -64
- data/lib/action_view/helpers/capture_helper.rb +0 -203
- data/lib/action_view/helpers/controller_helper.rb +0 -25
- data/lib/action_view/helpers/csrf_helper.rb +0 -32
- data/lib/action_view/helpers/date_helper.rb +0 -1062
- data/lib/action_view/helpers/debug_helper.rb +0 -40
- data/lib/action_view/helpers/form_helper.rb +0 -1486
- data/lib/action_view/helpers/form_options_helper.rb +0 -658
- data/lib/action_view/helpers/form_tag_helper.rb +0 -685
- data/lib/action_view/helpers/javascript_helper.rb +0 -110
- data/lib/action_view/helpers/number_helper.rb +0 -622
- data/lib/action_view/helpers/output_safety_helper.rb +0 -38
- data/lib/action_view/helpers/record_tag_helper.rb +0 -111
- data/lib/action_view/helpers/rendering_helper.rb +0 -90
- data/lib/action_view/helpers/sanitize_helper.rb +0 -259
- data/lib/action_view/helpers/tag_helper.rb +0 -160
- data/lib/action_view/helpers/text_helper.rb +0 -426
- data/lib/action_view/helpers/translation_helper.rb +0 -91
- data/lib/action_view/helpers/url_helper.rb +0 -693
- data/lib/action_view/helpers.rb +0 -60
- data/lib/action_view/locale/en.yml +0 -160
- data/lib/action_view/log_subscriber.rb +0 -28
- data/lib/action_view/lookup_context.rb +0 -254
- data/lib/action_view/path_set.rb +0 -89
- data/lib/action_view/railtie.rb +0 -55
- data/lib/action_view/renderer/abstract_renderer.rb +0 -41
- data/lib/action_view/renderer/partial_renderer.rb +0 -415
- data/lib/action_view/renderer/renderer.rb +0 -54
- data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
- data/lib/action_view/renderer/template_renderer.rb +0 -94
- data/lib/action_view/template/error.rb +0 -128
- data/lib/action_view/template/handlers/builder.rb +0 -26
- data/lib/action_view/template/handlers/erb.rb +0 -125
- data/lib/action_view/template/handlers.rb +0 -50
- data/lib/action_view/template/resolver.rb +0 -272
- data/lib/action_view/template/text.rb +0 -30
- data/lib/action_view/template.rb +0 -337
- data/lib/action_view/test_case.rb +0 -245
- data/lib/action_view/testing/resolvers.rb +0 -50
- data/lib/action_view.rb +0 -84
- data/lib/sprockets/assets.rake +0 -99
- data/lib/sprockets/bootstrap.rb +0 -37
- data/lib/sprockets/compressors.rb +0 -83
- data/lib/sprockets/helpers/isolated_helper.rb +0 -13
- data/lib/sprockets/helpers/rails_helper.rb +0 -182
- data/lib/sprockets/helpers.rb +0 -6
- data/lib/sprockets/railtie.rb +0 -62
- data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,187 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
-
|
4
|
-
module ActionController #:nodoc:
|
5
|
-
module Caching
|
6
|
-
# Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
|
7
|
-
# can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
|
8
|
-
# through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
|
9
|
-
# where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
|
10
|
-
# a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
|
11
|
-
# likely candidates.
|
12
|
-
#
|
13
|
-
# Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
|
14
|
-
#
|
15
|
-
# class WeblogController < ActionController::Base
|
16
|
-
# caches_page :show, :new
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used
|
20
|
-
# that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the
|
21
|
-
# existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack.
|
22
|
-
# This is much faster than handling the full dynamic request in the usual way.
|
23
|
-
#
|
24
|
-
# Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
|
25
|
-
# is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
|
26
|
-
#
|
27
|
-
# class WeblogController < ActionController::Base
|
28
|
-
# def update
|
29
|
-
# List.update(params[:list][:id], params[:list])
|
30
|
-
# expire_page :action => "show", :id => params[:list][:id]
|
31
|
-
# redirect_to :action => "show", :id => params[:list][:id]
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
|
36
|
-
# expired.
|
37
|
-
module Pages
|
38
|
-
extend ActiveSupport::Concern
|
39
|
-
|
40
|
-
included do
|
41
|
-
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
|
42
|
-
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing
|
43
|
-
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
|
44
|
-
# web server to look in the new location for cached files.
|
45
|
-
class_attribute :page_cache_directory
|
46
|
-
self.page_cache_directory ||= ''
|
47
|
-
|
48
|
-
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
|
49
|
-
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
|
50
|
-
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
|
51
|
-
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
|
52
|
-
class_attribute :page_cache_extension
|
53
|
-
self.page_cache_extension ||= '.html'
|
54
|
-
|
55
|
-
# The compression used for gzip. If false (default), the page is not compressed.
|
56
|
-
# If can be a symbol showing the ZLib compression method, for example, :best_compression
|
57
|
-
# or :best_speed or an integer configuring the compression level.
|
58
|
-
class_attribute :page_cache_compression
|
59
|
-
self.page_cache_compression ||= false
|
60
|
-
end
|
61
|
-
|
62
|
-
module ClassMethods
|
63
|
-
# Expires the page that was cached with the +path+ as a key. Example:
|
64
|
-
# expire_page "/lists/show"
|
65
|
-
def expire_page(path)
|
66
|
-
return unless perform_caching
|
67
|
-
path = page_cache_path(path)
|
68
|
-
|
69
|
-
instrument_page_cache :expire_page, path do
|
70
|
-
File.delete(path) if File.exist?(path)
|
71
|
-
File.delete(path + '.gz') if File.exist?(path + '.gz')
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# Manually cache the +content+ in the key determined by +path+. Example:
|
76
|
-
# cache_page "I'm the cached content", "/lists/show"
|
77
|
-
def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
|
78
|
-
return unless perform_caching
|
79
|
-
path = page_cache_path(path, extension)
|
80
|
-
|
81
|
-
instrument_page_cache :write_page, path do
|
82
|
-
FileUtils.makedirs(File.dirname(path))
|
83
|
-
File.open(path, "wb+") { |f| f.write(content) }
|
84
|
-
if gzip
|
85
|
-
Zlib::GzipWriter.open(path + '.gz', gzip) { |f| f.write(content) }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Caches the +actions+ using the page-caching approach that'll store
|
91
|
-
# the cache in a path within the page_cache_directory that
|
92
|
-
# matches the triggering url.
|
93
|
-
#
|
94
|
-
# You can also pass a :gzip option to override the class configuration one.
|
95
|
-
#
|
96
|
-
# Usage:
|
97
|
-
#
|
98
|
-
# # cache the index action
|
99
|
-
# caches_page :index
|
100
|
-
#
|
101
|
-
# # cache the index action except for JSON requests
|
102
|
-
# caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
|
103
|
-
#
|
104
|
-
# # don't gzip images
|
105
|
-
# caches_page :image, :gzip => false
|
106
|
-
def caches_page(*actions)
|
107
|
-
return unless perform_caching
|
108
|
-
options = actions.extract_options!
|
109
|
-
|
110
|
-
gzip_level = options.fetch(:gzip, page_cache_compression)
|
111
|
-
gzip_level = case gzip_level
|
112
|
-
when Symbol
|
113
|
-
Zlib.const_get(gzip_level.to_s.upcase)
|
114
|
-
when Fixnum
|
115
|
-
gzip_level
|
116
|
-
when false
|
117
|
-
nil
|
118
|
-
else
|
119
|
-
Zlib::BEST_COMPRESSION
|
120
|
-
end
|
121
|
-
|
122
|
-
after_filter({:only => actions}.merge(options)) do |c|
|
123
|
-
c.cache_page(nil, nil, gzip_level)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
private
|
128
|
-
def page_cache_file(path, extension)
|
129
|
-
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
|
130
|
-
unless (name.split('/').last || name).include? '.'
|
131
|
-
name << (extension || self.page_cache_extension)
|
132
|
-
end
|
133
|
-
return name
|
134
|
-
end
|
135
|
-
|
136
|
-
def page_cache_path(path, extension = nil)
|
137
|
-
page_cache_directory.to_s + page_cache_file(path, extension)
|
138
|
-
end
|
139
|
-
|
140
|
-
def instrument_page_cache(name, path)
|
141
|
-
ActiveSupport::Notifications.instrument("#{name}.action_controller", :path => path){ yield }
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# Expires the page that was cached with the +options+ as a key. Example:
|
146
|
-
# expire_page :controller => "lists", :action => "show"
|
147
|
-
def expire_page(options = {})
|
148
|
-
return unless self.class.perform_caching
|
149
|
-
|
150
|
-
if options.is_a?(Hash)
|
151
|
-
if options[:action].is_a?(Array)
|
152
|
-
options[:action].each do |action|
|
153
|
-
self.class.expire_page(url_for(options.merge(:only_path => true, :action => action)))
|
154
|
-
end
|
155
|
-
else
|
156
|
-
self.class.expire_page(url_for(options.merge(:only_path => true)))
|
157
|
-
end
|
158
|
-
else
|
159
|
-
self.class.expire_page(options)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used.
|
164
|
-
# If no options are provided, the url of the current request being handled is used. Example:
|
165
|
-
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
|
166
|
-
def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
|
167
|
-
return unless self.class.perform_caching && caching_allowed?
|
168
|
-
|
169
|
-
path = case options
|
170
|
-
when Hash
|
171
|
-
url_for(options.merge(:only_path => true, :format => params[:format]))
|
172
|
-
when String
|
173
|
-
options
|
174
|
-
else
|
175
|
-
request.path
|
176
|
-
end
|
177
|
-
|
178
|
-
if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
|
179
|
-
extension = ".#{type_symbol}"
|
180
|
-
end
|
181
|
-
|
182
|
-
self.class.cache_page(content || response.body, path, extension, gzip)
|
183
|
-
end
|
184
|
-
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
module ActionController #:nodoc:
|
2
|
-
module Caching
|
3
|
-
# Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
|
4
|
-
# They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
|
5
|
-
#
|
6
|
-
# class ListSweeper < ActionController::Caching::Sweeper
|
7
|
-
# observe List, Item
|
8
|
-
#
|
9
|
-
# def after_save(record)
|
10
|
-
# list = record.is_a?(List) ? record : record.list
|
11
|
-
# expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
|
12
|
-
# expire_action(:controller => "lists", :action => "all")
|
13
|
-
# list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
|
14
|
-
# end
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
|
18
|
-
#
|
19
|
-
# class ListsController < ApplicationController
|
20
|
-
# caches_action :index, :show, :public, :feed
|
21
|
-
# cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
|
25
|
-
#
|
26
|
-
# You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
|
27
|
-
#
|
28
|
-
# class ListsController < ApplicationController
|
29
|
-
# caches_action :index, :show, :public, :feed
|
30
|
-
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
|
31
|
-
# end
|
32
|
-
module Sweeping
|
33
|
-
extend ActiveSupport::Concern
|
34
|
-
|
35
|
-
module ClassMethods #:nodoc:
|
36
|
-
def cache_sweeper(*sweepers)
|
37
|
-
configuration = sweepers.extract_options!
|
38
|
-
|
39
|
-
sweepers.each do |sweeper|
|
40
|
-
ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
|
41
|
-
sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
|
42
|
-
|
43
|
-
if sweeper_instance.is_a?(Sweeper)
|
44
|
-
around_filter(sweeper_instance, :only => configuration[:only])
|
45
|
-
else
|
46
|
-
after_filter(sweeper_instance, :only => configuration[:only])
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
|
54
|
-
class Sweeper < ActiveRecord::Observer #:nodoc:
|
55
|
-
attr_accessor :controller
|
56
|
-
|
57
|
-
def before(controller)
|
58
|
-
self.controller = controller
|
59
|
-
callback(:before) if controller.perform_caching
|
60
|
-
true # before method from sweeper should always return true
|
61
|
-
end
|
62
|
-
|
63
|
-
def after(controller)
|
64
|
-
self.controller = controller
|
65
|
-
callback(:after) if controller.perform_caching
|
66
|
-
# Clean up, so that the controller can be collected after this request
|
67
|
-
self.controller = nil
|
68
|
-
end
|
69
|
-
|
70
|
-
protected
|
71
|
-
# gets the action cache path for the given options.
|
72
|
-
def action_path_for(options)
|
73
|
-
Actions::ActionCachePath.new(controller, options).path
|
74
|
-
end
|
75
|
-
|
76
|
-
# Retrieve instance variables set in the controller.
|
77
|
-
def assigns(key)
|
78
|
-
controller.instance_variable_get("@#{key}")
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
def callback(timing)
|
83
|
-
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
|
84
|
-
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
|
85
|
-
|
86
|
-
__send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
|
87
|
-
__send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
|
88
|
-
end
|
89
|
-
|
90
|
-
def method_missing(method, *arguments, &block)
|
91
|
-
return unless @controller
|
92
|
-
@controller.__send__(method, *arguments, &block)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
ActionController::PerformanceTest = ActionDispatch::PerformanceTest
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'active_support/deprecation'
|
2
|
-
|
3
|
-
module ActionController
|
4
|
-
module Compatibility
|
5
|
-
extend ActiveSupport::Concern
|
6
|
-
|
7
|
-
# Temporary hax
|
8
|
-
included do
|
9
|
-
::ActionController::UnknownAction = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActionController::UnknownAction', '::AbstractController::ActionNotFound')
|
10
|
-
::ActionController::DoubleRenderError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActionController::DoubleRenderError', '::AbstractController::DoubleRenderError')
|
11
|
-
|
12
|
-
# ROUTES TODO: This should be handled by a middleware and route generation
|
13
|
-
# should be able to handle SCRIPT_NAME
|
14
|
-
self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
|
15
|
-
|
16
|
-
def self.default_charset=(new_charset)
|
17
|
-
ActiveSupport::Deprecation.warn "Setting default charset at controller level" \
|
18
|
-
" is deprecated, please use `config.action_dispatch.default_charset` instead", caller
|
19
|
-
ActionDispatch::Response.default_charset = new_charset
|
20
|
-
end
|
21
|
-
|
22
|
-
self.protected_instance_variables = %w(
|
23
|
-
@_status @_headers @_params @_env @_response @_request
|
24
|
-
@_view_runtime @_stream @_url_options @_action_has_layout
|
25
|
-
)
|
26
|
-
|
27
|
-
def rescue_action(env)
|
28
|
-
ActiveSupport::Deprecation.warn "Calling `rescue_action` is deprecated and will be removed in Rails 4.0.", caller
|
29
|
-
raise env["action_dispatch.rescue.exception"]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# For old tests
|
34
|
-
def initialize_template_class(*)
|
35
|
-
ActiveSupport::Deprecation.warn "Calling `initialize_template_class` is deprecated and has no effect anymore.", caller
|
36
|
-
end
|
37
|
-
|
38
|
-
def assign_shortcuts(*)
|
39
|
-
ActiveSupport::Deprecation.warn "Calling `assign_shortcuts` is deprecated and has no effect anymore.", caller
|
40
|
-
end
|
41
|
-
|
42
|
-
def _normalize_options(options)
|
43
|
-
options[:text] = nil if options.delete(:nothing) == true
|
44
|
-
options[:text] = " " if options.key?(:text) && options[:text].nil?
|
45
|
-
super
|
46
|
-
end
|
47
|
-
|
48
|
-
def render_to_body(options)
|
49
|
-
options[:template].sub!(/^\//, '') if options.key?(:template)
|
50
|
-
super || " "
|
51
|
-
end
|
52
|
-
|
53
|
-
def _handle_method_missing
|
54
|
-
ActiveSupport::Deprecation.warn "Using `method_missing` to handle non" \
|
55
|
-
" existing actions is deprecated and will be removed in Rails 4.0, " \
|
56
|
-
" please use `action_missing` instead.", caller
|
57
|
-
method_missing(@_action_name.to_sym)
|
58
|
-
end
|
59
|
-
|
60
|
-
def method_for_action(action_name)
|
61
|
-
super || ((self.class.public_method_defined?(:method_missing) ||
|
62
|
-
self.class.protected_method_defined?(:method_missing)) && "_handle_method_missing")
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,286 +0,0 @@
|
|
1
|
-
require 'active_support/json'
|
2
|
-
|
3
|
-
module ActionController #:nodoc:
|
4
|
-
# Responsible for exposing a resource to different mime requests,
|
5
|
-
# usually depending on the HTTP verb. The responder is triggered when
|
6
|
-
# <code>respond_with</code> is called. The simplest case to study is a GET request:
|
7
|
-
#
|
8
|
-
# class PeopleController < ApplicationController
|
9
|
-
# respond_to :html, :xml, :json
|
10
|
-
#
|
11
|
-
# def index
|
12
|
-
# @people = Person.all
|
13
|
-
# respond_with(@people)
|
14
|
-
# end
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# When a request comes in, for example for an XML response, three steps happen:
|
18
|
-
#
|
19
|
-
# 1) the responder searches for a template at people/index.xml;
|
20
|
-
#
|
21
|
-
# 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
|
22
|
-
#
|
23
|
-
# 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
|
24
|
-
#
|
25
|
-
# === Builtin HTTP verb semantics
|
26
|
-
#
|
27
|
-
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
|
28
|
-
# content type, verb and the resource status, it will behave differently.
|
29
|
-
#
|
30
|
-
# Using \Rails default responder, a POST request for creating an object could
|
31
|
-
# be written as:
|
32
|
-
#
|
33
|
-
# def create
|
34
|
-
# @user = User.new(params[:user])
|
35
|
-
# flash[:notice] = 'User was successfully created.' if @user.save
|
36
|
-
# respond_with(@user)
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# Which is exactly the same as:
|
40
|
-
#
|
41
|
-
# def create
|
42
|
-
# @user = User.new(params[:user])
|
43
|
-
#
|
44
|
-
# respond_to do |format|
|
45
|
-
# if @user.save
|
46
|
-
# flash[:notice] = 'User was successfully created.'
|
47
|
-
# format.html { redirect_to(@user) }
|
48
|
-
# format.xml { render :xml => @user, :status => :created, :location => @user }
|
49
|
-
# else
|
50
|
-
# format.html { render :action => "new" }
|
51
|
-
# format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
|
52
|
-
# end
|
53
|
-
# end
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# The same happens for PUT and DELETE requests.
|
57
|
-
#
|
58
|
-
# === Nested resources
|
59
|
-
#
|
60
|
-
# You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
|
61
|
-
# Consider the project has many tasks example. The create action for
|
62
|
-
# TasksController would be like:
|
63
|
-
#
|
64
|
-
# def create
|
65
|
-
# @project = Project.find(params[:project_id])
|
66
|
-
# @task = @project.comments.build(params[:task])
|
67
|
-
# flash[:notice] = 'Task was successfully created.' if @task.save
|
68
|
-
# respond_with(@project, @task)
|
69
|
-
# end
|
70
|
-
#
|
71
|
-
# Giving several resources ensures that the responder will redirect to
|
72
|
-
# <code>project_task_url</code> instead of <code>task_url</code>.
|
73
|
-
#
|
74
|
-
# Namespaced and singleton resources require a symbol to be given, as in
|
75
|
-
# polymorphic urls. If a project has one manager which has many tasks, it
|
76
|
-
# should be invoked as:
|
77
|
-
#
|
78
|
-
# respond_with(@project, :manager, @task)
|
79
|
-
#
|
80
|
-
# Note that if you give an array, it will be treated as a collection,
|
81
|
-
# so the following is not equivalent:
|
82
|
-
#
|
83
|
-
# respond_with [@project, :manager, @task]
|
84
|
-
#
|
85
|
-
# === Custom options
|
86
|
-
#
|
87
|
-
# <code>respond_with</code> also allows you to pass options that are forwarded
|
88
|
-
# to the underlying render call. Those options are only applied for success
|
89
|
-
# scenarios. For instance, you can do the following in the create method above:
|
90
|
-
#
|
91
|
-
# def create
|
92
|
-
# @project = Project.find(params[:project_id])
|
93
|
-
# @task = @project.comments.build(params[:task])
|
94
|
-
# flash[:notice] = 'Task was successfully created.' if @task.save
|
95
|
-
# respond_with(@project, @task, :status => 201)
|
96
|
-
# end
|
97
|
-
#
|
98
|
-
# This will return status 201 if the task was saved successfully. If not,
|
99
|
-
# it will simply ignore the given options and return status 422 and the
|
100
|
-
# resource errors. To customize the failure scenario, you can pass a
|
101
|
-
# a block to <code>respond_with</code>:
|
102
|
-
#
|
103
|
-
# def create
|
104
|
-
# @project = Project.find(params[:project_id])
|
105
|
-
# @task = @project.comments.build(params[:task])
|
106
|
-
# respond_with(@project, @task, :status => 201) do |format|
|
107
|
-
# if @task.save
|
108
|
-
# flash[:notice] = 'Task was successfully created.'
|
109
|
-
# else
|
110
|
-
# format.html { render "some_special_template" }
|
111
|
-
# end
|
112
|
-
# end
|
113
|
-
# end
|
114
|
-
#
|
115
|
-
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
|
116
|
-
class Responder
|
117
|
-
attr_reader :controller, :request, :format, :resource, :resources, :options
|
118
|
-
|
119
|
-
ACTIONS_FOR_VERBS = {
|
120
|
-
:post => :new,
|
121
|
-
:put => :edit
|
122
|
-
}
|
123
|
-
|
124
|
-
def initialize(controller, resources, options={})
|
125
|
-
@controller = controller
|
126
|
-
@request = @controller.request
|
127
|
-
@format = @controller.formats.first
|
128
|
-
@resource = resources.last
|
129
|
-
@resources = resources
|
130
|
-
@options = options
|
131
|
-
@action = options.delete(:action)
|
132
|
-
@default_response = options.delete(:default_response)
|
133
|
-
end
|
134
|
-
|
135
|
-
delegate :head, :render, :redirect_to, :to => :controller
|
136
|
-
delegate :get?, :post?, :put?, :delete?, :to => :request
|
137
|
-
|
138
|
-
# Undefine :to_json and :to_yaml since it's defined on Object
|
139
|
-
undef_method(:to_json) if method_defined?(:to_json)
|
140
|
-
undef_method(:to_yaml) if method_defined?(:to_yaml)
|
141
|
-
|
142
|
-
# Initializes a new responder an invoke the proper format. If the format is
|
143
|
-
# not defined, call to_format.
|
144
|
-
#
|
145
|
-
def self.call(*args)
|
146
|
-
new(*args).respond
|
147
|
-
end
|
148
|
-
|
149
|
-
# Main entry point for responder responsible to dispatch to the proper format.
|
150
|
-
#
|
151
|
-
def respond
|
152
|
-
method = "to_#{format}"
|
153
|
-
respond_to?(method) ? send(method) : to_format
|
154
|
-
end
|
155
|
-
|
156
|
-
# HTML format does not render the resource, it always attempt to render a
|
157
|
-
# template.
|
158
|
-
#
|
159
|
-
def to_html
|
160
|
-
default_render
|
161
|
-
rescue ActionView::MissingTemplate => e
|
162
|
-
navigation_behavior(e)
|
163
|
-
end
|
164
|
-
|
165
|
-
# to_js simply tries to render a template. If no template is found, raises the error.
|
166
|
-
def to_js
|
167
|
-
default_render
|
168
|
-
end
|
169
|
-
|
170
|
-
# All other formats follow the procedure below. First we try to render a
|
171
|
-
# template, if the template is not available, we verify if the resource
|
172
|
-
# responds to :to_format and display it.
|
173
|
-
#
|
174
|
-
def to_format
|
175
|
-
if get? || !has_errors? || response_overridden?
|
176
|
-
default_render
|
177
|
-
else
|
178
|
-
display_errors
|
179
|
-
end
|
180
|
-
rescue ActionView::MissingTemplate => e
|
181
|
-
api_behavior(e)
|
182
|
-
end
|
183
|
-
|
184
|
-
protected
|
185
|
-
|
186
|
-
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
|
187
|
-
def navigation_behavior(error)
|
188
|
-
if get?
|
189
|
-
raise error
|
190
|
-
elsif has_errors? && default_action
|
191
|
-
render :action => default_action
|
192
|
-
else
|
193
|
-
redirect_to navigation_location
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
# This is the common behavior for formats associated with APIs, such as :xml and :json.
|
198
|
-
def api_behavior(error)
|
199
|
-
raise error unless resourceful?
|
200
|
-
|
201
|
-
if get?
|
202
|
-
display resource
|
203
|
-
elsif post?
|
204
|
-
display resource, :status => :created, :location => api_location
|
205
|
-
else
|
206
|
-
head :no_content
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# Checks whether the resource responds to the current format or not.
|
211
|
-
#
|
212
|
-
def resourceful?
|
213
|
-
resource.respond_to?("to_#{format}")
|
214
|
-
end
|
215
|
-
|
216
|
-
# Returns the resource location by retrieving it from the options or
|
217
|
-
# returning the resources array.
|
218
|
-
#
|
219
|
-
def resource_location
|
220
|
-
options[:location] || resources
|
221
|
-
end
|
222
|
-
alias :navigation_location :resource_location
|
223
|
-
alias :api_location :resource_location
|
224
|
-
|
225
|
-
# If a response block was given, use it, otherwise call render on
|
226
|
-
# controller.
|
227
|
-
#
|
228
|
-
def default_render
|
229
|
-
if @default_response
|
230
|
-
@default_response.call(options)
|
231
|
-
else
|
232
|
-
controller.default_render(options)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
# Display is just a shortcut to render a resource with the current format.
|
237
|
-
#
|
238
|
-
# display @user, :status => :ok
|
239
|
-
#
|
240
|
-
# For XML requests it's equivalent to:
|
241
|
-
#
|
242
|
-
# render :xml => @user, :status => :ok
|
243
|
-
#
|
244
|
-
# Options sent by the user are also used:
|
245
|
-
#
|
246
|
-
# respond_with(@user, :status => :created)
|
247
|
-
# display(@user, :status => :ok)
|
248
|
-
#
|
249
|
-
# Results in:
|
250
|
-
#
|
251
|
-
# render :xml => @user, :status => :created
|
252
|
-
#
|
253
|
-
def display(resource, given_options={})
|
254
|
-
controller.render given_options.merge!(options).merge!(format => resource)
|
255
|
-
end
|
256
|
-
|
257
|
-
def display_errors
|
258
|
-
controller.render format => resource_errors, :status => :unprocessable_entity
|
259
|
-
end
|
260
|
-
|
261
|
-
# Check whether the resource has errors.
|
262
|
-
#
|
263
|
-
def has_errors?
|
264
|
-
resource.respond_to?(:errors) && !resource.errors.empty?
|
265
|
-
end
|
266
|
-
|
267
|
-
# By default, render the <code>:edit</code> action for HTML requests with failure, unless
|
268
|
-
# the verb is POST.
|
269
|
-
#
|
270
|
-
def default_action
|
271
|
-
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
|
272
|
-
end
|
273
|
-
|
274
|
-
def resource_errors
|
275
|
-
respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
|
276
|
-
end
|
277
|
-
|
278
|
-
def json_resource_errors
|
279
|
-
{:errors => resource.errors}
|
280
|
-
end
|
281
|
-
|
282
|
-
def response_overridden?
|
283
|
-
@default_response.present?
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module ActionController #:nodoc:
|
2
|
-
module SessionManagement #:nodoc:
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
ActiveSupport::Deprecation.warn "ActionController::SessionManagement " \
|
7
|
-
"is deprecated because it has no contents since Rails 3.1", caller
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module ActionController
|
2
|
-
module Railties
|
3
|
-
module Paths
|
4
|
-
def self.with(app)
|
5
|
-
Module.new do
|
6
|
-
define_method(:inherited) do |klass|
|
7
|
-
super(klass)
|
8
|
-
|
9
|
-
if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
|
10
|
-
paths = namespace.railtie_helpers_paths
|
11
|
-
else
|
12
|
-
paths = app.helpers_paths
|
13
|
-
end
|
14
|
-
|
15
|
-
klass.helpers_path = paths
|
16
|
-
|
17
|
-
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
|
18
|
-
klass.helper :all
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|