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 From a98cd7ca9b2f24a4500963e58ba5c37d6bdf9259 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 25 Jan 2009 22:50:02 -0600 Subject: Add localized templates # Default locale app/views/messages/index.html.erb # I18n.locale is set to :da (Danish) app/views/messages/index.da.html.erb --- actionpack/lib/action_view/paths.rb | 6 +++- actionpack/lib/action_view/template.rb | 56 +++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 15 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 19207e7262..cb351620d2 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -37,7 +37,11 @@ module ActionView #:nodoc: template_path = original_template_path.sub(/^\//, '') each do |load_path| - if format && (template = load_path["#{template_path}.#{format}"]) + if format && (template = load_path["#{template_path}.#{I18n.locale}.#{format}"]) + return template + elsif format && (template = load_path["#{template_path}.#{format}"]) + return template + elsif template = load_path["#{template_path}.#{I18n.locale}"] return template elsif template = load_path[template_path] return template diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 9d1e0d3ac5..6f3bf576eb 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -93,13 +93,14 @@ module ActionView #:nodoc: @@exempt_from_layout.merge(regexps) end - attr_accessor :filename, :load_path, :base_path, :name, :format, :extension + attr_accessor :filename, :load_path, :base_path + attr_accessor :locale, :name, :format, :extension delegate :to_s, :to => :path def initialize(template_path, load_paths = []) template_path = template_path.dup @load_path, @filename = find_full_path(template_path, load_paths) - @base_path, @name, @format, @extension = split(template_path) + @base_path, @name, @locale, @format, @extension = split(template_path) @base_path.to_s.gsub!(/\/$/, '') # Push to split method # Extend with partial super powers @@ -137,17 +138,17 @@ module ActionView #:nodoc: memoize :mime_type def path - [base_path, [name, format, extension].compact.join('.')].compact.join('/') + [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/') end memoize :path def path_without_extension - [base_path, [name, format].compact.join('.')].compact.join('/') + [base_path, [name, locale, format].compact.join('.')].compact.join('/') end memoize :path_without_extension def path_without_format_and_extension - [base_path, name].compact.join('/') + [base_path, [name, locale].compact.join('.')].compact.join('/') end memoize :path_without_format_and_extension @@ -207,6 +208,10 @@ module ActionView #:nodoc: !Template.registered_template_handler(extension).nil? end + def valid_locale?(locale) + I18n.available_locales.include?(locale.to_sym) + end + def find_full_path(path, load_paths) load_paths = Array(load_paths) + [nil] load_paths.each do |load_path| @@ -217,19 +222,42 @@ module ActionView #:nodoc: end # Returns file split into an array - # [base_path, name, format, extension] + # [base_path, name, locale, format, extension] def split(file) - if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/) - if valid_extension?(m[5]) # Multipart formats - [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]] - elsif valid_extension?(m[4]) # Single format - [m[1], m[2], m[3], m[4]] - elsif valid_extension?(m[3]) # No format - [m[1], m[2], nil, m[3]] + if m = file.match(/^(.*\/)?([^\.]+)\.(.*)$/) + base_path = m[1] + name = m[2] + extensions = m[3] + else + return + end + + locale = nil + format = nil + extension = nil + + if m = extensions.match(/^(\w+)?\.?(\w+)?\.?(\w+)?\.?/) + if valid_locale?(m[1]) && m[2] && valid_extension?(m[3]) # All three + locale = m[1] + format = m[2] + extension = m[3] + elsif m[1] && m[2] && valid_extension?(m[3]) # Multipart formats + format = "#{m[1]}.#{m[2]}" + extension = m[3] + elsif valid_extension?(m[1]) # Just extension + extension = m[1] + elsif valid_locale?(m[1]) && valid_extension?(m[2]) # locale and extension + locale = m[1] + extension = m[2] + elsif valid_extension?(m[2]) # format and extension + format = m[1] + extension = m[2] else # No extension - [m[1], m[2], m[3], nil] + format = m[1] end end + + [base_path, name, locale, format, extension] end end end -- cgit v1.2.3 From 57f0b859004698a38d6aa7d5cebc00de3fcb346d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 26 Jan 2009 13:15:29 -0600 Subject: Fix for failing ActionMailer multipart tests --- actionpack/lib/action_view/template.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 6f3bf576eb..1361a969a9 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -244,14 +244,14 @@ module ActionView #:nodoc: elsif m[1] && m[2] && valid_extension?(m[3]) # Multipart formats format = "#{m[1]}.#{m[2]}" extension = m[3] - elsif valid_extension?(m[1]) # Just extension - extension = m[1] elsif valid_locale?(m[1]) && valid_extension?(m[2]) # locale and extension locale = m[1] extension = m[2] elsif valid_extension?(m[2]) # format and extension format = m[1] extension = m[2] + elsif valid_extension?(m[1]) # Just extension + extension = m[1] else # No extension format = m[1] end -- cgit v1.2.3 From a1ac635d9655cd26f86593263c3e9b956d330141 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 27 Jan 2009 11:08:29 -0600 Subject: Ensure the full path is used when searching for layouts [#1803 state:resolved] --- actionpack/lib/action_controller/layout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index 159c5c7326..183d56c2e8 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -179,7 +179,7 @@ module ActionController #:nodoc: end def layout_list #:nodoc: - Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] } + Array(view_paths).sum([]) { |path| Dir["#{path.to_str}/layouts/**/*"] } end def find_layout(layout, *formats) #:nodoc: -- cgit v1.2.3 From 17db28f1ca0597fa1e9abdcacd934ca27d589fb9 Mon Sep 17 00:00:00 2001 From: Yaroslav Markin Date: Thu, 1 Jan 2009 21:29:11 +0300 Subject: Improve i18n support for number_to_human_size helper: * now using pluralization properly * storage unit translations moved to number.human.storage_units.units * introduced number.human.storage_units.format for languages that do not follow "{{number}} {{unit}}" format (Japanese) NOTE: I18n table changed, you will need to update your translations. [#1634 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_view/helpers/number_helper.rb | 45 ++++++++++++++-------- actionpack/lib/action_view/locale/en.yml | 13 ++++++- 2 files changed, 40 insertions(+), 18 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 3e734ccaab..e622f97b9e 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -220,6 +220,8 @@ module ActionView end end + STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze + # Formats the bytes in +size+ into a more understandable representation # (e.g., giving it 1500 yields 1.5 KB). This method is useful for # reporting file sizes to users. This method returns nil if @@ -247,7 +249,7 @@ module ActionView # number_to_human_size(1234567, 2) # => 1.18 MB # number_to_human_size(483989, 0) # => 473 KB def number_to_human_size(number, *args) - return number.nil? ? nil : pluralize(number.to_i, "Byte") if number.to_i < 1024 + return nil if number.nil? options = args.extract_options! options.symbolize_keys! @@ -255,7 +257,6 @@ module ActionView defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {} human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {} defaults = defaults.merge(human) - storage_units = I18n.translate(:'number.human.storage_units', :locale => options[:locale], :raise => true) unless args.empty? ActiveSupport::Deprecation.warn('number_to_human_size takes an option hash ' + @@ -267,22 +268,32 @@ module ActionView separator ||= (options[:separator] || defaults[:separator]) delimiter ||= (options[:delimiter] || defaults[:delimiter]) - max_exp = storage_units.size - 1 - number = Float(number) - exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024 - exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit - number /= 1024 ** exponent - unit = storage_units[exponent] + storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true) - begin - escaped_separator = Regexp.escape(separator) - number_with_precision(number, - :precision => precision, - :separator => separator, - :delimiter => delimiter - ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + " #{unit}" - rescue - number + if number.to_i < 1024 + unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true) + storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit) + else + max_exp = STORAGE_UNITS.size - 1 + number = Float(number) + exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024 + exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit + number /= 1024 ** exponent + + unit_key = STORAGE_UNITS[exponent] + unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true) + + begin + escaped_separator = Regexp.escape(separator) + formatted_number = number_with_precision(number, + :precision => precision, + :separator => separator, + :delimiter => delimiter + ).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '') + storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit) + rescue + number + end end end end diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index a880fd83ef..afe35691bc 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -44,7 +44,18 @@ # separator: delimiter: "" precision: 1 - storage_units: [Bytes, KB, MB, GB, TB] + storage_units: + # Storage units output formatting. + # %u is the storage unit, %n is the number (default: 2 MB) + format: "%n %u" + units: + byte: + one: "Byte" + other: "Bytes" + kb: "KB" + mb: "MB" + gb: "GB" + tb: "TB" # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words() datetime: -- cgit v1.2.3 From 57b156b33874431fedd1dacc952a4bc781628a07 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 27 Jan 2009 17:26:07 +0000 Subject: Dont use Memoizable for ActionController::Request --- actionpack/lib/action_controller/request.rb | 42 +++++++++--------------- actionpack/lib/action_controller/test_process.rb | 15 +++++---- 2 files changed, 23 insertions(+), 34 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 09dcd684e8..935326c347 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -7,7 +7,6 @@ require 'action_controller/cgi_ext' module ActionController class Request < Rack::Request - extend ActiveSupport::Memoizable %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_TRANSLATED REMOTE_HOST @@ -33,9 +32,8 @@ module ActionController # :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}") + @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") end - memoize :request_method # Returns the HTTP request \method used for action processing as a # lowercase symbol, such as :post. (Unlike #request_method, this @@ -75,9 +73,8 @@ module ActionController # # request.headers["Content-Type"] # => "text/plain" def headers - ActionController::Http::Headers.new(@env) + @headers ||= ActionController::Http::Headers.new(@env) end - memoize :headers # Returns the content length of the request as an integer. def content_length @@ -89,32 +86,33 @@ module ActionController # For backward compatibility, the post \format is extracted from the # X-Post-Data-Format HTTP header if present. def content_type - if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/ - Mime::Type.lookup($1.strip.downcase) - else - nil + @content_type ||= begin + if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/ + Mime::Type.lookup($1.strip.downcase) + else + nil + end end end - memoize :content_type # Returns the accepted MIME type for the request. def accepts - header = @env['HTTP_ACCEPT'].to_s.strip + @accepts ||= begin + header = @env['HTTP_ACCEPT'].to_s.strip - if header.empty? - [content_type, Mime::ALL].compact - else - Mime::Type.parse(header) + if header.empty? + [content_type, Mime::ALL].compact + else + Mime::Type.parse(header) + end end end - memoize :accepts def if_modified_since if since = env['HTTP_IF_MODIFIED_SINCE'] Time.rfc2822(since) rescue nil end end - memoize :if_modified_since def if_none_match env['HTTP_IF_NONE_MATCH'] @@ -248,25 +246,21 @@ EOM @env['REMOTE_ADDR'] end - memoize :remote_ip # Returns the lowercase name of the HTTP server software. def server_software (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil end - memoize :server_software # Returns the complete URL used for this request. def url protocol + host_with_port + request_uri end - memoize :url # Returns 'https://' if this is an SSL request and 'http://' otherwise. def protocol ssl? ? 'https://' : 'http://' end - memoize :protocol # Is this an SSL request? def ssl? @@ -286,14 +280,12 @@ EOM def host raw_host_with_port.sub(/:\d+$/, '') end - memoize :host # Returns a \host:\port string for this request, such as "example.com" or # "example.com:8080". def host_with_port "#{host}#{port_string}" end - memoize :host_with_port # Returns the port number of this request as an integer. def port @@ -303,7 +295,6 @@ EOM standard_port end end - memoize :port # Returns the standard \port number for this request's protocol. def standard_port @@ -341,7 +332,6 @@ EOM def query_string @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '') end - memoize :query_string # Returns the request URI, accounting for server idiosyncrasies. # WEBrick includes the full URL. IIS leaves REQUEST_URI blank. @@ -367,7 +357,6 @@ EOM end end end - memoize :request_uri # Returns the interpreted \path to requested resource after all the installation # directory of this application was taken into account. @@ -376,7 +365,6 @@ EOM path.sub!(/\A#{ActionController::Base.relative_url_root}/, '') path end - memoize :path # Read the request \body. This is useful for web services that need to # work with raw requests directly. diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 22b97fc157..d2c4ebf98c 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -35,7 +35,6 @@ module ActionController #:nodoc: def port=(number) @env["SERVER_PORT"] = number.to_i - port(true) end def action=(action_name) @@ -49,8 +48,6 @@ module ActionController #:nodoc: @env["REQUEST_URI"] = value @request_uri = nil @path = nil - request_uri(true) - path(true) end def request_uri=(uri) @@ -58,9 +55,13 @@ module ActionController #:nodoc: @path = uri.split("?").first end + def request_method=(method) + @request_method = method + end + def accept=(mime_types) @env["HTTP_ACCEPT"] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",") - accepts(true) + @accepts = nil end def if_modified_since=(last_modified) @@ -76,11 +77,11 @@ module ActionController #:nodoc: end def request_uri(*args) - @request_uri || super + @request_uri || super() end def path(*args) - @path || super + @path || super() end def assign_parameters(controller_path, action, parameters) @@ -107,7 +108,7 @@ module ActionController #:nodoc: def recycle! self.query_parameters = {} self.path_parameters = {} - unmemoize_all + @headers, @request_method, @accepts, @content_type = nil, nil, nil, nil end def user_agent=(user_agent) -- cgit v1.2.3 From f17c87665e36206bcae9c260cb25cd67bf9695e6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 27 Jan 2009 12:26:50 -0600 Subject: Fixed deprecated methods on TestSession [#1801 state:resolved] --- actionpack/lib/action_controller/base.rb | 5 ---- actionpack/lib/action_controller/test_process.rb | 33 +++++++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 50b965ce4c..9f418bb2f3 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1315,10 +1315,6 @@ module ActionController #:nodoc: "#{request.protocol}#{request.host}#{request.request_uri}" end - def close_session - @_session.close if @_session && @_session.respond_to?(:close) - end - def default_template(action_name = self.action_name) self.view_paths.find_template(default_template_name(action_name), default_template_format) end @@ -1342,7 +1338,6 @@ module ActionController #:nodoc: end def process_cleanup - close_session end end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index d2c4ebf98c..ea17363c47 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -280,38 +280,47 @@ module ActionController #:nodoc: end end - class TestSession #:nodoc: + class TestSession < Hash #:nodoc: attr_accessor :session_id def initialize(attributes = nil) @session_id = '' - @attributes = attributes.nil? ? nil : attributes.stringify_keys - @saved_attributes = nil + attributes ||= {} + replace(attributes.stringify_keys) end def data - @attributes ||= @saved_attributes || {} + to_hash end def [](key) - data[key.to_s] + super(key.to_s) end def []=(key, value) - data[key.to_s] = value + super(key.to_s, value) end - def update - @saved_attributes = @attributes + def update(hash = nil) + if hash.nil? + ActiveSupport::Deprecation.warn('use replace instead', caller) + replace({}) + else + super(hash) + end end - def delete - @attributes = nil + def delete(key = nil) + if key.nil? + ActiveSupport::Deprecation.warn('use clear instead', caller) + clear + else + super(key.to_s) + end end def close - update - delete + ActiveSupport::Deprecation.warn('sessions should no longer be closed', caller) end end -- cgit v1.2.3 From 093f758bd05d5b6144fd06aa0606c2f987c4ee09 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 27 Jan 2009 14:09:11 -0600 Subject: Restore implicit rendering for XHR requests that want a HTML template [#1590 state:resolved] --- actionpack/lib/action_view/paths.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index cb351620d2..ee26542a07 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -45,6 +45,9 @@ module ActionView #:nodoc: return template elsif template = load_path[template_path] return template + # Try to find html version if the format is javascript + elsif format == :js && template = load_path["#{template_path}.html"] + return template end end -- cgit v1.2.3 From dd02af5c7e74b4ea0383b0df87824fb2b37f2243 Mon Sep 17 00:00:00 2001 From: Tys von Gaza Date: Mon, 15 Dec 2008 14:38:22 -0700 Subject: Fix for sweepers method_missing missing &block argument [#1581 status:committed] Signed-off-by: David Heinemeier Hansson --- actionpack/lib/action_controller/caching/sweeping.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index c7992d7769..c1be264ffb 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -87,9 +87,9 @@ module ActionController #:nodoc: __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) end - def method_missing(method, *arguments) + def method_missing(method, *arguments, &block) return if @controller.nil? - @controller.__send__(method, *arguments) + @controller.__send__(method, *arguments, &block) end end end -- cgit v1.2.3 From 9714a9b0019e6ca0e120a253037fa8a561b22823 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 27 Jan 2009 09:44:07 -0800 Subject: Use toplevel reference to ::StringIO since we're in a BasicObject. --- actionpack/lib/action_controller/rewindable_input.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_controller/rewindable_input.rb index 36f655c51e..cedfb7fd75 100644 --- a/actionpack/lib/action_controller/rewindable_input.rb +++ b/actionpack/lib/action_controller/rewindable_input.rb @@ -3,12 +3,12 @@ module ActionController class RewindableIO < ActiveSupport::BasicObject def initialize(io) @io = io - @rewindable = io.is_a?(StringIO) + @rewindable = io.is_a?(::StringIO) end def method_missing(method, *args, &block) unless @rewindable - @io = StringIO.new(@io.read) + @io = ::StringIO.new(@io.read) @rewindable = true end -- cgit v1.2.3 From 2ae8d3079b96d343f8cea8513929d656013f880e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 28 Jan 2009 05:05:07 +0000 Subject: Session cookie header should always be set if :expire_after option is specified --- actionpack/lib/action_controller/session/abstract_store.rb | 6 ++++-- actionpack/lib/action_controller/session/cookie_store.rb | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb index bf09fd33c5..9434c2e05e 100644 --- a/actionpack/lib/action_controller/session/abstract_store.rb +++ b/actionpack/lib/action_controller/session/abstract_store.rb @@ -102,8 +102,10 @@ module ActionController response = @app.call(env) session_data = env[ENV_SESSION_KEY] - if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) - options = env[ENV_SESSION_OPTIONS_KEY] + options = env[ENV_SESSION_OPTIONS_KEY] + + if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] + session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) if session_data.is_a?(AbstractStore::SessionHash) sid = session_data.id diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index 6ad6369950..5a728d1877 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -93,12 +93,14 @@ module ActionController status, headers, body = @app.call(env) session_data = env[ENV_SESSION_KEY] - if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) + options = env[ENV_SESSION_OPTIONS_KEY] + + if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] + session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) session_data = marshal(session_data.to_hash) raise CookieOverflow if session_data.size > MAX - options = env[ENV_SESSION_OPTIONS_KEY] cookie = Hash.new cookie[:value] = session_data unless options[:expire_after].nil? -- cgit v1.2.3 From 32eeb3e5211a4a7bfc7a1d0aa0cab1486bed3581 Mon Sep 17 00:00:00 2001 From: Nathan de Vries Date: Wed, 28 Jan 2009 19:31:48 +0000 Subject: Ensure that when UrlWriter is included in multiple classes, the default_url_options of one don't affect the other. [#1277 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_controller/url_rewriter.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb index d86e2db67d..bb6cb437b7 100644 --- a/actionpack/lib/action_controller/url_rewriter.rb +++ b/actionpack/lib/action_controller/url_rewriter.rb @@ -92,15 +92,12 @@ module ActionController # end # end module UrlWriter - # The default options for urls written by this writer. Typically a :host - # pair is provided. - mattr_accessor :default_url_options - self.default_url_options = {} - def self.included(base) #:nodoc: ActionController::Routing::Routes.install_helpers(base) base.mattr_accessor :default_url_options - base.default_url_options ||= default_url_options + + # The default options for urls written by this writer. Typically a :host pair is provided. + base.default_url_options ||= {} end # Generate a url based on the options provided, default_url_options and the -- cgit v1.2.3 From 9a8e2a059c823498e0fb318f25b926a6d015ef87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Fri, 12 Sep 2008 23:56:56 +0200 Subject: Ensure selected option is not ignored for collection_select. [#1037 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_view/helpers/form_options_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 9ed50a9653..3991c5c0cc 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -349,8 +349,9 @@ module ActionView html_options = html_options.stringify_keys add_default_name_and_id(html_options) value = value(object) + selected_value = options.has_key?(:selected) ? options[:selected] : value content_tag( - "select", add_options(options_from_collection_for_select(collection, value_method, text_method, value), options, value), html_options + "select", add_options(options_from_collection_for_select(collection, value_method, text_method, selected_value), options, value), html_options ) end -- cgit v1.2.3 From 91eeb0ff119d34d0fcdb44d3d7fcbb7924208e05 Mon Sep 17 00:00:00 2001 From: Dan Weinand Date: Thu, 30 Oct 2008 12:52:12 -0500 Subject: Using the highlight helper on text with html shouldn't highlight text inside html attributes. [#1302 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_view/helpers/text_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 1d9e4fe9b8..b1eb6891fa 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -107,7 +107,7 @@ module ActionView text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') - text.gsub(/(#{match})/i, options[:highlighter]) + text.gsub(/(#{match})(?!(?:[^<]*?)?(?:["'])[^<>]*>)/i, options[:highlighter]) end end -- cgit v1.2.3 From 306cc2b920203cfa51cee82d2fc452484efc72f8 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Thu, 29 Jan 2009 16:00:07 +0000 Subject: Implement HTTP Digest authentication. [#1230 state:resolved] [Gregg Kellogg, Pratik Naik] Signed-off-by: Pratik Naik --- actionpack/lib/action_controller/base.rb | 4 +- .../lib/action_controller/http_authentication.rb | 196 ++++++++++++++++++--- 2 files changed, 175 insertions(+), 25 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 9f418bb2f3..36b80d5780 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1344,8 +1344,8 @@ module ActionController #:nodoc: Base.class_eval do [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers, Cookies, Caching, Verification, Streaming, SessionManagement, - HttpAuthentication::Basic::ControllerMethods, RecordIdentifier, - RequestForgeryProtection, Translation + HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, + RecordIdentifier, RequestForgeryProtection, Translation ].each do |mod| include mod end diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 2ed810db7d..c91ef2ca48 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -1,42 +1,42 @@ module ActionController module HttpAuthentication # Makes it dead easy to do HTTP Basic authentication. - # + # # Simple Basic example: - # + # # class PostsController < ApplicationController # USER_NAME, PASSWORD = "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_basic do |user_name, password| + # authenticate_or_request_with_http_basic do |user_name, password| # user_name == USER_NAME && password == PASSWORD # end # end # end - # - # - # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, + # + # + # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, # the regular HTML interface is protected by a session approach: - # + # # class ApplicationController < ActionController::Base # before_filter :set_account, :authenticate - # + # # protected # def set_account # @account = Account.find_by_url_name(request.subdomains.first) # end - # + # # def authenticate # case request.format # when Mime::XML, Mime::ATOM @@ -54,24 +54,48 @@ module ActionController # end # end # end - # - # + # # In your integration tests, you can do something like this: - # + # # def test_access_granted_from_xml # get( - # "/notes/1.xml", nil, + # "/notes/1.xml", nil, # :authorization => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password) # ) - # + # # assert_equal 200, status # end - # - # + # + # Simple Digest example: + # + # 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 |username| + # USERS[username] + # end + # end + # end + # + # NOTE: The +authenticate_or_request_with_http_digest+ 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. + # # On shared hosts, Apache sometimes doesn't pass authentication headers to # FCGI instances. If your environment matches this description and you cannot # authenticate, try this rule in your Apache setup: - # + # # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] module Basic extend self @@ -99,14 +123,14 @@ module ActionController def user_name_and_password(request) decode_credentials(request).split(/:/, 2) 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 - + def decode_credentials(request) ActiveSupport::Base64.decode64(authorization(request).split.last || '') end @@ -120,5 +144,131 @@ 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) + authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm) + end + + # Authenticate with HTTP Digest, returns true or false + def authenticate_with_http_digest(realm = "Application", &password_procedure) + HttpAuthentication::Digest.authenticate(self, realm, &password_procedure) + 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 + end + + # Returns false on a valid response, true otherwise + def authenticate(controller, realm, &password_procedure) + authorization(controller.request) && validate_digest_response(controller, realm, &password_procedure) + 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_header(controller.request) + valid_nonce = validate_nonce(controller.request, credentials[:nonce]) + + if valid_nonce && realm == credentials[:realm] && opaque(controller.request.session.session_id) == credentials[:opaque] + password = password_procedure.call(credentials[:username]) + expected = expected_response(controller.request.env['REQUEST_METHOD'], controller.request.url, credentials, password) + expected == credentials[:response] + end + 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_header(request) + decode_credentials(authorization(request)) + end + + def decode_credentials(header) + header.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) + session_id = controller.request.session.session_id + controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(session_id)}", opaque="#{opaque(session_id)}") + end + + def authentication_request(controller, realm, message = nil) + message ||= "HTTP Digest: Access denied.\n" + 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(session_id, time = Time.now) + 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 + nonce(request.session.session_id, t) == value && (t - Time.now.to_i).abs <= 10 * 60 + end + + # Opaque based on digest of session_id + def opaque(session_id) + Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '') + end + end end end -- cgit v1.2.3 From 8761663a68bd7ddd918f78fb3def4697784024f2 Mon Sep 17 00:00:00 2001 From: Jon Crawford Date: Thu, 29 Jan 2009 17:59:44 +0000 Subject: Added grouped_options_for_select helper method for wrapping option tags in optgroups. [#977 state:resolved] Signed-off-by: Pratik Naik --- .../lib/action_view/helpers/form_options_helper.rb | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 3991c5c0cc..54c82cbd1d 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -277,6 +277,62 @@ module ActionView end end + # Returns a string of tags, like options_for_select, but + # wraps them with tags. + # + # Parameters: + # * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the + # label while the second value must be an array of options. The second value can be a + # nested array of text-value pairs. See options_for_select for more info. + # Ex. ["North America",[["United States","US"],["Canada","CA"]]] + # * +selected_key+ - A value equal to the +value+ attribute for one of the tags, + # which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options + # as you might have the same option in multiple groups. Each will then get selected="selected". + # * +prompt+ - set to true or a prompt string. When the select element doesn’t have a value yet, this + # prepends an option with a generic prompt — "Please select" — or the given prompt string. + # + # Sample usage (Array): + # grouped_options = [ + # ['North America', + # [['United States','US'],'Canada']], + # ['Europe', + # ['Denmark','Germany','France']] + # ] + # grouped_options_for_select(grouped_options) + # + # Sample usage (Hash): + # grouped_options = { + # 'North America' => [['United States','US], 'Canada'], + # 'Europe' => ['Denmark','Germany','France'] + # } + # grouped_options_for_select(grouped_options) + # + # Possible output: + # + # + # + # + # + # + # + # + # + # + # Note: Only the and tags are returned, so you still have to + # wrap the output in an appropriate