From 2e053aec9bafa8735d70886f36dea06ea10dc4ce Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 22 Dec 2008 14:48:32 -0800 Subject: Don't construct object deprecation proxy if unneeded --- actionpack/lib/action_view/renderable_partial.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb index d92ff1b8d3..3ea836fa25 100644 --- a/actionpack/lib/action_view/renderable_partial.rb +++ b/actionpack/lib/action_view/renderable_partial.rb @@ -25,12 +25,11 @@ module ActionView end def render_partial(view, object = nil, local_assigns = {}, as = nil) - object ||= local_assigns[:object] || - local_assigns[variable_name] + object ||= local_assigns[:object] || local_assigns[variable_name] - if view.respond_to?(:controller) + if object.nil? && view.respond_to?(:controller) ivar = :"@#{variable_name}" - object ||= + object = if view.controller.instance_variable_defined?(ivar) ActiveSupport::Deprecation::DeprecatedObjectProxy.new( view.controller.instance_variable_get(ivar), -- cgit v1.2.3 From e898f82a743063652aed802d99ea8b5deac2ec3c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Dec 2008 03:51:04 +0000 Subject: Move request parsing related code to ActionController::RequestParser --- actionpack/lib/action_controller.rb | 4 + actionpack/lib/action_controller/request.rb | 423 +-------------------- actionpack/lib/action_controller/request_parser.rb | 314 +++++++++++++++ actionpack/lib/action_controller/test_process.rb | 31 +- actionpack/lib/action_controller/uploaded_file.rb | 37 ++ .../action_controller/url_encoded_pair_parser.rb | 95 +++++ 6 files changed, 478 insertions(+), 426 deletions(-) create mode 100644 actionpack/lib/action_controller/request_parser.rb create mode 100644 actionpack/lib/action_controller/uploaded_file.rb create mode 100644 actionpack/lib/action_controller/url_encoded_pair_parser.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 3bb755376f..98fb490d64 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -60,6 +60,10 @@ module ActionController autoload :MimeResponds, 'action_controller/mime_responds' autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' autoload :Request, 'action_controller/request' + autoload :RequestParser, 'action_controller/request_parser' + autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser' + autoload :UploadedStringIO, 'action_controller/uploaded_file' + autoload :UploadedTempfile, 'action_controller/uploaded_file' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Response, 'action_controller/response' autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection' diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index d9eb5af849..8a02130d88 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -101,7 +101,7 @@ module ActionController # For backward compatibility, the post \format is extracted from the # X-Post-Data-Format HTTP header if present. def content_type - Mime::Type.lookup(content_type_without_parameters) + Mime::Type.lookup(parser.content_type_without_parameters) end memoize :content_type @@ -389,11 +389,7 @@ EOM # Read the request \body. This is useful for web services that need to # work with raw requests directly. def raw_post - unless env.include? 'RAW_POST_DATA' - env['RAW_POST_DATA'] = body.read(content_length) - body.rewind if body.respond_to?(:rewind) - end - env['RAW_POST_DATA'] + parser.raw_post end # Returns both GET and POST \parameters in a single hash. @@ -421,15 +417,8 @@ EOM @path_parameters ||= {} end - # The request body is an IO input stream. If the RAW_POST_DATA environment - # variable is already set, wrap it in a StringIO. def body - if raw_post = env['RAW_POST_DATA'] - raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) - StringIO.new(raw_post) - else - body_stream - end + parser.body end def remote_addr @@ -442,11 +431,11 @@ EOM alias referer referrer def query_parameters - @query_parameters ||= self.class.parse_query_parameters(query_string) + @query_parameters ||= parser.query_parameters end def request_parameters - @request_parameters ||= parse_formatted_request_parameters + @request_parameters ||= parser.request_parameters end def body_stream #:nodoc: @@ -481,411 +470,13 @@ EOM @env['SERVER_PORT'].to_i end - protected - # The raw content type string. Use when you need parameters such as - # charset or boundary which aren't included in the content_type MIME type. - # Overridden by the X-POST_DATA_FORMAT header for backward compatibility. - def content_type_with_parameters - content_type_from_legacy_post_data_format_header || - env['CONTENT_TYPE'].to_s - end - - # The raw content type string with its parameters stripped off. - def content_type_without_parameters - self.class.extract_content_type_without_parameters(content_type_with_parameters) - end - memoize :content_type_without_parameters - private - def content_type_from_legacy_post_data_format_header - if x_post_format = @env['HTTP_X_POST_DATA_FORMAT'] - case x_post_format.to_s.downcase - when 'yaml'; 'application/x-yaml' - when 'xml'; 'application/xml' - end - end - end - - def parse_formatted_request_parameters - return {} if content_length.zero? - - content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters) - - # Don't parse params for unknown requests. - return {} if content_type.blank? - - mime_type = Mime::Type.lookup(content_type) - strategy = ActionController::Base.param_parsers[mime_type] - - # Only multipart form parsing expects a stream. - body = (strategy && strategy != :multipart_form) ? raw_post : self.body - - case strategy - when Proc - strategy.call(body) - when :url_encoded_form - self.class.clean_up_ajax_request_body! body - self.class.parse_query_parameters(body) - when :multipart_form - self.class.parse_multipart_form_parameters(body, boundary, content_length, env) - when :xml_simple, :xml_node - body.blank? ? {} : Hash.from_xml(body).with_indifferent_access - when :yaml - YAML.load(body) - when :json - if body.blank? - {} - else - data = ActiveSupport::JSON.decode(body) - data = {:_json => data} unless data.is_a?(Hash) - data.with_indifferent_access - end - else - {} - end - rescue Exception => e # YAML, XML or Ruby code block errors - raise - { "body" => body, - "content_type" => content_type_with_parameters, - "content_length" => content_length, - "exception" => "#{e.message} (#{e.class})", - "backtrace" => e.backtrace } - end - def named_host?(host) !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) end - class << self - def parse_query_parameters(query_string) - return {} if query_string.blank? - - pairs = query_string.split('&').collect do |chunk| - next if chunk.empty? - key, value = chunk.split('=', 2) - next if key.empty? - value = value.nil? ? nil : CGI.unescape(value) - [ CGI.unescape(key), value ] - end.compact - - UrlEncodedPairParser.new(pairs).result - end - - def parse_request_parameters(params) - parser = UrlEncodedPairParser.new - - params = params.dup - until params.empty? - for key, value in params - if key.blank? - params.delete key - elsif !key.include?('[') - # much faster to test for the most common case first (GET) - # and avoid the call to build_deep_hash - parser.result[key] = get_typed_value(value[0]) - params.delete key - elsif value.is_a?(Array) - parser.parse(key, get_typed_value(value.shift)) - params.delete key if value.empty? - else - raise TypeError, "Expected array, found #{value.inspect}" - end - end - end - - parser.result - end - - def parse_multipart_form_parameters(body, boundary, body_size, env) - parse_request_parameters(read_multipart(body, boundary, body_size, env)) - end - - def extract_multipart_boundary(content_type_with_parameters) - if content_type_with_parameters =~ MULTIPART_BOUNDARY - ['multipart/form-data', $1.dup] - else - extract_content_type_without_parameters(content_type_with_parameters) - end - end - - def extract_content_type_without_parameters(content_type_with_parameters) - $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/ - end - - def clean_up_ajax_request_body!(body) - body.chop! if body[-1] == 0 - body.gsub!(/&_=$/, '') - end - - - private - def get_typed_value(value) - case value - when String - value - when NilClass - '' - when Array - value.map { |v| get_typed_value(v) } - else - if value.respond_to? :original_filename - # Uploaded file - if value.original_filename - value - # Multipart param - else - result = value.read - value.rewind - result - end - # Unknown value, neither string nor multipart. - else - raise "Unknown form value: #{value.inspect}" - end - end - end - - MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n - - EOL = "\015\012" - - def read_multipart(body, boundary, body_size, env) - params = Hash.new([]) - boundary = "--" + boundary - quoted_boundary = Regexp.quote(boundary) - buf = "" - bufsize = 10 * 1024 - boundary_end="" - - # start multipart/form-data - body.binmode if defined? body.binmode - case body - when File - body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding) - when StringIO - body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding) - end - boundary_size = boundary.size + EOL.size - body_size -= boundary_size - status = body.read(boundary_size) - if nil == status - raise EOFError, "no content body" - elsif boundary + EOL != status - raise EOFError, "bad content body" - end - - loop do - head = nil - content = - if 10240 < body_size - UploadedTempfile.new("CGI") - else - UploadedStringIO.new - end - content.binmode if defined? content.binmode - - until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf) - - if (not head) and /#{EOL}#{EOL}/n.match(buf) - buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do - head = $1.dup - "" - end - next - end - - if head and ( (EOL + boundary + EOL).size < buf.size ) - content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] - buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" - end - - c = if bufsize < body_size - body.read(bufsize) - else - body.read(body_size) - end - if c.nil? || c.empty? - raise EOFError, "bad content body" - end - buf.concat(c) - body_size -= c.size - end - - buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do - content.print $1 - if "--" == $2 - body_size = -1 - end - boundary_end = $2.dup - "" - end - - content.rewind - - head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni - if filename = $1 || $2 - if /Mac/ni.match(env['HTTP_USER_AGENT']) and - /Mozilla/ni.match(env['HTTP_USER_AGENT']) and - (not /MSIE/ni.match(env['HTTP_USER_AGENT'])) - filename = CGI.unescape(filename) - end - content.original_path = filename.dup - end - - head =~ /Content-Type: ([^\r]*)/ni - content.content_type = $1.dup if $1 - - head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni - name = $1.dup if $1 - - if params.has_key?(name) - params[name].push(content) - else - params[name] = [content] - end - break if body_size == -1 - end - raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ - - begin - body.rewind if body.respond_to?(:rewind) - rescue Errno::ESPIPE - # Handles exceptions raised by input streams that cannot be rewound - # such as when using plain CGI under Apache - end - - params - end - end - end - - class UrlEncodedPairParser < StringScanner #:nodoc: - attr_reader :top, :parent, :result - - def initialize(pairs = []) - super('') - @result = {} - pairs.each { |key, value| parse(key, value) } - end - - KEY_REGEXP = %r{([^\[\]=&]+)} - BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]} - - # Parse the query string - def parse(key, value) - self.string = key - @top, @parent = result, nil - - # First scan the bare key - key = scan(KEY_REGEXP) or return - key = post_key_check(key) - - # Then scan as many nestings as present - until eos? - r = scan(BRACKETED_KEY_REGEXP) or return - key = self[1] - key = post_key_check(key) - end - - bind(key, value) - end - - private - # After we see a key, we must look ahead to determine our next action. Cases: - # - # [] follows the key. Then the value must be an array. - # = follows the key. (A value comes next) - # & or the end of string follows the key. Then the key is a flag. - # otherwise, a hash follows the key. - def post_key_check(key) - if scan(/\[\]/) # a[b][] indicates that b is an array - container(key, Array) - nil - elsif check(/\[[^\]]/) # a[b] indicates that a is a hash - container(key, Hash) - nil - else # End of key? We do nothing. - key - end - end - - # Add a container to the stack. - def container(key, klass) - type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass) - value = bind(key, klass.new) - type_conflict! klass, value unless value.is_a?(klass) - push(value) - end - - # Push a value onto the 'stack', which is actually only the top 2 items. - def push(value) - @parent, @top = @top, value + def parser + @parser ||= ActionController::RequestParser.new(@env) end - - # Bind a key (which may be nil for items in an array) to the provided value. - def bind(key, value) - if top.is_a? Array - if key - if top[-1].is_a?(Hash) && ! top[-1].key?(key) - top[-1][key] = value - else - top << {key => value}.with_indifferent_access - push top.last - value = top[key] - end - else - top << value - end - elsif top.is_a? Hash - key = CGI.unescape(key) - parent << (@top = {}) if top.key?(key) && parent.is_a?(Array) - top[key] ||= value - return top[key] - else - raise ArgumentError, "Don't know what to do: top is #{top.inspect}" - end - - return value - end - - def type_conflict!(klass, value) - raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)" - end - end - - module UploadedFile - def self.included(base) - base.class_eval do - attr_accessor :original_path, :content_type - alias_method :local_path, :path - end - end - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - # The Windows regexp is adapted from Perl's File::Basename. - def original_filename - unless defined? @original_filename - @original_filename = - unless original_path.blank? - if original_path =~ /^(?:.*[:\\\/])?(.*)/m - $1 - else - File.basename original_path - end - end - end - @original_filename - end - end - - class UploadedStringIO < StringIO - include UploadedFile - end - - class UploadedTempfile < Tempfile - include UploadedFile end end diff --git a/actionpack/lib/action_controller/request_parser.rb b/actionpack/lib/action_controller/request_parser.rb new file mode 100644 index 0000000000..82ee4c84c4 --- /dev/null +++ b/actionpack/lib/action_controller/request_parser.rb @@ -0,0 +1,314 @@ +module ActionController + class RequestParser + def initialize(env) + @env = env + end + + def request_parameters + @request_parameters ||= parse_formatted_request_parameters + end + + def query_parameters + @query_parameters ||= self.class.parse_query_parameters(query_string) + end + + # Returns the query string, accounting for server idiosyncrasies. + def query_string + @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '') + end + + # The request body is an IO input stream. If the RAW_POST_DATA environment + # variable is already set, wrap it in a StringIO. + def body + if raw_post = @env['RAW_POST_DATA'] + raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) + StringIO.new(raw_post) + else + @env['rack.input'] + end + end + + # The raw content type string with its parameters stripped off. + def content_type_without_parameters + self.class.extract_content_type_without_parameters(content_type_with_parameters) + end + + def raw_post + unless @env.include? 'RAW_POST_DATA' + @env['RAW_POST_DATA'] = body.read(content_length) + body.rewind if body.respond_to?(:rewind) + end + @env['RAW_POST_DATA'] + end + + private + + def parse_formatted_request_parameters + return {} if content_length.zero? + + content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters) + + # Don't parse params for unknown requests. + return {} if content_type.blank? + + mime_type = Mime::Type.lookup(content_type) + strategy = ActionController::Base.param_parsers[mime_type] + + # Only multipart form parsing expects a stream. + body = (strategy && strategy != :multipart_form) ? raw_post : self.body + + case strategy + when Proc + strategy.call(body) + when :url_encoded_form + self.class.clean_up_ajax_request_body! body + self.class.parse_query_parameters(body) + when :multipart_form + self.class.parse_multipart_form_parameters(body, boundary, content_length, @env) + when :xml_simple, :xml_node + body.blank? ? {} : Hash.from_xml(body).with_indifferent_access + when :yaml + YAML.load(body) + when :json + if body.blank? + {} + else + data = ActiveSupport::JSON.decode(body) + data = {:_json => data} unless data.is_a?(Hash) + data.with_indifferent_access + end + else + {} + end + rescue Exception => e # YAML, XML or Ruby code block errors + raise + { "body" => body, + "content_type" => content_type_with_parameters, + "content_length" => content_length, + "exception" => "#{e.message} (#{e.class})", + "backtrace" => e.backtrace } + end + + def content_length + @content_length ||= @env['CONTENT_LENGTH'].to_i + end + + # The raw content type string. Use when you need parameters such as + # charset or boundary which aren't included in the content_type MIME type. + # Overridden by the X-POST_DATA_FORMAT header for backward compatibility. + def content_type_with_parameters + content_type_from_legacy_post_data_format_header || @env['CONTENT_TYPE'].to_s + end + + def content_type_from_legacy_post_data_format_header + if x_post_format = @env['HTTP_X_POST_DATA_FORMAT'] + case x_post_format.to_s.downcase + when 'yaml'; 'application/x-yaml' + when 'xml'; 'application/xml' + end + end + end + + class << self + def parse_query_parameters(query_string) + return {} if query_string.blank? + + pairs = query_string.split('&').collect do |chunk| + next if chunk.empty? + key, value = chunk.split('=', 2) + next if key.empty? + value = value.nil? ? nil : CGI.unescape(value) + [ CGI.unescape(key), value ] + end.compact + + UrlEncodedPairParser.new(pairs).result + end + + def parse_request_parameters(params) + parser = UrlEncodedPairParser.new + + params = params.dup + until params.empty? + for key, value in params + if key.blank? + params.delete key + elsif !key.include?('[') + # much faster to test for the most common case first (GET) + # and avoid the call to build_deep_hash + parser.result[key] = get_typed_value(value[0]) + params.delete key + elsif value.is_a?(Array) + parser.parse(key, get_typed_value(value.shift)) + params.delete key if value.empty? + else + raise TypeError, "Expected array, found #{value.inspect}" + end + end + end + + parser.result + end + + def parse_multipart_form_parameters(body, boundary, body_size, env) + parse_request_parameters(read_multipart(body, boundary, body_size, env)) + end + + def extract_multipart_boundary(content_type_with_parameters) + if content_type_with_parameters =~ MULTIPART_BOUNDARY + ['multipart/form-data', $1.dup] + else + extract_content_type_without_parameters(content_type_with_parameters) + end + end + + def extract_content_type_without_parameters(content_type_with_parameters) + $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/ + end + + def clean_up_ajax_request_body!(body) + body.chop! if body[-1] == 0 + body.gsub!(/&_=$/, '') + end + + + private + def get_typed_value(value) + case value + when String + value + when NilClass + '' + when Array + value.map { |v| get_typed_value(v) } + else + if value.respond_to? :original_filename + # Uploaded file + if value.original_filename + value + # Multipart param + else + result = value.read + value.rewind + result + end + # Unknown value, neither string nor multipart. + else + raise "Unknown form value: #{value.inspect}" + end + end + end + + MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n + + EOL = "\015\012" + + def read_multipart(body, boundary, body_size, env) + params = Hash.new([]) + boundary = "--" + boundary + quoted_boundary = Regexp.quote(boundary) + buf = "" + bufsize = 10 * 1024 + boundary_end="" + + # start multipart/form-data + body.binmode if defined? body.binmode + case body + when File + body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding) + when StringIO + body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding) + end + boundary_size = boundary.size + EOL.size + body_size -= boundary_size + status = body.read(boundary_size) + if nil == status + raise EOFError, "no content body" + elsif boundary + EOL != status + raise EOFError, "bad content body" + end + + loop do + head = nil + content = + if 10240 < body_size + UploadedTempfile.new("CGI") + else + UploadedStringIO.new + end + content.binmode if defined? content.binmode + + until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf) + + if (not head) and /#{EOL}#{EOL}/n.match(buf) + buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do + head = $1.dup + "" + end + next + end + + if head and ( (EOL + boundary + EOL).size < buf.size ) + content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] + buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" + end + + c = if bufsize < body_size + body.read(bufsize) + else + body.read(body_size) + end + if c.nil? || c.empty? + raise EOFError, "bad content body" + end + buf.concat(c) + body_size -= c.size + end + + buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do + content.print $1 + if "--" == $2 + body_size = -1 + end + boundary_end = $2.dup + "" + end + + content.rewind + + head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni + if filename = $1 || $2 + if /Mac/ni.match(env['HTTP_USER_AGENT']) and + /Mozilla/ni.match(env['HTTP_USER_AGENT']) and + (not /MSIE/ni.match(env['HTTP_USER_AGENT'])) + filename = CGI.unescape(filename) + end + content.original_path = filename.dup + end + + head =~ /Content-Type: ([^\r]*)/ni + content.content_type = $1.dup if $1 + + head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni + name = $1.dup if $1 + + if params.has_key?(name) + params[name].push(content) + else + params[name] = [content] + end + break if body_size == -1 + end + raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ + + begin + body.rewind if body.respond_to?(:rewind) + rescue Errno::ESPIPE + # Handles exceptions raised by input streams that cannot be rewound + # such as when using plain CGI under Apache + end + + params + end + end # class << self + end +end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 211e22ff58..dddad1756a 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -29,18 +29,21 @@ module ActionController #:nodoc: class TestRequest < Request #:nodoc: attr_accessor :cookies, :session_options - attr_accessor :query_parameters, :request_parameters, :path, :session - attr_accessor :host, :user_agent + attr_accessor :query_parameters, :path, :session + attr_accessor :host def initialize - super(Rack::MockRequest.env_for('/')) + env = Rack::MockRequest.env_for("/") + + # TODO: Fix Request to assume env['SERVER_ADDR'] doesn't contain port number + env['SERVER_ADDR'] = env.delete("SERVER_NAME") + super(env) @query_parameters = {} - @request_parameters = {} @session = TestSession.new - initialize_containers initialize_default_values + initialize_containers end def reset_session @@ -55,7 +58,11 @@ module ActionController #:nodoc: # Either the RAW_POST_DATA environment variable or the URL-encoded request # parameters. def raw_post - env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) } + @env['RAW_POST_DATA'] ||= begin + data = url_encoded_request_parameters + data.force_encoding(Encoding::BINARY) if data.respond_to?(:force_encoding) + data + end end def port=(number) @@ -125,26 +132,30 @@ module ActionController #:nodoc: path_parameters[key.to_s] = value end end + raw_post # populate env['RAW_POST_DATA'] @parameters = nil # reset TestRequest#parameters to use the new path_parameters end def recycle! - self.request_parameters = {} self.query_parameters = {} self.path_parameters = {} unmemoize_all end + def user_agent=(user_agent) + @env['HTTP_USER_AGENT'] = user_agent + end + private def initialize_containers - @env, @cookies = {}, {} + @cookies = {} end def initialize_default_values @host = "test.host" @request_uri = "/" - @user_agent = "Rails Testing" - self.remote_addr = "0.0.0.0" + @env['HTTP_USER_AGENT'] = "Rails Testing" + @env['REMOTE_ADDR'] = "0.0.0.0" @env["SERVER_PORT"] = 80 @env['REQUEST_METHOD'] = "GET" end diff --git a/actionpack/lib/action_controller/uploaded_file.rb b/actionpack/lib/action_controller/uploaded_file.rb new file mode 100644 index 0000000000..ea4845c68f --- /dev/null +++ b/actionpack/lib/action_controller/uploaded_file.rb @@ -0,0 +1,37 @@ +module ActionController + module UploadedFile + def self.included(base) + base.class_eval do + attr_accessor :original_path, :content_type + alias_method :local_path, :path + end + end + + # Take the basename of the upload's original filename. + # This handles the full Windows paths given by Internet Explorer + # (and perhaps other broken user agents) without affecting + # those which give the lone filename. + # The Windows regexp is adapted from Perl's File::Basename. + def original_filename + unless defined? @original_filename + @original_filename = + unless original_path.blank? + if original_path =~ /^(?:.*[:\\\/])?(.*)/m + $1 + else + File.basename original_path + end + end + end + @original_filename + end + end + + class UploadedStringIO < StringIO + include UploadedFile + end + + class UploadedTempfile < Tempfile + include UploadedFile + end +end diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb new file mode 100644 index 0000000000..bea96c711d --- /dev/null +++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb @@ -0,0 +1,95 @@ +module ActionController + class UrlEncodedPairParser < StringScanner #:nodoc: + attr_reader :top, :parent, :result + + def initialize(pairs = []) + super('') + @result = {} + pairs.each { |key, value| parse(key, value) } + end + + KEY_REGEXP = %r{([^\[\]=&]+)} + BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]} + + # Parse the query string + def parse(key, value) + self.string = key + @top, @parent = result, nil + + # First scan the bare key + key = scan(KEY_REGEXP) or return + key = post_key_check(key) + + # Then scan as many nestings as present + until eos? + r = scan(BRACKETED_KEY_REGEXP) or return + key = self[1] + key = post_key_check(key) + end + + bind(key, value) + end + + private + # After we see a key, we must look ahead to determine our next action. Cases: + # + # [] follows the key. Then the value must be an array. + # = follows the key. (A value comes next) + # & or the end of string follows the key. Then the key is a flag. + # otherwise, a hash follows the key. + def post_key_check(key) + if scan(/\[\]/) # a[b][] indicates that b is an array + container(key, Array) + nil + elsif check(/\[[^\]]/) # a[b] indicates that a is a hash + container(key, Hash) + nil + else # End of key? We do nothing. + key + end + end + + # Add a container to the stack. + def container(key, klass) + type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass) + value = bind(key, klass.new) + type_conflict! klass, value unless value.is_a?(klass) + push(value) + end + + # Push a value onto the 'stack', which is actually only the top 2 items. + def push(value) + @parent, @top = @top, value + end + + # Bind a key (which may be nil for items in an array) to the provided value. + def bind(key, value) + if top.is_a? Array + if key + if top[-1].is_a?(Hash) && ! top[-1].key?(key) + top[-1][key] = value + else + top << {key => value}.with_indifferent_access + push top.last + value = top[key] + end + else + top << value + end + elsif top.is_a? Hash + key = CGI.unescape(key) + parent << (@top = {}) if top.key?(key) && parent.is_a?(Array) + top[key] ||= value + return top[key] + else + raise ArgumentError, "Don't know what to do: top is #{top.inspect}" + end + + return value + end + + def type_conflict!(klass, value) + raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)" + end + end +end \ No newline at end of file -- cgit v1.2.3 From 6e2a771661a47fb682108648244837f8616e350d Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Dec 2008 17:54:44 +0000 Subject: Undry ActionController::TestCase# for better documentation --- actionpack/lib/action_controller/test_process.rb | 37 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index dddad1756a..acfb10cdca 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -388,20 +388,33 @@ module ActionController #:nodoc: module TestProcess def self.included(base) - # execute the request simulating a specific HTTP method and set/volley the response - # TODO: this should be un-DRY'ed for the sake of API documentation. - %w( get post put delete head ).each do |method| - base.class_eval <<-EOV, __FILE__, __LINE__ - def #{method}(action, parameters = nil, session = nil, flash = nil) - @request.env['REQUEST_METHOD'] = "#{method.upcase}" if defined?(@request) - process(action, parameters, session, flash) - end - EOV + # Executes a request simulating GET HTTP method and set/volley the response + def get(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "GET") + end + + # Executes a request simulating POST HTTP method and set/volley the response + def post(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "POST") + end + + # Executes a request simulating PUT HTTP method and set/volley the response + def put(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "PUT") + end + + # Executes a request simulating DELETE HTTP method and set/volley the response + def delete(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "DELETE") + end + + # Executes a request simulating HEAD HTTP method and set/volley the response + def head(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "HEAD") end end - # execute the request and set/volley the response - def process(action, parameters = nil, session = nil, flash = nil) + def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') # Sanity check for required instance variables so we can give an # understandable error message. %w(@controller @request @response).each do |iv_name| @@ -414,7 +427,7 @@ module ActionController #:nodoc: @response.recycle! @html_document = nil - @request.env['REQUEST_METHOD'] ||= "GET" + @request.env['REQUEST_METHOD'] = http_method @request.action = action.to_s -- cgit v1.2.3 From dd0753458f2a16c876c52734f84a242f56746607 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Dec 2008 20:45:59 +0000 Subject: Move ActionController::Base#render arguments validation to a separate method --- actionpack/lib/action_controller/base.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4d4793c4e3..552075025f 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -859,16 +859,12 @@ module ActionController #:nodoc: def render(options = nil, extra_options = {}, &block) #:doc: raise DoubleRenderError, "Can only render or redirect once per action" if performed? + validate_render_arguments(options, extra_options) + if options.nil? return render(:file => default_template, :layout => true) - elsif !extra_options.is_a?(Hash) - raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}" - else - if options == :update - options = extra_options.merge({ :update => true }) - elsif !options.is_a?(Hash) - raise RenderError, "You called render with invalid options : #{options.inspect}" - end + elsif options == :update + options = extra_options.merge({ :update => true }) end layout = pick_layout(options) @@ -1186,6 +1182,16 @@ module ActionController #:nodoc: end end + def validate_render_arguments(options, extra_options) + if options && options != :update && !options.is_a?(Hash) + raise RenderError, "You called render with invalid options : #{options.inspect}" + end + + if !extra_options.is_a?(Hash) + raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}" + end + end + def initialize_template_class(response) response.template = ActionView::Base.new(self.class.view_paths, {}, self) response.template.helpers.send :include, self.class.master_helper_module -- cgit v1.2.3 From 061952392afd1dae1aa97a816e9a0c79df7c4514 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Dec 2008 21:27:56 +0000 Subject: Make ActionController#render(string) work as a shortcut for render :file => string. [#1435] Examples: # Instead of render(:file => '/Users/lifo/home.html.erb') render('/Users/lifo/home.html.erb') Note : Filename must begin with a forward slash ('/') --- actionpack/lib/action_controller/base.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 552075025f..9bf044b6c0 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -865,6 +865,13 @@ module ActionController #:nodoc: return render(:file => default_template, :layout => true) elsif options == :update options = extra_options.merge({ :update => true }) + elsif options.is_a?(String) + case options.index('/') + when 0 + extra_options[:file] = options + end + + options = extra_options end layout = pick_layout(options) @@ -1183,7 +1190,7 @@ module ActionController #:nodoc: end def validate_render_arguments(options, extra_options) - if options && options != :update && !options.is_a?(Hash) + if options && options != :update && !options.is_a?(String) && !options.is_a?(Hash) raise RenderError, "You called render with invalid options : #{options.inspect}" end -- cgit v1.2.3 From d67e03871eabb912434dafac3eeb8e6ea7c5585f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Dec 2008 22:11:06 +0000 Subject: Make ActionController#render(string) work as a shortcut for render :template => string. [#1435] Examples: # Instead of render(:template => 'controller/action') render('controller/action') Note : Argument must not begin with a '/', but have at least one '/' --- actionpack/lib/action_controller/base.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 9bf044b6c0..29f1c84f03 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -866,9 +866,11 @@ module ActionController #:nodoc: elsif options == :update options = extra_options.merge({ :update => true }) elsif options.is_a?(String) - case options.index('/') + case position = options.index('/') when 0 extra_options[:file] = options + else + extra_options[:template] = options end options = extra_options -- cgit v1.2.3 From cd1d6e8768ae13b11bc343701037b20ad35e6f1e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 25 Dec 2008 23:01:17 +0000 Subject: Make ActionController#render(string) work as a shortcut for render :action => string. [#1435] Examples: # Instead of render(:action => 'other_action') render('other_action') Note : Argument must not have any '/' --- actionpack/lib/action_controller/base.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 29f1c84f03..e9c96b0ba4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -866,9 +866,11 @@ module ActionController #:nodoc: elsif options == :update options = extra_options.merge({ :update => true }) elsif options.is_a?(String) - case position = options.index('/') + case options.index('/') when 0 extra_options[:file] = options + when nil + extra_options[:action] = options else extra_options[:template] = options end -- cgit v1.2.3 From 80307c8b0a889acc7abb7f4e52fd4c02e1063ba8 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 26 Dec 2008 01:03:18 +0000 Subject: Make ActionController#render(symbol) behave same as ActionController#render(string) [#1435] --- actionpack/lib/action_controller/base.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e9c96b0ba4..cb654534af 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -859,14 +859,14 @@ module ActionController #:nodoc: def render(options = nil, extra_options = {}, &block) #:doc: raise DoubleRenderError, "Can only render or redirect once per action" if performed? - validate_render_arguments(options, extra_options) + validate_render_arguments(options, extra_options, block_given?) if options.nil? return render(:file => default_template, :layout => true) elsif options == :update options = extra_options.merge({ :update => true }) - elsif options.is_a?(String) - case options.index('/') + elsif options.is_a?(String) || options.is_a?(Symbol) + case options.to_s.index('/') when 0 extra_options[:file] = options when nil @@ -1193,8 +1193,8 @@ module ActionController #:nodoc: end end - def validate_render_arguments(options, extra_options) - if options && options != :update && !options.is_a?(String) && !options.is_a?(Hash) + def validate_render_arguments(options, extra_options, has_block) + if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol) raise RenderError, "You called render with invalid options : #{options.inspect}" end -- cgit v1.2.3 From 07298fd0929ae1c6dd6d1b41bf320112d6bfc6a0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 26 Dec 2008 01:49:14 +0000 Subject: Don't recurse when ActionController#render is called without any arguments --- actionpack/lib/action_controller/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index cb654534af..5b83494eb4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -862,7 +862,7 @@ module ActionController #:nodoc: validate_render_arguments(options, extra_options, block_given?) if options.nil? - return render(:file => default_template, :layout => true) + options = { :template => default_template.filename, :layout => true } elsif options == :update options = extra_options.merge({ :update => true }) elsif options.is_a?(String) || options.is_a?(Symbol) -- cgit v1.2.3 From dce0da77e7ef602f7420f43c0d1aba5a99a00bdb Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Thu, 25 Dec 2008 11:11:00 +0000 Subject: Fix assert_select_rjs not checking id for inserts [#540 state:resolved] --- actionpack/lib/action_controller/assertions/selector_assertions.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb index 248ca85994..7f8fe9ab19 100644 --- a/actionpack/lib/action_controller/assertions/selector_assertions.rb +++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb @@ -402,6 +402,7 @@ module ActionController if rjs_type if rjs_type == :insert position = args.shift + id = args.shift insertion = "insert_#{position}".to_sym raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion] statement = "(#{RJS_STATEMENTS[insertion]})" -- cgit v1.2.3 From 6dc12881110d26bb952bd0f565623144f10a07b6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 26 Dec 2008 13:37:42 -0800 Subject: Remove method missing use in respond_to --- actionpack/lib/action_controller/mime_responds.rb | 27 +++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb index 29294476f7..76fcae5f51 100644 --- a/actionpack/lib/action_controller/mime_responds.rb +++ b/actionpack/lib/action_controller/mime_responds.rb @@ -143,12 +143,31 @@ module ActionController #:nodoc: custom(@mime_type_priority.first, &block) end end + + def self.generate_method_for_mime(mime) + sym = mime.is_a?(Symbol) ? mime : mime.to_sym + const = sym.to_s.upcase + class_eval <<-RUBY + def #{sym}(&block) # def html(&block) + if Mime::SET.include?(Mime::#{const}) # if Mime::Set.include?(Mime::HTML) + custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) + else # else + super # super + end # end + end # end + RUBY + end - def method_missing(symbol, &block) - mime_constant = symbol.to_s.upcase + Mime::SET.each do |mime| + generate_method_for_mime(mime) + end - if Mime::SET.include?(Mime.const_get(mime_constant)) - custom(Mime.const_get(mime_constant), &block) + def method_missing(symbol, &block) + mime_constant = Mime.const_get(symbol.to_s.upcase) + + if Mime::SET.include?(mime_constant) + self.class.generate_method_for_mime(mime_constant) + send(symbol, &block) else super end -- cgit v1.2.3 From 4f043a48381c142e308824e3b7e15435a61bbb53 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 27 Dec 2008 00:06:57 -0800 Subject: More optimizations on respond_to after a profile and benching: App with simple respond_to: def index respond_to do |format| format.html format.xml format.json end end On JRuby (after complete hotspot warmup) -- 8% improvement: 550 requests per second after this commit 510 requests per second with old method_missing technique On MRI (8% improvement): 430 requests per second after this commit 400 requests per second with old method_missing technique --- actionpack/lib/action_controller/mime_responds.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb index 76fcae5f51..55cb212a10 100644 --- a/actionpack/lib/action_controller/mime_responds.rb +++ b/actionpack/lib/action_controller/mime_responds.rb @@ -147,13 +147,9 @@ module ActionController #:nodoc: def self.generate_method_for_mime(mime) sym = mime.is_a?(Symbol) ? mime : mime.to_sym const = sym.to_s.upcase - class_eval <<-RUBY + class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{sym}(&block) # def html(&block) - if Mime::SET.include?(Mime::#{const}) # if Mime::Set.include?(Mime::HTML) - custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) - else # else - super # super - end # end + custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) end # end RUBY end -- cgit v1.2.3 From 5138f755ff31a8f317d649a6f256c74bc371db70 Mon Sep 17 00:00:00 2001 From: Mark Reginald James Date: Sun, 28 Dec 2008 01:15:48 +0000 Subject: Fixed incorrect parsing of query parameters with mixed-depth nesting inside an array [#1622 state:resolved] Signed-off-by: Frederick Cheung --- actionpack/lib/action_controller/url_encoded_pair_parser.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb index bea96c711d..9883ad0d85 100644 --- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb +++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb @@ -70,11 +70,12 @@ module ActionController top[-1][key] = value else top << {key => value}.with_indifferent_access - push top.last - value = top[key] end + push top.last + return top[key] else top << value + return value end elsif top.is_a? Hash key = CGI.unescape(key) @@ -84,12 +85,10 @@ module ActionController else raise ArgumentError, "Don't know what to do: top is #{top.inspect}" end - - return value end def type_conflict!(klass, value) raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)" end end -end \ No newline at end of file +end -- cgit v1.2.3 From fec0ea9d6d4ca56a09e3e83002c38d69c8ad924e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 28 Dec 2008 17:05:12 +0000 Subject: Request#env['SERVER_NAME'] does not contain port number --- actionpack/lib/action_controller/request.rb | 2 +- actionpack/lib/action_controller/test_process.rb | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 8a02130d88..3390324162 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -286,7 +286,7 @@ EOM if forwarded = env["HTTP_X_FORWARDED_HOST"] forwarded.split(/,\s?/).last else - env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" + env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}" end end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index acfb10cdca..285a8b09e4 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -33,11 +33,7 @@ module ActionController #:nodoc: attr_accessor :host def initialize - env = Rack::MockRequest.env_for("/") - - # TODO: Fix Request to assume env['SERVER_ADDR'] doesn't contain port number - env['SERVER_ADDR'] = env.delete("SERVER_NAME") - super(env) + super(Rack::MockRequest.env_for("/")) @query_parameters = {} @session = TestSession.new -- cgit v1.2.3 From a2270ef2594b97891994848138614657363f2806 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 28 Dec 2008 19:48:05 +0000 Subject: Inline code comments for class_eval/module_eval [#1657 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_controller/helpers.rb | 6 +-- actionpack/lib/action_controller/mime_responds.rb | 6 +-- .../lib/action_controller/polymorphic_routes.rb | 18 ++++--- .../lib/action_controller/routing/route_set.rb | 61 +++++++++++----------- actionpack/lib/action_view/helpers/form_helper.rb | 10 ++-- 5 files changed, 55 insertions(+), 46 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/helpers.rb index 402750c57d..ba65032f6a 100644 --- a/actionpack/lib/action_controller/helpers.rb +++ b/actionpack/lib/action_controller/helpers.rb @@ -163,9 +163,9 @@ module ActionController #:nodoc: def helper_method(*methods) methods.flatten.each do |method| master_helper_module.module_eval <<-end_eval - def #{method}(*args, &block) - controller.send(%(#{method}), *args, &block) - end + def #{method}(*args, &block) # def current_user(*args, &block) + controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block) + end # end end_eval end end diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb index 55cb212a10..b755363873 100644 --- a/actionpack/lib/action_controller/mime_responds.rb +++ b/actionpack/lib/action_controller/mime_responds.rb @@ -148,9 +148,9 @@ module ActionController #:nodoc: sym = mime.is_a?(Symbol) ? mime : mime.to_sym const = sym.to_s.upcase class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{sym}(&block) # def html(&block) - custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) - end # end + def #{sym}(&block) # def html(&block) + custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) + end # end RUBY end diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index dce50c6c3b..924d1aa6bd 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -118,13 +118,17 @@ module ActionController %w(edit new).each do |action| module_eval <<-EOT, __FILE__, __LINE__ - def #{action}_polymorphic_url(record_or_hash, options = {}) - polymorphic_url(record_or_hash, options.merge(:action => "#{action}")) - end - - def #{action}_polymorphic_path(record_or_hash, options = {}) - polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path)) - end + def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {}) + polymorphic_url( # polymorphic_url( + record_or_hash, # record_or_hash, + options.merge(:action => "#{action}")) # options.merge(:action => "edit")) + end # end + # + def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {}) + polymorphic_url( # polymorphic_url( + record_or_hash, # record_or_hash, + options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path)) + end # end EOT end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 13646aef61..5975977365 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -145,10 +145,10 @@ module ActionController def define_hash_access(route, name, kind, options) selector = hash_access_name(name, kind) named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks - def #{selector}(options = nil) - options ? #{options.inspect}.merge(options) : #{options.inspect} - end - protected :#{selector} + def #{selector}(options = nil) # def hash_for_users_url(options = nil) + options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false} + end # end + protected :#{selector} # protected :hash_for_users_url end_eval helpers << selector end @@ -173,32 +173,33 @@ module ActionController # foo_url(bar, baz, bang, :sort_by => 'baz') # named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks - def #{selector}(*args) - - #{generate_optimisation_block(route, kind)} - - opts = if args.empty? || Hash === args.first - args.first || {} - else - options = args.extract_options! - args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| - h[k] = v - h - end - options.merge(args) - end - - url_for(#{hash_access_method}(opts)) - - end - #Add an alias to support the now deprecated formatted_* URL. - def formatted_#{selector}(*args) - ActiveSupport::Deprecation.warn( - "formatted_#{selector}() has been deprecated. please pass format to the standard" + - "#{selector}() method instead.", caller) - #{selector}(*args) - end - protected :#{selector} + def #{selector}(*args) # def users_url(*args) + # + #{generate_optimisation_block(route, kind)} # #{generate_optimisation_block(route, kind)} + # + opts = if args.empty? || Hash === args.first # opts = if args.empty? || Hash === args.first + args.first || {} # args.first || {} + else # else + options = args.extract_options! # options = args.extract_options! + args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| # args = args.zip([]).inject({}) do |h, (v, k)| + h[k] = v # h[k] = v + h # h + end # end + options.merge(args) # options.merge(args) + end # end + # + url_for(#{hash_access_method}(opts)) # url_for(hash_for_users_url(opts)) + # + end # end + #Add an alias to support the now deprecated formatted_* URL. # #Add an alias to support the now deprecated formatted_* URL. + def formatted_#{selector}(*args) # def formatted_users_url(*args) + ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn( + "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " + + "please pass format to the standard" + # "please pass format to the standard" + + "#{selector}() method instead.", caller) # "users_url() method instead.", caller) + #{selector}(*args) # users_url(*args) + end # end + protected :#{selector} # protected :users_url end_eval helpers << selector end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 621e2946b5..a85751c657 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -737,9 +737,13 @@ module ActionView (field_helpers - %w(label check_box radio_button fields_for)).each do |selector| src = <<-end_src - def #{selector}(method, options = {}) - @template.send(#{selector.inspect}, @object_name, method, objectify_options(options)) - end + def #{selector}(method, options = {}) # def text_field(method, options = {}) + @template.send( # @template.send( + #{selector.inspect}, # "text_field", + @object_name, # @object_name, + method, # method, + objectify_options(options)) # objectify_options(options)) + end # end end_src class_eval src, __FILE__, __LINE__ end -- cgit v1.2.3 From 45dee3842d68359a189fe7c0729359bd5a905ea4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 28 Dec 2008 15:13:16 -0600 Subject: HTTP Digest authentication [#1230 state:resolved] --- .../lib/action_controller/http_authentication.rb | 191 ++++++++++++++++++++- actionpack/lib/action_controller/integration.rb | 82 +++++++++ 2 files changed, 271 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 2ed810db7d..3cb5829eca 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -55,7 +55,31 @@ module ActionController # end # end # - # + # Simple Digest example. Note the block must return the user's password so the framework + # can appropriately hash it to check the user's credentials. Returning nil will cause authentication to fail. + # + # class PostsController < ApplicationController + # Users = {"dhh" => "secret"} + # + # before_filter :authenticate, :except => [ :index ] + # + # def index + # render :text => "Everyone can see me!" + # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_digest(realm) do |user_name| + # Users[user_name] + # end + # end + # end + # + # # In your integration tests, you can do something like this: # # def test_access_granted_from_xml @@ -108,7 +132,10 @@ module ActionController end def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') + # Properly decode credentials spanning a new-line + auth = authorization(request) + auth.slice!('Basic ') + ActiveSupport::Base64.decode64(auth || '') end def encode_credentials(user_name, password) @@ -120,5 +147,165 @@ module ActionController controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized end end + + module Digest + extend self + + module ControllerMethods + def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) + begin + authenticate_with_http_digest!(realm, &password_procedure) + rescue ActionController::HttpAuthentication::Error => e + msg = e.message + msg = "#{msg} expected '#{e.expected}' was '#{e.was}'" unless e.expected.nil? + raise msg if e.fatal? + request_http_digest_authentication(realm, msg) + end + end + + # Authenticate using HTTP Digest, throwing ActionController::HttpAuthentication::Error on failure. + # This allows more detailed analysis of authentication failures + # to be relayed to the client. + def authenticate_with_http_digest!(realm = "Application", &login_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) + end + + # Authenticate with HTTP Digest, returns true or false + def authenticate_with_http_digest(realm = "Application", &login_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) rescue false + end + + # Render output including the HTTP Digest authentication header + def request_http_digest_authentication(realm = "Application", message = nil) + HttpAuthentication::Digest.authentication_request(self, realm, message) + end + + # Add HTTP Digest authentication header to result headers + def http_digest_authentication_header(realm = "Application") + HttpAuthentication::Digest.authentication_header(self, realm) + end + end + + # Raises error unless authentictaion succeeds, returns true otherwise + def authenticate(controller, realm, &password_procedure) + raise Error.new(false), "No authorization header found" unless authorization(controller.request) + validate_digest_response(controller, realm, &password_procedure) + true + end + + def authorization(request) + request.env['HTTP_AUTHORIZATION'] || + request.env['X-HTTP_AUTHORIZATION'] || + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] + end + + # Raises error unless the request credentials response value matches the expected value. + def validate_digest_response(controller, realm, &password_procedure) + credentials = decode_credentials(controller.request) + + # Check the nonce, opaque and realm. + # Ignore nc, as we have no way to validate the number of times this nonce has been used + validate_nonce(controller.request, credentials[:nonce]) + raise Error.new(false, realm, credentials[:realm]), "Realm doesn't match" unless realm == credentials[:realm] + raise Error.new(true, opaque(controller.request), credentials[:opaque]),"Opaque doesn't match" unless opaque(controller.request) == credentials[:opaque] + + password = password_procedure.call(credentials[:username]) + raise Error.new(false), "No password" if password.nil? + expected = expected_response(controller.request.env['REQUEST_METHOD'], controller.request.url, credentials, password) + raise Error.new(false, expected, credentials[:response]), "Invalid response" unless expected == credentials[:response] + end + + # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ + def expected_response(http_method, uri, credentials, password) + ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase,uri].join(':')) + ::Digest::MD5.hexdigest([ha1,credentials[:nonce], credentials[:nc], credentials[:cnonce],credentials[:qop],ha2].join(':')) + end + + def encode_credentials(http_method, credentials, password) + credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password) + "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') + end + + def decode_credentials(request) + authorization(request).to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| + key, value = pair.split('=', 2) + hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') + hash + end + end + + def authentication_header(controller, realm) + controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(controller.request)}", opaque="#{opaque(controller.request)}") + end + + def authentication_request(controller, realm, message = "HTTP Digest: Access denied") + authentication_header(controller, realm) + controller.send! :render, :text => message, :status => :unauthorized + end + + # Uses an MD5 digest based on time to generate a value to be used only once. + # + # A server-specified data string which should be uniquely generated each time a 401 response is made. + # It is recommended that this string be base64 or hexadecimal data. + # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. + # + # The contents of the nonce are implementation dependent. + # The quality of the implementation depends on a good choice. + # A nonce might, for example, be constructed as the base 64 encoding of + # + # => time-stamp H(time-stamp ":" ETag ":" private-key) + # + # where time-stamp is a server-generated time or other non-repeating value, + # ETag is the value of the HTTP ETag header associated with the requested entity, + # and private-key is data known only to the server. + # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and + # reject the request if it did not match the nonce from that header or + # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. + # The inclusion of the ETag prevents a replay request for an updated version of the resource. + # (Note: including the IP address of the client in the nonce would appear to offer the server the ability + # to limit the reuse of the nonce to the same client that originally got it. + # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. + # Also, IP address spoofing is not that hard.) + # + # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to + # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for + # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 + # of this document. + # + # The nonce is opaque to the client. + def nonce(request, time = Time.now) + session_id = request.is_a?(String) ? request : request.session.session_id + t = time.to_i + hashed = [t, session_id] + digest = ::Digest::MD5.hexdigest(hashed.join(":")) + Base64.encode64("#{t}:#{digest}").gsub("\n", '') + end + + def validate_nonce(request, value) + t = Base64.decode64(value).split(":").first.to_i + raise Error.new(true), "Stale Nonce" if (t - Time.now.to_i).abs > 10 * 60 + n = nonce(request, t) + raise Error.new(true, value, n), "Bad Nonce" unless n == value + end + + # Opaque based on digest of session_id + def opaque(request) + session_id = request.is_a?(String) ? request : request.session.session_id + @opaque ||= Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '') + end + end + + class Error < RuntimeError + attr_accessor :expected, :was + def initialize(fatal = false, expected = nil, was = nil) + @fatal = fatal + @expected = expected + @was = was + end + + def fatal?; @fatal; end + end end end diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 71e2524e81..a8e54c2fc7 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -68,6 +68,15 @@ module ActionController # A running counter of the number of requests processed. attr_accessor :request_count + # Nonce value for Digest Authentication, implicitly set on response with WWW-Authentication + attr_accessor :nonce + + # Opaque value for Digest Authentication, implicitly set on response with WWW-Authentication + attr_accessor :opaque + + # Opaque value for Authentication, implicitly set on response with WWW-Authentication + attr_accessor :realm + class MultiPartNeededException < Exception end @@ -243,6 +252,53 @@ module ActionController end alias xhr :xml_http_request + def request_with_noauth(http_method, uri, parameters, headers) + process_with_auth http_method, uri, parameters, headers + end + + # Performs a request with the given http_method and parameters, including HTTP Basic authorization headers. + # See get() for more details on paramters and headers. + # + # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_basic, #post_with_basic, + # #put_with_basic, #delete_with_basic, and #head_with_basic. + def request_with_basic(http_method, uri, parameters, headers, user_name, password) + process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Basic.encode_credentials(user_name, password)) + end + + # Performs a request with the given http_method and parameters, including HTTP Digest authorization headers. + # See get() for more details on paramters and headers. + # + # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_digest, #post_with_digest, + # #put_with_digest, #delete_with_digest, and #head_with_digest. + def request_with_digest(http_method, uri, parameters, headers, user_name, password) + # Realm, Nonce, and Opaque taken from previoius 401 response + + credentials = { + :username => user_name, + :realm => @realm, + :nonce => @nonce, + :qop => "auth", + :nc => "00000001", + :cnonce => "0a4f113b", + :opaque => @opaque, + :uri => uri + } + + raise "Digest request without previous 401 response" if @opaque.nil? + + process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Digest.encode_credentials(http_method, credentials, password)) + end + + # def get_with_basic, def post_with_basic, def put_with_basic, def delete_with_basic, def head_with_basic + # def get_with_digest, def post_with_digest, def put_with_digest, def delete_with_digest, def head_with_digest + [:get, :post, :put, :delete, :head].each do |method| + [:noauth, :basic, :digest].each do |auth_type| + define_method("#{method}_with_#{auth_type}") do |uri, parameters, headers, *auth| + send("request_with_#{auth_type}", method, uri, parameters, headers, *auth) + end + end + end + # Returns the URL for the given options, according to the rules specified # in the application's routes. def url_for(options) @@ -364,6 +420,32 @@ module ActionController return status end + # Same as process, but handles authentication returns to perform + # Basic or Digest authentication + def process_with_auth(method, path, parameters = nil, headers = nil) + status = process(method, path, parameters, headers) + + if status == 401 + # Extract authentication information from response + auth_data = @response.headers['WWW-Authenticate'] + if /^Basic /.match(auth_data) + # extract realm, to be used in subsequent request + @realm = auth_header.split(' ')[1] + elsif /^Digest/.match(auth_data) + creds = auth_data.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| + key, value = pair.split('=', 2) + hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') + hash + end + @realm = creds[:realm] + @nonce = creds[:nonce] + @opaque = creds[:opaque] + end + end + + return status + end + # Encode the cookies hash in a format suitable for passing to a # request. def encode_cookies -- cgit v1.2.3 From 5d89605c11cc54acadfdd76ccd226d38989ec600 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 28 Dec 2008 15:31:03 -0600 Subject: Make router and controller classes better rack citizens --- actionpack/lib/action_controller/base.rb | 7 +++++++ actionpack/lib/action_controller/dispatcher.rb | 11 ++++------- actionpack/lib/action_controller/request.rb | 4 ++-- actionpack/lib/action_controller/rescue.rb | 4 +++- actionpack/lib/action_controller/routing/route_set.rb | 6 ++++++ 5 files changed, 22 insertions(+), 10 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 5b83494eb4..da3d1f46ee 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -382,6 +382,13 @@ module ActionController #:nodoc: attr_accessor :action_name class << self + def call(env) + # HACK: For global rescue to have access to the original request and response + request = env["actioncontroller.rescue.request"] ||= Request.new(env) + response = env["actioncontroller.rescue.response"] ||= Response.new + process(request, response) + end + # Factory for the standard create, process loop where the controller is discarded after processing. def process(request, response) #:nodoc: new.process(request, response) diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 4dc76e1b49..c4e7357b81 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -60,11 +60,10 @@ module ActionController def dispatch begin run_callbacks :before_dispatch - controller = Routing::Routes.recognize(@request) - controller.process(@request, @response).to_a + Routing::Routes.call(@env) rescue Exception => exception if controller ||= (::ApplicationController rescue Base) - controller.process_with_exception(@request, @response, exception).to_a + controller.call_with_exception(@env, exception).to_a else raise exception end @@ -83,8 +82,7 @@ module ActionController end def _call(env) - @request = Request.new(env) - @response = Response.new + @env = env dispatch end @@ -110,8 +108,7 @@ module ActionController def checkin_connections # Don't return connection (and peform implicit rollback) if this request is a part of integration test - # TODO: This callback should have direct access to env - return if @request.key?("rack.test") + return if @env.key?("rack.test") ActiveRecord::Base.clear_active_connections! end end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 3390324162..ba27c0d294 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -398,7 +398,7 @@ EOM end def path_parameters=(parameters) #:nodoc: - @path_parameters = parameters + @env["routing_args"] = parameters @symbolized_path_parameters = @parameters = nil end @@ -414,7 +414,7 @@ EOM # # See symbolized_path_parameters for symbolized keys. def path_parameters - @path_parameters ||= {} + @env["routing_args"] ||= {} end def body diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 5ef79a36ce..3a5e5071bb 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -59,7 +59,9 @@ module ActionController #:nodoc: end module ClassMethods - def process_with_exception(request, response, exception) #:nodoc: + def call_with_exception(env, exception) #:nodoc: + request = env["actioncontroller.rescue.request"] + response = env["actioncontroller.rescue.response"] new.process(request, response, :rescue_action, exception) end end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 5975977365..06aef6e169 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -427,6 +427,12 @@ module ActionController end end + def call(env) + request = Request.new(env) + app = Routing::Routes.recognize(request) + app.call(env).to_a + end + def recognize(request) params = recognize_path(request.path, extract_request_environment(request)) request.path_parameters = params.with_indifferent_access -- cgit v1.2.3 From c20c72e3d9321f8c00587aab479d962e80b02c35 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 28 Dec 2008 15:34:59 -0600 Subject: Use rack namespace for routing args --- actionpack/lib/action_controller/request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index ba27c0d294..822955d1db 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -398,7 +398,7 @@ EOM end def path_parameters=(parameters) #:nodoc: - @env["routing_args"] = parameters + @env["rack.routing_args"] = parameters @symbolized_path_parameters = @parameters = nil end @@ -414,7 +414,7 @@ EOM # # See symbolized_path_parameters for symbolized keys. def path_parameters - @env["routing_args"] ||= {} + @env["rack.routing_args"] ||= {} end def body -- cgit v1.2.3 From 558ab327b733717f4a8de3ed62b8dcd62e9ff9c3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 29 Dec 2008 19:27:19 -0600 Subject: Clean up view path cruft and split path implementations into Template::Path and Template::EagerPath --- actionpack/lib/action_controller/base.rb | 12 +-- actionpack/lib/action_controller/rescue.rb | 4 +- actionpack/lib/action_view/base.rb | 2 +- actionpack/lib/action_view/inline_template.rb | 2 +- actionpack/lib/action_view/paths.rb | 107 +------------------------- actionpack/lib/action_view/renderable.rb | 9 +-- actionpack/lib/action_view/template.rb | 85 +++++++++++++++++++- 7 files changed, 95 insertions(+), 126 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index da3d1f46ee..bc18dfaec8 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -869,7 +869,7 @@ module ActionController #:nodoc: validate_render_arguments(options, extra_options, block_given?) if options.nil? - options = { :template => default_template.filename, :layout => true } + options = { :template => default_template, :layout => true } elsif options == :update options = extra_options.merge({ :update => true }) elsif options.is_a?(String) || options.is_a?(Symbol) @@ -1125,7 +1125,7 @@ module ActionController #:nodoc: end # Sets the etag, last_modified, or both on the response and renders a - # "304 Not Modified" response if the request is already fresh. + # "304 Not Modified" response if the request is already fresh. # # Example: # @@ -1133,8 +1133,8 @@ module ActionController #:nodoc: # @article = Article.find(params[:id]) # fresh_when(:etag => @article, :last_modified => @article.created_at.utc) # end - # - # This will render the show template if the request isn't sending a matching etag or + # + # This will render the show template if the request isn't sending a matching etag or # If-Modified-Since header and just a "304 Not Modified" response if there's a match. def fresh_when(options) options.assert_valid_keys(:etag, :last_modified) @@ -1239,7 +1239,7 @@ module ActionController #:nodoc: log_processing_for_parameters end end - + def log_processing_for_request_id request_id = "\n\nProcessing #{self.class.name}\##{action_name} " request_id << "to #{params[:format]} " if params[:format] @@ -1251,7 +1251,7 @@ module ActionController #:nodoc: def log_processing_for_parameters parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup parameters = parameters.except!(:controller, :action, :format, :_method) - + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? end diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 3a5e5071bb..de35b53872 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -38,8 +38,8 @@ module ActionController #:nodoc: 'ActionView::TemplateError' => 'template_error' } - RESCUES_TEMPLATE_PATH = ActionView::PathSet::Path.new( - File.join(File.dirname(__FILE__), "templates"), true) + RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new( + File.join(File.dirname(__FILE__), "templates")) def self.included(base) #:nodoc: base.cattr_accessor :rescue_responses diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 8958e61e9d..70a0ba91a7 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -225,7 +225,7 @@ module ActionView #:nodoc: end # Returns the result of a render that's dictated by the options hash. The primary options are: - # + # # * :partial - See ActionView::Partials. # * :update - Calls update_page with the block given. # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb index 5e00cef13f..54efa543c8 100644 --- a/actionpack/lib/action_view/inline_template.rb +++ b/actionpack/lib/action_view/inline_template.rb @@ -12,7 +12,7 @@ module ActionView #:nodoc: private # Always recompile inline templates - def recompile?(local_assigns) + def recompile? true end end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index b030156889..19207e7262 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -2,7 +2,7 @@ module ActionView #:nodoc: class PathSet < Array #:nodoc: def self.type_cast(obj) if obj.is_a?(String) - Path.new(obj) + Template::EagerPath.new(obj) else obj end @@ -32,111 +32,6 @@ module ActionView #:nodoc: super(*objs.map { |obj| self.class.type_cast(obj) }) end - class Path #:nodoc: - attr_reader :path, :paths - delegate :hash, :inspect, :to => :path - - def initialize(path, load = false) - raise ArgumentError, "path already is a Path class" if path.is_a?(Path) - @path = path.freeze - reload! if load - end - - def to_s - if defined?(RAILS_ROOT) - path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') - else - path.to_s - end - end - - def to_str - path.to_str - end - - def ==(path) - to_str == path.to_str - end - - def eql?(path) - to_str == path.to_str - end - - # Returns a ActionView::Template object for the given path string. The - # input path should be relative to the view path directory, - # +hello/index.html.erb+. This method also has a special exception to - # match partial file names without a handler extension. So - # +hello/index.html+ will match the first template it finds with a - # known template extension, +hello/index.html.erb+. Template extensions - # should not be confused with format extensions +html+, +js+, +xml+, - # etc. A format must be supplied to match a formated file. +hello/index+ - # will never match +hello/index.html.erb+. - # - # This method also has two different implementations, one that is "lazy" - # and makes file system calls every time and the other is cached, - # "eager" which looks up the template in an in memory index. The "lazy" - # version is designed for development where you want to automatically - # find new templates between requests. The "eager" version is designed - # for production mode and it is much faster but requires more time - # upfront to build the file index. - def [](path) - if loaded? - @paths[path] - else - Dir.glob("#{@path}/#{path}*").each do |file| - template = create_template(file) - if template.accessible_paths.include?(path) - return template - end - end - nil - end - end - - def loaded? - @loaded ? true : false - end - - def load - reload! unless loaded? - self - end - - # Rebuild load path directory cache - def reload! - @paths = {} - - templates_in_path do |template| - template.load! - template.accessible_paths.each do |path| - @paths[path] = template - end - end - - @paths.freeze - @loaded = true - end - - private - def templates_in_path - (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file| - yield create_template(file) unless File.directory?(file) - end - end - - def create_template(file) - Template.new(file.split("#{self}/").last, self) - end - end - - def load - each { |path| path.load } - end - - def reload! - each { |path| path.reload! } - end - def find_template(original_template_path, format = nil) return original_template_path if original_template_path.respond_to?(:render) template_path = original_template_path.sub(/^\//, '') diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index d8e72f1179..153e14f68b 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -60,7 +60,7 @@ module ActionView def compile(local_assigns) render_symbol = method_name(local_assigns) - if recompile?(render_symbol) + if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile? compile!(render_symbol, local_assigns) end end @@ -89,11 +89,8 @@ module ActionView end end - # Method to check whether template compilation is necessary. - # The template will be compiled if the file has not been compiled yet, or - # if local_assigns has a new key, which isn't supported by the compiled code yet. - def recompile?(symbol) - !Base::CompiledTemplates.method_defined?(symbol) || !loaded? + def recompile? + false end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 5b384d0e4d..88ee07d95b 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,5 +1,83 @@ module ActionView #:nodoc: class Template + class Path + attr_reader :path, :paths + delegate :hash, :inspect, :to => :path + + def initialize(path) + raise ArgumentError, "path already is a Path class" if path.is_a?(Path) + @path = path.freeze + end + + def to_s + if defined?(RAILS_ROOT) + path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') + else + path.to_s + end + end + + def to_str + path.to_str + end + + def ==(path) + to_str == path.to_str + end + + def eql?(path) + to_str == path.to_str + end + + # Returns a ActionView::Template object for the given path string. The + # input path should be relative to the view path directory, + # +hello/index.html.erb+. This method also has a special exception to + # match partial file names without a handler extension. So + # +hello/index.html+ will match the first template it finds with a + # known template extension, +hello/index.html.erb+. Template extensions + # should not be confused with format extensions +html+, +js+, +xml+, + # etc. A format must be supplied to match a formated file. +hello/index+ + # will never match +hello/index.html.erb+. + def [](path) + templates_in_path do |template| + if template.accessible_paths.include?(path) + return template + end + end + nil + end + + private + def templates_in_path + (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file| + yield create_template(file) unless File.directory?(file) + end + end + + def create_template(file) + Template.new(file.split("#{self}/").last, self) + end + end + + class EagerPath < Path + def initialize(path) + super + + @paths = {} + templates_in_path do |template| + template.load! + template.accessible_paths.each do |path| + @paths[path] = template + end + end + @paths.freeze + end + + def [](path) + @paths[path] + end + end + extend TemplateHandlers extend ActiveSupport::Memoizable include Renderable @@ -115,13 +193,12 @@ module ActionView #:nodoc: File.mtime(filename) > mtime end - def loaded? - @loaded + def recompile? + !@cached end def load! - @loaded = true - compile({}) + @cached = true freeze end -- cgit v1.2.3 From c69d8c043f8d0a587708fb2247b585ca93df822d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 30 Dec 2008 15:16:51 -0800 Subject: Fix formatted_* deprecation message --- actionpack/lib/action_controller/routing/route_set.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 06aef6e169..044ace7de1 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -195,8 +195,8 @@ module ActionController def formatted_#{selector}(*args) # def formatted_users_url(*args) ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn( "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " + - "please pass format to the standard" + # "please pass format to the standard" + - "#{selector}() method instead.", caller) # "users_url() method instead.", caller) + "Please pass format to the standard " + # "Please pass format to the standard " + + "#{selector} method instead.", caller) # "users_url method instead.", caller) #{selector}(*args) # users_url(*args) end # end protected :#{selector} # protected :users_url -- cgit v1.2.3 From 2e1132fad8fa2ab58476b9ecc30523ed02a43181 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 30 Dec 2008 18:06:56 -0800 Subject: Test that exceptions raised in filters are properly rescued --- actionpack/lib/action_controller/base.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index bc18dfaec8..aa604395a9 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1350,9 +1350,12 @@ module ActionController #:nodoc: end Base.class_eval do - include Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers - include Cookies, Caching, Verification, Streaming - include SessionManagement, HttpAuthentication::Basic::ControllerMethods - include RecordIdentifier, RequestForgeryProtection, Translation + [ Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers, + Cookies, Caching, Verification, Streaming, SessionManagement, + HttpAuthentication::Basic::ControllerMethods, RecordIdentifier, + RequestForgeryProtection, Translation + ].each do |mod| + include mod + end end end -- cgit v1.2.3 From 49a055dff639164435dfb71bf18d695970eedac9 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 1 Jan 2009 18:12:49 +0100 Subject: Fixed the AssetTagHelper cache to use the computed asset host as part of the cache key instead of just assuming the its a string [#1299 state:committed] --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 0633d5414e..79b1cdbc48 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -545,12 +545,12 @@ module ActionView @source = source @include_host = include_host @cache_key = if controller.respond_to?(:request) - [self.class.name,controller.request.protocol, - ActionController::Base.asset_host, - ActionController::Base.relative_url_root, - source, include_host] + [ self.class.name,controller.request.protocol, + compute_asset_host(source), + ActionController::Base.relative_url_root, + source, include_host ] else - [self.class.name,ActionController::Base.asset_host, source, include_host] + [ self.class.name, compute_asset_host(source), source, include_host ] end end -- cgit v1.2.3 From a1fb57aa6940253dbed5423ac3e064db272eab2a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 1 Jan 2009 18:53:16 +0100 Subject: Added :silence option to BenchmarkHelper#benchmark and turned log_level into a hash parameter and deprecated the old use [DHH] --- .../lib/action_view/helpers/benchmark_helper.rb | 29 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb index 372d24a22e..61c2cecb04 100644 --- a/actionpack/lib/action_view/helpers/benchmark_helper.rb +++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb @@ -18,12 +18,33 @@ module ActionView # That would add something like "Process data files (345.2ms)" to the log, # which you can then use to compare timings when optimizing your code. # - # You may give an optional logger level as the second argument + # You may give an optional logger level as the :level option. # (:debug, :info, :warn, :error); the default value is :info. - def benchmark(message = "Benchmarking", level = :info) + # + # <% benchmark "Low-level files", :level => :debug do %> + # <%= lowlevel_files_operation %> + # <% end %> + # + # Finally, you can pass true as the third argument to silence all log activity + # inside the block. This is great for boiling down a noisy block to just a single statement: + # + # <% benchmark "Process data files", :level => :info, :silence => true do %> + # <%= expensive_and_chatty_files_operation %> + # <% end %> + def benchmark(message = "Benchmarking", options = {}) if controller.logger - ms = Benchmark.ms { yield } - controller.logger.send(level, '%s (%.1fms)' % [message, ms]) + if options.is_a?(Symbol) + ActiveSupport::Deprecation.warn("use benchmark('#{message}', :level => :#{options}) instead", caller) + options = { :level => options, :silence => false } + else + options.assert_valid_keys(:level, :silence) + options[:level] ||= :info + end + + result = nil + ms = Benchmark.ms { result = options[:silence] ? controller.logger.silence { yield } : yield } + controller.logger.send(options[:level], '%s (%.1fms)' % [ message, ms ]) + result else yield end -- cgit v1.2.3 From f90160c6c1ec457f0b4b01c6fef78146271bc070 Mon Sep 17 00:00:00 2001 From: ddemaree Date: Fri, 2 Jan 2009 10:31:21 -0600 Subject: Fixed bug where calling app method from console would raise ArgumentError [#1629 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/integration.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index a8e54c2fc7..d9899112c3 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -81,8 +81,8 @@ module ActionController end # Create and initialize a new Session instance. - def initialize(app) - @application = app + def initialize(app = nil) + @application = app || ActionController::Dispatcher.new reset! end @@ -591,7 +591,6 @@ EOF # can use this method to open multiple sessions that ought to be tested # simultaneously. def open_session(application = nil) - application ||= ActionController::Dispatcher.new session = Integration::Session.new(application) # delegate the fixture accessors back to the test instance -- cgit v1.2.3 From 606176a55b90c27687ae17f40fd1af0a86b62246 Mon Sep 17 00:00:00 2001 From: Laszlo Bacsi Date: Fri, 2 Jan 2009 10:46:48 -0600 Subject: Fixed call_with_exception for Routing Errors [#1684 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/rescue.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index de35b53872..8824d983b4 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -60,8 +60,8 @@ module ActionController #:nodoc: module ClassMethods def call_with_exception(env, exception) #:nodoc: - request = env["actioncontroller.rescue.request"] - response = env["actioncontroller.rescue.response"] + request = env["actioncontroller.rescue.request"] ||= Request.new(env) + response = env["actioncontroller.rescue.response"] ||= Response.new new.process(request, response, :rescue_action, exception) end end -- cgit v1.2.3 From 104898fcb7958bcb69ba0239d6de8aa37f2da9ba Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 2 Jan 2009 13:40:23 -0600 Subject: Revert to the good old days when AssetTag didn't cause anyone problems --- actionpack/lib/action_controller/dispatcher.rb | 1 - .../lib/action_view/helpers/asset_tag_helper.rb | 456 ++++++--------------- 2 files changed, 131 insertions(+), 326 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index c4e7357b81..d5af45f0da 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -91,7 +91,6 @@ module ActionController run_callbacks :prepare_dispatch Routing::Routes.reload - ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear end # Cleanup the application by clearing out loaded classes so they can diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 79b1cdbc48..a341b453ec 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -157,7 +157,7 @@ module ActionView # javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr.js # javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js def javascript_path(source) - JavaScriptTag.new(self, @controller, source).public_path + compute_public_path(source, 'javascripts', 'js') end alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route @@ -255,17 +255,15 @@ module ActionView joined_javascript_name = (cache == true ? "all" : cache) + ".js" joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name) - unless File.exists?(joined_javascript_path) - JavaScriptSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_javascript_path) - end + write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path) javascript_src_tag(joined_javascript_name, options) else - JavaScriptSources.create(self, @controller, sources, recursive).expand_sources.collect { |source| - javascript_src_tag(source, options) - }.join("\n") + expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n") end end + @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup } + # Register one or more javascript files to be included when symbol # is passed to javascript_include_tag. This method is typically intended # to be called from plugin initialization to register javascript files @@ -278,9 +276,11 @@ module ActionView # # def self.register_javascript_expansion(expansions) - JavaScriptSources.expansions.merge!(expansions) + @@javascript_expansions.merge!(expansions) end + @@stylesheet_expansions = {} + # Register one or more stylesheet files to be included when symbol # is passed to stylesheet_link_tag. This method is typically intended # to be called from plugin initialization to register stylesheet files @@ -293,7 +293,7 @@ module ActionView # # def self.register_stylesheet_expansion(expansions) - StylesheetSources.expansions.merge!(expansions) + @@stylesheet_expansions.merge!(expansions) end # Register one or more additional JavaScript files to be included when @@ -301,11 +301,11 @@ module ActionView # typically intended to be called from plugin initialization to register additional # .js files that the plugin installed in public/javascripts. def self.register_javascript_include_default(*sources) - JavaScriptSources.expansions[:defaults].concat(sources) + @@javascript_expansions[:defaults].concat(sources) end def self.reset_javascript_include_default #:nodoc: - JavaScriptSources.expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup + @@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup end # Computes the path to a stylesheet asset in the public stylesheets directory. @@ -320,7 +320,7 @@ module ActionView # stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style.css # stylesheet_path "http://www.railsapplication.com/css/style.js" # => http://www.railsapplication.com/css/style.css def stylesheet_path(source) - StylesheetTag.new(self, @controller, source).public_path + compute_public_path(source, 'stylesheets', 'css') end alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route @@ -365,7 +365,6 @@ module ActionView # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching # is set to true (which is the case by default for the Rails production environment, but not for the development # environment). Examples: - # # ==== Examples # stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false => @@ -396,14 +395,10 @@ module ActionView joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name) - unless File.exists?(joined_stylesheet_path) - StylesheetSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_stylesheet_path) - end + write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path) stylesheet_tag(joined_stylesheet_name, options) else - StylesheetSources.create(self, @controller, sources, recursive).expand_sources.collect { |source| - stylesheet_tag(source, options) - }.join("\n") + expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n") end end @@ -418,7 +413,7 @@ module ActionView # image_path("/icons/edit.png") # => /icons/edit.png # image_path("http://www.railsapplication.com/img/edit.png") # => http://www.railsapplication.com/img/edit.png def image_path(source) - ImageTag.new(self, @controller, source).public_path + compute_public_path(source, 'images') end alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route @@ -474,352 +469,163 @@ module ActionView end private - def javascript_src_tag(source, options) - content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options)) - end - - def stylesheet_tag(source, options) - tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false) - end - - module ImageAsset - DIRECTORY = 'images'.freeze - - def directory - DIRECTORY + # Add the the extension +ext+ if not present. Return full URLs otherwise untouched. + # Prefix with /dir/ if lacking a leading +/+. Account for relative URL + # roots. Rewrite the asset path for cache-busting asset ids. Include + # asset host, if configured, with the correct request protocol. + def compute_public_path(source, dir, ext = nil, include_host = true) + has_request = @controller.respond_to?(:request) + + if ext && (File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))) + source += ".#{ext}" end - def extension - nil - end - end - - module JavaScriptAsset - DIRECTORY = 'javascripts'.freeze - EXTENSION = 'js'.freeze + unless source =~ %r{^[-a-z]+://} + source = "/#{dir}/#{source}" unless source[0] == ?/ - def public_directory - JAVASCRIPTS_DIR - end + source = rewrite_asset_path(source) - def directory - DIRECTORY + if has_request && include_host + unless source =~ %r{^#{ActionController::Base.relative_url_root}/} + source = "#{ActionController::Base.relative_url_root}#{source}" + end + end end - def extension - EXTENSION - end - end + if include_host && source !~ %r{^[-a-z]+://} + host = compute_asset_host(source) - module StylesheetAsset - DIRECTORY = 'stylesheets'.freeze - EXTENSION = 'css'.freeze - - def public_directory - STYLESHEETS_DIR - end - - def directory - DIRECTORY - end + if has_request && !host.blank? && host !~ %r{^[-a-z]+://} + host = "#{@controller.request.protocol}#{host}" + end - def extension - EXTENSION + "#{host}#{source}" + else + source end end - class AssetTag - extend ActiveSupport::Memoizable - - Cache = {} - CacheGuard = Mutex.new - - ProtocolRegexp = %r{^[-a-z]+://}.freeze - - def initialize(template, controller, source, include_host = true) - # NOTE: The template arg is temporarily needed for a legacy plugin - # hook that is expected to call rewrite_asset_path on the - # template. This should eventually be removed. - @template = template - @controller = controller - @source = source - @include_host = include_host - @cache_key = if controller.respond_to?(:request) - [ self.class.name,controller.request.protocol, - compute_asset_host(source), - ActionController::Base.relative_url_root, - source, include_host ] + # Pick an asset host for this source. Returns +nil+ if no host is set, + # the host if no wildcard is set, the host interpolated with the + # numbers 0-3 if it contains %d (the number is the source hash mod 4), + # or the value returned from invoking the proc if it's a proc or the value from + # invoking call if it's an object responding to call. + def compute_asset_host(source) + if host = ActionController::Base.asset_host + if host.is_a?(Proc) || host.respond_to?(:call) + case host.is_a?(Proc) ? host.arity : host.method(:call).arity + when 2 + request = @controller.respond_to?(:request) && @controller.request + host.call(source, request) + else + host.call(source) + end else - [ self.class.name, compute_asset_host(source), source, include_host ] + (host =~ /%d/) ? host % (source.hash % 4) : host end end - - def public_path - compute_public_path(@source) - end - memoize :public_path + end - def asset_file_path - File.join(ASSETS_DIR, public_path.split('?').first) - end - memoize :asset_file_path + # Use the RAILS_ASSET_ID environment variable or the source's + # modification time as its cache-busting asset id. + def rails_asset_id(source) + if asset_id = ENV["RAILS_ASSET_ID"] + asset_id + else + path = File.join(ASSETS_DIR, source) - def contents - File.read(asset_file_path) + if File.exist?(path) + File.mtime(path).to_i.to_s + else + '' + end end + end - def mtime - File.mtime(asset_file_path) + # Break out the asset path rewrite in case plugins wish to put the asset id + # someplace other than the query string. + def rewrite_asset_path(source) + asset_id = rails_asset_id(source) + if asset_id.blank? + source + else + source + "?#{asset_id}" end - - private - def request - request? && @controller.request - end - - def request? - @controller.respond_to?(:request) - end - - # Add the the extension +ext+ if not present. Return full URLs otherwise untouched. - # Prefix with /dir/ if lacking a leading +/+. Account for relative URL - # roots. Rewrite the asset path for cache-busting asset ids. Include - # asset host, if configured, with the correct request protocol. - def compute_public_path(source) - if source =~ ProtocolRegexp - source += ".#{extension}" if missing_extension?(source) - source = prepend_asset_host(source) - source - else - CacheGuard.synchronize do - Cache[@cache_key + [source]] ||= begin - source += ".#{extension}" if missing_extension?(source) || file_exists_with_extension?(source) - source = "/#{directory}/#{source}" unless source[0] == ?/ - source = rewrite_asset_path(source) - source = prepend_relative_url_root(source) - source = prepend_asset_host(source) - source - end - end - end - end - - def missing_extension?(source) - extension && File.extname(source).blank? - end - - def file_exists_with_extension?(source) - extension && File.exist?(File.join(ASSETS_DIR, directory, "#{source}.#{extension}")) - end - - def prepend_relative_url_root(source) - relative_url_root = ActionController::Base.relative_url_root - if request? && @include_host && source !~ %r{^#{relative_url_root}/} - "#{relative_url_root}#{source}" - else - source - end - end - - def prepend_asset_host(source) - if @include_host && source !~ ProtocolRegexp - host = compute_asset_host(source) - if request? && !host.blank? && host !~ ProtocolRegexp - host = "#{request.protocol}#{host}" - end - "#{host}#{source}" - else - source - end - end - - # Pick an asset host for this source. Returns +nil+ if no host is set, - # the host if no wildcard is set, the host interpolated with the - # numbers 0-3 if it contains %d (the number is the source hash mod 4), - # or the value returned from invoking the proc if it's a proc or the value from - # invoking call if it's an object responding to call. - def compute_asset_host(source) - if host = ActionController::Base.asset_host - if host.is_a?(Proc) || host.respond_to?(:call) - case host.is_a?(Proc) ? host.arity : host.method(:call).arity - when 2 - host.call(source, request) - else - host.call(source) - end - else - (host =~ /%d/) ? host % (source.hash % 4) : host - end - end - end - - # Use the RAILS_ASSET_ID environment variable or the source's - # modification time as its cache-busting asset id. - def rails_asset_id(source) - if asset_id = ENV["RAILS_ASSET_ID"] - asset_id - else - path = File.join(ASSETS_DIR, source) - - if File.exist?(path) - File.mtime(path).to_i.to_s - else - '' - end - end - end - - # Break out the asset path rewrite in case plugins wish to put the asset id - # someplace other than the query string. - def rewrite_asset_path(source) - if @template.respond_to?(:rewrite_asset_path) - # DEPRECATE: This way to override rewrite_asset_path - @template.send(:rewrite_asset_path, source) - else - asset_id = rails_asset_id(source) - if asset_id.blank? - source - else - "#{source}?#{asset_id}" - end - end - end end - class ImageTag < AssetTag - include ImageAsset + def javascript_src_tag(source, options) + content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options)) end - class JavaScriptTag < AssetTag - include JavaScriptAsset + def stylesheet_tag(source, options) + tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false) end - class StylesheetTag < AssetTag - include StylesheetAsset + def compute_javascript_paths(*args) + expand_javascript_sources(*args).collect { |source| compute_public_path(source, 'javascripts', 'js', false) } end - class AssetCollection - extend ActiveSupport::Memoizable - - Cache = {} - CacheGuard = Mutex.new - - def self.create(template, controller, sources, recursive) - CacheGuard.synchronize do - key = [self, sources, recursive] - Cache[key] ||= new(template, controller, sources, recursive).freeze - end - end - - def initialize(template, controller, sources, recursive) - # NOTE: The template arg is temporarily needed for a legacy plugin - # hook. See NOTE under AssetTag#initialize for more details - @template = template - @controller = controller - @sources = sources - @recursive = recursive - end + def compute_stylesheet_paths(*args) + expand_stylesheet_sources(*args).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) } + end - def write_asset_file_contents(joined_asset_path) - FileUtils.mkdir_p(File.dirname(joined_asset_path)) - File.open(joined_asset_path, "w+") { |cache| cache.write(joined_contents) } - mt = latest_mtime - File.utime(mt, mt, joined_asset_path) + def expand_javascript_sources(sources, recursive = false) + if sources.include?(:all) + all_javascript_files = collect_asset_files(JAVASCRIPTS_DIR, ('**' if recursive), '*.js') + ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq + else + expanded_sources = sources.collect do |source| + determine_source(source, @@javascript_expansions) + end.flatten + expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, "application.js")) + expanded_sources end - - private - def determine_source(source, collection) - case source - when Symbol - collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}") - else - source - end - end - - def validate_sources! - @sources.collect { |source| determine_source(source, self.class.expansions) }.flatten - end - - def all_asset_files - path = [public_directory, ('**' if @recursive), "*.#{extension}"].compact - Dir[File.join(*path)].collect { |file| - file[-(file.size - public_directory.size - 1)..-1].sub(/\.\w+$/, '') - }.sort - end - - def tag_sources - expand_sources.collect { |source| tag_class.new(@template, @controller, source, false) } - end - - def joined_contents - tag_sources.collect { |source| source.contents }.join("\n\n") - end - - # Set mtime to the latest of the combined files to allow for - # consistent ETag without a shared filesystem. - def latest_mtime - tag_sources.map { |source| source.mtime }.max - end end - class JavaScriptSources < AssetCollection - include JavaScriptAsset - - EXPANSIONS = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup } - - def self.expansions - EXPANSIONS + def expand_stylesheet_sources(sources, recursive) + if sources.first == :all + collect_asset_files(STYLESHEETS_DIR, ('**' if recursive), '*.css') + else + sources.collect do |source| + determine_source(source, @@stylesheet_expansions) + end.flatten end + end - APPLICATION_JS = "application".freeze - APPLICATION_FILE = "application.js".freeze - - def expand_sources - if @sources.include?(:all) - assets = all_asset_files - ((defaults.dup & assets) + assets).uniq! - else - expanded_sources = validate_sources! - expanded_sources << APPLICATION_JS if include_application? - expanded_sources - end + def determine_source(source, collection) + case source + when Symbol + collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}") + else + source end - memoize :expand_sources - - private - def tag_class - JavaScriptTag - end - - def defaults - determine_source(:defaults, self.class.expansions) - end + end - def include_application? - @sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, APPLICATION_FILE)) - end + def join_asset_file_contents(paths) + paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n") end - class StylesheetSources < AssetCollection - include StylesheetAsset + def write_asset_file_contents(joined_asset_path, asset_paths) + FileUtils.mkdir_p(File.dirname(joined_asset_path)) + File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) } - EXPANSIONS = {} + # Set mtime to the latest of the combined files to allow for + # consistent ETag without a shared filesystem. + mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max + File.utime(mt, mt, joined_asset_path) + end - def self.expansions - EXPANSIONS - end + def asset_file_path(path) + File.join(ASSETS_DIR, path.split('?').first) + end - def expand_sources - @sources.first == :all ? all_asset_files : validate_sources! - end - memoize :expand_sources + def collect_asset_files(*path) + dir = path.first - private - def tag_class - StylesheetTag - end + Dir[File.join(*path.compact)].collect do |file| + file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '') + end.sort end end end -end +end \ No newline at end of file -- cgit v1.2.3 From ed2e776bdec3f0764433a6dc4f592f9bebfea859 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 3 Jan 2009 23:02:29 -0600 Subject: Move metal above method piggybacking middleware and add some test coverage --- .../lib/action_controller/middleware_stack.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb index 74f28565c0..2bccba2ba1 100644 --- a/actionpack/lib/action_controller/middleware_stack.rb +++ b/actionpack/lib/action_controller/middleware_stack.rb @@ -1,6 +1,14 @@ module ActionController class MiddlewareStack < Array class Middleware + def self.new(klass, *args, &block) + if klass.is_a?(self) + klass + else + super + end + end + attr_reader :args, :block def initialize(klass, *args, &block) @@ -65,6 +73,19 @@ module ActionController block.call(self) if block_given? end + def insert(index, *objs) + index = self.index(index) unless index.is_a?(Integer) + objs = objs.map { |obj| Middleware.new(obj) } + super(index, *objs) + end + + alias_method :insert_before, :insert + + def insert_after(index, *objs) + index = self.index(index) unless index.is_a?(Integer) + insert(index + 1, *objs) + end + def use(*args, &block) middleware = Middleware.new(*args, &block) push(middleware) -- cgit v1.2.3 From f00e86d7e9c7a4689a49fc085bcb757c5a2c0b03 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 4 Jan 2009 12:15:15 -0600 Subject: Memoize request accessors on the Rack env so other request objects have access to the same cache [#1668 state:resolved] --- actionpack/lib/action_controller/base.rb | 4 ++-- actionpack/lib/action_controller/request.rb | 20 ++++++++------------ actionpack/lib/action_controller/request_parser.rb | 7 ++++--- actionpack/lib/action_controller/rescue.rb | 4 ++-- 4 files changed, 16 insertions(+), 19 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index aa604395a9..1093a9bc04 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -384,8 +384,8 @@ module ActionController #:nodoc: class << self def call(env) # HACK: For global rescue to have access to the original request and response - request = env["actioncontroller.rescue.request"] ||= Request.new(env) - response = env["actioncontroller.rescue.response"] ||= Response.new + request = env["action_controller.rescue.request"] ||= Request.new(env) + response = env["action_controller.rescue.response"] ||= Response.new process(request, response) end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 822955d1db..6ac8c6f4a0 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -19,6 +19,7 @@ module ActionController def initialize(env) @env = env + @parser = ActionController::RequestParser.new(env) end %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO @@ -92,16 +93,15 @@ module ActionController # Returns the content length of the request as an integer. def content_length - @env['CONTENT_LENGTH'].to_i + @env["action_controller.request.content_length"] ||= @env['CONTENT_LENGTH'].to_i end - memoize :content_length # The MIME type of the HTTP request, such as Mime::XML. # # For backward compatibility, the post \format is extracted from the # X-Post-Data-Format HTTP header if present. def content_type - Mime::Type.lookup(parser.content_type_without_parameters) + Mime::Type.lookup(@parser.content_type_without_parameters) end memoize :content_type @@ -389,7 +389,7 @@ EOM # Read the request \body. This is useful for web services that need to # work with raw requests directly. def raw_post - parser.raw_post + @parser.raw_post end # Returns both GET and POST \parameters in a single hash. @@ -418,7 +418,7 @@ EOM end def body - parser.body + @parser.body end def remote_addr @@ -431,11 +431,11 @@ EOM alias referer referrer def query_parameters - @query_parameters ||= parser.query_parameters + @parser.query_parameters end def request_parameters - @request_parameters ||= parser.request_parameters + @parser.request_parameters end def body_stream #:nodoc: @@ -451,7 +451,7 @@ EOM end def session=(session) #:nodoc: - @session = session + @env['rack.session'] = session end def reset_session @@ -474,9 +474,5 @@ EOM def named_host?(host) !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) end - - def parser - @parser ||= ActionController::RequestParser.new(@env) - end end end diff --git a/actionpack/lib/action_controller/request_parser.rb b/actionpack/lib/action_controller/request_parser.rb index 82ee4c84c4..20d53f5d92 100644 --- a/actionpack/lib/action_controller/request_parser.rb +++ b/actionpack/lib/action_controller/request_parser.rb @@ -2,14 +2,15 @@ module ActionController class RequestParser def initialize(env) @env = env + freeze end def request_parameters - @request_parameters ||= parse_formatted_request_parameters + @env["action_controller.request_parser.request_parameters"] ||= parse_formatted_request_parameters end def query_parameters - @query_parameters ||= self.class.parse_query_parameters(query_string) + @env["action_controller.request_parser.query_parameters"] ||= self.class.parse_query_parameters(query_string) end # Returns the query string, accounting for server idiosyncrasies. @@ -90,7 +91,7 @@ module ActionController end def content_length - @content_length ||= @env['CONTENT_LENGTH'].to_i + @env["action_controller.request.content_length"] ||= @env['CONTENT_LENGTH'].to_i end # The raw content type string. Use when you need parameters such as diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 8824d983b4..4b7d1e32fd 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -60,8 +60,8 @@ module ActionController #:nodoc: module ClassMethods def call_with_exception(env, exception) #:nodoc: - request = env["actioncontroller.rescue.request"] ||= Request.new(env) - response = env["actioncontroller.rescue.response"] ||= Response.new + request = env["action_controller.rescue.request"] ||= Request.new(env) + response = env["action_controller.rescue.response"] ||= Response.new new.process(request, response, :rescue_action, exception) end end -- cgit v1.2.3 From ce706b4b9be03a3f2e7d11438e6550d64c5f4461 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 4 Jan 2009 15:39:16 -0600 Subject: Cache AssetTag timestamps --- actionpack/lib/action_controller/dispatcher.rb | 2 ++ .../lib/action_view/helpers/asset_tag_helper.rb | 36 +++++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index d5af45f0da..781bc48887 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -8,6 +8,8 @@ module ActionController # Development mode callbacks before_dispatch :reload_application after_dispatch :cleanup_application + + ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false end if defined?(ActiveRecord) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index a341b453ec..58f8cca6be 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -468,6 +468,22 @@ module ActionView tag("img", options) end + def self.cache_asset_timestamps + @@cache_asset_timestamps + end + + # You can enable or disable the asset tag timestamps cache. + # With the cache enabled, the asset tag helper methods will make fewer + # expense file system calls. However this prevents you from modifying + # any asset files while the server is running. + # + # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false + def self.cache_asset_timestamps=(value) + @@cache_asset_timestamps = value + end + + @@cache_asset_timestamps = true + private # Add the the extension +ext+ if not present. Return full URLs otherwise untouched. # Prefix with /dir/ if lacking a leading +/+. Account for relative URL @@ -526,18 +542,28 @@ module ActionView end end + @@asset_timestamps_cache = {} + @@asset_timestamps_cache_guard = Mutex.new + # Use the RAILS_ASSET_ID environment variable or the source's # modification time as its cache-busting asset id. def rails_asset_id(source) if asset_id = ENV["RAILS_ASSET_ID"] asset_id else - path = File.join(ASSETS_DIR, source) - - if File.exist?(path) - File.mtime(path).to_i.to_s + if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source]) + asset_id else - '' + path = File.join(ASSETS_DIR, source) + asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : '' + + if @@cache_asset_timestamps + @@asset_timestamps_cache_guard.synchronize do + @@asset_timestamps_cache[source] = asset_id + end + end + + asset_id end end end -- cgit v1.2.3 From b7ea4add86231ef628d479516c8a09ca55e610bc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 6 Jan 2009 15:20:57 -0600 Subject: Bump Rack version to 0.9 --- actionpack/lib/action_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 98fb490d64..8c022e5625 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,7 +31,7 @@ rescue LoadError end end -gem 'rack', '~> 0.4.0' +gem 'rack', '>= 0.9.0' require 'rack' module ActionController -- cgit v1.2.3 From 84194ce93619f1c74826fae64c1f9745d4014d3a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 6 Jan 2009 15:35:46 -0800 Subject: Explicitly require AS::TestCase --- actionpack/lib/action_view/test_case.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 1a9ef983a5..65839256aa 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -1,3 +1,5 @@ +require 'active_support/test_case' + module ActionView class Base alias_method :initialize_without_template_tracking, :initialize -- cgit v1.2.3 From 8736dd324117af90e0bf120cfd2074b06ccb45eb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 6 Jan 2009 16:57:41 -0800 Subject: Fix failing flash test --- actionpack/lib/action_controller/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 1093a9bc04..e22114195c 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1350,7 +1350,7 @@ module ActionController #:nodoc: end Base.class_eval do - [ Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers, + [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers, Cookies, Caching, Verification, Streaming, SessionManagement, HttpAuthentication::Basic::ControllerMethods, RecordIdentifier, RequestForgeryProtection, Translation -- cgit v1.2.3 From 35fa00731329120fa1d0c2a9d66af6813203195a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 7 Jan 2009 13:23:10 -0800 Subject: Include process methods in ActionController::TestCase only. No need to alias_method_chain :process either. --- actionpack/lib/action_controller/test_case.rb | 3 ++ actionpack/lib/action_controller/test_process.rb | 54 +++++++++--------------- actionpack/lib/action_view/test_case.rb | 1 + 3 files changed, 24 insertions(+), 34 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 7ed1a3e160..93a0be4127 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,4 +1,5 @@ require 'active_support/test_case' +require 'action_controller/test_process' module ActionController # Superclass for ActionController functional tests. Functional tests allow you to @@ -102,6 +103,8 @@ module ActionController # # assert_redirected_to page_url(:title => 'foo') class TestCase < ActiveSupport::TestCase + include TestProcess + module Assertions %w(response selector tag dom routing model).each do |kind| include ActionController::Assertions.const_get("#{kind.camelize}Assertions") diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 285a8b09e4..8180d4ee93 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -1,32 +1,4 @@ -require 'action_controller/test_case' - module ActionController #:nodoc: - class Base - attr_reader :assigns - - # Process a test request called with a TestRequest object. - def self.process_test(request) - new.process_test(request) - end - - def process_test(request) #:nodoc: - process(request, TestResponse.new) - end - - def process_with_test(*args) - returning process_without_test(*args) do - @assigns = {} - (instance_variable_names - @@protected_instance_variables).each do |var| - value = instance_variable_get(var) - @assigns[var[1..-1]] = value - response.template.assigns[var[1..-1]] = value if response - end - end - end - - alias_method_chain :process, :test - end - class TestRequest < Request #:nodoc: attr_accessor :cookies, :session_options attr_accessor :query_parameters, :path, :session @@ -433,7 +405,9 @@ module ActionController #:nodoc: @request.session = ActionController::TestSession.new(session) unless session.nil? @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash build_request_uri(action, parameters) - @controller.process(@request, @response) + + Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest + @controller.process_with_test(@request, @response) end def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) @@ -545,12 +519,24 @@ module ActionController #:nodoc: ActionController::Routing.const_set(:Routes, real_routes) if real_routes end end -end -module Test - module Unit - class TestCase #:nodoc: - include ActionController::TestProcess + module ProcessWithTest #:nodoc: + def self.included(base) + base.class_eval { attr_reader :assigns } + end + + def process_with_test(*args) + process(*args).tap { set_test_assigns } end + + private + def set_test_assigns + @assigns = {} + (instance_variable_names - self.class.protected_instance_variables).each do |var| + name, value = var[1..-1], instance_variable_get(var) + @assigns[name] = value + response.template.assigns[name] = value if response + end + end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 65839256aa..ec337bb05b 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -23,6 +23,7 @@ module ActionView class TestCase < ActiveSupport::TestCase include ActionController::TestCase::Assertions + include ActionController::TestProcess class_inheritable_accessor :helper_class @@helper_class = nil -- cgit v1.2.3 From 347db97eddfc4c303a005b71fce9faf12e74e63a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 7 Jan 2009 14:39:47 -0800 Subject: Take care not to mix in public methods --- actionpack/lib/action_controller/test_case.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 93a0be4127..0b0d0c799b 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -127,17 +127,18 @@ module ActionController # # The exception is stored in the exception accessor for further inspection. module RaiseActionExceptions - attr_accessor :exception + protected + attr_accessor :exception - def rescue_action_without_handler(e) - self.exception = e - - if request.remote_addr == "0.0.0.0" - raise(e) - else - super(e) + def rescue_action_without_handler(e) + self.exception = e + + if request.remote_addr == "0.0.0.0" + raise(e) + else + super(e) + end end - end end setup :setup_controller_request_and_response -- cgit v1.2.3 From 48963a55c7b4cbd06a66a4f9717801a7417acba9 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 7 Jan 2009 15:52:19 -0800 Subject: Set assigns for integration tests also --- actionpack/lib/action_controller/integration.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index d9899112c3..ded72a71fb 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -367,8 +367,10 @@ module ActionController env[key] = value end - unless ActionController::Base.respond_to?(:clear_last_instantiation!) - ActionController::Base.module_eval { include ControllerCapture } + [ControllerCapture, ActionController::ProcessWithTest].each do |mod| + unless ActionController::Base < mod + ActionController::Base.class_eval { include mod } + end end ActionController::Base.clear_last_instantiation! @@ -396,6 +398,7 @@ module ActionController if @controller = ActionController::Base.last_instantiation @request = @controller.request @response = @controller.response + @controller.send(:set_test_assigns) else # Decorate responses from Rack Middleware and Rails Metal # as an Response for the purposes of integration testing -- cgit v1.2.3 From 074414883cd39c24a6197f7450723c6fc60132d0 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 7 Jan 2009 15:55:28 -0800 Subject: Remove Content-Length header from :no_content responses --- actionpack/lib/action_controller/response.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index 64319fe102..27860a6207 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -231,10 +231,13 @@ module ActionController # :nodoc: # Don't set the Content-Length for block-based bodies as that would mean # reading it all into memory. Not nice for, say, a 2GB streaming file. def set_content_length! - unless body.respond_to?(:call) || (status && status.to_s[0..2] == '304') - self.headers["Content-Length"] ||= body.size + if status && status.to_s[0..2] == '204' + headers.delete('Content-Length') + elsif length = headers['Content-Length'] + headers['Content-Length'] = length.to_s + elsif !body.respond_to?(:call) && (!status || status.to_s[0..2] != '304') + headers["Content-Length"] = body.size.to_s end - headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"] end def convert_language! -- cgit v1.2.3 From e1f73aab8ca679a68a32bec6c0d72eb8a58d8788 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 9 Jan 2009 11:15:38 -0600 Subject: Inherit ActionController::Request from Rack::Request --- actionpack/lib/action_controller/request.rb | 34 ++++------------------ actionpack/lib/action_controller/request_parser.rb | 2 +- 2 files changed, 7 insertions(+), 29 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 6ac8c6f4a0..29d06441e4 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -6,25 +6,17 @@ require 'active_support/memoizable' require 'action_controller/cgi_ext' module ActionController - # CgiRequest and TestRequest provide concrete implementations. - class Request + class Request < Rack::Request extend ActiveSupport::Memoizable - class SessionFixationAttempt < StandardError #:nodoc: - end - - # The hash of environment variables for this request, - # such as { 'RAILS_ENV' => 'production' }. - attr_reader :env - def initialize(env) - @env = env + super @parser = ActionController::RequestParser.new(env) end - %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO + %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_TRANSLATED REMOTE_HOST - REMOTE_IDENT REMOTE_USER SCRIPT_NAME + REMOTE_IDENT REMOTE_USER REMOTE_ADDR SERVER_NAME SERVER_PROTOCOL HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING @@ -45,8 +37,7 @@ module ActionController # The true HTTP request \method as a lowercase symbol, such as :get. # UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS. def request_method - method = @env['REQUEST_METHOD'] - HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") + HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") end memoize :request_method @@ -93,7 +84,7 @@ module ActionController # Returns the content length of the request as an integer. def content_length - @env["action_controller.request.content_length"] ||= @env['CONTENT_LENGTH'].to_i + super.to_i end # The MIME type of the HTTP request, such as Mime::XML. @@ -421,15 +412,6 @@ EOM @parser.body end - def remote_addr - @env['REMOTE_ADDR'] - end - - def referrer - @env['HTTP_REFERER'] - end - alias referer referrer - def query_parameters @parser.query_parameters end @@ -442,10 +424,6 @@ EOM @env['rack.input'] end - def cookies - Rack::Request.new(@env).cookies - end - def session @env['rack.session'] ||= {} end diff --git a/actionpack/lib/action_controller/request_parser.rb b/actionpack/lib/action_controller/request_parser.rb index 20d53f5d92..d1739ef4d0 100644 --- a/actionpack/lib/action_controller/request_parser.rb +++ b/actionpack/lib/action_controller/request_parser.rb @@ -91,7 +91,7 @@ module ActionController end def content_length - @env["action_controller.request.content_length"] ||= @env['CONTENT_LENGTH'].to_i + @env['CONTENT_LENGTH'].to_i end # The raw content type string. Use when you need parameters such as -- cgit v1.2.3 From 282c1d6159a06dce4dd52c1849daad9e73480808 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 9 Jan 2009 12:52:59 -0600 Subject: Refactor request query string parsing tests --- actionpack/lib/action_controller/request.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 29d06441e4..8371f1bf5c 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -412,9 +412,11 @@ EOM @parser.body end - def query_parameters + # Override Rack's GET method to support nested query strings + def GET @parser.query_parameters end + alias_method :query_parameters, :GET def request_parameters @parser.request_parameters -- cgit v1.2.3 From ac4bf1180aa0f82616038522bddaf3ff3d5020c8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 9 Jan 2009 13:12:39 -0600 Subject: Ensure we override Rack::Request's POST method too --- actionpack/lib/action_controller/request.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 8371f1bf5c..b4ab1ccda1 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -387,6 +387,7 @@ EOM def parameters @parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access end + alias_method :params, :parameters def path_parameters=(parameters) #:nodoc: @env["rack.routing_args"] = parameters @@ -418,9 +419,11 @@ EOM end alias_method :query_parameters, :GET - def request_parameters + # Override Rack's POST method to support nested query strings + def POST @parser.request_parameters end + alias_method :request_parameters, :POST def body_stream #:nodoc: @env['rack.input'] -- cgit v1.2.3 From 5339f813be99012aba01586743d8b24f065e7034 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 13 Jan 2009 03:28:32 +0000 Subject: Change Object#try to raise NoMethodError on private methods and always return nil when Object is nil [Pratik Naik, Lawrence Pit] --- actionpack/lib/action_controller/test_process.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 8180d4ee93..22b97fc157 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -484,7 +484,8 @@ module ActionController #:nodoc: # # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary) def fixture_file_upload(path, mime_type = nil, binary = false) - ActionController::TestUploadedFile.new("#{ActionController::TestCase.try(:fixture_path)}#{path}", mime_type, binary) + fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path) + ActionController::TestUploadedFile.new("#{fixture_path}#{path}", mime_type, binary) end # A helper to make it easier to test different route configurations. -- cgit v1.2.3 From c99ef814b0ce5d6b6a677ee6116edac03c8a35b3 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 13 Jan 2009 16:13:42 +0000 Subject: Revert "HTTP Digest authentication [#1230 state:resolved]" This reverts commit 45dee3842d68359a189fe7c0729359bd5a905ea4. Reasons : 1. The code is not working in it's current state 2. Should not be using exceptions for flow control --- .../lib/action_controller/http_authentication.rb | 191 +-------------------- actionpack/lib/action_controller/integration.rb | 82 --------- 2 files changed, 2 insertions(+), 271 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 3cb5829eca..2ed810db7d 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -55,31 +55,7 @@ module ActionController # end # end # - # Simple Digest example. Note the block must return the user's password so the framework - # can appropriately hash it to check the user's credentials. Returning nil will cause authentication to fail. - # - # class PostsController < ApplicationController - # Users = {"dhh" => "secret"} - # - # before_filter :authenticate, :except => [ :index ] - # - # def index - # render :text => "Everyone can see me!" - # end - # - # def edit - # render :text => "I'm only accessible if you know the password" - # end - # - # private - # def authenticate - # authenticate_or_request_with_http_digest(realm) do |user_name| - # Users[user_name] - # end - # end - # end - # - # + # # In your integration tests, you can do something like this: # # def test_access_granted_from_xml @@ -132,10 +108,7 @@ module ActionController end def decode_credentials(request) - # Properly decode credentials spanning a new-line - auth = authorization(request) - auth.slice!('Basic ') - ActiveSupport::Base64.decode64(auth || '') + ActiveSupport::Base64.decode64(authorization(request).split.last || '') end def encode_credentials(user_name, password) @@ -147,165 +120,5 @@ module ActionController controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized end end - - module Digest - extend self - - module ControllerMethods - def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) - begin - authenticate_with_http_digest!(realm, &password_procedure) - rescue ActionController::HttpAuthentication::Error => e - msg = e.message - msg = "#{msg} expected '#{e.expected}' was '#{e.was}'" unless e.expected.nil? - raise msg if e.fatal? - request_http_digest_authentication(realm, msg) - end - end - - # Authenticate using HTTP Digest, throwing ActionController::HttpAuthentication::Error on failure. - # This allows more detailed analysis of authentication failures - # to be relayed to the client. - def authenticate_with_http_digest!(realm = "Application", &login_procedure) - HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) - end - - # Authenticate with HTTP Digest, returns true or false - def authenticate_with_http_digest(realm = "Application", &login_procedure) - HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) rescue false - end - - # Render output including the HTTP Digest authentication header - def request_http_digest_authentication(realm = "Application", message = nil) - HttpAuthentication::Digest.authentication_request(self, realm, message) - end - - # Add HTTP Digest authentication header to result headers - def http_digest_authentication_header(realm = "Application") - HttpAuthentication::Digest.authentication_header(self, realm) - end - end - - # Raises error unless authentictaion succeeds, returns true otherwise - def authenticate(controller, realm, &password_procedure) - raise Error.new(false), "No authorization header found" unless authorization(controller.request) - validate_digest_response(controller, realm, &password_procedure) - true - end - - def authorization(request) - request.env['HTTP_AUTHORIZATION'] || - request.env['X-HTTP_AUTHORIZATION'] || - request.env['X_HTTP_AUTHORIZATION'] || - request.env['REDIRECT_X_HTTP_AUTHORIZATION'] - end - - # Raises error unless the request credentials response value matches the expected value. - def validate_digest_response(controller, realm, &password_procedure) - credentials = decode_credentials(controller.request) - - # Check the nonce, opaque and realm. - # Ignore nc, as we have no way to validate the number of times this nonce has been used - validate_nonce(controller.request, credentials[:nonce]) - raise Error.new(false, realm, credentials[:realm]), "Realm doesn't match" unless realm == credentials[:realm] - raise Error.new(true, opaque(controller.request), credentials[:opaque]),"Opaque doesn't match" unless opaque(controller.request) == credentials[:opaque] - - password = password_procedure.call(credentials[:username]) - raise Error.new(false), "No password" if password.nil? - expected = expected_response(controller.request.env['REQUEST_METHOD'], controller.request.url, credentials, password) - raise Error.new(false, expected, credentials[:response]), "Invalid response" unless expected == credentials[:response] - end - - # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ - def expected_response(http_method, uri, credentials, password) - ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) - ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase,uri].join(':')) - ::Digest::MD5.hexdigest([ha1,credentials[:nonce], credentials[:nc], credentials[:cnonce],credentials[:qop],ha2].join(':')) - end - - def encode_credentials(http_method, credentials, password) - credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password) - "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') - end - - def decode_credentials(request) - authorization(request).to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| - key, value = pair.split('=', 2) - hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') - hash - end - end - - def authentication_header(controller, realm) - controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(controller.request)}", opaque="#{opaque(controller.request)}") - end - - def authentication_request(controller, realm, message = "HTTP Digest: Access denied") - authentication_header(controller, realm) - controller.send! :render, :text => message, :status => :unauthorized - end - - # Uses an MD5 digest based on time to generate a value to be used only once. - # - # A server-specified data string which should be uniquely generated each time a 401 response is made. - # It is recommended that this string be base64 or hexadecimal data. - # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed. - # - # The contents of the nonce are implementation dependent. - # The quality of the implementation depends on a good choice. - # A nonce might, for example, be constructed as the base 64 encoding of - # - # => time-stamp H(time-stamp ":" ETag ":" private-key) - # - # where time-stamp is a server-generated time or other non-repeating value, - # ETag is the value of the HTTP ETag header associated with the requested entity, - # and private-key is data known only to the server. - # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and - # reject the request if it did not match the nonce from that header or - # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity. - # The inclusion of the ETag prevents a replay request for an updated version of the resource. - # (Note: including the IP address of the client in the nonce would appear to offer the server the ability - # to limit the reuse of the nonce to the same client that originally got it. - # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm. - # Also, IP address spoofing is not that hard.) - # - # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to - # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for - # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 - # of this document. - # - # The nonce is opaque to the client. - def nonce(request, time = Time.now) - session_id = request.is_a?(String) ? request : request.session.session_id - t = time.to_i - hashed = [t, session_id] - digest = ::Digest::MD5.hexdigest(hashed.join(":")) - Base64.encode64("#{t}:#{digest}").gsub("\n", '') - end - - def validate_nonce(request, value) - t = Base64.decode64(value).split(":").first.to_i - raise Error.new(true), "Stale Nonce" if (t - Time.now.to_i).abs > 10 * 60 - n = nonce(request, t) - raise Error.new(true, value, n), "Bad Nonce" unless n == value - end - - # Opaque based on digest of session_id - def opaque(request) - session_id = request.is_a?(String) ? request : request.session.session_id - @opaque ||= Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '') - end - end - - class Error < RuntimeError - attr_accessor :expected, :was - def initialize(fatal = false, expected = nil, was = nil) - @fatal = fatal - @expected = expected - @was = was - end - - def fatal?; @fatal; end - end end end diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index ded72a71fb..08b54658ee 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -68,15 +68,6 @@ module ActionController # A running counter of the number of requests processed. attr_accessor :request_count - # Nonce value for Digest Authentication, implicitly set on response with WWW-Authentication - attr_accessor :nonce - - # Opaque value for Digest Authentication, implicitly set on response with WWW-Authentication - attr_accessor :opaque - - # Opaque value for Authentication, implicitly set on response with WWW-Authentication - attr_accessor :realm - class MultiPartNeededException < Exception end @@ -252,53 +243,6 @@ module ActionController end alias xhr :xml_http_request - def request_with_noauth(http_method, uri, parameters, headers) - process_with_auth http_method, uri, parameters, headers - end - - # Performs a request with the given http_method and parameters, including HTTP Basic authorization headers. - # See get() for more details on paramters and headers. - # - # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_basic, #post_with_basic, - # #put_with_basic, #delete_with_basic, and #head_with_basic. - def request_with_basic(http_method, uri, parameters, headers, user_name, password) - process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Basic.encode_credentials(user_name, password)) - end - - # Performs a request with the given http_method and parameters, including HTTP Digest authorization headers. - # See get() for more details on paramters and headers. - # - # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_digest, #post_with_digest, - # #put_with_digest, #delete_with_digest, and #head_with_digest. - def request_with_digest(http_method, uri, parameters, headers, user_name, password) - # Realm, Nonce, and Opaque taken from previoius 401 response - - credentials = { - :username => user_name, - :realm => @realm, - :nonce => @nonce, - :qop => "auth", - :nc => "00000001", - :cnonce => "0a4f113b", - :opaque => @opaque, - :uri => uri - } - - raise "Digest request without previous 401 response" if @opaque.nil? - - process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Digest.encode_credentials(http_method, credentials, password)) - end - - # def get_with_basic, def post_with_basic, def put_with_basic, def delete_with_basic, def head_with_basic - # def get_with_digest, def post_with_digest, def put_with_digest, def delete_with_digest, def head_with_digest - [:get, :post, :put, :delete, :head].each do |method| - [:noauth, :basic, :digest].each do |auth_type| - define_method("#{method}_with_#{auth_type}") do |uri, parameters, headers, *auth| - send("request_with_#{auth_type}", method, uri, parameters, headers, *auth) - end - end - end - # Returns the URL for the given options, according to the rules specified # in the application's routes. def url_for(options) @@ -423,32 +367,6 @@ module ActionController return status end - # Same as process, but handles authentication returns to perform - # Basic or Digest authentication - def process_with_auth(method, path, parameters = nil, headers = nil) - status = process(method, path, parameters, headers) - - if status == 401 - # Extract authentication information from response - auth_data = @response.headers['WWW-Authenticate'] - if /^Basic /.match(auth_data) - # extract realm, to be used in subsequent request - @realm = auth_header.split(' ')[1] - elsif /^Digest/.match(auth_data) - creds = auth_data.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair| - key, value = pair.split('=', 2) - hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '') - hash - end - @realm = creds[:realm] - @nonce = creds[:nonce] - @opaque = creds[:opaque] - end - end - - return status - end - # Encode the cookies hash in a format suitable for passing to a # request. def encode_cookies -- cgit v1.2.3 From d3107ce3b04a14bd5674da6812acbff30aedaf73 Mon Sep 17 00:00:00 2001 From: Cody Fauser Date: Tue, 13 Jan 2009 14:27:23 -0600 Subject: Use :key instead of old :session_key in session_store.rb generator and docs [#1746 state:resovled] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/session/cookie_store.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index 135bedaf50..e061c4d4a1 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -163,9 +163,9 @@ module ActionController def ensure_session_key(key) if key.blank? - raise ArgumentError, 'A session_key is required to write a ' + + raise ArgumentError, 'A key is required to write a ' + 'cookie containing the session data. Use ' + - 'config.action_controller.session = { :session_key => ' + + 'config.action_controller.session = { :key => ' + '"_myapp_session", :secret => "some secret phrase" } in ' + 'config/environment.rb' end @@ -181,7 +181,7 @@ module ActionController if secret.blank? raise ArgumentError, "A secret is required to generate an " + "integrity hash for cookie session data. Use " + - "config.action_controller.session = { :session_key => " + + "config.action_controller.session = { :key => " + "\"_myapp_session\", :secret => \"some secret phrase of at " + "least #{SECRET_MIN_LENGTH} characters\" } " + "in config/environment.rb" -- cgit v1.2.3 From 5a43908c7414996354ca427354d98d789e0210e7 Mon Sep 17 00:00:00 2001 From: Bryan Ash Date: Tue, 13 Jan 2009 14:42:43 -0600 Subject: Explicitly read as binary in multipart_body for Windows [#1065 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/integration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 08b54658ee..5b08e30d49 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -431,7 +431,7 @@ module ActionController def multipart_body(params, boundary) multipart_requestify(params).map do |key, value| if value.respond_to?(:original_filename) - File.open(value.path) do |f| + File.open(value.path, "rb") do |f| f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) <<-EOF -- cgit v1.2.3 From 1adc1496f9152c893e1f08abcb1e5e7272829899 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 13 Jan 2009 16:09:51 -0600 Subject: Add RewindableInput wrapper to fix issues with middleware that impolitely eat up non-rewindable input --- actionpack/lib/action_controller.rb | 13 +++---- actionpack/lib/action_controller/middlewares.rb | 1 + .../lib/action_controller/rewindable_input.rb | 40 ++++++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 actionpack/lib/action_controller/rewindable_input.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 8c022e5625..0d81b9c346 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -59,16 +59,14 @@ module ActionController autoload :MiddlewareStack, 'action_controller/middleware_stack' autoload :MimeResponds, 'action_controller/mime_responds' autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' - autoload :Request, 'action_controller/request' - autoload :RequestParser, 'action_controller/request_parser' - autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser' - autoload :UploadedStringIO, 'action_controller/uploaded_file' - autoload :UploadedTempfile, 'action_controller/uploaded_file' autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Response, 'action_controller/response' + autoload :Request, 'action_controller/request' autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection' + autoload :RequestParser, 'action_controller/request_parser' autoload :Rescue, 'action_controller/rescue' autoload :Resources, 'action_controller/resources' + autoload :Response, 'action_controller/response' + autoload :RewindableInput, 'action_controller/rewindable_input' autoload :Routing, 'action_controller/routing' autoload :SessionManagement, 'action_controller/session_management' autoload :StatusCodes, 'action_controller/status_codes' @@ -76,6 +74,9 @@ module ActionController autoload :TestCase, 'action_controller/test_case' autoload :TestProcess, 'action_controller/test_process' autoload :Translation, 'action_controller/translation' + autoload :UploadedStringIO, 'action_controller/uploaded_file' + autoload :UploadedTempfile, 'action_controller/uploaded_file' + autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser' autoload :UrlRewriter, 'action_controller/url_rewriter' autoload :UrlWriter, 'action_controller/url_rewriter' autoload :VerbPiggybacking, 'action_controller/verb_piggybacking' diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index 793739723f..914750bc0c 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -18,4 +18,5 @@ use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) } ) end +use ActionController::RewindableInput use ActionController::VerbPiggybacking diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_controller/rewindable_input.rb new file mode 100644 index 0000000000..296d8aed22 --- /dev/null +++ b/actionpack/lib/action_controller/rewindable_input.rb @@ -0,0 +1,40 @@ +module ActionController + class RewindableInput + class RewindableIO < ActiveSupport::BasicObject + def initialize(io) + @io = io + end + + def read(*args) + read_original_io + @io.read(*args) + end + + def rewind + read_original_io + @io.rewind + end + + def method_missing(method, *args, &block) + @io.send(method, *args, &block) + end + + private + def read_original_io + unless @str + @str = @io.read + @io = StringIO.new(@str) + end + end + end + + def initialize(app) + @app = app + end + + def call(env) + env['rack.input'] = RewindableIO.new(env['rack.input']) + @app.call(env) + end + end +end -- cgit v1.2.3 From 9775c25824feb35a5c42f3838d21c7e5faba9ca0 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 13 Jan 2009 17:21:45 -0600 Subject: Update multipart tests to expose (another) bug in Rack's multipart parser --- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/integration.rb | 11 ----------- .../lib/action_controller/middleware_stack.rb | 2 ++ actionpack/lib/action_controller/rack_ext.rb | 22 ++++++++++++++++++++++ .../lib/action_controller/rewindable_input.rb | 10 +++++++--- 5 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 actionpack/lib/action_controller/rack_ext.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 0d81b9c346..a9cd09e58d 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -33,6 +33,7 @@ end gem 'rack', '>= 0.9.0' require 'rack' +require 'action_controller/rack_ext' module ActionController # TODO: Review explicit to see if they will automatically be handled by diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 5b08e30d49..163ba84a3e 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -2,17 +2,6 @@ require 'stringio' require 'uri' require 'active_support/test_case' -# Monkey patch Rack::Lint to support rewind -module Rack - class Lint - class InputWrapper - def rewind - @input.rewind - end - end - end -end - module ActionController module Integration #:nodoc: # An integration Session instance represents a set of requests and responses diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb index 2bccba2ba1..b94bf6ec4a 100644 --- a/actionpack/lib/action_controller/middleware_stack.rb +++ b/actionpack/lib/action_controller/middleware_stack.rb @@ -32,6 +32,8 @@ module ActionController else @klass.to_s.constantize end + rescue NameError + @klass end def active? diff --git a/actionpack/lib/action_controller/rack_ext.rb b/actionpack/lib/action_controller/rack_ext.rb new file mode 100644 index 0000000000..3b142307e9 --- /dev/null +++ b/actionpack/lib/action_controller/rack_ext.rb @@ -0,0 +1,22 @@ +module Rack + module Utils + module Multipart + class << self + def parse_multipart_with_rewind(env) + result = parse_multipart_without_rewind(env) + + begin + env['rack.input'].rewind if env['rack.input'].respond_to?(:rewind) + rescue Errno::ESPIPE + # Handles exceptions raised by input streams that cannot be rewound + # such as when using plain CGI under Apache + end + + result + end + + alias_method_chain :parse_multipart, :rewind + end + end + end +end diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_controller/rewindable_input.rb index 296d8aed22..058453ea68 100644 --- a/actionpack/lib/action_controller/rewindable_input.rb +++ b/actionpack/lib/action_controller/rewindable_input.rb @@ -15,15 +15,19 @@ module ActionController @io.rewind end + def string + @string + end + def method_missing(method, *args, &block) @io.send(method, *args, &block) end private def read_original_io - unless @str - @str = @io.read - @io = StringIO.new(@str) + unless @string + @string = @io.read + @io = StringIO.new(@string) end end end -- cgit v1.2.3 From b281a6a5b2137548501ef590379d7af5f6955d2d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 13 Jan 2009 17:26:29 -0600 Subject: Use Rack's MethodOverride lib [#1699 state:resolved] --- actionpack/lib/action_controller.rb | 1 - actionpack/lib/action_controller/middlewares.rb | 2 +- .../lib/action_controller/verb_piggybacking.rb | 24 ---------------------- 3 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 actionpack/lib/action_controller/verb_piggybacking.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index a9cd09e58d..f808bdd910 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -80,7 +80,6 @@ module ActionController autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser' autoload :UrlRewriter, 'action_controller/url_rewriter' autoload :UrlWriter, 'action_controller/url_rewriter' - autoload :VerbPiggybacking, 'action_controller/verb_piggybacking' autoload :Verification, 'action_controller/verification' module Assertions diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index 914750bc0c..0f038b8856 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -19,4 +19,4 @@ use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) } end use ActionController::RewindableInput -use ActionController::VerbPiggybacking +use Rack::MethodOverride diff --git a/actionpack/lib/action_controller/verb_piggybacking.rb b/actionpack/lib/action_controller/verb_piggybacking.rb deleted file mode 100644 index 86cde304a0..0000000000 --- a/actionpack/lib/action_controller/verb_piggybacking.rb +++ /dev/null @@ -1,24 +0,0 @@ -module ActionController - # TODO: Use Rack::MethodOverride when it is released - class VerbPiggybacking - HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) - - def initialize(app) - @app = app - end - - def call(env) - if env["REQUEST_METHOD"] == "POST" - req = Request.new(env) - if method = (req.parameters[:_method] || env["HTTP_X_HTTP_METHOD_OVERRIDE"]) - method = method.to_s.upcase - if HTTP_METHODS.include?(method) - env["REQUEST_METHOD"] = method - end - end - end - - @app.call(env) - end - end -end -- cgit v1.2.3 From 72608521871f73d6b07afa5f6f36e0dedcf1d079 Mon Sep 17 00:00:00 2001 From: Josh Date: Sun, 4 Jan 2009 14:54:35 +0000 Subject: Fix date_select within fields_for with an index [#1666 state:resolved] [Josh, Frederick Cheung] Signed-off-by: Frederick Cheung --- actionpack/lib/action_view/helpers/date_helper.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 4305617ac8..b4c1adbe76 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -860,7 +860,7 @@ module ActionView # => post[written_on(1i)] def input_name_from_type(type) prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX - prefix += "[#{@options[:index]}]" if @options[:index] + prefix += "[#{@options[:index]}]" if @options.has_key?(:index) field_name = @options[:field_name] || type if @options[:include_position] @@ -923,7 +923,7 @@ module ActionView options[:field_name] = @method_name options[:include_position] = true options[:prefix] ||= @object_name - options[:index] ||= @auto_index + options[:index] = @auto_index if @auto_index && !options.has_key?(:index) options[:datetime_separator] ||= ' — ' options[:time_separator] ||= ' : ' @@ -961,15 +961,15 @@ module ActionView class FormBuilder def date_select(method, options = {}, html_options = {}) - @template.date_select(@object_name, method, options.merge(:object => @object), html_options) + @template.date_select(@object_name, method, objectify_options(options), html_options) end def time_select(method, options = {}, html_options = {}) - @template.time_select(@object_name, method, options.merge(:object => @object), html_options) + @template.time_select(@object_name, method, objectify_options(options), html_options) end def datetime_select(method, options = {}, html_options = {}) - @template.datetime_select(@object_name, method, options.merge(:object => @object), html_options) + @template.datetime_select(@object_name, method, objectify_options(options), html_options) end end end -- cgit v1.2.3 From 78af2710695973bbd747738d175fb3b1f488df6c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 14 Jan 2009 19:00:07 -0800 Subject: Skip respond_to check so rack.input doesn't have to implement it --- actionpack/lib/action_controller/rack_ext.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/rack_ext.rb b/actionpack/lib/action_controller/rack_ext.rb index 3b142307e9..3d6f1f9256 100644 --- a/actionpack/lib/action_controller/rack_ext.rb +++ b/actionpack/lib/action_controller/rack_ext.rb @@ -6,8 +6,8 @@ module Rack result = parse_multipart_without_rewind(env) begin - env['rack.input'].rewind if env['rack.input'].respond_to?(:rewind) - rescue Errno::ESPIPE + env['rack.input'].rewind + rescue NoMethodError, Errno::ESPIPE # Handles exceptions raised by input streams that cannot be rewound # such as when using plain CGI under Apache end -- cgit v1.2.3 From fe013ce93415a88b6c23fd750bd8cbab60d6395d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 16 Jan 2009 20:36:50 -0800 Subject: Fix performance regression --- actionpack/lib/action_view/template.rb | 2 +- actionpack/lib/action_view/template_handlers.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 88ee07d95b..9d1e0d3ac5 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -204,7 +204,7 @@ module ActionView #:nodoc: private def valid_extension?(extension) - Template.template_handler_extensions.include?(extension) + !Template.registered_template_handler(extension).nil? end def find_full_path(path, load_paths) diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template_handlers.rb index d06ddd5fb5..205f8628f0 100644 --- a/actionpack/lib/action_view/template_handlers.rb +++ b/actionpack/lib/action_view/template_handlers.rb @@ -32,13 +32,17 @@ module ActionView #:nodoc: @@template_handlers.keys.map(&:to_s).sort end + def registered_template_handler(extension) + extension && @@template_handlers[extension.to_sym] + end + def register_default_template_handler(extension, klass) register_template_handler(extension, klass) @@default_template_handlers = klass end def handler_class_for_extension(extension) - (extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers + registered_template_handler(extension) || @@default_template_handlers end end end -- cgit v1.2.3 From 515a1a332808eb7c2f9c006fc1903e1e8555b7fa Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 17 Jan 2009 10:16:31 -0600 Subject: Lock middleware has been committed upstream --- actionpack/lib/action_controller.rb | 1 - actionpack/lib/action_controller/lock.rb | 16 --------------- actionpack/lib/action_controller/middlewares.rb | 2 +- actionpack/lib/action_controller/rack_ext.rb | 24 ++-------------------- actionpack/lib/action_controller/rack_ext/lock.rb | 21 +++++++++++++++++++ .../lib/action_controller/rack_ext/multipart.rb | 22 ++++++++++++++++++++ 6 files changed, 46 insertions(+), 40 deletions(-) delete mode 100644 actionpack/lib/action_controller/lock.rb create mode 100644 actionpack/lib/action_controller/rack_ext/lock.rb create mode 100644 actionpack/lib/action_controller/rack_ext/multipart.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index f808bdd910..7a71c64695 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -56,7 +56,6 @@ module ActionController autoload :Integration, 'action_controller/integration' autoload :IntegrationTest, 'action_controller/integration' autoload :Layout, 'action_controller/layout' - autoload :Lock, 'action_controller/lock' autoload :MiddlewareStack, 'action_controller/middleware_stack' autoload :MimeResponds, 'action_controller/mime_responds' autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' diff --git a/actionpack/lib/action_controller/lock.rb b/actionpack/lib/action_controller/lock.rb deleted file mode 100644 index c50762216e..0000000000 --- a/actionpack/lib/action_controller/lock.rb +++ /dev/null @@ -1,16 +0,0 @@ -module ActionController - class Lock - FLAG = 'rack.multithread'.freeze - - def initialize(app, lock = Mutex.new) - @app, @lock = app, lock - end - - def call(env) - old, env[FLAG] = env[FLAG], false - @lock.synchronize { @app.call(env) } - ensure - env[FLAG] = old - end - end -end diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index 0f038b8856..cbcb5cb3e4 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -1,4 +1,4 @@ -use "ActionController::Lock", :if => lambda { +use "Rack::Lock", :if => lambda { !ActionController::Base.allow_concurrency } diff --git a/actionpack/lib/action_controller/rack_ext.rb b/actionpack/lib/action_controller/rack_ext.rb index 3d6f1f9256..17cd08f39a 100644 --- a/actionpack/lib/action_controller/rack_ext.rb +++ b/actionpack/lib/action_controller/rack_ext.rb @@ -1,22 +1,2 @@ -module Rack - module Utils - module Multipart - class << self - def parse_multipart_with_rewind(env) - result = parse_multipart_without_rewind(env) - - begin - env['rack.input'].rewind - rescue NoMethodError, Errno::ESPIPE - # Handles exceptions raised by input streams that cannot be rewound - # such as when using plain CGI under Apache - end - - result - end - - alias_method_chain :parse_multipart, :rewind - end - end - end -end +require 'action_controller/rack_ext/lock' +require 'action_controller/rack_ext/multipart' diff --git a/actionpack/lib/action_controller/rack_ext/lock.rb b/actionpack/lib/action_controller/rack_ext/lock.rb new file mode 100644 index 0000000000..9bf1889065 --- /dev/null +++ b/actionpack/lib/action_controller/rack_ext/lock.rb @@ -0,0 +1,21 @@ +module Rack + # Rack::Lock was commited to Rack core + # http://github.com/rack/rack/commit/7409b0c + # Remove this when Rack 1.0 is released + unless defined? Lock + class Lock + FLAG = 'rack.multithread'.freeze + + def initialize(app, lock = Mutex.new) + @app, @lock = app, lock + end + + def call(env) + old, env[FLAG] = env[FLAG], false + @lock.synchronize { @app.call(env) } + ensure + env[FLAG] = old + end + end + end +end diff --git a/actionpack/lib/action_controller/rack_ext/multipart.rb b/actionpack/lib/action_controller/rack_ext/multipart.rb new file mode 100644 index 0000000000..3d6f1f9256 --- /dev/null +++ b/actionpack/lib/action_controller/rack_ext/multipart.rb @@ -0,0 +1,22 @@ +module Rack + module Utils + module Multipart + class << self + def parse_multipart_with_rewind(env) + result = parse_multipart_without_rewind(env) + + begin + env['rack.input'].rewind + rescue NoMethodError, Errno::ESPIPE + # Handles exceptions raised by input streams that cannot be rewound + # such as when using plain CGI under Apache + end + + result + end + + alias_method_chain :parse_multipart, :rewind + end + end + end +end -- cgit v1.2.3 From 29e7a0242853a5e102b6846b87723fc26a1ffb08 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 17 Jan 2009 11:12:18 -0600 Subject: Ensure any method sent to RewindableIO reads the original IO object [#1767 state:resolved] --- .../lib/action_controller/rack_ext/multipart.rb | 4 +-- .../lib/action_controller/rewindable_input.rb | 30 +++++----------------- 2 files changed, 9 insertions(+), 25 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/rack_ext/multipart.rb b/actionpack/lib/action_controller/rack_ext/multipart.rb index 3d6f1f9256..3b142307e9 100644 --- a/actionpack/lib/action_controller/rack_ext/multipart.rb +++ b/actionpack/lib/action_controller/rack_ext/multipart.rb @@ -6,8 +6,8 @@ module Rack result = parse_multipart_without_rewind(env) begin - env['rack.input'].rewind - rescue NoMethodError, Errno::ESPIPE + env['rack.input'].rewind if env['rack.input'].respond_to?(:rewind) + rescue Errno::ESPIPE # Handles exceptions raised by input streams that cannot be rewound # such as when using plain CGI under Apache end diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_controller/rewindable_input.rb index 058453ea68..36f655c51e 100644 --- a/actionpack/lib/action_controller/rewindable_input.rb +++ b/actionpack/lib/action_controller/rewindable_input.rb @@ -3,33 +3,17 @@ module ActionController class RewindableIO < ActiveSupport::BasicObject def initialize(io) @io = io - end - - def read(*args) - read_original_io - @io.read(*args) - end - - def rewind - read_original_io - @io.rewind - end - - def string - @string + @rewindable = io.is_a?(StringIO) end def method_missing(method, *args, &block) - @io.send(method, *args, &block) - end - - private - def read_original_io - unless @string - @string = @io.read - @io = StringIO.new(@string) - end + unless @rewindable + @io = StringIO.new(@io.read) + @rewindable = true end + + @io.__send__(method, *args, &block) + end end def initialize(app) -- cgit v1.2.3 From ff0a2678c4bce9da348e1263915558795e3a3640 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 17 Jan 2009 20:29:50 -0600 Subject: Build query string and POST params parser on top of Rack::Request. Also switch our multipart parser to use Racks. Moved XML, JSON, and YAML parsers into ActionController::ParamsParser middleware [#1661 state:resolved] --- actionpack/lib/action_controller.rb | 2 + actionpack/lib/action_controller/base.rb | 5 +- actionpack/lib/action_controller/middlewares.rb | 1 + actionpack/lib/action_controller/params_parser.rb | 71 +++++ actionpack/lib/action_controller/rack_ext.rb | 1 + .../lib/action_controller/rack_ext/parse_query.rb | 18 ++ actionpack/lib/action_controller/request.rb | 34 ++- actionpack/lib/action_controller/request_parser.rb | 315 --------------------- actionpack/lib/action_controller/uploaded_file.rb | 7 + .../action_controller/url_encoded_pair_parser.rb | 61 ++++ 10 files changed, 186 insertions(+), 329 deletions(-) create mode 100644 actionpack/lib/action_controller/params_parser.rb create mode 100644 actionpack/lib/action_controller/rack_ext/parse_query.rb delete mode 100644 actionpack/lib/action_controller/request_parser.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 7a71c64695..5113a7b0cb 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -58,6 +58,7 @@ module ActionController autoload :Layout, 'action_controller/layout' autoload :MiddlewareStack, 'action_controller/middleware_stack' autoload :MimeResponds, 'action_controller/mime_responds' + autoload :ParamsParser, 'action_controller/params_parser' autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Request, 'action_controller/request' @@ -74,6 +75,7 @@ module ActionController autoload :TestCase, 'action_controller/test_case' autoload :TestProcess, 'action_controller/test_process' autoload :Translation, 'action_controller/translation' + autoload :UploadedFile, 'action_controller/uploaded_file' autoload :UploadedStringIO, 'action_controller/uploaded_file' autoload :UploadedTempfile, 'action_controller/uploaded_file' autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e22114195c..7a380bd1fb 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -301,10 +301,7 @@ module ActionController #:nodoc: # A YAML parser is also available and can be turned on with: # # ActionController::Base.param_parsers[Mime::YAML] = :yaml - @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form, - Mime::URL_ENCODED_FORM => :url_encoded_form, - Mime::XML => :xml_simple, - Mime::JSON => :json } + @@param_parsers = {} cattr_accessor :param_parsers # Controls the default charset for all renders. diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index cbcb5cb3e4..30dbc26f11 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -19,4 +19,5 @@ use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) } end use ActionController::RewindableInput +use ActionController::ParamsParser use Rack::MethodOverride diff --git a/actionpack/lib/action_controller/params_parser.rb b/actionpack/lib/action_controller/params_parser.rb new file mode 100644 index 0000000000..d269fe07fa --- /dev/null +++ b/actionpack/lib/action_controller/params_parser.rb @@ -0,0 +1,71 @@ +module ActionController + class ParamsParser + ActionController::Base.param_parsers[Mime::XML] = :xml_simple + ActionController::Base.param_parsers[Mime::JSON] = :json + + def initialize(app) + @app = app + end + + def call(env) + if params = parse_formatted_parameters(env) + env["action_controller.request.request_parameters"] = params + end + + @app.call(env) + end + + private + def parse_formatted_parameters(env) + request = Request.new(env) + + return false if request.content_length.zero? + + mime_type = content_type_from_legacy_post_data_format_header(env) || request.content_type + strategy = ActionController::Base.param_parsers[mime_type] + + return false unless strategy + + case strategy + when Proc + strategy.call(request.raw_post) + when :xml_simple, :xml_node + body = request.raw_post + body.blank? ? {} : Hash.from_xml(body).with_indifferent_access + when :yaml + YAML.load(request.raw_post) + when :json + body = request.raw_post + if body.blank? + {} + else + data = ActiveSupport::JSON.decode(body) + data = {:_json => data} unless data.is_a?(Hash) + data.with_indifferent_access + end + else + false + end + rescue Exception => e # YAML, XML or Ruby code block errors + raise + { "body" => request.raw_post, + "content_type" => request.content_type, + "content_length" => request.content_length, + "exception" => "#{e.message} (#{e.class})", + "backtrace" => e.backtrace } + end + + def content_type_from_legacy_post_data_format_header(env) + if x_post_format = env['HTTP_X_POST_DATA_FORMAT'] + case x_post_format.to_s.downcase + when 'yaml' + return Mime::YAML + when 'xml' + return Mime::XML + end + end + + nil + end + end +end diff --git a/actionpack/lib/action_controller/rack_ext.rb b/actionpack/lib/action_controller/rack_ext.rb index 17cd08f39a..2ba6654e3d 100644 --- a/actionpack/lib/action_controller/rack_ext.rb +++ b/actionpack/lib/action_controller/rack_ext.rb @@ -1,2 +1,3 @@ require 'action_controller/rack_ext/lock' require 'action_controller/rack_ext/multipart' +require 'action_controller/rack_ext/parse_query' diff --git a/actionpack/lib/action_controller/rack_ext/parse_query.rb b/actionpack/lib/action_controller/rack_ext/parse_query.rb new file mode 100644 index 0000000000..2f21a57770 --- /dev/null +++ b/actionpack/lib/action_controller/rack_ext/parse_query.rb @@ -0,0 +1,18 @@ +# Rack does not automatically cleanup Safari 2 AJAX POST body +# This has not yet been commited to Rack, please +1 this ticket: +# http://rack.lighthouseapp.com/projects/22435/tickets/19 + +module Rack + module Utils + alias_method :parse_query_without_ajax_body_cleanup, :parse_query + module_function :parse_query_without_ajax_body_cleanup + + def parse_query(qs, d = '&;') + qs = qs.dup + qs.chop! if qs[-1] == 0 + qs.gsub!(/&_=$/, '') + parse_query_without_ajax_body_cleanup(qs, d) + end + module_function :parse_query + end +end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index b4ab1ccda1..cbbfca41f6 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -9,11 +9,6 @@ module ActionController class Request < Rack::Request extend ActiveSupport::Memoizable - def initialize(env) - super - @parser = ActionController::RequestParser.new(env) - end - %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR @@ -92,7 +87,11 @@ module ActionController # For backward compatibility, the post \format is extracted from the # X-Post-Data-Format HTTP header if present. def content_type - Mime::Type.lookup(@parser.content_type_without_parameters) + if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/ + Mime::Type.lookup($1.strip.downcase) + else + nil + end end memoize :content_type @@ -380,7 +379,11 @@ EOM # Read the request \body. This is useful for web services that need to # work with raw requests directly. def raw_post - @parser.raw_post + unless @env.include? 'RAW_POST_DATA' + @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i) + body.rewind if body.respond_to?(:rewind) + end + @env['RAW_POST_DATA'] end # Returns both GET and POST \parameters in a single hash. @@ -409,19 +412,30 @@ EOM @env["rack.routing_args"] ||= {} end + # The request body is an IO input stream. If the RAW_POST_DATA environment + # variable is already set, wrap it in a StringIO. def body - @parser.body + if raw_post = @env['RAW_POST_DATA'] + raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) + StringIO.new(raw_post) + else + @env['rack.input'] + end + end + + def form_data? + FORM_DATA_MEDIA_TYPES.include?(content_type.to_s) end # Override Rack's GET method to support nested query strings def GET - @parser.query_parameters + @env["action_controller.request.query_parameters"] ||= UrlEncodedPairParser.parse_query_parameters(query_string) end alias_method :query_parameters, :GET # Override Rack's POST method to support nested query strings def POST - @parser.request_parameters + @env["action_controller.request.request_parameters"] ||= UrlEncodedPairParser.parse_hash_parameters(super) end alias_method :request_parameters, :POST diff --git a/actionpack/lib/action_controller/request_parser.rb b/actionpack/lib/action_controller/request_parser.rb deleted file mode 100644 index d1739ef4d0..0000000000 --- a/actionpack/lib/action_controller/request_parser.rb +++ /dev/null @@ -1,315 +0,0 @@ -module ActionController - class RequestParser - def initialize(env) - @env = env - freeze - end - - def request_parameters - @env["action_controller.request_parser.request_parameters"] ||= parse_formatted_request_parameters - end - - def query_parameters - @env["action_controller.request_parser.query_parameters"] ||= self.class.parse_query_parameters(query_string) - end - - # Returns the query string, accounting for server idiosyncrasies. - def query_string - @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '') - end - - # The request body is an IO input stream. If the RAW_POST_DATA environment - # variable is already set, wrap it in a StringIO. - def body - if raw_post = @env['RAW_POST_DATA'] - raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) - StringIO.new(raw_post) - else - @env['rack.input'] - end - end - - # The raw content type string with its parameters stripped off. - def content_type_without_parameters - self.class.extract_content_type_without_parameters(content_type_with_parameters) - end - - def raw_post - unless @env.include? 'RAW_POST_DATA' - @env['RAW_POST_DATA'] = body.read(content_length) - body.rewind if body.respond_to?(:rewind) - end - @env['RAW_POST_DATA'] - end - - private - - def parse_formatted_request_parameters - return {} if content_length.zero? - - content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters) - - # Don't parse params for unknown requests. - return {} if content_type.blank? - - mime_type = Mime::Type.lookup(content_type) - strategy = ActionController::Base.param_parsers[mime_type] - - # Only multipart form parsing expects a stream. - body = (strategy && strategy != :multipart_form) ? raw_post : self.body - - case strategy - when Proc - strategy.call(body) - when :url_encoded_form - self.class.clean_up_ajax_request_body! body - self.class.parse_query_parameters(body) - when :multipart_form - self.class.parse_multipart_form_parameters(body, boundary, content_length, @env) - when :xml_simple, :xml_node - body.blank? ? {} : Hash.from_xml(body).with_indifferent_access - when :yaml - YAML.load(body) - when :json - if body.blank? - {} - else - data = ActiveSupport::JSON.decode(body) - data = {:_json => data} unless data.is_a?(Hash) - data.with_indifferent_access - end - else - {} - end - rescue Exception => e # YAML, XML or Ruby code block errors - raise - { "body" => body, - "content_type" => content_type_with_parameters, - "content_length" => content_length, - "exception" => "#{e.message} (#{e.class})", - "backtrace" => e.backtrace } - end - - def content_length - @env['CONTENT_LENGTH'].to_i - end - - # The raw content type string. Use when you need parameters such as - # charset or boundary which aren't included in the content_type MIME type. - # Overridden by the X-POST_DATA_FORMAT header for backward compatibility. - def content_type_with_parameters - content_type_from_legacy_post_data_format_header || @env['CONTENT_TYPE'].to_s - end - - def content_type_from_legacy_post_data_format_header - if x_post_format = @env['HTTP_X_POST_DATA_FORMAT'] - case x_post_format.to_s.downcase - when 'yaml'; 'application/x-yaml' - when 'xml'; 'application/xml' - end - end - end - - class << self - def parse_query_parameters(query_string) - return {} if query_string.blank? - - pairs = query_string.split('&').collect do |chunk| - next if chunk.empty? - key, value = chunk.split('=', 2) - next if key.empty? - value = value.nil? ? nil : CGI.unescape(value) - [ CGI.unescape(key), value ] - end.compact - - UrlEncodedPairParser.new(pairs).result - end - - def parse_request_parameters(params) - parser = UrlEncodedPairParser.new - - params = params.dup - until params.empty? - for key, value in params - if key.blank? - params.delete key - elsif !key.include?('[') - # much faster to test for the most common case first (GET) - # and avoid the call to build_deep_hash - parser.result[key] = get_typed_value(value[0]) - params.delete key - elsif value.is_a?(Array) - parser.parse(key, get_typed_value(value.shift)) - params.delete key if value.empty? - else - raise TypeError, "Expected array, found #{value.inspect}" - end - end - end - - parser.result - end - - def parse_multipart_form_parameters(body, boundary, body_size, env) - parse_request_parameters(read_multipart(body, boundary, body_size, env)) - end - - def extract_multipart_boundary(content_type_with_parameters) - if content_type_with_parameters =~ MULTIPART_BOUNDARY - ['multipart/form-data', $1.dup] - else - extract_content_type_without_parameters(content_type_with_parameters) - end - end - - def extract_content_type_without_parameters(content_type_with_parameters) - $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/ - end - - def clean_up_ajax_request_body!(body) - body.chop! if body[-1] == 0 - body.gsub!(/&_=$/, '') - end - - - private - def get_typed_value(value) - case value - when String - value - when NilClass - '' - when Array - value.map { |v| get_typed_value(v) } - else - if value.respond_to? :original_filename - # Uploaded file - if value.original_filename - value - # Multipart param - else - result = value.read - value.rewind - result - end - # Unknown value, neither string nor multipart. - else - raise "Unknown form value: #{value.inspect}" - end - end - end - - MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n - - EOL = "\015\012" - - def read_multipart(body, boundary, body_size, env) - params = Hash.new([]) - boundary = "--" + boundary - quoted_boundary = Regexp.quote(boundary) - buf = "" - bufsize = 10 * 1024 - boundary_end="" - - # start multipart/form-data - body.binmode if defined? body.binmode - case body - when File - body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding) - when StringIO - body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding) - end - boundary_size = boundary.size + EOL.size - body_size -= boundary_size - status = body.read(boundary_size) - if nil == status - raise EOFError, "no content body" - elsif boundary + EOL != status - raise EOFError, "bad content body" - end - - loop do - head = nil - content = - if 10240 < body_size - UploadedTempfile.new("CGI") - else - UploadedStringIO.new - end - content.binmode if defined? content.binmode - - until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf) - - if (not head) and /#{EOL}#{EOL}/n.match(buf) - buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do - head = $1.dup - "" - end - next - end - - if head and ( (EOL + boundary + EOL).size < buf.size ) - content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] - buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" - end - - c = if bufsize < body_size - body.read(bufsize) - else - body.read(body_size) - end - if c.nil? || c.empty? - raise EOFError, "bad content body" - end - buf.concat(c) - body_size -= c.size - end - - buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do - content.print $1 - if "--" == $2 - body_size = -1 - end - boundary_end = $2.dup - "" - end - - content.rewind - - head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni - if filename = $1 || $2 - if /Mac/ni.match(env['HTTP_USER_AGENT']) and - /Mozilla/ni.match(env['HTTP_USER_AGENT']) and - (not /MSIE/ni.match(env['HTTP_USER_AGENT'])) - filename = CGI.unescape(filename) - end - content.original_path = filename.dup - end - - head =~ /Content-Type: ([^\r]*)/ni - content.content_type = $1.dup if $1 - - head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni - name = $1.dup if $1 - - if params.has_key?(name) - params[name].push(content) - else - params[name] = [content] - end - break if body_size == -1 - end - raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ - - begin - body.rewind if body.respond_to?(:rewind) - rescue Errno::ESPIPE - # Handles exceptions raised by input streams that cannot be rewound - # such as when using plain CGI under Apache - end - - params - end - end # class << self - end -end diff --git a/actionpack/lib/action_controller/uploaded_file.rb b/actionpack/lib/action_controller/uploaded_file.rb index ea4845c68f..376ba3621a 100644 --- a/actionpack/lib/action_controller/uploaded_file.rb +++ b/actionpack/lib/action_controller/uploaded_file.rb @@ -7,6 +7,13 @@ module ActionController end end + def self.extended(object) + object.class_eval do + attr_accessor :original_path, :content_type + alias_method :local_path, :path + end + end + # Take the basename of the upload's original filename. # This handles the full Windows paths given by Internet Explorer # (and perhaps other broken user agents) without affecting diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb index 9883ad0d85..b63dca987d 100644 --- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb +++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb @@ -1,5 +1,66 @@ module ActionController class UrlEncodedPairParser < StringScanner #:nodoc: + class << self + def parse_query_parameters(query_string) + return {} if query_string.blank? + + pairs = query_string.split('&').collect do |chunk| + next if chunk.empty? + key, value = chunk.split('=', 2) + next if key.empty? + value = value.nil? ? nil : CGI.unescape(value) + [ CGI.unescape(key), value ] + end.compact + + new(pairs).result + end + + def parse_hash_parameters(params) + parser = new + + params = params.dup + until params.empty? + for key, value in params + if key.blank? + params.delete(key) + elsif value.is_a?(Array) + parser.parse(key, get_typed_value(value.shift)) + params.delete(key) if value.empty? + else + parser.parse(key, get_typed_value(value)) + params.delete(key) + end + end + end + + parser.result + end + + private + def get_typed_value(value) + case value + when String + value + when NilClass + '' + when Array + value.map { |v| get_typed_value(v) } + when Hash + if value.has_key?(:tempfile) + upload = value[:tempfile] + upload.extend(UploadedFile) + upload.original_path = value[:filename] + upload.content_type = value[:type] + upload + else + nil + end + else + raise "Unknown form value: #{value.inspect}" + end + end + end + attr_reader :top, :parent, :result def initialize(pairs = []) -- cgit v1.2.3 From 41af606db385abe429888c5aca8b2e86c8830c24 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 18 Jan 2009 05:16:39 +0000 Subject: Remove script/performance/profiler in favour of performance integration tests. To continue using script/performance/profiler, install the request_profiler plugin : script/plugin install git://github.com/rails/request_profiler.git --- .../lib/action_controller/request_profiler.rb | 168 --------------------- 1 file changed, 168 deletions(-) delete mode 100644 actionpack/lib/action_controller/request_profiler.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb deleted file mode 100644 index 80cd55334f..0000000000 --- a/actionpack/lib/action_controller/request_profiler.rb +++ /dev/null @@ -1,168 +0,0 @@ -require 'optparse' - -module ActionController - class RequestProfiler - # Wrap up the integration session runner. - class Sandbox - include Integration::Runner - - def self.benchmark(n, script) - new(script).benchmark(n) - end - - def initialize(script_path) - @quiet = false - define_run_method(script_path) - reset! - end - - def benchmark(n, profiling = false) - @quiet = true - print ' ' - - ms = Benchmark.ms do - n.times do |i| - run(profiling) - print_progress(i) - end - end - - puts - ms - ensure - @quiet = false - end - - def say(message) - puts " #{message}" unless @quiet - end - - private - def define_run_method(script_path) - script = File.read(script_path) - - source = <<-end_source - def run(profiling = false) - if profiling - RubyProf.resume do - #{script} - end - else - #{script} - end - - old_request_count = request_count - reset! - self.request_count = old_request_count - end - end_source - - instance_eval source, script_path, 1 - end - - def print_progress(i) - print "\n " if i % 60 == 0 - print ' ' if i % 10 == 0 - print '.' - $stdout.flush - end - end - - - attr_reader :options - - def initialize(options = {}) - @options = default_options.merge(options) - end - - - def self.run(args = nil, options = {}) - profiler = new(options) - profiler.parse_options(args) if args - profiler.run - end - - def run - sandbox = Sandbox.new(options[:script]) - - puts 'Warming up once' - - elapsed = warmup(sandbox) - puts '%.0f ms, %d requests, %d req/sec' % [elapsed, sandbox.request_count, 1000 * sandbox.request_count / elapsed] - puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x" - - options[:benchmark] ? benchmark(sandbox) : profile(sandbox) - end - - def profile(sandbox) - load_ruby_prof - - benchmark(sandbox, true) - results = RubyProf.stop - - show_profile_results results - results - end - - def benchmark(sandbox, profiling = false) - sandbox.request_count = 0 - elapsed = sandbox.benchmark(options[:n], profiling) - count = sandbox.request_count.to_i - puts '%.0f ms, %d requests, %d req/sec' % [elapsed, count, 1000 * count / elapsed] - end - - def warmup(sandbox) - Benchmark.ms { sandbox.run(false) } - end - - def default_options - { :n => 100, :open => 'open %s &' } - end - - # Parse command-line options - def parse_options(args) - OptionParser.new do |opt| - opt.banner = "USAGE: #{$0} [options] [session script path]" - - opt.on('-n', '--times [100]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i if v } - opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v } - opt.on('-m', '--measure [mode]', 'Which ruby-prof measure mode to use: process_time, wall_time, cpu_time, allocations, or memory. Defaults to process_time.') { |v| options[:measure] = v } - opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v } - opt.on('-h', '--help', 'Show this help') { puts opt; exit } - - opt.parse args - - if args.empty? - puts opt - exit - end - options[:script] = args.pop - end - end - - protected - def load_ruby_prof - begin - gem 'ruby-prof', '>= 0.6.1' - require 'ruby-prof' - if mode = options[:measure] - RubyProf.measure_mode = RubyProf.const_get(mode.upcase) - end - rescue LoadError - abort '`gem install ruby-prof` to use the profiler' - end - end - - def show_profile_results(results) - File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file| - RubyProf::GraphHtmlPrinter.new(results).print(file) - `#{options[:open] % file.path}` if options[:open] - end - - File.open "#{RAILS_ROOT}/tmp/profile-flat.txt", 'w' do |file| - RubyProf::FlatPrinter.new(results).print(file) - `#{options[:open] % file.path}` if options[:open] - end - end - end -end -- cgit v1.2.3 From 085991891e610ed0ab616ce434eabf42a9437039 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 18 Jan 2009 05:28:21 +0000 Subject: Bump up the year in MIT license files --- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_pack.rb | 2 +- actionpack/lib/action_view.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 5113a7b0cb..dca07a0afc 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2008 David Heinemeier Hansson +# Copyright (c) 2004-2009 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionpack/lib/action_pack.rb b/actionpack/lib/action_pack.rb index c7fd3092e7..b90f89be39 100644 --- a/actionpack/lib/action_pack.rb +++ b/actionpack/lib/action_pack.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2008 David Heinemeier Hansson +# Copyright (c) 2004-2009 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 210a5f1a93..0b710bd8d9 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2008 David Heinemeier Hansson +# Copyright (c) 2004-2009 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the -- cgit v1.2.3 From 39e1ac658efc80e4c54abef4f1c7679e4b3dc2ac Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 18 Jan 2009 18:10:58 +0000 Subject: Merge docrails --- .../assertions/selector_assertions.rb | 23 ++-- actionpack/lib/action_controller/flash.rb | 12 +- actionpack/lib/action_controller/request.rb | 12 +- actionpack/lib/action_controller/routing.rb | 5 +- .../lib/action_controller/session_management.rb | 2 +- .../lib/action_view/helpers/asset_tag_helper.rb | 124 ++++++++++++--------- 6 files changed, 106 insertions(+), 72 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb index 7f8fe9ab19..0d56ea5ef7 100644 --- a/actionpack/lib/action_controller/assertions/selector_assertions.rb +++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb @@ -109,20 +109,27 @@ module ActionController # starting from (and including) that element and all its children in # depth-first order. # - # If no element if specified, calling +assert_select+ will select from the - # response HTML. Calling #assert_select inside an +assert_select+ block will - # run the assertion for each element selected by the enclosing assertion. + # If no element if specified, calling +assert_select+ selects from the + # response HTML unless +assert_select+ is called from within an +assert_select+ block. + # + # When called with a block +assert_select+ passes an array of selected elements + # to the block. Calling +assert_select+ from the block, with no element specified, + # runs the assertion on the complete set of elements selected by the enclosing assertion. + # Alternatively the array may be iterated through so that +assert_select+ can be called + # separately for each element. + # # # ==== Example - # assert_select "ol>li" do |elements| + # If the response contains two ordered lists, each with four list elements then: + # assert_select "ol" do |elements| # elements.each do |element| - # assert_select element, "li" + # assert_select element, "li", 4 # end # end # - # Or for short: - # assert_select "ol>li" do - # assert_select "li" + # will pass, as will: + # assert_select "ol" do + # assert_select "li", 8 # end # # The selector may be a CSS selector expression (String), an expression diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/flash.rb index 9856dbed2a..56ee9c67e2 100644 --- a/actionpack/lib/action_controller/flash.rb +++ b/actionpack/lib/action_controller/flash.rb @@ -4,20 +4,22 @@ module ActionController #:nodoc: # action that sets flash[:notice] = "Successfully created" before redirecting to a display action that can # then expose the flash to its template. Actually, that exposure is automatically done. Example: # - # class WeblogController < ActionController::Base + # class PostsController < ActionController::Base # def create # # save post # flash[:notice] = "Successfully created post" - # redirect_to :action => "display", :params => { :id => post.id } + # redirect_to posts_path(@post) # end # - # def display + # def show # # doesn't need to assign the flash notice to the template, that's done automatically # end # end # - # display.erb - # <% if flash[:notice] %>
<%= flash[:notice] %>
<% end %> + # show.html.erb + # <% if flash[:notice] %> + #
<%= flash[:notice] %>
+ # <% end %> # # This example just places a string in the flash, but you can put any object in there. And of course, you can put as # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed. diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index cbbfca41f6..09dcd684e8 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -29,16 +29,18 @@ module ActionController HTTP_METHODS = %w(get head put post delete options) HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h } - # The true HTTP request \method as a lowercase symbol, such as :get. - # UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS. + # Returns the true HTTP request \method as a lowercase symbol, such as + # :get. If the request \method is not listed in the HTTP_METHODS + # constant above, an UnknownHttpMethod exception is raised. def request_method HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") end memoize :request_method - # The HTTP request \method as a lowercase symbol, such as :get. - # Note, HEAD is returned as :get since the two are functionally - # equivalent from the application's perspective. + # Returns the HTTP request \method used for action processing as a + # lowercase symbol, such as :post. (Unlike #request_method, this + # method returns :get for a HEAD request because the two are + # functionally equivalent from the application's perspective.) def method request_method == :head ? :get : request_method end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index da9b56fdf9..a2141a77dc 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -193,9 +193,8 @@ module ActionController # # map.connect '*path' , :controller => 'blog' , :action => 'unrecognized?' # - # will glob all remaining parts of the route that were not recognized earlier. This idiom - # must appear at the end of the path. The globbed values are in params[:path] in - # this case. + # will glob all remaining parts of the route that were not recognized earlier. + # The globbed values are in params[:path] as an array of path segments. # # == Route conditions # diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session_management.rb index f06a0da75c..b556f04649 100644 --- a/actionpack/lib/action_controller/session_management.rb +++ b/actionpack/lib/action_controller/session_management.rb @@ -37,7 +37,7 @@ module ActionController #:nodoc: # Returns the hash used to configure the session. Example use: # - # ActionController::Base.session_options[:session_secure] = true # session only available over HTTPS + # ActionController::Base.session_options[:secure] = true # session only available over HTTPS def session_options @session_options ||= {} end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 58f8cca6be..f6abea38ed 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -6,54 +6,70 @@ module ActionView module Helpers #:nodoc: # This module provides methods for generating HTML that links views to assets such # as images, javascripts, stylesheets, and feeds. These methods do not verify - # the assets exist before linking to them. + # the assets exist before linking to them: + # + # image_tag("rails.png") + # # => Rails src= + # stylesheet_link_tag("application") + # # => # # === Using asset hosts + # # By default, Rails links to these assets on the current host in the public - # folder, but you can direct Rails to link to assets from a dedicated assets server by - # setting ActionController::Base.asset_host in your config/environment.rb. For example, - # let's say your asset host is assets.example.com. + # folder, but you can direct Rails to link to assets from a dedicated asset + # server by setting ActionController::Base.asset_host in the application + # configuration, typically in config/environments/production.rb. + # For example, you'd define assets.example.com to be your asset + # host this way: # # ActionController::Base.asset_host = "assets.example.com" + # + # Helpers take that into account: + # # image_tag("rails.png") - # => Rails + # # => Rails # stylesheet_link_tag("application") - # => + # # => # - # This is useful since browsers typically open at most two connections to a single host, - # which means your assets often wait in single file for their turn to load. You can - # alleviate this by using a %d wildcard in asset_host (for example, "assets%d.example.com") - # to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com") - # so browsers will open eight connections rather than two. + # Browsers typically open at most two simultaneous connections to a single + # host, which means your assets often have to wait for other assets to finish + # downloading. You can alleviate this by using a %d wildcard in the + # +asset_host+. For example, "assets%d.example.com". If that wildcard is + # present Rails distributes asset requests among the corresponding four hosts + # "assets0.example.com", ..., "assets3.example.com". With this trick browsers + # will open eight simultaneous connections rather than two. # # image_tag("rails.png") - # => Rails + # # => Rails # stylesheet_link_tag("application") - # => + # # => # - # To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME - # the wildcard to a single asset host. You can read more about setting up your DNS CNAME records from - # your ISP. + # To do this, you can either setup four actual hosts, or you can use wildcard + # DNS to CNAME the wildcard to a single asset host. You can read more about + # setting up your DNS CNAME records from your ISP. # # Note: This is purely a browser performance optimization and is not meant # for server load balancing. See http://www.die.net/musings/page_load_time/ # for background. # - # Alternatively, you can exert more control over the asset host by setting asset_host to a proc - # that takes a single source argument. This is useful if you are unable to setup 4 actual hosts or have - # fewer/more than 4 hosts. The example proc below generates http://assets1.example.com and - # http://assets2.example.com randomly. + # Alternatively, you can exert more control over the asset host by setting + # +asset_host+ to a proc like this: # - # ActionController::Base.asset_host = Proc.new { |source| "http://assets#{rand(2) + 1}.example.com" } + # ActionController::Base.asset_host = Proc.new { |source| + # "http://assets#{rand(2) + 1}.example.com" + # } # image_tag("rails.png") - # => Rails + # # => Rails # stylesheet_link_tag("application") - # => + # # => # - # The proc takes a source parameter (which is the path of the source asset) and an optional - # request parameter (which is an entire instance of an ActionController::AbstractRequest - # subclass). This can be used to generate a particular asset host depending on the asset path and the particular - # request. + # The example above generates "http://assets1.example.com" and + # "http://assets2.example.com" randomly. This option is useful for example if + # you need fewer/more than four hosts, custom host names, etc. + # + # As you see the proc takes a +source+ parameter. That's a string with the + # absolute path of the asset with any extensions and timestamps in place, + # for example "/images/rails.png?1230601161". # # ActionController::Base.asset_host = Proc.new { |source| # if source.starts_with?('/images') @@ -63,14 +79,16 @@ module ActionView # end # } # image_tag("rails.png") - # => Rails + # # => Rails # stylesheet_link_tag("application") - # => + # # => # - # The optional request parameter to the proc is useful in particular for serving assets from an - # SSL-protected page. The example proc below disables asset hosting for HTTPS connections, while still sending - # assets for plain HTTP requests from asset hosts. This is useful for avoiding mixed media warnings when serving - # non-HTTP assets from HTTPS web pages when you don't have an SSL certificate for each of the asset hosts. + # Alternatively you may ask for a second parameter +request+. That one is + # particularly useful for serving assets from an SSL-protected page. The + # example proc below disables asset hosting for HTTPS connections, while + # still sending assets for plain HTTP requests from asset hosts. If you don't + # have SSL certificates for each of the asset hosts this technique allows you + # to avoid warnings in the client about mixed media. # # ActionController::Base.asset_host = Proc.new { |source, request| # if request.ssl? @@ -80,7 +98,8 @@ module ActionView # end # } # - # You can also implement a custom asset host object that responds to the call method and tasks one or two parameters just like the proc. + # You can also implement a custom asset host object that responds to +call+ + # and takes either one or two parameters just like the proc. # # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new( # "http://asset%d.example.com", "https://asset1.example.com" @@ -88,24 +107,29 @@ module ActionView # # === Using asset timestamps # - # By default, Rails will append all asset paths with that asset's timestamp. This allows you to set a cache-expiration date for the - # asset far into the future, but still be able to instantly invalidate it by simply updating the file (and hence updating the timestamp, - # which then updates the URL as the timestamp is part of that, which in turn busts the cache). + # By default, Rails appends asset's timestamps to all asset paths. This allows + # you to set a cache-expiration date for the asset far into the future, but + # still be able to instantly invalidate it by simply updating the file (and + # hence updating the timestamp, which then updates the URL as the timestamp + # is part of that, which in turn busts the cache). # - # It's the responsibility of the web server you use to set the far-future expiration date on cache assets that you need to take - # advantage of this feature. Here's an example for Apache: + # It's the responsibility of the web server you use to set the far-future + # expiration date on cache assets that you need to take advantage of this + # feature. Here's an example for Apache: # - # # Asset Expiration - # ExpiresActive On - # - # ExpiresDefault "access plus 1 year" - # + # # Asset Expiration + # ExpiresActive On + # + # ExpiresDefault "access plus 1 year" + # # - # Also note that in order for this to work, all your application servers must return the same timestamps. This means that they must - # have their clocks synchronized. If one of them drift out of sync, you'll see different timestamps at random and the cache won't - # work. Which means that the browser will request the same assets over and over again even thought they didn't change. You can use - # something like Live HTTP Headers for Firefox to verify that the cache is indeed working (and that the assets are not being - # requested over and over). + # Also note that in order for this to work, all your application servers must + # return the same timestamps. This means that they must have their clocks + # synchronized. If one of them drifts out of sync, you'll see different + # timestamps at random and the cache won't work. In that case the browser + # will request the same assets over and over again even thought they didn't + # change. You can use something like Live HTTP Headers for Firefox to verify + # that the cache is indeed working. module AssetTagHelper ASSETS_DIR = defined?(Rails.public_path) ? Rails.public_path : "public" JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts" @@ -117,7 +141,7 @@ module ActionView # :atom. Control the link options in url_for format using the # +url_options+. You can modify the LINK tag itself in +tag_options+. # - # ==== Options: + # ==== Options # * :rel - Specify the relation of this link, defaults to "alternate" # * :type - Override the auto-generated mime type # * :title - Specify the title of the link, defaults to the +type+ -- cgit v1.2.3 From 9cefd5ea0c21595d73762b5d60a760a3ed9fe8bf Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 19 Jan 2009 18:53:14 +0000 Subject: Deprecate ActionController::Base#session_enabled? --- actionpack/lib/action_controller/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 7a380bd1fb..50b965ce4c 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -644,7 +644,7 @@ module ActionController #:nodoc: end def session_enabled? - request.session_options && request.session_options[:disabled] != false + ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller) end self.view_paths = [] -- cgit v1.2.3 From c090e5e0755bea3a7cd7135329f8dae6094810b6 Mon Sep 17 00:00:00 2001 From: Cody Fauser Date: Tue, 20 Jan 2009 11:50:43 -0600 Subject: Restore cookie store httponly default to true. Remove extraneous dup of options on initialization [#1784 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/session/cookie_store.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index e061c4d4a1..6ad6369950 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -45,7 +45,7 @@ module ActionController :domain => nil, :path => "/", :expire_after => nil, - :httponly => false + :httponly => true }.freeze ENV_SESSION_KEY = "rack.session".freeze @@ -56,8 +56,6 @@ module ActionController class CookieOverflow < StandardError; end def initialize(app, options = {}) - options = options.dup - # Process legacy CGI options options = options.symbolize_keys if options.has_key?(:session_path) -- cgit v1.2.3 From 01f06fc7f4dda52035d5a2273d402d8555a897a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Tue, 20 Jan 2009 12:38:25 -0600 Subject: Don't let empty Tempfiles come through as uploaded files [#1785 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/url_encoded_pair_parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb index b63dca987d..93ab2255da 100644 --- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb +++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb @@ -46,7 +46,7 @@ module ActionController when Array value.map { |v| get_typed_value(v) } when Hash - if value.has_key?(:tempfile) + if value.has_key?(:tempfile) && value[:tempfile].size > 0 upload = value[:tempfile] upload.extend(UploadedFile) upload.original_path = value[:filename] -- cgit v1.2.3 From 7e4d13d357b1e8bdf42e80359de0e480ec9f5008 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 20 Jan 2009 20:19:52 -0600 Subject: Add MiddlewareStack#swap config.middleware.swap ActionController::Session::CookieStore, MySessionStore --- actionpack/lib/action_controller/middleware_stack.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb index b94bf6ec4a..dbc2fda41e 100644 --- a/actionpack/lib/action_controller/middleware_stack.rb +++ b/actionpack/lib/action_controller/middleware_stack.rb @@ -75,17 +75,22 @@ module ActionController block.call(self) if block_given? end - def insert(index, *objs) + def insert(index, *args, &block) index = self.index(index) unless index.is_a?(Integer) - objs = objs.map { |obj| Middleware.new(obj) } - super(index, *objs) + middleware = Middleware.new(*args, &block) + super(index, middleware) end alias_method :insert_before, :insert - def insert_after(index, *objs) + def insert_after(index, *args, &block) index = self.index(index) unless index.is_a?(Integer) - insert(index + 1, *objs) + insert(index + 1, *args, &block) + end + + def swap(target, *args, &block) + insert_before(target, *args, &block) + delete(target) end def use(*args, &block) -- cgit v1.2.3 From a8ad6568f9fe21668df9b6b631c0cd9783cb5ab3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 20 Jan 2009 20:34:35 -0600 Subject: Allow empty files to be uploaded --- actionpack/lib/action_controller/url_encoded_pair_parser.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb index 93ab2255da..57594c4259 100644 --- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb +++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb @@ -46,7 +46,7 @@ module ActionController when Array value.map { |v| get_typed_value(v) } when Hash - if value.has_key?(:tempfile) && value[:tempfile].size > 0 + if value.has_key?(:tempfile) && value[:filename].any? upload = value[:tempfile] upload.extend(UploadedFile) upload.original_path = value[:filename] -- cgit v1.2.3 From ae3a93ad897fa9c481ee16454e9e29fe0a9e3493 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 21 Jan 2009 12:29:41 -0600 Subject: Missed RequestParser in ff0a267 --- actionpack/lib/action_controller.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index dca07a0afc..3e77970367 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -63,7 +63,6 @@ module ActionController autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Request, 'action_controller/request' autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection' - autoload :RequestParser, 'action_controller/request_parser' autoload :Rescue, 'action_controller/rescue' autoload :Resources, 'action_controller/resources' autoload :Response, 'action_controller/response' -- cgit v1.2.3 From 73cc5f270a5c2a2eab76c6c02615fec608822494 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 21 Jan 2009 12:44:07 -0600 Subject: Setup ActiveRecord QueryCache middleware in the initializer --- actionpack/lib/action_controller/middlewares.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index 30dbc26f11..f9cfc2b18e 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -4,8 +4,6 @@ use "Rack::Lock", :if => lambda { use "ActionController::Failsafe" -use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) } - ["ActionController::Session::CookieStore", "ActionController::Session::MemCacheStore", "ActiveRecord::SessionStore"].each do |store| @@ -18,6 +16,6 @@ use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) } ) end -use ActionController::RewindableInput -use ActionController::ParamsParser -use Rack::MethodOverride +use "ActionController::RewindableInput" +use "ActionController::ParamsParser" +use "Rack::MethodOverride" -- cgit v1.2.3