halorgium-actionpack 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,15 @@
1
+ module ActionController
2
+ module Session
3
+ extend ActiveSupport::Concern
4
+
5
+ include RackConvenience
6
+
7
+ def session
8
+ @_request.session
9
+ end
10
+
11
+ def reset_session
12
+ @_request.reset_session
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ module ActionController #:nodoc:
2
+ module SessionManagement #:nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ include ActionController::Configuration
6
+
7
+ module ClassMethods
8
+ # Set the session store to be used for keeping the session data between requests.
9
+ # By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
10
+ # but you can also specify one of the other included stores (<tt>:active_record_store</tt>,
11
+ # <tt>:mem_cache_store</tt>, or your own custom class.
12
+ def session_store=(store)
13
+ if store == :active_record_store
14
+ self.session_store = ActiveRecord::SessionStore
15
+ else
16
+ @@session_store = store.is_a?(Symbol) ?
17
+ ActionDispatch::Session.const_get(store.to_s.camelize) :
18
+ store
19
+ end
20
+ end
21
+
22
+ # Returns the session store class currently used.
23
+ def session_store
24
+ if defined? @@session_store
25
+ @@session_store
26
+ else
27
+ ActionDispatch::Session::CookieStore
28
+ end
29
+ end
30
+
31
+ def session=(options = {})
32
+ self.session_store = nil if options.delete(:disabled)
33
+ session_options.merge!(options)
34
+ end
35
+
36
+ def session(*args)
37
+ ActiveSupport::Deprecation.warn(
38
+ "Disabling sessions for a single controller has been deprecated. " +
39
+ "Sessions are now lazy loaded. So if you don't access them, " +
40
+ "consider them off. You can still modify the session cookie " +
41
+ "options with request.session_options.", caller)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,188 @@
1
+ require 'active_support/core_ext/string/bytesize'
2
+
3
+ module ActionController #:nodoc:
4
+ # Methods for sending arbitrary data and for streaming files to the browser,
5
+ # instead of rendering.
6
+ module Streaming
7
+ extend ActiveSupport::Concern
8
+
9
+ include ActionController::RenderingController
10
+
11
+ DEFAULT_SEND_FILE_OPTIONS = {
12
+ :type => 'application/octet-stream'.freeze,
13
+ :disposition => 'attachment'.freeze,
14
+ :stream => true,
15
+ :buffer_size => 4096,
16
+ :x_sendfile => false
17
+ }.freeze
18
+
19
+ X_SENDFILE_HEADER = 'X-Sendfile'.freeze
20
+
21
+ protected
22
+ # Sends the file, by default streaming it 4096 bytes at a time. This way the
23
+ # whole file doesn't need to be read into memory at once. This makes it
24
+ # feasible to send even large files. You can optionally turn off streaming
25
+ # and send the whole file at once.
26
+ #
27
+ # Be careful to sanitize the path parameter if it is coming from a web
28
+ # page. <tt>send_file(params[:path])</tt> allows a malicious user to
29
+ # download any file on your server.
30
+ #
31
+ # Options:
32
+ # * <tt>:filename</tt> - suggests a filename for the browser to use.
33
+ # Defaults to <tt>File.basename(path)</tt>.
34
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
35
+ # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
36
+ # * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
37
+ # is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
38
+ # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
39
+ # Valid values are 'inline' and 'attachment' (default).
40
+ # * <tt>:stream</tt> - whether to send the file to the user agent as it is read (+true+)
41
+ # or to read the entire file before sending (+false+). Defaults to +true+.
42
+ # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
43
+ # Defaults to 4096.
44
+ # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
45
+ # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
46
+ # the URL, which is necessary for i18n filenames on certain browsers
47
+ # (setting <tt>:filename</tt> overrides this option).
48
+ # * <tt>:x_sendfile</tt> - uses X-Sendfile to send the file when set to +true+. This is currently
49
+ # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this
50
+ # uses the web server to send the file, this may lower memory consumption on your server and
51
+ # it will not block your application for further requests.
52
+ # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and
53
+ # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+.
54
+ #
55
+ # The default Content-Type and Content-Disposition headers are
56
+ # set to download arbitrary binary files in as many browsers as
57
+ # possible. IE versions 4, 5, 5.5, and 6 are all known to have
58
+ # a variety of quirks (especially when downloading over SSL).
59
+ #
60
+ # Simple download:
61
+ #
62
+ # send_file '/path/to.zip'
63
+ #
64
+ # Show a JPEG in the browser:
65
+ #
66
+ # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
67
+ #
68
+ # Show a 404 page in the browser:
69
+ #
70
+ # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
71
+ #
72
+ # Read about the other Content-* HTTP headers if you'd like to
73
+ # provide the user with more information (such as Content-Description) in
74
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
75
+ #
76
+ # Also be aware that the document may be cached by proxies and browsers.
77
+ # The Pragma and Cache-Control headers declare how the file may be cached
78
+ # by intermediaries. They default to require clients to validate with
79
+ # the server before releasing cached responses. See
80
+ # http://www.mnot.net/cache_docs/ for an overview of web caching and
81
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
82
+ # for the Cache-Control header spec.
83
+ def send_file(path, options = {}) #:doc:
84
+ raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
85
+
86
+ options[:length] ||= File.size(path)
87
+ options[:filename] ||= File.basename(path) unless options[:url_based_filename]
88
+ send_file_headers! options
89
+
90
+ @performed_render = false
91
+
92
+ if options[:x_sendfile]
93
+ logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger
94
+ head options[:status], X_SENDFILE_HEADER => path
95
+ else
96
+ if options[:stream]
97
+ # TODO : Make render :text => proc {} work with the new base
98
+ render :status => options[:status], :text => Proc.new { |response, output|
99
+ logger.info "Streaming file #{path}" unless logger.nil?
100
+ len = options[:buffer_size] || 4096
101
+ File.open(path, 'rb') do |file|
102
+ while buf = file.read(len)
103
+ output.write(buf)
104
+ end
105
+ end
106
+ }
107
+ else
108
+ logger.info "Sending file #{path}" unless logger.nil?
109
+ File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
110
+ end
111
+ end
112
+ end
113
+
114
+ # Sends the given binary data to the browser. This method is similar to
115
+ # <tt>render :text => data</tt>, but also allows you to specify whether
116
+ # the browser should display the response as a file attachment (i.e. in a
117
+ # download dialog) or as inline data. You may also set the content type,
118
+ # the apparent file name, and other things.
119
+ #
120
+ # Options:
121
+ # * <tt>:filename</tt> - suggests a filename for the browser to use.
122
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
123
+ # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
124
+ # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
125
+ # Valid values are 'inline' and 'attachment' (default).
126
+ # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
127
+ #
128
+ # Generic data download:
129
+ #
130
+ # send_data buffer
131
+ #
132
+ # Download a dynamically-generated tarball:
133
+ #
134
+ # send_data generate_tgz('dir'), :filename => 'dir.tgz'
135
+ #
136
+ # Display an image Active Record in the browser:
137
+ #
138
+ # send_data image.data, :type => image.content_type, :disposition => 'inline'
139
+ #
140
+ # See +send_file+ for more information on HTTP Content-* headers and caching.
141
+ #
142
+ # <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
143
+ # data to the browser, then use <tt>render :text => proc { ... }</tt>
144
+ # instead. See ActionController::Base#render for more information.
145
+ def send_data(data, options = {}) #:doc:
146
+ logger.info "Sending data #{options[:filename]}" if logger
147
+ send_file_headers! options.merge(:length => data.bytesize)
148
+ render :status => options[:status], :text => data
149
+ end
150
+
151
+ private
152
+ def send_file_headers!(options)
153
+ options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
154
+ [:length, :type, :disposition].each do |arg|
155
+ raise ArgumentError, ":#{arg} option required" if options[arg].nil?
156
+ end
157
+
158
+ disposition = options[:disposition].dup || 'attachment'
159
+
160
+ disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
161
+
162
+ content_type = options[:type]
163
+
164
+ if content_type.is_a?(Symbol)
165
+ raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s)
166
+ self.content_type = Mime::Type.lookup_by_extension(content_type.to_s)
167
+ else
168
+ self.content_type = content_type
169
+ end
170
+
171
+ headers.merge!(
172
+ 'Content-Length' => options[:length].to_s,
173
+ 'Content-Disposition' => disposition,
174
+ 'Content-Transfer-Encoding' => 'binary'
175
+ )
176
+
177
+ response.sending_file = true
178
+
179
+ # Fix a problem with IE 6.0 on opening downloaded files:
180
+ # If Cache-Control: no-cache is set (which Rails does by default),
181
+ # IE removes the file it just downloaded from its cache immediately
182
+ # after it displays the "open/save" dialog, which means that if you
183
+ # hit "open" the file isn't there anymore when the application that
184
+ # is called for handling the download is run, so let's workaround that
185
+ response.cache_control[:public] ||= false
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,39 @@
1
+ module ActionController
2
+ module Testing
3
+ extend ActiveSupport::Concern
4
+
5
+ include RackConvenience
6
+
7
+ # OMG MEGA HAX
8
+ def process_with_new_base_test(request, response)
9
+ @_request = request
10
+ @_response = response
11
+ @_response.request = request
12
+ ret = process(request.parameters[:action])
13
+ @_response.body ||= self.response_body
14
+ @_response.prepare!
15
+ set_test_assigns
16
+ ret
17
+ end
18
+
19
+ def set_test_assigns
20
+ @assigns = {}
21
+ (instance_variable_names - self.class.protected_instance_variables).each do |var|
22
+ name, value = var[1..-1], instance_variable_get(var)
23
+ @assigns[name] = value
24
+ end
25
+ end
26
+
27
+ # TODO : Rewrite tests using controller.headers= to use Rack env
28
+ def headers=(new_headers)
29
+ @_response ||= ActionDispatch::Response.new
30
+ @_response.headers.replace(new_headers)
31
+ end
32
+
33
+ module ClassMethods
34
+ def before_filters
35
+ _process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module ActionController
2
+ module UrlFor
3
+ extend ActiveSupport::Concern
4
+
5
+ include RackConvenience
6
+
7
+ # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
8
+ # the form of a hash, just like the one you would use for url_for directly. Example:
9
+ #
10
+ # def default_url_options(options)
11
+ # { :project => @project.active? ? @project.url_name : "unknown" }
12
+ # end
13
+ #
14
+ # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
15
+ # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
16
+ # by this method.
17
+ def default_url_options(options = nil)
18
+ end
19
+
20
+ def rewrite_options(options) #:nodoc:
21
+ if defaults = default_url_options(options)
22
+ defaults.merge(options)
23
+ else
24
+ options
25
+ end
26
+ end
27
+
28
+ def url_for(options = {})
29
+ options ||= {}
30
+ case options
31
+ when String
32
+ options
33
+ when Hash
34
+ @url ||= UrlRewriter.new(request, params)
35
+ @url.rewrite(rewrite_options(options))
36
+ else
37
+ polymorphic_url(options)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,130 @@
1
+ module ActionController #:nodoc:
2
+ module Verification #:nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ include AbstractController::Callbacks, Session, Flash, RenderingController
6
+
7
+ # This module provides a class-level method for specifying that certain
8
+ # actions are guarded against being called without certain prerequisites
9
+ # being met. This is essentially a special kind of before_filter.
10
+ #
11
+ # An action may be guarded against being invoked without certain request
12
+ # parameters being set, or without certain session values existing.
13
+ #
14
+ # When a verification is violated, values may be inserted into the flash, and
15
+ # a specified redirection is triggered. If no specific action is configured,
16
+ # verification failures will by default result in a 400 Bad Request response.
17
+ #
18
+ # Usage:
19
+ #
20
+ # class GlobalController < ActionController::Base
21
+ # # Prevent the #update_settings action from being invoked unless
22
+ # # the 'admin_privileges' request parameter exists. The
23
+ # # settings action will be redirected to in current controller
24
+ # # if verification fails.
25
+ # verify :params => "admin_privileges", :only => :update_post,
26
+ # :redirect_to => { :action => "settings" }
27
+ #
28
+ # # Disallow a post from being updated if there was no information
29
+ # # submitted with the post, and if there is no active post in the
30
+ # # session, and if there is no "note" key in the flash. The route
31
+ # # named category_url will be redirected to if verification fails.
32
+ #
33
+ # verify :params => "post", :session => "post", "flash" => "note",
34
+ # :only => :update_post,
35
+ # :add_flash => { "alert" => "Failed to create your message" },
36
+ # :redirect_to => :category_url
37
+ #
38
+ # Note that these prerequisites are not business rules. They do not examine
39
+ # the content of the session or the parameters. That level of validation should
40
+ # be encapsulated by your domain model or helper methods in the controller.
41
+ module ClassMethods
42
+ # Verify the given actions so that if certain prerequisites are not met,
43
+ # the user is redirected to a different action. The +options+ parameter
44
+ # is a hash consisting of the following key/value pairs:
45
+ #
46
+ # <tt>:params</tt>::
47
+ # a single key or an array of keys that must be in the <tt>params</tt>
48
+ # hash in order for the action(s) to be safely called.
49
+ # <tt>:session</tt>::
50
+ # a single key or an array of keys that must be in the <tt>session</tt>
51
+ # in order for the action(s) to be safely called.
52
+ # <tt>:flash</tt>::
53
+ # a single key or an array of keys that must be in the flash in order
54
+ # for the action(s) to be safely called.
55
+ # <tt>:method</tt>::
56
+ # a single key or an array of keys--any one of which must match the
57
+ # current request method in order for the action(s) to be safely called.
58
+ # (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
59
+ # example.)
60
+ # <tt>:xhr</tt>::
61
+ # true/false option to ensure that the request is coming from an Ajax
62
+ # call or not.
63
+ # <tt>:add_flash</tt>::
64
+ # a hash of name/value pairs that should be merged into the session's
65
+ # flash if the prerequisites cannot be satisfied.
66
+ # <tt>:add_headers</tt>::
67
+ # a hash of name/value pairs that should be merged into the response's
68
+ # headers hash if the prerequisites cannot be satisfied.
69
+ # <tt>:redirect_to</tt>::
70
+ # the redirection parameters to be used when redirecting if the
71
+ # prerequisites cannot be satisfied. You can redirect either to named
72
+ # route or to the action in some controller.
73
+ # <tt>:render</tt>::
74
+ # the render parameters to be used when the prerequisites cannot be satisfied.
75
+ # <tt>:only</tt>::
76
+ # only apply this verification to the actions specified in the associated
77
+ # array (may also be a single value).
78
+ # <tt>:except</tt>::
79
+ # do not apply this verification to the actions specified in the associated
80
+ # array (may also be a single value).
81
+ def verify(options={})
82
+ before_filter :only => options[:only], :except => options[:except] do
83
+ verify_action options
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def verify_action(options) #:nodoc:
91
+ if prereqs_invalid?(options)
92
+ flash.update(options[:add_flash]) if options[:add_flash]
93
+ response.headers.merge!(options[:add_headers]) if options[:add_headers]
94
+ apply_remaining_actions(options) unless performed?
95
+ end
96
+ end
97
+
98
+ def prereqs_invalid?(options) # :nodoc:
99
+ verify_presence_of_keys_in_hash_flash_or_params(options) ||
100
+ verify_method(options) ||
101
+ verify_request_xhr_status(options)
102
+ end
103
+
104
+ def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
105
+ [*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
106
+ [*options[:session]].find { |v| session[v].nil? } ||
107
+ [*options[:flash] ].find { |v| flash[v].nil? }
108
+ end
109
+
110
+ def verify_method(options) # :nodoc:
111
+ [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method]
112
+ end
113
+
114
+ def verify_request_xhr_status(options) # :nodoc:
115
+ request.xhr? != options[:xhr] unless options[:xhr].nil?
116
+ end
117
+
118
+ def apply_redirect_to(redirect_to_option) # :nodoc:
119
+ (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option
120
+ end
121
+
122
+ def apply_remaining_actions(options) # :nodoc:
123
+ case
124
+ when options[:render] ; render(options[:render])
125
+ when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
126
+ else head(:bad_request)
127
+ end
128
+ end
129
+ end
130
+ end