diff options
Diffstat (limited to 'actionpack/lib/action_controller')
16 files changed, 191 insertions, 80 deletions
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index abeb49d16f..9c77b0ccf4 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -5,48 +5,18 @@ module ActionController #:nodoc: # useful when certain elements of an action change frequently or # depend on complicated state while other parts rarely change or # can be shared amongst multiple parties. The caching is done using - # the <tt>cache</tt> helper available in the Action View. A - # template with fragment caching might look like: + # the <tt>cache</tt> helper available in the Action View. See + # ActionView::Helpers::CacheHelper for more information. # - # <b>Hello <%= @name %></b> + # While it's strongly recommended that you use key-based cache + # expiration (see links in CacheHelper for more information), + # it is also possible to manually expire caches. For example: # - # <% cache do %> - # All the topics in the system: - # <%= render :partial => "topic", :collection => Topic.all %> - # <% end %> - # - # This cache will bind the name of the action that called it, so if - # this code was part of the view for the topics/list action, you - # would be able to invalidate it using: - # - # expire_fragment(:controller => "topics", :action => "list") - # - # This default behavior is limited if you need to cache multiple - # fragments per action or if the action itself is cached using - # <tt>caches_action</tt>. To remedy this, there is an option to - # qualify the name of the cached fragment by using the - # <tt>:action_suffix</tt> option: - # - # <% cache(:action => "list", :action_suffix => "all_topics") do %> - # - # That would result in a name such as - # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the - # action cache and with any fragments that use a different suffix. - # Note that the URL doesn't have to really exist or be callable - # - the url_for system is just used to generate unique cache names - # that we can refer to when we need to expire the cache. - # - # The expiration call for this example is: - # - # expire_fragment(:controller => "topics", - # :action => "list", - # :action_suffix => "all_topics") + # expire_fragment("name_of_cache") module Fragments # Given a key (as described in <tt>expire_fragment</tt>), returns # a key suitable for use in reading, writing, or expiring a - # cached fragment. If the key is a hash, the generated key is the - # return value of url_for on that hash (without the protocol). - # All keys are prefixed with <tt>views/</tt> and uses + # cached fragment. All keys are prefixed with <tt>views/</tt> and uses # ActiveSupport::Cache.expand_cache_key for the expansion. def fragment_cache_key(key) ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views) diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 0fb419f941..a7c0e971e7 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/object/blank' module ActionController class LogSubscriber < ActiveSupport::LogSubscriber diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 92433ab462..b38f990efa 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/class/attribute' -require 'active_support/core_ext/object/blank' require 'action_dispatch/middleware/stack' module ActionController @@ -187,7 +185,7 @@ module ActionController end def performed? - !!response_body + response_body || (response && response.committed?) end def dispatch(name, request) #:nodoc: diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index 77d799a38a..e905a3cf1d 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -32,7 +32,7 @@ module ActionController # ==== Options # * <tt>host</tt> - Redirect to a different host name # * <tt>only</tt> - The callback should be run only for this action - # * <tt>except<tt> - The callback should be run for all actions except this action + # * <tt>except</tt> - The callback should be run for all actions except this action # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback # will be called only when it returns a true value. # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 2fcd933d32..747e1273be 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -29,19 +29,19 @@ module ActionController self.status = status self.location = url_for(location) if location - if include_content_headers?(self.status) + if include_content?(self.status) self.content_type = content_type || (Mime[formats.first] if formats) + self.response_body = " " else headers.delete('Content-Type') headers.delete('Content-Length') + self.response_body = "" end - - self.response_body = " " end private # :nodoc: - def include_content_headers?(status) + def include_content?(status) case status when 100..199 false diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 66cdfd40ff..d2cbbd3330 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/class/attribute' module ActionController # The \Rails framework provides a large number of helpers for working with assets, dates, forms, diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb index b55c4643be..420b22cf56 100644 --- a/actionpack/lib/action_controller/metal/hide_actions.rb +++ b/actionpack/lib/action_controller/metal/hide_actions.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/class/attribute' module ActionController # Adds the ability to prevent public methods on a controller to be called as actions. diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 0050ede806..03b8d8db1a 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -1,5 +1,4 @@ require 'base64' -require 'active_support/core_ext/object/blank' module ActionController # Makes it dead easy to do HTTP Basic, Digest and Token authentication. @@ -194,7 +193,7 @@ module ActionController return false unless password method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] - uri = credentials[:uri][0,1] == '/' ? request.original_fullpath : request.original_url + uri = credentials[:uri] [true, false].any? do |trailing_question_mark| [true, false].any? do |password_is_ha1| @@ -229,9 +228,9 @@ module ActionController end def decode_credentials(header) - Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair| + HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair| key, value = pair.split('=', 2) - [key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').delete('\'')] + [key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')] end] end @@ -372,7 +371,7 @@ module ActionController # def test_access_granted_from_xml # get( # "/notes/1.xml", nil, - # :authorization => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token) + # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token) # ) # # assert_equal 200, status diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb new file mode 100644 index 0000000000..32e5afa335 --- /dev/null +++ b/actionpack/lib/action_controller/metal/live.rb @@ -0,0 +1,141 @@ +require 'action_dispatch/http/response' +require 'delegate' + +module ActionController + # Mix this module in to your controller, and all actions in that controller + # will be able to stream data to the client as it's written. + # + # class MyController < ActionController::Base + # include ActionController::Live + # + # def stream + # response.headers['Content-Type'] = 'text/event-stream' + # 100.times { + # response.stream.write "hello world\n" + # sleep 1 + # } + # response.stream.close + # end + # end + # + # There are a few caveats with this use. You *cannot* write headers after the + # response has been committed (Response#committed? will return truthy). + # Calling +write+ or +close+ on the response stream will cause the response + # object to be committed. Make sure all headers are set before calling write + # or close on your stream. + # + # You *must* call close on your stream when you're finished, otherwise the + # socket may be left open forever. + # + # The final caveat is that your actions are executed in a separate thread than + # the main thread. Make sure your actions are thread safe, and this shouldn't + # be a problem (don't share state across threads, etc). + module Live + class Buffer < ActionDispatch::Response::Buffer #:nodoc: + def initialize(response) + super(response, SizedQueue.new(10)) + end + + def write(string) + unless @response.committed? + @response.headers["Cache-Control"] = "no-cache" + @response.headers.delete "Content-Length" + end + + super + end + + def each + while str = @buf.pop + yield str + end + end + + def close + super + @buf.push nil + end + end + + class Response < ActionDispatch::Response #:nodoc: all + class Header < DelegateClass(Hash) + def initialize(response, header) + @response = response + super(header) + end + + def []=(k,v) + if @response.committed? + raise ActionDispatch::IllegalStateError, 'header already sent' + end + + super + end + + def merge(other) + self.class.new @response, __getobj__.merge(other) + end + + def to_hash + __getobj__.dup + end + end + + def commit! + headers.freeze + super + end + + private + + def build_buffer(response, body) + buf = Live::Buffer.new response + body.each { |part| buf.write part } + buf + end + + def merge_default_headers(original, default) + Header.new self, super + end + end + + def process(name) + t1 = Thread.current + locals = t1.keys.map { |key| [key, t1[key]] } + + # This processes the action in a child thread. It lets us return the + # response code and headers back up the rack stack, and still process + # the body in parallel with sending data to the client + Thread.new { + t2 = Thread.current + t2.abort_on_exception = true + + # Since we're processing the view in a different thread, copy the + # thread locals from the main thread to the child thread. :'( + locals.each { |k,v| t2[k] = v } + + begin + super(name) + ensure + @_response.commit! + end + } + + @_response.await_commit + end + + def response_body=(body) + super + response.stream.close if response + end + + def set_response!(request) + if request.env["HTTP_VERSION"] == "HTTP/1.0" + super + else + @_response = Live::Response.new + @_response.request = request + end + end + end +end diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 4665fea91a..18ae2c3bfc 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -1,6 +1,4 @@ require 'abstract_controller/collector' -require 'active_support/core_ext/class/attribute' -require 'active_support/core_ext/object/inclusion' module ActionController #:nodoc: module MimeResponds @@ -343,9 +341,9 @@ module ActionController #:nodoc: config = self.class.mimes_for_respond_to[mime] if config[:except] - !action.in?(config[:except]) + !config[:except].include?(action) elsif config[:only] - action.in?(config[:only]) + config[:only].include?(action) else true end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index aa67fa7f23..2736948ce0 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/module/anonymous' diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 1927c8bdc7..78aeeef2bf 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/class/attribute' -require 'active_support/core_ext/object/blank' require 'set' module ActionController diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 53534c0307..d5f1cbc1a8 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/class/attribute' require 'action_controller/metal/exceptions' module ActionController #:nodoc: diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index e28c05cc2d..0cdd17bc2e 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -27,12 +27,18 @@ module ActionController :host => request.host, :port => request.optional_port, :protocol => request.protocol, - :_path_segments => request.symbolized_path_parameters + :_recall => request.symbolized_path_parameters ).freeze - if _routes.equal?(env["action_dispatch.routes"]) + if (same_origin = _routes.equal?(env["action_dispatch.routes"])) || + (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) || + (original_script_name = env['SCRIPT_NAME']) @_url_options.dup.tap do |options| - options[:script_name] = request.script_name.dup + if original_script_name + options[:original_script_name] = original_script_name + else + options[:script_name] = same_origin ? request.script_name.dup : script_name + end options.freeze end else diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 5f50bf5de6..bb693c6494 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,7 +1,5 @@ require 'rack/session/abstract/id' -require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/to_query' -require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/module/anonymous' module ActionController @@ -220,14 +218,7 @@ module ActionController class TestResponse < ActionDispatch::TestResponse def recycle! - @status = 200 - @header = {} - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - @body = [] - @charset = @content_type = nil - @request = @template = nil + initialize end end @@ -524,20 +515,36 @@ module ActionController end def setup_controller_request_and_response - @request = TestRequest.new - @response = TestResponse.new + @request = build_request + @response = build_response @response.request = @request + @controller = nil unless defined? @controller + if klass = self.class.controller_class - @controller ||= klass.new rescue nil + unless @controller + begin + @controller = klass.new + rescue + warn "could not construct controller #{klass}" if $VERBOSE + end + end end - if defined?(@controller) && @controller + if @controller @controller.request = @request @controller.params = {} end end + def build_request + TestRequest.new + end + + def build_response + TestResponse.new + end + included do include ActionController::TemplateAssertions include ActionDispatch::Assertions @@ -574,7 +581,7 @@ module ActionController :only_path => true, :action => action, :relative_url_root => nil, - :_path_segments => @request.symbolized_path_parameters) + :_recall => @request.symbolized_path_parameters) url, query_string = @routes.url_for(options).split("?", 2) diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index 6b269e7a31..6b4ececda2 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -1,6 +1,5 @@ require 'set' require 'cgi' -require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' module HTML |