halorgium-actionpack 3.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,27 @@
1
+ module ActionController
2
+ module RackConvenience
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ delegate :headers, :status=, :location=, :content_type=,
7
+ :status, :location, :content_type, :to => "@_response"
8
+ attr_internal :request
9
+ end
10
+
11
+ def dispatch(action, env)
12
+ @_request = ActionDispatch::Request.new(env)
13
+ @_response = ActionDispatch::Response.new
14
+ @_response.request = request
15
+ super
16
+ end
17
+
18
+ def params
19
+ @_params ||= @_request.parameters
20
+ end
21
+
22
+ def response_body=(body)
23
+ response.body = body if response
24
+ super
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ module ActionController
2
+ class RedirectBackError < AbstractController::Error #:nodoc:
3
+ DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
4
+
5
+ def initialize(message = nil)
6
+ super(message || DEFAULT_MESSAGE)
7
+ end
8
+ end
9
+
10
+ module Redirector
11
+ extend ActiveSupport::Concern
12
+ include AbstractController::Logger
13
+
14
+ def redirect_to(url, status) #:doc:
15
+ raise AbstractController::DoubleRenderError if response_body
16
+ logger.info("Redirected to #{url}") if logger && logger.info?
17
+ self.status = status
18
+ self.location = url.gsub(/[\r\n]/, '')
19
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(url)}\">redirected</a>.</body></html>"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,103 @@
1
+ module ActionController
2
+ module RenderOptions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extlib_inheritable_accessor :_renderers
7
+ self._renderers = []
8
+ end
9
+
10
+ module ClassMethods
11
+ def _write_render_options
12
+ renderers = _renderers.map do |r|
13
+ <<-RUBY_EVAL
14
+ if options.key?(:#{r})
15
+ _process_options(options)
16
+ return render_#{r}(options[:#{r}], options)
17
+ end
18
+ RUBY_EVAL
19
+ end
20
+
21
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
22
+ def _handle_render_options(options)
23
+ #{renderers.join}
24
+ end
25
+ RUBY_EVAL
26
+ end
27
+
28
+ def _add_render_option(name)
29
+ _renderers << name
30
+ _write_render_options
31
+ end
32
+ end
33
+
34
+ def render_to_body(options)
35
+ _handle_render_options(options) || super
36
+ end
37
+ end
38
+
39
+ module RenderOption #:nodoc:
40
+ def self.extended(base)
41
+ base.extend ActiveSupport::Concern
42
+ base.send :include, ::ActionController::RenderOptions
43
+
44
+ def base.register_renderer(name)
45
+ included { _add_render_option(name) }
46
+ end
47
+ end
48
+ end
49
+
50
+ module RenderOptions
51
+ module Json
52
+ extend RenderOption
53
+ register_renderer :json
54
+
55
+ def render_json(json, options)
56
+ json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
57
+ json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
58
+ self.content_type ||= Mime::JSON
59
+ self.response_body = json
60
+ end
61
+ end
62
+
63
+ module Js
64
+ extend RenderOption
65
+ register_renderer :js
66
+
67
+ def render_js(js, options)
68
+ self.content_type ||= Mime::JS
69
+ self.response_body = js.respond_to?(:to_js) ? js.to_js : js
70
+ end
71
+ end
72
+
73
+ module Xml
74
+ extend RenderOption
75
+ register_renderer :xml
76
+
77
+ def render_xml(xml, options)
78
+ self.content_type ||= Mime::XML
79
+ self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml
80
+ end
81
+ end
82
+
83
+ module RJS
84
+ extend RenderOption
85
+ register_renderer :update
86
+
87
+ def render_update(proc, options)
88
+ generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
89
+ self.content_type = Mime::JS
90
+ self.response_body = generator.to_s
91
+ end
92
+ end
93
+
94
+ module All
95
+ extend ActiveSupport::Concern
96
+
97
+ include ActionController::RenderOptions::Json
98
+ include ActionController::RenderOptions::Js
99
+ include ActionController::RenderOptions::Xml
100
+ include ActionController::RenderOptions::RJS
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,57 @@
1
+ module ActionController
2
+ module RenderingController
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include AbstractController::RenderingController
7
+ include AbstractController::LocalizedCache
8
+ end
9
+
10
+ def process_action(*)
11
+ self.formats = request.formats.map {|x| x.to_sym}
12
+ super
13
+ end
14
+
15
+ def render(options)
16
+ super
17
+ self.content_type ||= options[:_template].mime_type.to_s
18
+ response_body
19
+ end
20
+
21
+ def render_to_body(options)
22
+ _process_options(options)
23
+
24
+ if options.key?(:partial)
25
+ options[:partial] = action_name if options[:partial] == true
26
+ options[:_details] = {:formats => formats}
27
+ end
28
+
29
+ super
30
+ end
31
+
32
+ private
33
+ def _prefix
34
+ controller_path
35
+ end
36
+
37
+ def _determine_template(options)
38
+ if (options.keys & [:partial, :file, :template, :text, :inline]).empty?
39
+ options[:_template_name] ||= options[:action]
40
+ options[:_prefix] = _prefix
41
+ end
42
+
43
+ super
44
+ end
45
+
46
+ def format_for_text
47
+ formats.first
48
+ end
49
+
50
+ def _process_options(options)
51
+ status, content_type, location = options.values_at(:status, :content_type, :location)
52
+ self.status = status if status
53
+ self.content_type = content_type if content_type
54
+ self.headers["Location"] = url_for(location) if location
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,108 @@
1
+ module ActionController #:nodoc:
2
+ class InvalidAuthenticityToken < ActionControllerError #:nodoc:
3
+ end
4
+
5
+ module RequestForgeryProtection
6
+ extend ActiveSupport::Concern
7
+
8
+ include AbstractController::Helpers, Session
9
+
10
+ included do
11
+ # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
12
+ # sets it to <tt>:authenticity_token</tt> by default.
13
+ cattr_accessor :request_forgery_protection_token
14
+
15
+ # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
16
+ class_inheritable_accessor :allow_forgery_protection
17
+ self.allow_forgery_protection = true
18
+
19
+ helper_method :form_authenticity_token
20
+ helper_method :protect_against_forgery?
21
+ end
22
+
23
+ # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current
24
+ # web application, not a forged link from another site, is done by embedding a token based on a random
25
+ # string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated
26
+ # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript
27
+ # requests are checked, so this will not protect your XML API (presumably you'll have a different
28
+ # authentication scheme there anyway). Also, GET requests are not protected as these should be
29
+ # idempotent anyway.
30
+ #
31
+ # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
32
+ # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
33
+ # error message in production by editing public/422.html. A call to this method in ApplicationController is
34
+ # generated by default in post-Rails 2.0 applications.
35
+ #
36
+ # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form
37
+ # manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to
38
+ # include a hidden field named like that and set its value to what is returned by
39
+ # <tt>form_authenticity_token</tt>.
40
+ #
41
+ # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
42
+ # 1.x, add this to config/environments/test.rb:
43
+ #
44
+ # # Disable request forgery protection in test environment
45
+ # config.action_controller.allow_forgery_protection = false
46
+ #
47
+ # == Learn more about CSRF (Cross-Site Request Forgery) attacks
48
+ #
49
+ # Here are some resources:
50
+ # * http://isc.sans.org/diary.html?storyid=1750
51
+ # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
52
+ #
53
+ # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
54
+ # There are a few guidelines you should follow:
55
+ #
56
+ # * Keep your GET requests safe and idempotent. More reading material:
57
+ # * http://www.xml.com/pub/a/2002/04/24/deviant.html
58
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
59
+ # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
60
+ # for "Expires: at end of session"
61
+ #
62
+ module ClassMethods
63
+ # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
64
+ #
65
+ # Example:
66
+ #
67
+ # class FooController < ApplicationController
68
+ # protect_from_forgery :except => :index
69
+ #
70
+ # # you can disable csrf protection on controller-by-controller basis:
71
+ # skip_before_filter :verify_authenticity_token
72
+ # end
73
+ #
74
+ # Valid Options:
75
+ #
76
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
77
+ def protect_from_forgery(options = {})
78
+ self.request_forgery_protection_token ||= :authenticity_token
79
+ before_filter :verify_authenticity_token, options
80
+ end
81
+ end
82
+
83
+ protected
84
+ # The actual before_filter that is used. Modify this to change how you handle unverified requests.
85
+ def verify_authenticity_token
86
+ verified_request? || raise(ActionController::InvalidAuthenticityToken)
87
+ end
88
+
89
+ # Returns true or false if a request is verified. Checks:
90
+ #
91
+ # * is the format restricted? By default, only HTML requests are checked.
92
+ # * is it a GET request? Gets should be safe and idempotent
93
+ # * Does the form_authenticity_token match the given token value from the params?
94
+ def verified_request?
95
+ !protect_against_forgery? || request.forgery_whitelisted? ||
96
+ form_authenticity_token == params[request_forgery_protection_token]
97
+ end
98
+
99
+ # Sets the token value for the current session.
100
+ def form_authenticity_token
101
+ session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
102
+ end
103
+
104
+ def protect_against_forgery?
105
+ allow_forgery_protection
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,13 @@
1
+ module ActionController #:nodoc:
2
+ module Rescue
3
+ extend ActiveSupport::Concern
4
+ include ActiveSupport::Rescuable
5
+
6
+ private
7
+ def process_action(*args)
8
+ super
9
+ rescue Exception => exception
10
+ rescue_with_handler(exception) || raise(exception)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,200 @@
1
+ module ActionController #:nodoc:
2
+ # Responder is responsible to expose a resource for different mime requests,
3
+ # usually depending on the HTTP verb. The responder is triggered when
4
+ # respond_with is called. The simplest case to study is a GET request:
5
+ #
6
+ # class PeopleController < ApplicationController
7
+ # respond_to :html, :xml, :json
8
+ #
9
+ # def index
10
+ # @people = Person.find(:all)
11
+ # respond_with(@people)
12
+ # end
13
+ # end
14
+ #
15
+ # When a request comes, for example with format :xml, three steps happen:
16
+ #
17
+ # 1) respond_with searches for a template at people/index.xml;
18
+ #
19
+ # 2) if the template is not available, it will create a responder, passing
20
+ # the controller and the resource and invoke :to_xml on it;
21
+ #
22
+ # 3) if the responder does not respond_to :to_xml, call to_format on it.
23
+ #
24
+ # === Builtin HTTP verb semantics
25
+ #
26
+ # Rails default responder holds semantics for each HTTP verb. Depending on the
27
+ # content type, verb and the resource status, it will behave differently.
28
+ #
29
+ # Using Rails default responder, a POST request for creating an object could
30
+ # be written as:
31
+ #
32
+ # def create
33
+ # @user = User.new(params[:user])
34
+ # flash[:notice] = 'User was successfully created.' if @user.save
35
+ # respond_with(@user)
36
+ # end
37
+ #
38
+ # Which is exactly the same as:
39
+ #
40
+ # def create
41
+ # @user = User.new(params[:user])
42
+ #
43
+ # respond_to do |format|
44
+ # if @user.save
45
+ # flash[:notice] = 'User was successfully created.'
46
+ # format.html { redirect_to(@user) }
47
+ # format.xml { render :xml => @user, :status => :created, :location => @user }
48
+ # else
49
+ # format.html { render :action => "new" }
50
+ # format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
51
+ # end
52
+ # end
53
+ # end
54
+ #
55
+ # The same happens for PUT and DELETE requests.
56
+ #
57
+ # === Nested resources
58
+ #
59
+ # You can given nested resource as you do in form_for and polymorphic_url.
60
+ # Consider the project has many tasks example. The create action for
61
+ # TasksController would be like:
62
+ #
63
+ # def create
64
+ # @project = Project.find(params[:project_id])
65
+ # @task = @project.comments.build(params[:task])
66
+ # flash[:notice] = 'Task was successfully created.' if @task.save
67
+ # respond_with(@project, @task)
68
+ # end
69
+ #
70
+ # Giving an array of resources, you ensure that the responder will redirect to
71
+ # project_task_url instead of task_url.
72
+ #
73
+ # Namespaced and singleton resources requires a symbol to be given, as in
74
+ # polymorphic urls. If a project has one manager which has many tasks, it
75
+ # should be invoked as:
76
+ #
77
+ # respond_with(@project, :manager, @task)
78
+ #
79
+ # Check polymorphic_url documentation for more examples.
80
+ #
81
+ class Responder
82
+ attr_reader :controller, :request, :format, :resource, :resources, :options
83
+
84
+ def initialize(controller, resources, options={})
85
+ @controller = controller
86
+ @request = controller.request
87
+ @format = controller.formats.first
88
+ @resource = resources.is_a?(Array) ? resources.last : resources
89
+ @resources = resources
90
+ @options = options
91
+ @default_response = options.delete(:default_response)
92
+ end
93
+
94
+ delegate :head, :render, :redirect_to, :to => :controller
95
+ delegate :get?, :post?, :put?, :delete?, :to => :request
96
+
97
+ # Undefine :to_json since it's defined on Object
98
+ undef_method(:to_json) if method_defined?(:to_json)
99
+
100
+ # Initializes a new responder an invoke the proper format. If the format is
101
+ # not defined, call to_format.
102
+ #
103
+ def self.call(*args)
104
+ responder = new(*args)
105
+ method = :"to_#{responder.format}"
106
+ responder.respond_to?(method) ? responder.send(method) : responder.to_format
107
+ end
108
+
109
+ # HTML format does not render the resource, it always attempt to render a
110
+ # template.
111
+ #
112
+ def to_html
113
+ default_render
114
+ rescue ActionView::MissingTemplate
115
+ if get?
116
+ raise
117
+ elsif has_errors?
118
+ render :action => default_action
119
+ else
120
+ redirect_to resource_location
121
+ end
122
+ end
123
+
124
+ # All others formats follow the procedure below. First we try to render a
125
+ # template, if the template is not available, we verify if the resource
126
+ # responds to :to_format and display it.
127
+ #
128
+ def to_format
129
+ default_render
130
+ rescue ActionView::MissingTemplate
131
+ raise unless resourceful?
132
+
133
+ if get?
134
+ display resource
135
+ elsif has_errors?
136
+ display resource.errors, :status => :unprocessable_entity
137
+ elsif post?
138
+ display resource, :status => :created, :location => resource_location
139
+ else
140
+ head :ok
141
+ end
142
+ end
143
+
144
+ protected
145
+
146
+ # Checks whether the resource responds to the current format or not.
147
+ #
148
+ def resourceful?
149
+ resource.respond_to?(:"to_#{format}")
150
+ end
151
+
152
+ # Returns the resource location by retrieving it from the options or
153
+ # returning the resources array.
154
+ #
155
+ def resource_location
156
+ options[:location] || resources
157
+ end
158
+
159
+ # If a given response block was given, use it, otherwise call render on
160
+ # controller.
161
+ #
162
+ def default_render
163
+ @default_response.call
164
+ end
165
+
166
+ # display is just a shortcut to render a resource with the current format.
167
+ #
168
+ # display @user, :status => :ok
169
+ #
170
+ # For xml request is equivalent to:
171
+ #
172
+ # render :xml => @user, :status => :ok
173
+ #
174
+ # Options sent by the user are also used:
175
+ #
176
+ # respond_with(@user, :status => :created)
177
+ # display(@user, :status => :ok)
178
+ #
179
+ # Results in:
180
+ #
181
+ # render :xml => @user, :status => :created
182
+ #
183
+ def display(resource, given_options={})
184
+ controller.render given_options.merge!(options).merge!(format => resource)
185
+ end
186
+
187
+ # Check if the resource has errors or not.
188
+ #
189
+ def has_errors?
190
+ resource.respond_to?(:errors) && !resource.errors.empty?
191
+ end
192
+
193
+ # By default, render the :edit action for html requests with failure, unless
194
+ # the verb is post.
195
+ #
196
+ def default_action
197
+ request.post? ? :new : :edit
198
+ end
199
+ end
200
+ end