diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2008-11-14 17:41:25 +0530 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2008-11-14 17:41:25 +0530 |
commit | 17b1387646a9537c978e9d7c5f3a68f740cd9377 (patch) | |
tree | 81735b97603471bb9780ed0fd133dad7a16f59a7 | |
parent | fa9ea057d1252a578f8e056defef41b93853bc8b (diff) | |
parent | 549b18c9286b6cccf4978093576325fd711dc421 (diff) | |
download | rails-17b1387646a9537c978e9d7c5f3a68f740cd9377.tar.gz rails-17b1387646a9537c978e9d7c5f3a68f740cd9377.tar.bz2 rails-17b1387646a9537c978e9d7c5f3a68f740cd9377.zip |
Merge commit 'mainstream/master'
Conflicts:
railties/doc/guides/html/actioncontroller_basics.html
railties/doc/guides/html/activerecord_validations_callbacks.html
railties/doc/guides/html/debugging_rails_applications.html
railties/doc/guides/html/testing_rails_applications.html
railties/doc/guides/source/actioncontroller_basics/methods.txt
railties/doc/guides/source/actioncontroller_basics/params.txt
railties/doc/guides/source/actioncontroller_basics/request_response_objects.txt
railties/doc/guides/source/actioncontroller_basics/session.txt
railties/doc/guides/source/activerecord_validations_callbacks.txt
railties/doc/guides/source/debugging_rails_applications.txt
railties/doc/guides/source/testing_rails_applications.txt
66 files changed, 758 insertions, 219 deletions
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index d8636fd83d..de5aeab07e 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,8 @@ +*2.2.1 [RC2] (November 14th, 2008)* + +* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 [Grant Hollingworth] + + *2.2.0 [RC1] (October 24th, 2008)* * Add layout functionality to mailers [Pratik] diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 043f56ba17..19ce77ea5a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -386,12 +386,15 @@ module ActionMailer #:nodoc: end def method_missing(method_symbol, *parameters) #:nodoc: - match = matches_dynamic_method?(method_symbol) - case match[1] - when 'create' then new(match[2], *parameters).mail - when 'deliver' then new(match[2], *parameters).deliver! - when 'new' then nil - else super + if match = matches_dynamic_method?(method_symbol) + case match[1] + when 'create' then new(match[2], *parameters).mail + when 'deliver' then new(match[2], *parameters).deliver! + when 'new' then nil + else super + end + else + super end end @@ -440,7 +443,7 @@ module ActionMailer #:nodoc: private def matches_dynamic_method?(method_name) #:nodoc: method_name = method_name.to_s - /(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) + /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) end end @@ -663,8 +666,10 @@ module ActionMailer #:nodoc: mail.ready_to_send sender = mail['return-path'] || mail.from - Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain], - smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp| + smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port]) + smtp.enable_starttls_auto if smtp.respond_to?(:enable_starttls_auto) + smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password], + smtp_settings[:authentication]) do |smtp| smtp.sendmail(mail.encoded, sender, destinations) end end diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 9728d1b4db..7ba13c7df8 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 905f25c9f7..1617b88c8e 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -24,11 +24,15 @@ class MockSMTP def sendmail(mail, from, to) @@deliveries << [mail, from, to] end + + def start(*args) + yield self + end end class Net::SMTP - def self.start(*args) - yield MockSMTP.new + def self.new(*args) + MockSMTP.new end end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 7f9540c44b..b88beb3314 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -938,6 +938,20 @@ EOF mail = TestMailer.create_body_ivar(@recipient) assert_equal "body: foo\nbar: baz", mail.body end + + def test_starttls_is_enabled_if_supported + MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true) + MockSMTP.any_instance.expects(:enable_starttls_auto) + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_signed_up(@recipient) + end + + def test_starttls_is_disabled_if_not_supported + MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(false) + MockSMTP.any_instance.expects(:enable_starttls_auto).never + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_signed_up(@recipient) + end end end # uses_mocha @@ -1031,4 +1045,16 @@ class RespondToTest < Test::Unit::TestCase def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_digit assert !RespondToMailer.respond_to?(:deliver_1_template) end + + def test_should_not_respond_to_method_where_deliver_is_not_a_suffix + assert !RespondToMailer.respond_to?(:foo_deliver_template) + end + + def test_should_still_raise_exception_with_expected_message_when_calling_an_undefined_method + error = assert_raises NoMethodError do + RespondToMailer.not_a_method + end + + assert_match /undefined method.*not_a_method/, error.message + end end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 3ce6522535..dc7ee64358 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,10 @@ -*2.2.1 [RC2 or 2.2 final]* +*2.2.1 [RC2] (November 14th, 2008)* + +* Added render :js for people who want to render inline JavaScript replies without using RJS [DHH] + +* Fixed that polymorphic_url should compact given array #1317 [hiroshi] + +* Fixed the sanitize helper to avoid double escaping already properly escaped entities #683 [antonmos/Ryan McGeary] * Fixed that FormTagHelper generated illegal html if name contained square brackets #1238 [Vladimir Dobriakov] diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 389ade425b..f35c42f929 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1241,7 +1241,7 @@ module ActionController #:nodoc: parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup parameters = parameters.except!(:controller, :action, :format, :_method) - logger.info " Parameters: #{parameters.inspect}" + logger.info " Parameters: #{parameters.inspect}" unless parameters.empty? end def default_render #:nodoc: diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index f3e173004a..2d5e80f0bb 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -23,7 +23,6 @@ module ActionController if defined?(ActiveRecord) after_dispatch :checkin_connections - before_dispatch { ActiveRecord::Base.verify_active_connections! } to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers } end diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb index 26edca3b69..8ca3a70341 100644 --- a/actionpack/lib/action_controller/mime_type.rb +++ b/actionpack/lib/action_controller/mime_type.rb @@ -20,8 +20,20 @@ module Mime # end class Type @@html_types = Set.new [:html, :all] + cattr_reader :html_types + + # These are the content types which browsers can generate without using ajax, flash, etc + # i.e. following a link, getting an image or posting a form. CSRF protection + # only needs to protect against these types. + @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form] + cattr_reader :browser_generated_types + + @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml] - cattr_reader :html_types, :unverifiable_types + def self.unverifiable_types + ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller) + @@unverifiable_types + end # A simple helper class used in parsing the accept header class AcceptItem #:nodoc: @@ -167,13 +179,17 @@ module Mime # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See # ActionController::RequestForgerProtection. def verify_request? - !@@unverifiable_types.include?(to_sym) + browser_generated? end def html? @@html_types.include?(to_sym) || @string =~ /html/ end + def browser_generated? + @@browser_generated_types.include?(to_sym) + end + private def method_missing(method, *args) if method.to_s =~ /(\w+)\?$/ diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index cc228c4230..2644c7f7c7 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -73,7 +73,7 @@ module ActionController # def polymorphic_url(record_or_hash_or_array, options = {}) if record_or_hash_or_array.kind_of?(Array) - record_or_hash_or_array = record_or_hash_or_array.dup + record_or_hash_or_array = record_or_hash_or_array.compact end record = extract_record(record_or_hash_or_array) diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index a6d4abf029..c079895683 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -13,7 +13,7 @@ module ActionController ActiveSupport::Deprecation.warn( "ActionController::AbstractRequest.relative_url_root= has been renamed." + "You can now set it with config.action_controller.relative_url_root=", caller) - ActionController::base.relative_url_root=relative_url_root + ActionController::Base.relative_url_root=relative_url_root end HTTP_METHODS = %w(get head put post delete options) diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb index 05a6d8bb79..3e0e94a06b 100644 --- a/actionpack/lib/action_controller/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/request_forgery_protection.rb @@ -99,7 +99,7 @@ module ActionController #:nodoc: end def verifiable_request_format? - request.content_type.nil? || request.content_type.verify_request? + !request.content_type.nil? && request.content_type.verify_request? end # Sets the token value for the current session. Pass a <tt>:secret</tt> option diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 872b0dab3d..7700b9d4d0 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -42,7 +42,11 @@ module ActionController # # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer module Resources + INHERITABLE_OPTIONS = :namespace, :shallow, :actions + class Resource #:nodoc: + DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy + attr_reader :collection_methods, :member_methods, :new_methods attr_reader :path_prefix, :name_prefix, :path_segment attr_reader :plural, :singular @@ -57,6 +61,7 @@ module ActionController arrange_actions add_default_actions + set_allowed_actions set_prefixes end @@ -113,6 +118,10 @@ module ActionController @singular.to_s == @plural.to_s end + def has_action?(action) + !DEFAULT_ACTIONS.include?(action) || @options[:actions].nil? || @options[:actions].include?(action) + end + protected def arrange_actions @collection_methods = arrange_actions_by_methods(options.delete(:collection)) @@ -125,6 +134,25 @@ module ActionController add_default_action(new_methods, :get, :new) end + def set_allowed_actions + only = @options.delete(:only) + except = @options.delete(:except) + + if only && except + raise ArgumentError, 'Please supply either :only or :except, not both.' + elsif only == :all || except == :none + options[:actions] = DEFAULT_ACTIONS + elsif only == :none || except == :all + options[:actions] = [] + elsif only + options[:actions] = DEFAULT_ACTIONS & Array(only).map(&:to_sym) + elsif except + options[:actions] = DEFAULT_ACTIONS - Array(except).map(&:to_sym) + else + # leave options[:actions] alone + end + end + def set_prefixes @path_prefix = options.delete(:path_prefix) @name_prefix = options.delete(:name_prefix) @@ -353,6 +381,25 @@ module ActionController # # map.resources :users, :has_many => { :posts => :comments }, :shallow => true # + # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to. + # + # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a + # list of action names. By default, routes are generated for all seven actions. + # + # For example: + # + # map.resources :posts, :only => [:index, :show] do |post| + # post.resources :comments, :except => [:update, :destroy] + # end + # # --> GET /posts (maps to the PostsController#index action) + # # --> POST /posts (fails) + # # --> GET /posts/1 (maps to the PostsController#show action) + # # --> DELETE /posts/1 (fails) + # # --> POST /posts/1/comments (maps to the CommentsController#create action) + # # --> PUT /posts/1/comments/1 (fails) + # + # The <tt>:only</tt> and <tt>:except</tt> options are inherited by any nested resource(s). + # # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied. # # Examples: @@ -478,7 +525,7 @@ module ActionController map_associations(resource, options) if block_given? - with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) + with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end end end @@ -495,7 +542,7 @@ module ActionController map_associations(resource, options) if block_given? - with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) + with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end end end @@ -507,7 +554,7 @@ module ActionController name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}" Array(options[:has_one]).each do |association| - resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace], :shallow => options[:shallow]) + resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix)) end end @@ -522,7 +569,7 @@ module ActionController map_has_many_associations(resource, association, options) end when Symbol, String - resources(associations, :path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], :has_many => options[:has_many]) + resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many])) else end end @@ -531,41 +578,39 @@ module ActionController resource.collection_methods.each do |method, actions| actions.each do |action| [method].flatten.each do |m| - action_options = action_options_for(action, resource, m) - map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options) + map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m) end end end end def map_default_collection_actions(map, resource) - index_action_options = action_options_for("index", resource) index_route_name = "#{resource.name_prefix}#{resource.plural}" if resource.uncountable? index_route_name << "_index" end - map_named_routes(map, index_route_name, resource.path, index_action_options) - - create_action_options = action_options_for("create", resource) - map_unnamed_routes(map, resource.path, create_action_options) + map_resource_routes(map, resource, :index, resource.path, index_route_name) + map_resource_routes(map, resource, :create, resource.path, index_route_name) end def map_default_singleton_actions(map, resource) - create_action_options = action_options_for("create", resource) - map_unnamed_routes(map, resource.path, create_action_options) + map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}") end def map_new_actions(map, resource) resource.new_methods.each do |method, actions| actions.each do |action| - action_options = action_options_for(action, resource, method) - if action == :new - map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options) - else - map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options) + route_path = resource.new_path + route_name = "new_#{resource.name_prefix}#{resource.singular}" + + unless action == :new + route_path = "#{route_path}#{resource.action_separator}#{action}" + route_name = "#{action}_#{route_name}" end + + map_resource_routes(map, resource, action, route_path, route_name, method) end end end @@ -574,34 +619,33 @@ module ActionController resource.member_methods.each do |method, actions| actions.each do |action| [method].flatten.each do |m| - action_options = action_options_for(action, resource, m) - action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) action_path ||= Base.resources_path_names[action] || action - map_named_routes(map, "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) + map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m) end end end - show_action_options = action_options_for("show", resource) - map_named_routes(map, "#{resource.shallow_name_prefix}#{resource.singular}", resource.member_path, show_action_options) - - update_action_options = action_options_for("update", resource) - map_unnamed_routes(map, resource.member_path, update_action_options) - - destroy_action_options = action_options_for("destroy", resource) - map_unnamed_routes(map, resource.member_path, destroy_action_options) - end - - def map_unnamed_routes(map, path_without_format, options) - map.connect(path_without_format, options) - map.connect("#{path_without_format}.:format", options) + route_path = "#{resource.shallow_name_prefix}#{resource.singular}" + map_resource_routes(map, resource, :show, resource.member_path, route_path) + map_resource_routes(map, resource, :update, resource.member_path, route_path) + map_resource_routes(map, resource, :destroy, resource.member_path, route_path) end - def map_named_routes(map, name, path_without_format, options) - map.named_route(name, path_without_format, options) - map.named_route("formatted_#{name}", "#{path_without_format}.:format", options) + def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil) + if resource.has_action?(action) + action_options = action_options_for(action, resource, method) + formatted_route_path = "#{route_path}.:format" + + if route_name && @set.named_routes[route_name.to_sym].nil? + map.named_route(route_name, route_path, action_options) + map.named_route("formatted_#{route_name}", formatted_route_path, action_options) + else + map.connect(route_path, action_options) + map.connect(formatted_route_path, action_options) + end + end end def add_conditions_for(conditions, method) diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index 7b888fa8d2..d4e501e780 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -1,23 +1,16 @@ module ActionController module Routing class RouteBuilder #:nodoc: - attr_accessor :separators, :optional_separators + attr_reader :separators, :optional_separators + attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp def initialize - self.separators = Routing::SEPARATORS - self.optional_separators = %w( / ) - end - - def separator_pattern(inverted = false) - "[#{'^' if inverted}#{Regexp.escape(separators.join)}]" - end - - def interval_regexp - Regexp.new "(.*?)(#{separators.source}|$)" - end + @separators = Routing::SEPARATORS + @optional_separators = %w( / ) - def multiline_regexp?(expression) - expression.options & Regexp::MULTILINE == Regexp::MULTILINE + @separator_regexp = /[#{Regexp.escape(separators.join)}]/ + @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/ + @interval_regexp = /(.*?)(#{separator_regexp}|$)/ end # Accepts a "route path" (a string defining a route), and returns the array @@ -30,7 +23,7 @@ module ActionController rest, segments = path, [] until rest.empty? - segment, rest = segment_for rest + segment, rest = segment_for(rest) segments << segment end segments @@ -39,20 +32,20 @@ module ActionController # A factory method that returns a new segment instance appropriate for the # format of the given string. def segment_for(string) - segment = case string - when /\A:(\w+)/ - key = $1.to_sym - case key - when :controller then ControllerSegment.new(key) - else DynamicSegment.new key - end - when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true) - when /\A\?(.*?)\?/ - StaticSegment.new($1, :optional => true) - when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1) - when Regexp.new(separator_pattern) then - DividerSegment.new($&, :optional => (optional_separators.include? $&)) - end + segment = + case string + when /\A:(\w+)/ + key = $1.to_sym + key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key) + when /\A\*(\w+)/ + PathSegment.new($1.to_sym, :optional => true) + when /\A\?(.*?)\?/ + StaticSegment.new($1, :optional => true) + when nonseparator_regexp + StaticSegment.new($1) + when separator_regexp + DividerSegment.new($&, :optional => optional_separators.include?($&)) + end [segment, $~.post_match] end @@ -98,7 +91,7 @@ module ActionController if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z} raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}" end - if multiline_regexp?(requirement) + if requirement.multiline? raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}" end segment.regexp = requirement diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index 6c47ced6d1..3b98b16683 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -148,7 +148,7 @@ module ActionController end nil end - }, __FILE__, __LINE__ + }, '(recognize_optimized)', 1 end def clear_recognize_optimized! diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb index 3b2cb28545..a610ec7e84 100644 --- a/actionpack/lib/action_controller/routing/route.rb +++ b/actionpack/lib/action_controller/routing/route.rb @@ -219,7 +219,7 @@ module ActionController next_capture = 1 extraction = segments.collect do |segment| x = segment.match_extraction(next_capture) - next_capture += Regexp.new(segment.regexp_chunk).number_of_captures + next_capture += segment.number_of_captures x end extraction.compact diff --git a/actionpack/lib/action_controller/routing/routing_ext.rb b/actionpack/lib/action_controller/routing/routing_ext.rb index 5f4ba90d0c..4a82b2af5f 100644 --- a/actionpack/lib/action_controller/routing/routing_ext.rb +++ b/actionpack/lib/action_controller/routing/routing_ext.rb @@ -27,6 +27,10 @@ class Regexp #:nodoc: Regexp.new("|#{source}").match('').captures.length end + def multiline? + options & MULTILINE == MULTILINE + end + class << self def optionalize(pattern) case unoptionalize(pattern) diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index e5f174ae2c..f6b03edcca 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -13,6 +13,10 @@ module ActionController @is_optional = false end + def number_of_captures + Regexp.new(regexp_chunk).number_of_captures + end + def extraction_code nil end @@ -84,6 +88,10 @@ module ActionController optional? ? Regexp.optionalize(chunk) : chunk end + def number_of_captures + 0 + end + def build_pattern(pattern) escaped = Regexp.escape(value) if optional? && ! pattern.empty? @@ -194,10 +202,16 @@ module ActionController end end + def number_of_captures + if regexp + regexp.number_of_captures + 1 + else + 1 + end + end + def build_pattern(pattern) - chunk = regexp_chunk - chunk = "(#{chunk})" if Regexp.new(chunk).number_of_captures == 0 - pattern = "#{chunk}#{pattern}" + pattern = "#{regexp_chunk}#{pattern}" optional? ? Regexp.optionalize(pattern) : pattern end @@ -230,6 +244,10 @@ module ActionController "(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))" end + def number_of_captures + 1 + end + # Don't URI.escape the controller name since it may contain slashes. def interpolation_chunk(value_code = local_name) "\#{#{value_code}.to_s}" @@ -275,6 +293,10 @@ module ActionController regexp || "(.*)" end + def number_of_captures + regexp ? regexp.number_of_captures : 1 + end + def optionality_implied? true end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 7a31f0e8d5..1e3a646bc9 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -395,6 +395,7 @@ module ActionController #:nodoc: @html_document = nil @request.env['REQUEST_METHOD'] ||= "GET" + @request.action = action.to_s parameters ||= {} diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index 12c8405101..ae20f9947c 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -160,7 +160,7 @@ module HTML if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value) node.attributes.delete(attr_name) else - node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(value) + node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value)) end end end diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 288b62778e..126d16e5f4 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index ccb7df212a..cd25684940 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -1,3 +1,5 @@ +require 'set' + # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other # template languages). module ActionView @@ -121,6 +123,8 @@ module ActionView end class AtomBuilder + XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set + def initialize(xml) @xml = xml end @@ -140,14 +144,15 @@ module ActionView @xml.__send__(method, *arguments, &block) end end - + # True if the method name matches one of the five elements defined # in the Atom spec as potentially containing XHTML content and # if :type => 'xhtml' is, in fact, specified. def xhtml_block?(method, arguments) - %w( content rights title subtitle summary ).include?(method.to_s) && - arguments.last.respond_to?(:[]) && - arguments.last[:type].to_s == 'xhtml' + if XHTML_TAG_NAMES.include?(method.to_s) + last = arguments.last + last.is_a?(Hash) && last[:type].to_s == 'xhtml' + end end end diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index de08672d2d..d37ca766af 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -133,10 +133,12 @@ module ActionView unless options.blank? attrs = [] if escape - options.each do |key, value| - next unless value - value = BOOLEAN_ATTRIBUTES.include?(key) ? key : escape_once(value) - attrs << %(#{key}="#{value}") + options.each_pair do |key, value| + if BOOLEAN_ATTRIBUTES.include?(key) + attrs << %(#{key}="#{key}") if value + else + attrs << %(#{key}="#{escape_once(value)}") if !value.nil? + end end else attrs = options.map { |key, value| %(#{key}="#{value}") } diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index d80e7c6e57..36f7575652 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -559,7 +559,7 @@ module ActionView (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port (?:/(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))*)* # path - (?:\?[\w\+@%&=.;-]+)? # query string + (?:\?[\w\+@%&=.;:-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) ([[:punct:]]|<|$|) # trailing text @@ -598,4 +598,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/html-scanner/sanitizer_test.rb b/actionpack/test/controller/html-scanner/sanitizer_test.rb index a9e8447e32..bae0f5c9fd 100644 --- a/actionpack/test/controller/html-scanner/sanitizer_test.rb +++ b/actionpack/test/controller/html-scanner/sanitizer_test.rb @@ -253,6 +253,10 @@ class SanitizerTest < Test::Unit::TestCase assert_sanitized "<![CDATA[<span>neverending...", "<![CDATA[<span>neverending...]]>" end + def test_should_not_mangle_urls_with_ampersand + assert_sanitized %{<a href=\"http://www.domain.com?var1=1&var2=2\">my link</a>} + end + protected def assert_sanitized(input, expected = nil) @sanitizer ||= HTML::WhiteListSanitizer.new diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb new file mode 100644 index 0000000000..3c936854dd --- /dev/null +++ b/actionpack/test/controller/logging_test.rb @@ -0,0 +1,46 @@ +require 'abstract_unit' + +class LoggingController < ActionController::Base + def show + render :nothing => true + end +end + +class LoggingTest < ActionController::TestCase + tests LoggingController + + class MockLogger + attr_reader :logged + + def method_missing(method, *args) + @logged ||= [] + @logged << args.first + end + end + + setup :set_logger + + def test_logging_without_parameters + get :show + assert_equal 2, logs.size + assert_nil logs.detect {|l| l =~ /Parameters/ } + end + + def test_logging_with_parameters + get :show, :id => 10 + assert_equal 3, logs.size + + params = logs.detect {|l| l =~ /Parameters/ } + assert_equal 'Parameters: {"id"=>"10"}', params + end + + private + + def set_logger + @controller.logger = MockLogger.new + end + + def logs + @logs ||= @controller.logger.logged.compact.map {|l| l.strip} + end +end diff --git a/actionpack/test/controller/mime_type_test.rb b/actionpack/test/controller/mime_type_test.rb index f16a3c68b4..21ae0419f1 100644 --- a/actionpack/test/controller/mime_type_test.rb +++ b/actionpack/test/controller/mime_type_test.rb @@ -61,7 +61,9 @@ class MimeTypeTest < Test::Unit::TestCase types.each do |type| mime = Mime.const_get(type.to_s.upcase) assert mime.send("#{type}?"), "#{mime.inspect} is not #{type}?" - (types - [type]).each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" } + invalid_types = types - [type] + invalid_types.delete(:html) if Mime::Type.html_types.include?(type) + invalid_types.each { |other_type| assert !mime.send("#{other_type}?"), "#{mime.inspect} is #{other_type}?" } end end @@ -71,14 +73,12 @@ class MimeTypeTest < Test::Unit::TestCase end def test_verifiable_mime_types - unverified_types = Mime::Type.unverifiable_types all_types = Mime::SET.to_a.map(&:to_sym) all_types.uniq! # Remove custom Mime::Type instances set in other tests, like Mime::GIF and Mime::IPHONE all_types.delete_if { |type| !Mime.const_defined?(type.to_s.upcase) } - - unverified, verified = all_types.partition { |type| Mime::Type.unverifiable_types.include? type } - assert verified.all? { |type| Mime.const_get(type.to_s.upcase).verify_request? }, "Not all Mime Types are verified: #{verified.inspect}" - assert unverified.all? { |type| !Mime.const_get(type.to_s.upcase).verify_request? }, "Some Mime Types are verified: #{unverified.inspect}" + verified, unverified = all_types.partition { |type| Mime::Type.browser_generated_types.include? type } + assert verified.each { |type| assert Mime.const_get(type.to_s.upcase).verify_request?, "Verifiable Mime Type is not verified: #{type.inspect}" } + assert unverified.each { |type| assert !Mime.const_get(type.to_s.upcase).verify_request?, "Nonverifiable Mime Type is verified: #{type.inspect}" } end end diff --git a/actionpack/test/controller/polymorphic_routes_test.rb b/actionpack/test/controller/polymorphic_routes_test.rb index 6ddf2826cd..620f2b3ab5 100644 --- a/actionpack/test/controller/polymorphic_routes_test.rb +++ b/actionpack/test/controller/polymorphic_routes_test.rb @@ -169,6 +169,17 @@ uses_mocha 'polymorphic URL helpers' do polymorphic_url([@article, :response, @tag], :format => :pdf) end + def test_nesting_with_array_containing_nil + expects(:article_response_url).with(@article) + polymorphic_url([@article, nil, :response]) + end + + def test_with_array_containing_single_object + @article.save + expects(:article_url).with(@article) + polymorphic_url([nil, @article]) + end + # TODO: Needs to be updated to correctly know about whether the object is in a hash or not def xtest_with_hash expects(:article_url).with(@article) diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index f7adaa7d4e..5669b8f358 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -77,57 +77,61 @@ module RequestForgeryProtectionTests ActionController::Base.request_forgery_protection_token = nil end + def test_should_render_form_with_token_tag - get :index - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token + get :index + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token + end + + def test_should_render_button_to_with_token_tag + get :show_button + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token + end + + def test_should_render_remote_form_with_only_one_token_parameter + get :remote_form + assert_equal 1, @response.body.scan(@token).size + end + + def test_should_allow_get + get :index + assert_response :success + end + + def test_should_allow_post_without_token_on_unsafe_action + post :unsafe + assert_response :success + end + + def test_should_not_allow_html_post_without_token + @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s + assert_raises(ActionController::InvalidAuthenticityToken) { post :index, :format => :html } end - def test_should_render_button_to_with_token_tag - get :show_button - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token - end - - def test_should_render_remote_form_with_only_one_token_parameter - get :remote_form - assert_equal 1, @response.body.scan(@token).size - end - - def test_should_allow_get - get :index - assert_response :success + def test_should_not_allow_html_put_without_token + @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s + assert_raises(ActionController::InvalidAuthenticityToken) { put :index, :format => :html } end - def test_should_allow_post_without_token_on_unsafe_action - post :unsafe - assert_response :success + def test_should_not_allow_html_delete_without_token + @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s + assert_raises(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html } end - def test_should_not_allow_post_without_token - assert_raises(ActionController::InvalidAuthenticityToken) { post :index } - end - - def test_should_not_allow_put_without_token - assert_raises(ActionController::InvalidAuthenticityToken) { put :index } - end - - def test_should_not_allow_delete_without_token - assert_raises(ActionController::InvalidAuthenticityToken) { delete :index } - end - - def test_should_not_allow_api_formatted_post_without_token - assert_raises(ActionController::InvalidAuthenticityToken) do + def test_should_allow_api_formatted_post_without_token + assert_nothing_raised do post :index, :format => 'xml' end end def test_should_not_allow_api_formatted_put_without_token - assert_raises(ActionController::InvalidAuthenticityToken) do + assert_nothing_raised do put :index, :format => 'xml' end end - def test_should_not_allow_api_formatted_delete_without_token - assert_raises(ActionController::InvalidAuthenticityToken) do + def test_should_allow_api_formatted_delete_without_token + assert_nothing_raised do delete :index, :format => 'xml' end end @@ -174,16 +178,20 @@ module RequestForgeryProtectionTests end end - def test_should_not_allow_xhr_post_without_token - assert_raises(ActionController::InvalidAuthenticityToken) { xhr :post, :index } + def test_should_allow_xhr_post_without_token + assert_nothing_raised { xhr :post, :index } + end + def test_should_not_allow_xhr_post_with_html_without_token + @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s + assert_raise(ActionController::InvalidAuthenticityToken) { xhr :post, :index } end - def test_should_not_allow_xhr_put_without_token - assert_raises(ActionController::InvalidAuthenticityToken) { xhr :put, :index } + def test_should_allow_xhr_put_without_token + assert_nothing_raised { xhr :put, :index } end - def test_should_not_allow_xhr_delete_without_token - assert_raises(ActionController::InvalidAuthenticityToken) { xhr :delete, :index } + def test_should_allow_xhr_delete_without_token + assert_nothing_raised { xhr :delete, :index } end def test_should_allow_post_with_token @@ -227,6 +235,7 @@ class RequestForgeryProtectionControllerTest < Test::Unit::TestCase def setup @controller = RequestForgeryProtectionController.new @request = ActionController::TestRequest.new + @request.format = :html @response = ActionController::TestResponse.new class << @request.session def session_id() '123' end @@ -248,11 +257,11 @@ class RequestForgeryProtectionWithoutSecretControllerTest < Test::Unit::TestCase ActionController::Base.request_forgery_protection_token = :authenticity_token end - def test_should_raise_error_without_secret - assert_raises ActionController::InvalidAuthenticityToken do - get :index - end - end + # def test_should_raise_error_without_secret + # assert_raises ActionController::InvalidAuthenticityToken do + # get :index + # end + # end end class CsrfCookieMonsterControllerTest < Test::Unit::TestCase @@ -304,10 +313,15 @@ class SessionOffControllerTest < Test::Unit::TestCase @token = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123') end - def test_should_raise_correct_exception - @request.session = {} # session(:off) doesn't appear to work with controller tests - assert_raises(ActionController::InvalidAuthenticityToken) do - post :index, :authenticity_token => @token - end - end + # TODO: Rewrite this test. + # This test was passing but for the wrong reason. + # Sessions aren't really being turned off, so an exception was raised + # because sessions weren't on - not because the token didn't match. + # + # def test_should_raise_correct_exception + # @request.session = {} # session(:off) doesn't appear to work with controller tests + # assert_raises(ActionController::InvalidAuthenticityToken) do + # post :index, :authenticity_token => @token, :format => :html + # end + # end end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 1fea82e564..04f7a0a528 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -14,6 +14,8 @@ class LogosController < ResourcesController; end class AccountsController < ResourcesController; end class AdminController < ResourcesController; end +class ProductsController < ResourcesController; end +class ImagesController < ResourcesController; end module Backoffice class ProductsController < ResourcesController; end @@ -776,6 +778,225 @@ class ResourcesTest < Test::Unit::TestCase end end + def test_resource_has_only_show_action + with_routing do |set| + set.draw do |map| + map.resources :products, :only => :show + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, :show, [:index, :new, :create, :edit, :update, :destroy]) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :show, [:index, :new, :create, :edit, :update, :destroy]) + end + end + + def test_singleton_resource_has_only_show_action + with_routing do |set| + set.draw do |map| + map.resource :account, :only => :show + end + + assert_singleton_resource_allowed_routes('accounts', {}, :show, [:index, :new, :create, :edit, :update, :destroy]) + assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :show, [:index, :new, :create, :edit, :update, :destroy]) + end + end + + def test_resource_does_not_have_destroy_action + with_routing do |set| + set.draw do |map| + map.resources :products, :except => :destroy + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, [:index, :new, :create, :show, :edit, :update], :destroy) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, [:index, :new, :create, :show, :edit, :update], :destroy) + end + end + + def test_singleton_resource_does_not_have_destroy_action + with_routing do |set| + set.draw do |map| + map.resource :account, :except => :destroy + end + + assert_singleton_resource_allowed_routes('accounts', {}, [:new, :create, :show, :edit, :update], :destroy) + assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, [:new, :create, :show, :edit, :update], :destroy) + end + end + + def test_resource_has_only_create_action_and_named_route + with_routing do |set| + set.draw do |map| + map.resources :products, :only => :create + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, :create, [:index, :new, :show, :edit, :update, :destroy]) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :create, [:index, :new, :show, :edit, :update, :destroy]) + + assert_not_nil set.named_routes[:products] + end + end + + def test_resource_has_only_update_action_and_named_route + with_routing do |set| + set.draw do |map| + map.resources :products, :only => :update + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, :update, [:index, :new, :create, :show, :edit, :destroy]) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :update, [:index, :new, :create, :show, :edit, :destroy]) + + assert_not_nil set.named_routes[:product] + end + end + + def test_resource_has_only_destroy_action_and_named_route + with_routing do |set| + set.draw do |map| + map.resources :products, :only => :destroy + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, :destroy, [:index, :new, :create, :show, :edit, :update]) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, :destroy, [:index, :new, :create, :show, :edit, :update]) + + assert_not_nil set.named_routes[:product] + end + end + + def test_singleton_resource_has_only_create_action_and_named_route + with_routing do |set| + set.draw do |map| + map.resource :account, :only => :create + end + + assert_singleton_resource_allowed_routes('accounts', {}, :create, [:new, :show, :edit, :update, :destroy]) + assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :create, [:new, :show, :edit, :update, :destroy]) + + assert_not_nil set.named_routes[:account] + end + end + + def test_singleton_resource_has_only_update_action_and_named_route + with_routing do |set| + set.draw do |map| + map.resource :account, :only => :update + end + + assert_singleton_resource_allowed_routes('accounts', {}, :update, [:new, :create, :show, :edit, :destroy]) + assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :update, [:new, :create, :show, :edit, :destroy]) + + assert_not_nil set.named_routes[:account] + end + end + + def test_singleton_resource_has_only_destroy_action_and_named_route + with_routing do |set| + set.draw do |map| + map.resource :account, :only => :destroy + end + + assert_singleton_resource_allowed_routes('accounts', {}, :destroy, [:new, :create, :show, :edit, :update]) + assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, :destroy, [:new, :create, :show, :edit, :update]) + + assert_not_nil set.named_routes[:account] + end + end + + def test_resource_has_only_collection_action + with_routing do |set| + set.draw do |map| + map.resources :products, :except => :all, :collection => { :sale => :get } + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) + + assert_recognizes({ :controller => 'products', :action => 'sale' }, :path => 'products/sale', :method => :get) + assert_recognizes({ :controller => 'products', :action => 'sale', :format => 'xml' }, :path => 'products/sale.xml', :method => :get) + end + end + + def test_resource_has_only_member_action + with_routing do |set| + set.draw do |map| + map.resources :products, :except => :all, :member => { :preview => :get } + end + + assert_resource_allowed_routes('products', {}, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) + assert_resource_allowed_routes('products', { :format => 'xml' }, { :id => '1' }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) + + assert_recognizes({ :controller => 'products', :action => 'preview', :id => '1' }, :path => 'products/1/preview', :method => :get) + assert_recognizes({ :controller => 'products', :action => 'preview', :id => '1', :format => 'xml' }, :path => 'products/1/preview.xml', :method => :get) + end + end + + def test_singleton_resource_has_only_member_action + with_routing do |set| + set.draw do |map| + map.resource :account, :except => :all, :member => { :signup => :get } + end + + assert_singleton_resource_allowed_routes('accounts', {}, [], [:new, :create, :show, :edit, :update, :destroy]) + assert_singleton_resource_allowed_routes('accounts', { :format => 'xml' }, [], [:new, :create, :show, :edit, :update, :destroy]) + + assert_recognizes({ :controller => 'accounts', :action => 'signup' }, :path => 'account/signup', :method => :get) + assert_recognizes({ :controller => 'accounts', :action => 'signup', :format => 'xml' }, :path => 'account/signup.xml', :method => :get) + end + end + + def test_nested_resource_inherits_only_show_action + with_routing do |set| + set.draw do |map| + map.resources :products, :only => :show do |product| + product.resources :images + end + end + + assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images') + assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images') + end + end + + def test_nested_resource_has_only_show_and_member_action + with_routing do |set| + set.draw do |map| + map.resources :products, :only => [:index, :show] do |product| + product.resources :images, :member => { :thumbnail => :get }, :only => :show + end + end + + assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images') + assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, :show, [:index, :new, :create, :edit, :update, :destroy], 'products/1/images') + + assert_recognizes({ :controller => 'images', :action => 'thumbnail', :product_id => '1', :id => '2' }, :path => 'products/1/images/2/thumbnail', :method => :get) + assert_recognizes({ :controller => 'images', :action => 'thumbnail', :product_id => '1', :id => '2', :format => 'jpg' }, :path => 'products/1/images/2/thumbnail.jpg', :method => :get) + end + end + + def test_nested_resource_ignores_only_option + with_routing do |set| + set.draw do |map| + map.resources :products, :only => :show do |product| + product.resources :images, :except => :destroy + end + end + + assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update], :destroy, 'products/1/images') + assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update], :destroy, 'products/1/images') + end + end + + def test_nested_resource_ignores_except_option + with_routing do |set| + set.draw do |map| + map.resources :products, :except => :show do |product| + product.resources :images, :only => :destroy + end + end + + assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, :destroy, [:index, :new, :create, :show, :edit, :update], 'products/1/images') + assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, :destroy, [:index, :new, :create, :show, :edit, :update], 'products/1/images') + end + end + protected def with_restful_routing(*args) with_routing do |set| @@ -979,6 +1200,51 @@ class ResourcesTest < Test::Unit::TestCase end end + def assert_resource_allowed_routes(controller, options, shallow_options, allowed, not_allowed, path = controller) + shallow_path = "#{path}/#{shallow_options[:id]}" + format = options[:format] && ".#{options[:format]}" + options.merge!(:controller => controller) + shallow_options.merge!(options) + + assert_whether_allowed(allowed, not_allowed, options, 'index', "#{path}#{format}", :get) + assert_whether_allowed(allowed, not_allowed, options, 'new', "#{path}/new#{format}", :get) + assert_whether_allowed(allowed, not_allowed, options, 'create', "#{path}#{format}", :post) + assert_whether_allowed(allowed, not_allowed, shallow_options, 'show', "#{shallow_path}#{format}", :get) + assert_whether_allowed(allowed, not_allowed, shallow_options, 'edit', "#{shallow_path}/edit#{format}", :get) + assert_whether_allowed(allowed, not_allowed, shallow_options, 'update', "#{shallow_path}#{format}", :put) + assert_whether_allowed(allowed, not_allowed, shallow_options, 'destroy', "#{shallow_path}#{format}", :delete) + end + + def assert_singleton_resource_allowed_routes(controller, options, allowed, not_allowed, path = controller.singularize) + format = options[:format] && ".#{options[:format]}" + options.merge!(:controller => controller) + + assert_whether_allowed(allowed, not_allowed, options, 'new', "#{path}/new#{format}", :get) + assert_whether_allowed(allowed, not_allowed, options, 'create', "#{path}#{format}", :post) + assert_whether_allowed(allowed, not_allowed, options, 'show', "#{path}#{format}", :get) + assert_whether_allowed(allowed, not_allowed, options, 'edit', "#{path}/edit#{format}", :get) + assert_whether_allowed(allowed, not_allowed, options, 'update', "#{path}#{format}", :put) + assert_whether_allowed(allowed, not_allowed, options, 'destroy', "#{path}#{format}", :delete) + end + + def assert_whether_allowed(allowed, not_allowed, options, action, path, method) + action = action.to_sym + options = options.merge(:action => action.to_s) + path_options = { :path => path, :method => method } + + if Array(allowed).include?(action) + assert_recognizes options, path_options + elsif Array(not_allowed).include?(action) + assert_not_recognizes options, path_options + end + end + + def assert_not_recognizes(expected_options, path) + assert_raise ActionController::RoutingError, ActionController::MethodNotAllowed, Test::Unit::AssertionFailedError do + assert_recognizes(expected_options, path) + end + end + def distinct_routes? (r1, r2) if r1.conditions == r2.conditions and r1.requirements == r2.requirements then if r1.segments.collect(&:to_s) == r2.segments.collect(&:to_s) then diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb index 010c00fa14..30422314a1 100644 --- a/actionpack/test/controller/session/cookie_store_test.rb +++ b/actionpack/test/controller/session/cookie_store_test.rb @@ -266,6 +266,7 @@ class CookieStoreTest < Test::Unit::TestCase @options = self.class.default_session_options.merge(options) session = CGI::Session.new(cgi, @options) + ObjectSpace.undefine_finalizer(session) assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}" assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}" diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index bade96fe17..1a3a6e86fa 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -239,7 +239,11 @@ class AssetTagHelperTest < ActionView::TestCase File.stubs(:exist?).with('template/../fixtures/public/images/rails.png.').returns(true) assert_equal '<img alt="Rails" src="/images/rails.png?1" />', image_tag('rails.png') ensure - ENV["RAILS_ASSET_ID"] = old_asset_id + if old_asset_id + ENV["RAILS_ASSET_ID"] = old_asset_id + else + ENV.delete("RAILS_ASSET_ID") + end end end diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 9247a42d33..06af8d1d6a 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -255,7 +255,8 @@ class AtomFeedTest < Test::Unit::TestCase def test_feed_xml_processing_instructions with_restful_routing(:scrolls) do get :index, :id => 'feed_with_xml_processing_instructions' - assert_match %r{<\?xml-stylesheet type="text/css" href="t.css"\?>}, @response.body + assert_match %r{<\?xml-stylesheet [^\?]*type="text/css"}, @response.body + assert_match %r{<\?xml-stylesheet [^\?]*href="t.css"}, @response.body end end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index de82647813..f8add0bab1 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -235,7 +235,7 @@ class FormTagHelperTest < ActionView::TestCase assert_match VALID_HTML_ID, label_elem['for'] end - def test_boolean_optios + def test_boolean_options assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes") assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil) assert_dom_equal %(<select id="people" multiple="multiple" name="people[]"><option>david</option></select>), select_tag("people", "<option>david</option>", :multiple => true) diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb index fc49d340ef..ef88cae5b8 100644 --- a/actionpack/test/template/tag_helper_test.rb +++ b/actionpack/test/template/tag_helper_test.rb @@ -19,6 +19,10 @@ class TagHelperTest < ActionView::TestCase assert_equal "<p />", tag("p", :ignored => nil) end + def test_tag_options_accepts_false_option + assert_equal "<p value=\"false\" />", tag("p", :value => false) + end + def test_tag_options_accepts_blank_option assert_equal "<p included=\"\" />", tag("p", :included => '') end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 5f9f715819..095c952d67 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -221,6 +221,7 @@ class TextHelperTest < ActionView::TestCase http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1 http://en.wikipedia.org/wiki/Sprite_(computer_graphics) http://en.wikipedia.org/wiki/Texas_hold'em + https://www.google.com/doku.php?id=gps:resource:scs:start ) urls.each do |url| diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 4ca062b535..c2299b56ad 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*2.2.1 [RC2 or 2.2 final]* +*2.2.1 [RC2] (November 14th, 2008)* * Ensure indices don't flip order in schema.rb #1266 [Jordi Bunster] diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 6e194ab9b4..69300e5ce5 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -312,7 +312,7 @@ module ActiveRecord table_name = klass.quoted_table_name primary_key = klass.primary_key column_type = klass.columns.detect{|c| c.name == primary_key}.type - ids = id_map.keys.uniq.map do |id| + ids = id_map.keys.map do |id| if column_type == :integer id.to_i elsif column_type == :float diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c7cb6eb966..7f7819115c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1609,7 +1609,7 @@ module ActiveRecord :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key, :select, :conditions, :include, :order, :group, :limit, :offset, :uniq, - :finder_sql, :delete_sql, :insert_sql, + :finder_sql, :counter_sql, :delete_sql, :insert_sql, :before_add, :after_add, :before_remove, :after_remove, :extend, :readonly, :validate diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a36a137f0d..757102eb6b 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2023,8 +2023,7 @@ module ActiveRecord #:nodoc: end def scoped_methods #:nodoc: - scoped_methods = (Thread.current[:scoped_methods] ||= {}) - scoped_methods[self] ||= [] + Thread.current[:"#{self}_scoped_methods"] ||= [] end def current_scoped_methods #:nodoc: diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 5e33cf1bd4..65512d534a 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -286,7 +286,7 @@ module ActiveRecord case operation when 'count' then value.to_i when 'sum' then type_cast_using_column(value || '0', column) - when 'avg' then value && value.to_d + when 'avg' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d else type_cast_using_column(value, column) end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 3016c329bd..901b17124c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -65,15 +65,23 @@ module ActiveRecord # The default ConnectionPool maximum size is 5. def initialize(spec) @spec = spec + # The cache of reserved connections mapped to threads @reserved_connections = {} + # The mutex used to synchronize pool access @connection_mutex = Monitor.new @queue = @connection_mutex.new_cond - # default 5 second timeout - @timeout = spec.config[:wait_timeout] || 5 + + # default 5 second timeout unless on ruby 1.9 + @timeout = + if RUBY_VERSION < '1.9' + spec.config[:wait_timeout] || 5 + end + # default max pool size to 5 @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5 + @connections = [] @checked_out = [] end @@ -187,7 +195,7 @@ module ActiveRecord # try looting dead threads clear_stale_cached_connections! if @size == @checked_out.size - raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?" + raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it." end end end @@ -292,10 +300,7 @@ module ActiveRecord # and also returns connections to the pool cached by threads that are no # longer alive. def clear_active_connections! - @connection_pools.each_value do |pool| - pool.release_connection - pool.clear_stale_cached_connections! - end + @connection_pools.each_value {|pool| pool.release_connection } end # Clears the cache which maps classes @@ -324,7 +329,8 @@ module ActiveRecord # Returns true if a connection that's accessible to this class has # already been opened. def connected?(klass) - conn = retrieve_connection_pool(klass) ? conn.connected? : false + conn = retrieve_connection_pool(klass) + conn ? conn.connected? : false end # Remove the connection for this class. This will close the active diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c5183357a1..f8fa969dc3 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -125,9 +125,8 @@ module ActiveRecord end # Returns true if its safe to reload the connection between requests for development mode. - # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite. def requires_reloading? - false + true end # Checks whether the connection to the database is still active (i.e. not stale). diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 2479b75789..3c5a9b7df8 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 2949f1d304..b5bedf3704 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -68,6 +68,16 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base :foreign_key => "developer_id" end +class DeveloperWithCounterSQL < ActiveRecord::Base + set_table_name 'developers' + has_and_belongs_to_many :projects, + :class_name => "DeveloperWithCounterSQL", + :join_table => "developers_projects", + :association_foreign_key => "project_id", + :foreign_key => "developer_id", + :counter_sql => 'SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}' +end + class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings @@ -739,6 +749,19 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_nothing_raised { david.projects.count(:all, :conditions => '1=1') } end + def test_count + david = Developer.find(1) + assert_equal 2, david.projects.count + end + + def test_count_with_counter_sql + developer = DeveloperWithCounterSQL.create(:name => 'tekin') + developer.project_ids = [projects(:active_record).id] + developer.save + developer.reload + assert_equal 1, developer.projects.count + end + uses_mocha 'mocking Post.transaction' do def test_association_proxy_transaction_method_starts_transaction_in_association_class Post.expects(:transaction) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 0fa61500c0..8bd0dd0f6e 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -25,6 +25,11 @@ class CalculationsTest < ActiveRecord::TestCase def test_should_return_nil_as_average assert_nil NumericData.average(:bank_balance) end + + def test_type_cast_calculated_value_should_convert_db_averages_of_fixnum_class_to_decimal + assert_equal 0, NumericData.send(:type_cast_calculated_value, 0, nil, 'avg') + assert_equal 53.0, NumericData.send(:type_cast_calculated_value, 53, nil, 'avg') + end def test_should_get_maximum_of_field assert_equal 60, Account.maximum(:credit_limit) diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 3e8c617a89..2649a9358a 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -28,10 +28,13 @@ class PooledConnectionsTest < ActiveRecord::TestCase end end - def test_pooled_connection_checkout - checkout_connections - assert_equal @connections.length, 2 - assert_equal @timed_out, 2 + # Will deadlock due to lack of Monitor timeouts in 1.9 + if RUBY_VERSION < '1.9' + def test_pooled_connection_checkout + checkout_connections + assert_equal @connections.length, 2 + assert_equal @timed_out, 2 + end end def checkout_checkin_connections(pool_size, threads) @@ -74,6 +77,11 @@ class PooledConnectionsTest < ActiveRecord::TestCase conn_pool.checkin(conn) end + def test_not_connected_defined_connection_returns_false + ActiveRecord::Base.establish_connection(@connection) + assert ! ActiveRecord::Base.connected? + end + def test_undefined_connection_returns_false old_handler = ActiveRecord::Base.connection_handler ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 114a63c415..e05a634a12 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,4 +1,4 @@ -*2.2.1 [RC2 or 2.2 final]* +*2.2.1 [RC2] (November 14th, 2008)* * Fixed that ActiveResource#post would post an empty string when it shouldn't be posting anything #525 [Paolo Angelini] diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index d56f4cf17e..3747ffc63c 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index e77affc315..3526c2e8fc 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,6 +1,4 @@ -*2.2.1 [RC2 or 2.2 final]* - -* Added render :js for people who want to render inline JavaScript replies without using RJS [DHH] +*2.2.1 [RC2] (November 14th, 2008)* * Fixed the option merging in Array#to_xml #1126 [Rudolf Gavlas] diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 1961fb43cf..ccbab525ba 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -1,7 +1,6 @@ require 'rake/testtask' require 'rake/rdoctask' require 'rake/gempackagetask' -require 'rake/contrib/sshpublisher' require File.join(File.dirname(__FILE__), 'lib', 'active_support', 'version') @@ -65,12 +64,14 @@ end desc "Publish the beta gem" task :pgem => [:package] do + require 'rake/contrib/sshpublisher' Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/as", "doc").upload end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 3d871eec11..fe568d6127 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -138,14 +138,22 @@ module ActiveSupport #:nodoc: end def load_with_new_constant_marking(file, *extras) #:nodoc: - Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) } + if Dependencies.load? + Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) } + else + load_without_new_constant_marking(file, *extras) + end rescue Exception => exception # errors from loading file exception.blame_file! file raise end def require(file, *extras) #:nodoc: - Dependencies.new_constants_in(Object) { super } + if Dependencies.load? + Dependencies.new_constants_in(Object) { super } + else + super + end rescue Exception => exception # errors from required file exception.blame_file! file raise diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 1ccfec4000..ba52e41c08 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -1,3 +1,4 @@ +# encoding: utf-8 require 'singleton' require 'iconv' diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb index f2bc12e832..c27c4ddb1a 100644 --- a/activesupport/lib/active_support/rescuable.rb +++ b/activesupport/lib/active_support/rescuable.rb @@ -78,7 +78,7 @@ module ActiveSupport def handler_for_rescue(exception) # We go from right to left because pairs are pushed onto rescue_handlers # as rescue_from declarations are found. - _, handler = Array(rescue_handlers).reverse.detect do |klass_name, handler| + _, rescuer = Array(rescue_handlers).reverse.detect do |klass_name, handler| # The purpose of allowing strings in rescue_from is to support the # declaration of handler associations for exception classes whose # definition is yet unknown. @@ -97,11 +97,11 @@ module ActiveSupport exception.is_a?(klass) if klass end - case handler + case rescuer when Symbol - method(handler) + method(rescuer) when Proc - handler.bind(self) + rescuer.bind(self) end end end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 4991f71683..1d87fa64b5 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -304,7 +304,8 @@ module ActiveSupport "Mexico City", "Monterrey", "Central America" ], [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota", "Lima", "Quito" ], - [-14_400, "Atlantic Time (Canada)", "Caracas", "La Paz", "Santiago" ], + [-16_200, "Caracas" ], + [-14_400, "Atlantic Time (Canada)", "La Paz", "Santiago" ], [-12_600, "Newfoundland" ], [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ], [ -7_200, "Mid-Atlantic" ], @@ -325,9 +326,9 @@ module ActiveSupport [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ], [ 16_200, "Kabul" ], [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ], - [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi" ], + [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi", "Sri Jayawardenepura" ], [ 20_700, "Kathmandu" ], - [ 21_600, "Astana", "Dhaka", "Sri Jayawardenepura", "Almaty", + [ 21_600, "Astana", "Dhaka", "Almaty", "Novosibirsk" ], [ 23_400, "Rangoon" ], [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ], diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 633eb2ef08..dc98936525 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -14,9 +14,9 @@ rescue Gem::LoadError end begin - gem 'memcache-client', '~> 1.5.0' + gem 'memcache-client', '~> 1.5.1' rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.0" + $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.1" end begin diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb index 30113201a6..99c9af0398 100644 --- a/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb +++ b/activesupport/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb @@ -1,10 +1,10 @@ # All original code copyright 2005, 2006, 2007 Bob Cottrell, Eric Hodel, # The Robot Co-op. All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: -# +# # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright @@ -13,7 +13,7 @@ # 3. Neither the names of the authors nor the names of their contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. -# +# # THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 8f5395fca6..6631f233f1 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index cc371b3a7b..e7dac4cc6b 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -160,6 +160,12 @@ uses_memcached 'memcached backed store' do @cache.read('foo').gsub!(/.*/, 'baz') assert_equal 'bar', @cache.read('foo') end + + def test_write_should_return_true_on_success + result = @cache.write('foo', 'bar') + assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written + assert result + end end class CompressedMemCacheStore < Test::Unit::TestCase diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 11ec1c6cd6..fd17f7a812 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -123,7 +123,7 @@ class TimeExtCalculationsTest < Test::Unit::TestCase def test_end_of_year assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,2,22,10,10,10).end_of_year assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,12,31,10,10,10).end_of_year - end + end def test_beginning_of_year assert_equal Time.local(2005,1,1,0,0,0), Time.local(2005,2,22,10,10,10).beginning_of_year diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 6c3bd1a4fd..fe04b91f2b 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -694,17 +694,17 @@ class DependenciesTest < Test::Unit::TestCase with_loading 'autoloading_fixtures' do ActiveSupport::Dependencies.mechanism = :require 2.times do - assert_raise(NameError) {"RaisesNameError".constantize} + assert_raise(NameError) { assert_equal 123, ::RaisesNameError::FooBarBaz } end end end def test_autoload_doesnt_shadow_name_error with_loading 'autoloading_fixtures' do - assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it hasn't been referenced yet!" + Object.send(:remove_const, :RaisesNameError) if defined?(::RaisesNameError) 2.times do begin - ::RaisesNameError.object_id + ::RaisesNameError::FooBarBaz.object_id flunk 'should have raised NameError when autoloaded file referenced FooBarBaz' rescue NameError => e assert_equal 'uninitialized constant RaisesNameError::FooBarBaz', e.message @@ -712,9 +712,9 @@ class DependenciesTest < Test::Unit::TestCase assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!" end - assert !defined?(RaisesNameError) + assert !defined?(::RaisesNameError) 2.times do - assert_raise(NameError) { RaisesNameError } + assert_raise(NameError) { ::RaisesNameError } assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!" end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 515ffcf0bf..d999b9f2a8 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -51,7 +51,7 @@ class TimeZoneTest < Test::Unit::TestCase define_method("test_utc_offset_for_#{name}") do silence_warnings do # silence warnings raised by tzinfo gem - period = zone.tzinfo.period_for_utc(Time.utc(2006,1,1,0,0,0)) + period = zone.tzinfo.period_for_utc(Time.utc(2009,1,1,0,0,0)) assert_equal period.utc_offset, zone.utc_offset end end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 058afddbde..ae20cb50da 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,4 +1,4 @@ -*2.2.1 [RC2 or 2.2 final]* +*2.2.1 [RC2] (November 14th, 2008)* * Fixed plugin generator so that generated unit tests would subclass ActiveSupport::TestCase, also introduced a helper script to reduce the needed require statements #1137 [Mathias Meyer] diff --git a/railties/environments/boot.rb b/railties/environments/boot.rb index 6a30b54973..57c256e438 100644 --- a/railties/environments/boot.rb +++ b/railties/environments/boot.rb @@ -82,7 +82,7 @@ module Rails def load_rubygems require 'rubygems' - min_version = '1.1.1' + min_version = '1.3.1' unless rubygems_version >= min_version $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.) exit 1 diff --git a/railties/lib/rails/vendor_gem_source_index.rb b/railties/lib/rails/vendor_gem_source_index.rb index dc821693ac..5b7721f303 100644 --- a/railties/lib/rails/vendor_gem_source_index.rb +++ b/railties/lib/rails/vendor_gem_source_index.rb @@ -81,7 +81,7 @@ module Rails spec.files = files else $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+ - " Giving up.") unless @silence_spec_warnings + " Giving up.") unless @@silence_spec_warnings next end end @@ -137,4 +137,4 @@ module Rails end end -end
\ No newline at end of file +end diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index a0986a2e05..bd835fba26 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 2 - TINY = 0 + TINY = 1 STRING = [MAJOR, MINOR, TINY].join('.') end |