diff options
79 files changed, 598 insertions, 153 deletions
diff --git a/.gitignore b/.gitignore index 52a431867c..24f9e9308d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,7 @@ debug.log .Gemfile /.bundle -/.rbenv-version -/.rvmrc +/.ruby-version /Gemfile.lock /pkg /dist diff --git a/.travis.yml b/.travis.yml index 8a35590e24..e130252522 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ script: 'ci/travis.rb' +before_install: + - gem install bundler rvm: - 1.8.7 - 1.9.2 - 1.9.3 + - 2.0.0 env: - "GEM=railties" - "GEM=ap,am,amo,ares,as" @@ -67,7 +67,7 @@ end platforms :jruby do gem 'json' - gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.0' + gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7' # This is needed by now to let tests work on JRuby # TODO: When the JRuby guys merge jruby-openssl in @@ -75,8 +75,8 @@ platforms :jruby do gem 'jruby-openssl' group :db do - gem 'activerecord-jdbcmysql-adapter', '>= 1.2.0' - gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.0' + gem 'activerecord-jdbcmysql-adapter', '>= 1.2.7' + gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.7' end end diff --git a/RAILS_VERSION b/RAILS_VERSION index 17ce91803c..275e51e5e5 100644 --- a/RAILS_VERSION +++ b/RAILS_VERSION @@ -1 +1 @@ -3.2.11 +3.2.12 diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index fec3a5c423..14b3df7f0f 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,4 +1,16 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* No changes. + + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## + +* No changes. + + +## Rails 3.2.12 (Feb 11, 2013) ## + +* No changes. ## Rails 3.2.11 (Jan 8, 2013) ## diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 695ea004f7..6c361b306f 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 = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 8cf9959831..e5facdb5a2 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,4 +1,26 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* Fix `ActionDispatch::Request#formats` when the Accept request-header is an + empty string. Fix #7774 [Backport #8977, #9541] + + *Soylent + Maxime Réty* + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## + +* Determine the controller#action from only the matched path when using the + shorthand syntax. Previously the complete path was used, which led + to problems with nesting (scopes and namespaces). + Fixes #7554. + Backport #9361. + + Example: + + # this will route to questions#new + scope ':locale' do + get 'questions/new' + end + + *Yves Senn* * Fix `assert_template` with `render :stream => true`. Fix #1743. @@ -6,7 +28,7 @@ *Sergey Nartimov* -* Eagerly populate the http method loookup cache so local project inflections do +* Eagerly populate the http method lookup cache so local project inflections do not interfere with use of underscore method ( and we don't need locks ) *Aditya Sanghi* @@ -64,7 +86,8 @@ * More descriptive error messages when calling `render :partial` with an invalid `:layout` argument. - #8376 + + Fixes #8376. render :partial => 'partial', :layout => true # results in ActionView::MissingTemplate: Missing partial /true @@ -123,6 +146,11 @@ *Daniel Fox, Grant Hutchins & Trace Wax* +## Rails 3.2.12 (Feb 11, 2013) ## + +* No changes. + + ## Rails 3.2.11 (Jan 8, 2013) ## * Strip nils from collections on JSON and XML posts. [CVE-2013-0155] diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 86e307255d..f01842575e 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', version) s.add_dependency('rack-cache', '~> 1.2') s.add_dependency('builder', '~> 3.0.0') - s.add_dependency('rack', '~> 1.4.3') + s.add_dependency('rack', '~> 1.4.5') s.add_dependency('rack-test', '~> 0.6.1') s.add_dependency('journey', '~> 1.0.4') s.add_dependency('sprockets', '~> 2.2.1') diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index de3354d4f9..9d1ff8cbe4 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -58,7 +58,8 @@ module ActionController end def method_for_action(action_name) - super || (respond_to?(:method_missing) && "_handle_method_missing") + super || ((self.class.public_method_defined?(:method_missing) || + self.class.protected_method_defined?(:method_missing)) && "_handle_method_missing") end end end diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb index b55c4643be..109484d88c 100644 --- a/actionpack/lib/action_controller/metal/hide_actions.rb +++ b/actionpack/lib/action_controller/metal/hide_actions.rb @@ -27,20 +27,14 @@ module ActionController self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze end - def inherited(klass) - klass.class_eval { @visible_actions = {} } - super - end - def visible_action?(action_name) - return @visible_actions[action_name] if @visible_actions.key?(action_name) - @visible_actions[action_name] = !hidden_actions.include?(action_name) + action_methods.include?(action_name) end # Overrides AbstractController::Base#action_methods to remove any methods # that are listed as hidden methods. def action_methods - @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }) + @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze end end end diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 9abb86caf8..7630ee2926 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -77,7 +77,7 @@ module ActionController private def _extract_redirect_to_status(options, response_status) - status = if options.is_a?(Hash) && options.key?(:status) + if options.is_a?(Hash) && options.key?(:status) Rack::Utils.status_code(options.delete(:status)) elsif response_status.key?(:status) Rack::Utils.status_code(response_status[:status]) diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 5c48a60469..42f14bc1e9 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -98,8 +98,8 @@ module ActionDispatch BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ def valid_accept_header - (xhr? && (accept || content_mime_type)) || - (accept && accept !~ BROWSER_LIKE_ACCEPTS) + (xhr? && (accept.present? || content_mime_type)) || + (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS) end def use_accept_header diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 9a474d2e3a..d71b21efc3 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -51,7 +51,6 @@ module ActionDispatch class Mapping #:nodoc: IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix] ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z} - SHORTHAND_REGEX = %r{/[\w/]+$} WILDCARD_PATH = %r{\*([^/\)]+)\)?$} def initialize(set, scope, path, options) @@ -68,14 +67,7 @@ module ActionDispatch private def normalize_options! - path_without_format = @path.sub(/\(\.:format\)$/, '') - - if using_match_shorthand?(path_without_format, @options) - to_shorthand = @options[:to].blank? - @options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1') - end - - @options.merge!(default_controller_and_action(to_shorthand)) + @options.merge!(default_controller_and_action) requirements.each do |name, requirement| # segment_keys.include?(k.to_s) || k == :controller @@ -153,7 +145,7 @@ module ActionDispatch end end - def default_controller_and_action(to_shorthand=nil) + def default_controller_and_action if to.respond_to?(:call) { } else @@ -166,7 +158,7 @@ module ActionDispatch controller ||= default_controller action ||= default_action - unless controller.is_a?(Regexp) || to_shorthand + unless controller.is_a?(Regexp) controller = [@scope[:module], controller].compact.join("/").presence end @@ -1261,6 +1253,11 @@ module ActionDispatch paths = [path] + rest end + path_without_format = path.to_s.sub(/\(\.:format\)$/, '') + if using_match_shorthand?(path_without_format, options) + options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1') + end + options[:anchor] = true unless options.key?(:anchor) if options[:on] && !VALID_ON_OPTIONS.include?(options[:on]) @@ -1271,6 +1268,10 @@ module ActionDispatch self end + def using_match_shorthand?(path, options) + path && (options[:to] || options[:action]).nil? && path =~ %r{/[\w/]+$} + end + def decomposed_match(path, options) # :nodoc: if on = options.delete(:on) send(on) { decomposed_match(path, options) } @@ -1288,9 +1289,10 @@ module ActionDispatch def add_route(action, options) # :nodoc: path = path_for_action(action, options.delete(:path)) + action = action.to_s.dup - if action.to_s =~ /^[\w\/]+$/ - options[:action] ||= action unless action.to_s.include?("/") + if action =~ /^[\w\/]+$/ + options[:action] ||= action unless action.include?("/") else action = nil end diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 10832373e1..9236f257c2 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack module VERSION #:nodoc: MAJOR = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 623c45fa13..f63a951c33 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -508,9 +508,9 @@ module ActionView convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } } if priority_zones - if priority_zones.is_a?(Regexp) + if priority_zones.is_a?(Regexp) priority_zones = model.all.find_all {|z| z =~ priority_zones} - end + end zone_options += options_for_select(convert_zones[priority_zones], selected) zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n" diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 2f372bd111..b627c77dca 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -1,5 +1,7 @@ # encoding: utf-8 +require 'active_support/core_ext/hash/keys' +require 'active_support/core_ext/hash/reverse_merge' require 'active_support/core_ext/big_decimal/conversions' require 'active_support/core_ext/float/rounding' require 'active_support/core_ext/object/blank' diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 38c4baceb0..d8b04119f2 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -81,7 +81,8 @@ end class StreamingLayoutController < LayoutTest def render(*args) options = args.extract_options! - super(*args, options.merge(:stream => true)) + options[:stream] = true + super(*(args << options)) end end @@ -129,10 +130,12 @@ class LayoutSetInResponseTest < ActionController::TestCase assert_template :layout => "layouts/layout_test" end - def test_layout_set_when_using_streaming_layout - @controller = StreamingLayoutController.new - get :hello - assert_template :hello + if RUBY_VERSION >= '1.9' + def test_layout_set_when_using_streaming_layout + @controller = StreamingLayoutController.new + get :hello + assert_template :hello + end end def test_layout_set_when_set_in_controller diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index dfd3ddbfa6..222cdfde1f 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -481,6 +481,15 @@ class RequestTest < ActiveSupport::TestCase request.expects(:parameters).at_least_once.returns({}) assert_equal [ Mime::HTML ], request.formats + request = stub_request 'HTTP_ACCEPT' => '' + request.expects(:parameters).at_least_once.returns({}) + assert_equal [Mime::HTML], request.formats + + request = stub_request 'HTTP_ACCEPT' => '', + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" + request.expects(:parameters).at_least_once.returns({}) + assert_equal [Mime::JS], request.formats + request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" request.expects(:parameters).at_least_once.returns({}) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 46d16598f7..88a5c37c43 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -515,6 +515,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match '/sculptors', :to => 'italians#sculptors' match '/painters/:painter', :to => 'italians#painters', :constraints => {:painter => /michelangelo/} end + + get 'search' => 'search' + + scope ':locale' do + match 'questions/new', :via => :get + end + + namespace :api do + namespace :v3 do + scope ':locale' do + get "products/list" + end + end + end end end @@ -1415,6 +1429,21 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_match_shorthand_inside_scope_with_variables_with_controller + with_test_routes do + get '/de/questions/new' + assert_equal 'questions#new', @response.body + assert_equal 'de', @request.params[:locale] + end + end + + def test_match_shorthand_inside_nested_namespaces_and_scopes_with_controller + with_test_routes do + get '/api/v3/en/products/list' + assert_equal 'api/v3/products#list', @response.body + end + end + def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper with_test_routes do assert_equal '/replies', replies_path @@ -2477,6 +2506,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal "/posts/1/admin", post_admin_root_path(:post_id => '1') end + def test_action_from_path_is_not_frozen + get '/search' + assert !@request.params[:action].frozen? + end + private def with_test_routes yield diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb index 62ad6be680..844484ee47 100644 --- a/actionpack/test/template/html-scanner/sanitizer_test.rb +++ b/actionpack/test/template/html-scanner/sanitizer_test.rb @@ -210,7 +210,7 @@ class SanitizerTest < ActionController::TestCase # TODO: Clean up def test_should_sanitize_attributes - assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'><script>alert()</script>">blah</span>) + assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>) end def test_should_sanitize_illegal_style_properties diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb index 56943381d8..b7f785fe3a 100644 --- a/actionpack/test/template/template_test.rb +++ b/actionpack/test/template/template_test.rb @@ -1,3 +1,4 @@ +# encoding: US-ASCII require "abstract_unit" require "logger" diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index fe83324848..73df2d6d10 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,8 +1,21 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* No changes. + + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## * Specify type of singular association during serialization *Steve Klabnik* +## Rails 3.2.12 (Feb 11, 2013) ## + +* Fix issue with `attr_protected` where malformed input could circumvent protection. + CVE-2013-0276 + + *joernchen* + + ## Rails 3.2.11 (Jan 8, 2013) ## * No changes. diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index f033a94c02..96f2c82631 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -365,7 +365,7 @@ module ActiveModel end @prefix, @suffix = options[:prefix] || '', options[:suffix] || '' - @regex = /^(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})$/ + @regex = /\A(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})\z/ @method_missing_target = "#{@prefix}attribute#{@suffix}" @method_name = "#{prefix}%s#{suffix}" end diff --git a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb index a1fcdf1a38..10faa29f31 100644 --- a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb +++ b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb @@ -19,7 +19,7 @@ module ActiveModel protected def remove_multiparameter_id(key) - key.to_s.gsub(/\(.+/, '') + key.to_s.gsub(/\(.+/m, '') end end diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb index 51a678d151..5f4fd126a6 100644 --- a/activemodel/lib/active_model/version.rb +++ b/activemodel/lib/active_model/version.rb @@ -2,7 +2,7 @@ module ActiveModel module VERSION #:nodoc: MAJOR = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 291e3539c4..2ac5287268 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,4 +1,132 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* Reload the association target if it's stale. `@stale_state` should be nil + when a model isn't saved. + Fixes #7526. + + *Larry Lv* + +* Don't read CSV files during execution of `db:fixtures:load`. CSV support for + fixtures was removed some time ago but the task was still loading them, even + though later the code was looking for the related yaml file instead. + + *kennyj* + + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## + +* Reverted 921a296a3390192a71abeec6d9a035cc6d1865c8, 'Quote numeric values + compared to string columns.' This caused several regressions. + + *Steve Klabnik* + +* Fix overriding of attributes by `default_scope` on `ActiveRecord::Base#dup`. + + *Hiroshige UMINO* + +* Fix issue with overriding Active Record reader methods with a composed object + and using that attribute as the scope of a `uniqueness_of` validation. + Backport #7072. + + *Peter Brown* + +* Sqlite now preserves custom primary keys when copying or altering tables. + Fixes #9367. + Backport #2312. + + *Sean Scally + Yves Senn* + +* Preloading `has_many :through` associations with conditions won't + cache the `:through` association. This will prevent invalid + subsets to be cached. + Fixes #8423. + Backport #9252. + + Example: + + class User + has_many :posts + has_many :recent_comments, -> { where('created_at > ?', 1.week.ago) }, :through => :posts + end + + a_user = User.includes(:recent_comments).first + + # this is preloaded + a_user.recent_comments + + # fetching the recent_comments through the posts association won't preload it. + a_user.posts + + *Yves Senn* + +* Fix handling of dirty time zone aware attributes + + Previously, when `time_zone_aware_attributes` were enabled, after + changing a datetime or timestamp attribute and then changing it back + to the original value, `changed_attributes` still tracked the + attribute as changed. This caused `[attribute]_changed?` and + `changed?` methods to return true incorrectly. + + Example: + + in_time_zone 'Paris' do + order = Order.new + original_time = Time.local(2012, 10, 10) + order.shipped_at = original_time + order.save + order.changed? # => false + + # changing value + order.shipped_at = Time.local(2013, 1, 1) + order.changed? # => true + + # reverting to original value + order.shipped_at = original_time + order.changed? # => false, used to return true + end + + Backport of #9073 + Fixes #8898 + + *Lilibeth De La Cruz* + +* Fix counter cache columns not updated when replacing `has_many :through` + associations. + Backport #8400. + Fix #7630. + + *Matthew Robertson* + +* Don't update `column_defaults` when calling destructive methods on column with default value. + Backport c517602. + Fix #6115. + + *Piotr Sarnacki + Aleksey Magusev + Alan Daud* + +* When `#count` is used in conjunction with `#uniq` we perform `count(:distinct => true)`. + Fix #6865. + + Example: + + relation.uniq.count # => SELECT COUNT(DISTINCT *) + + *Yves Senn + Kaspar Schiess* + +* Fix `ActiveRecord::Relation#pluck` when columns or tables are reserved words. + Backport #7536. + Fix #8968. + + *Ian Lesperance + Yves Senn + Kaspar Schiess* + +* Don't run explain on slow queries for database adapters that don't support it. + Backport #6197. + + *Blake Smith* + +* Revert round usec when comparing timestamp attributes in the dirty tracking. + Fixes #8460. + + *Andrew White* * Revert creation of through association models when using `collection=[]` on a `has_many :through` association from an unsaved model. @@ -179,6 +307,20 @@ *Gabriel Sobrinho, Ricardo Henrique* + +## Rails 3.2.12 (Feb 11, 2013) ## + +* Quote numeric values being compared to non-numeric columns. Otherwise, + in some database, the string column values will be coerced to a numeric + allowing 0, 0.0 or false to match any string starting with a non-digit. + + Example: + + App.where(apikey: 0) # => SELECT * FROM users WHERE apikey = '0' + + *Dylan Smith* + + ## Rails 3.2.11 (Jan 8, 2013) ## * Fix querying with an empty hash *Damien Mathieu* [CVE-2013-0155] diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index ab0d888b16..99f307922e 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -46,6 +46,7 @@ module ActiveRecord @loaded = false IdentityMap.remove(target) if IdentityMap.enabled? && target @target = nil + @stale_state = nil end # Reloads the \target and returns +self+ on success. @@ -128,16 +129,21 @@ module ActiveRecord # This method is abstract in the sense that it relies on +find_target+, # which is expected to be provided by descendants. # - # If the \target is already \loaded it is just returned. Thus, you can call - # +load_target+ unconditionally to get the \target. + # If the \target is stale(the target no longer points to the record(s) that the + # relevant foreign_key(s) refers to.), force reload the \target. + # + # Otherwise if the \target is already \loaded it is just returned. Thus, you can + # call +load_target+ unconditionally to get the \target. # # ActiveRecord::RecordNotFound is rescued within the method, and it is # not reraised. The proxy is \reset and +nil+ is the return value. def load_target - if find_target? + if (@stale_state && stale_target?) || find_target? begin if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class) @target = IdentityMap.get(association_class, owner[reflection.foreign_key]) + elsif @stale_state && stale_target? + @target = find_target end rescue NameError nil diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 97f531d064..52c67df646 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -72,7 +72,7 @@ module ActiveRecord end def stale_state - owner[reflection.foreign_key].to_s + owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s end end end diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index 2ee5dbbd70..88ce03a3cd 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -27,7 +27,8 @@ module ActiveRecord end def stale_state - [super, owner[reflection.foreign_type].to_s] + foreign_key = super + foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s] end end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 53d49fef2e..2b9e7ea55d 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -139,6 +139,11 @@ module ActiveRecord delete_through_records(records) + if source_reflection.options[:counter_cache] + counter = source_reflection.counter_cache_column + klass.decrement_counter counter, records.map(&:id) + end + if through_reflection.macro == :has_many && update_through_counter?(method) update_counter(-count, through_reflection) end diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb index ad6374d09a..4cb7b56b57 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -37,7 +37,8 @@ module ActiveRecord through_records = Array.wrap(owner.send(through_reflection.name)) # Dont cache the association - we would only be caching a subset - if reflection.options[:source_type] && through_reflection.collection? + if (preload_options != through_options) || + (reflection.options[:source_type] && through_reflection.collection?) owner.association(through_reflection.name).reset end diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index fd0e90aaf0..be890e5767 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -62,7 +62,7 @@ module ActiveRecord # properly support stale-checking for nested associations. def stale_state if through_reflection.macro == :belongs_to - owner[through_reflection.foreign_key].to_s + owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s end end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 39d81cf6ef..6f3112e654 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -42,14 +42,11 @@ module ActiveRecord unless time.acts_like?(:time) time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time end - zoned_time = time && time.in_time_zone rescue nil - rounded_time = round_usec(zoned_time) - rounded_value = round_usec(read_attribute("#{attr_name}")) - if (rounded_value != rounded_time) || (!rounded_value && original_time) - write_attribute("#{attr_name}", original_time) - #{attr_name}_will_change! - @attributes_cache["#{attr_name}"] = zoned_time - end + time = time.in_time_zone rescue nil if time + previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name}) + write_attribute(:#{attr_name}, original_time) + #{attr_name}_will_change! if previous_time != time + @attributes_cache["#{attr_name}"] = time end EOV generated_attribute_methods.module_eval(method_body, __FILE__, line) @@ -63,12 +60,6 @@ module ActiveRecord time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp]) end end - - private - def round_usec(value) - return unless value - value.change(:usec => 0) - end end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 612114bbce..5bd1721439 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -479,7 +479,8 @@ module ActiveRecord #:nodoc: # # Instantiates a single new object bypassing mass-assignment security # User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true) def initialize(attributes = nil, options = {}) - @attributes = self.class.initialize_attributes(self.class.column_defaults.dup) + defaults = Hash[self.class.column_defaults.map { |k, v| [k, v.duplicable? ? v.dup : v] }] + @attributes = self.class.initialize_attributes(defaults) @association_cache = {} @aggregation_cache = {} @attributes_cache = {} @@ -552,7 +553,6 @@ module ActiveRecord #:nodoc: @new_record = true ensure_proper_type - populate_with_current_scope_attributes super end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index ddb6896257..6a5cff6acd 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -42,8 +42,8 @@ module ActiveRecord # Represents the schema of an SQL table in an abstract way. This class # provides methods for manipulating the schema representation. # - # Inside migration files, the +t+ object in +create_table+ and - # +change_table+ is actually of this type: + # Inside migration files, the +t+ object in +create_table+ + # is actually of this type: # # class SomeMigration < ActiveRecord::Migration # def up diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index ca84c95bdc..455560aa55 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -490,7 +490,11 @@ module ActiveRecord end def copy_table(from, to, options = {}) #:nodoc: - options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) + from_primary_key = primary_key(from) + options[:primary_key] = from_primary_key if from_primary_key != 'id' + unless options[:primary_key] + options[:id] = !columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == from_primary_key + end create_table(to, options) do |definition| @definition = definition columns(from).each do |column| @@ -504,7 +508,7 @@ module ActiveRecord :precision => column.precision, :scale => column.scale, :null => column.null) end - @definition.primary_key(primary_key(from)) if primary_key(from) + @definition.primary_key(from_primary_key) if from_primary_key yield @definition if block_given? end diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index bdccf535ea..236834fd84 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -11,11 +11,12 @@ module ActiveRecord end end - # If auto explain is enabled, this method triggers EXPLAIN logging for the - # queries triggered by the block if it takes more than the threshold as a - # whole. That is, the threshold is not checked against each individual - # query, but against the duration of the entire block. This approach is - # convenient for relations. + # If the database adapter supports explain and auto explain is enabled, + # this method triggers EXPLAIN logging for the queries triggered by the + # block if it takes more than the threshold as a whole. That is, the + # threshold is not checked against each individual query, but against the + # duration of the entire block. This approach is convenient for relations. + # # The available_queries_for_explain thread variable collects the queries # to be explained. If the value is nil, it means queries are not being @@ -26,7 +27,7 @@ module ActiveRecord threshold = auto_explain_threshold_in_seconds current = Thread.current - if threshold && current[:available_queries_for_explain].nil? + if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil? begin queries = current[:available_queries_for_explain] = [] start = Time.now diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 13b7c6e214..055d27d85c 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -83,6 +83,13 @@ module ActiveRecord end end + initializer "active_record.validate_explain_support" do |app| + if app.config.active_record[:auto_explain_threshold_in_seconds] && + !ActiveRecord::Base.connection.supports_explain? + warn "auto_explain_threshold_in_seconds is set but will be ignored because your adapter does not support this feature. Please unset the configuration to avoid this warning." + end + end + # Expose database runtime to controller for logging. initializer "active_record.log_runtime" do |app| require "active_record/railties/controller_runtime" diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 1bdb841398..7f1b84b848 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -355,7 +355,7 @@ db_namespace = namespace :db do base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact - (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file) end end @@ -433,11 +433,11 @@ db_namespace = namespace :db do when 'sqlserver' `smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U` when "firebird" - set_firebird_env(abcs[Rails.env]) - db_string = firebird_db_string(abcs[Rails.env]) + set_firebird_env(config) + db_string = firebird_db_string(config) sh "isql -a #{db_string} > #{filename}" else - raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'" + raise "Task not supported by '#{config['adapter']}'" end if ActiveRecord::Base.connection.supports_migrations? diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index ceacf93b60..1f9dbdc3d4 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -178,7 +178,7 @@ module ActiveRecord # def pluck(column_name) if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s) - column_name = "#{table_name}.#{column_name}" + column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}" else column_name = column_name.to_s end @@ -195,7 +195,8 @@ module ActiveRecord def perform_calculation(operation, column_name, options = {}) operation = operation.to_s.downcase - distinct = options[:distinct] + # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count) + distinct = options[:distinct] || self.uniq_value if operation == "count" column_name ||= (select_for_count || :all) diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 9ceab2eabc..b8d2cd2866 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -26,9 +26,15 @@ module ActiveRecord private def hash_rows - @hash_rows ||= @rows.map { |row| - Hash[@columns.zip(row)] - } + @hash_rows ||= + begin + # We freeze the strings to prevent them getting duped when + # used as keys in ActiveRecord::Model's @attributes hash + columns = @columns.map { |c| c.dup.freeze } + @rows.map { |row| + Hash[columns.zip(row)] + } + end end end end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 154aacef9f..795088530e 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -26,7 +26,7 @@ module ActiveRecord relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted? Array.wrap(options[:scope]).each do |scope_item| - scope_value = record.send(scope_item) + scope_value = record.read_attribute(scope_item) relation = relation.and(table[scope_item].eq(scope_value)) end diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index ff9fa279f4..a340cfaf7d 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 = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb index 575b4806c1..74288a98d1 100644 --- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb @@ -57,6 +57,14 @@ class CopyTableTest < ActiveRecord::TestCase end end + def test_copy_table_with_unconventional_primary_key + test_copy_table('owners', 'owners_unconventional') do |from, to, options| + original_pk = @connection.primary_key('owners') + copied_pk = @connection.primary_key('owners_unconventional') + assert_equal original_pk, copied_pk + end + end + protected def copy_table(from, to, options = {}) @connection.copy_table(from, to, {:temporary => true}.merge(options)) diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index ee338a3b99..6556219205 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -11,8 +11,8 @@ if ActiveRecord::Base.connection.supports_migrations? def teardown @connection.drop_table :fruits rescue nil - @connection.drop_table :"_pre_fruits_suf_" rescue nil - @connection.drop_table :"_pre_schema_migrations_suf_" rescue nil + @connection.drop_table :"_p_fruits_s_" rescue nil + @connection.drop_table :"_p_schema_migrations_s_" rescue nil end def test_schema_define @@ -24,8 +24,9 @@ if ActiveRecord::Base.connection.supports_migrations? end def test_schema_define_with_table_prefix_and_suffix - ActiveRecord::Base.table_name_prefix = '_pre_' - ActiveRecord::Base.table_name_suffix = '_suf_' + # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters + ActiveRecord::Base.table_name_prefix = '_p_' + ActiveRecord::Base.table_name_suffix = '_s_' perform_schema_define! diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index f392366c19..c9b26895ae 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -14,6 +14,8 @@ require 'models/sponsor' require 'models/member' require 'models/essay' require 'models/toy' +require 'models/person' +require 'models/reader' class BelongsToAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :topics, @@ -716,4 +718,16 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal toy, sponsor.reload.sponsorable end + + def test_saving_nested_association + post1, post2 = Post.limit(2) + person = Person.new(:first_name => 'foo') + reader = Reader.new(:post => post1) + + reader.post_id = post2.id + person.readers = [reader] + + assert person.save + assert_equal reader.post_id, post2.id + end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 20c6b691fc..944f135153 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1112,4 +1112,10 @@ class EagerAssociationTest < ActiveRecord::TestCase Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first end end + + test "preloading does not cache has many association subset when preloaded with a through association" do + author = Author.order(:id).includes(:comments_with_order_and_conditions, :posts).first + assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size } + assert_no_queries { assert_equal 5, author.posts.size, "should not cache a subset of the association" } + end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 40d2e9568d..277a3bddaa 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -346,6 +346,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end end + def test_update_counter_caches_on_replace_association + post = posts(:welcome) + tag = post.tags.create!(:name => 'doomed') + tag.tagged_posts << posts(:thinking) + + tag.tagged_posts = [] + post.reload + + assert_equal(post.taggings.count, post.taggings_count) + end + def test_replace_association assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)} diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index b849eead26..97d6c0cf88 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -325,6 +325,12 @@ class BasicsTest < ActiveRecord::TestCase assert parrot.valid? end + def test_default_values_are_deeply_dupped + company = Company.new + company.description << "foo" + assert_equal "", Company.new.description + end + def test_load topics = Topic.find(:all, :order => 'id') assert_equal(4, topics.size) @@ -2148,7 +2154,7 @@ class BasicsTest < ActiveRecord::TestCase end def test_attribute_names - assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"], + assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id", "description"], Company.attribute_names end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index f931b39998..a1dc1de38d 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -1,10 +1,11 @@ require "cases/helper" +require 'models/club' require 'models/company' require "models/contract" -require 'models/topic' require 'models/edge' -require 'models/club' require 'models/organization' +require 'models/possession' +require 'models/topic' Company.has_many :accounts @@ -368,6 +369,10 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 5, Account.count(:firm_id) end + def test_count_with_uniq + assert_equal 4, Account.select(:credit_limit).uniq.count + end + def test_count_with_column_and_options_parameter assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50 AND firm_id IS NOT NULL") end @@ -503,4 +508,10 @@ class CalculationsTest < ActiveRecord::TestCase Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)]) assert_equal [7], Company.joins(:contracts).pluck(:developer_id).map(&:to_i) end + + def test_pluck_with_reserved_words + Possession.create!(:where => "Over There") + + assert_equal ["Over There"], Possession.pluck(:where) + end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 86a28d95ad..9756c182b9 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -76,6 +76,8 @@ class DirtyTest < ActiveRecord::TestCase assert pirate.created_on_changed? assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was assert_equal old_created_on, pirate.created_on_was + pirate.created_on = old_created_on + assert !pirate.created_on_changed? end end @@ -549,18 +551,17 @@ class DirtyTest < ActiveRecord::TestCase end end - def test_setting_time_attributes_with_time_zone_field_to_same_time_should_not_be_marked_as_a_change + def test_datetime_attribute_can_be_updated_with_fractional_seconds in_time_zone 'Paris' do target = Class.new(ActiveRecord::Base) - target.table_name = 'pirates' + target.table_name = 'topics' - created_on = Time.now + written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone('Paris') - pirate = target.create(:created_on => created_on) - pirate.reload # Here mysql truncate the usec value to 0 + topic = target.create(:written_on => written_on) + topic.written_on += 0.3 - pirate.created_on = created_on - assert !pirate.created_on_changed? + assert topic.written_on_changed?, 'Fractional second update not detected' end end diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index b2a3cb5733..17a02f139a 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -113,5 +113,14 @@ module ActiveRecord assert topic.invalid? assert duped.valid? end + + def test_dup_with_default_scope + prev_default_scopes = Topic.default_scopes + Topic.default_scopes = [Topic.where(:approved => true)] + topic = Topic.new(:approved => false) + assert !topic.dup.approved?, "should not be overriden by default scopes" + ensure + Topic.default_scopes = prev_default_scopes + end end end diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb index cb7781f8e7..bdeb0a033c 100644 --- a/activerecord/test/cases/explain_test.rb +++ b/activerecord/test/cases/explain_test.rb @@ -98,6 +98,16 @@ if ActiveRecord::Base.connection.supports_explain? assert_equal expected, base.exec_explain(queries) end + def test_unsupported_connection_adapter + connection.stubs(:supports_explain?).returns(false) + + base.logger.expects(:warn).never + + with_threshold(0) do + Car.where(:name => 'honda').to_a + end + end + def test_silence_auto_explain base.expects(:collecting_sqls_for_explain).never base.logger.expects(:warn).never diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 7bb71b7b6e..7f0d921545 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -61,7 +61,7 @@ if ActiveRecord::Base.connection.supports_migrations? ActiveRecord::Base.connection.initialize_schema_migrations_table ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}" - %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table| + %w(things awesome_things prefix_things_suffix p_awesome_things_s).each do |table| Thing.connection.drop_table(table) rescue nil end Thing.reset_column_information @@ -874,8 +874,6 @@ if ActiveRecord::Base.connection.supports_migrations? end def test_remove_column_with_array_as_an_argument_is_deprecated - return skip "remove_column with array as argument is not supported with OracleAdapter" if current_adapter? :OracleAdapter - ActiveRecord::Base.connection.create_table(:hats) do |table| table.column :hat_name, :string, :limit => 100 table.column :hat_size, :integer @@ -886,7 +884,21 @@ if ActiveRecord::Base.connection.supports_migrations? Person.connection.remove_column("hats", ["hat_name", "hat_size"]) end ensure - ActiveRecord::Base.connection.drop_table(:hats) rescue nil + ActiveRecord::Base.connection.drop_table(:hats) + end + + def test_removing_and_renaming_column_preserves_custom_primary_key + ActiveRecord::Base.connection.create_table "my_table", :primary_key => "my_table_id", :force => true do |t| + t.integer "col_one" + t.string "col_two", :limit => 128, :null => false + end + + ActiveRecord::Base.connection.remove_column("my_table", "col_two") + ActiveRecord::Base.connection.rename_column("my_table", "col_one", "col_three") + + assert_equal 'my_table_id', ActiveRecord::Base.connection.primary_key('my_table') + ensure + ActiveRecord::Base.connection.drop_table(:my_table) rescue nil end def test_change_type_of_not_null_column @@ -1633,8 +1645,8 @@ if ActiveRecord::Base.connection.supports_migrations? def test_rename_table_with_prefix_and_suffix assert !Thing.table_exists? - ActiveRecord::Base.table_name_prefix = 'prefix_' - ActiveRecord::Base.table_name_suffix = '_suffix' + ActiveRecord::Base.table_name_prefix = 'p_' + ActiveRecord::Base.table_name_suffix = '_s' Thing.reset_table_name Thing.reset_sequence_name WeNeedThings.up @@ -1643,7 +1655,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "hello world", Thing.find(:first).content RenameThings.up - Thing.table_name = "prefix_awesome_things_suffix" + Thing.table_name = "p_awesome_things_s" assert_equal "hello world", Thing.find(:first).content ensure diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb index 8708117129..0f9cb10f0f 100644 --- a/activerecord/test/cases/validations/uniqueness_validation_test.rb +++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb @@ -22,6 +22,14 @@ end class Thaumaturgist < IneptWizard end +class ReplyTitle; end + +class ReplyWithTitleObject < Reply + validates_uniqueness_of :content, :scope => :title + + def title; ReplyTitle.new; end +end + class UniquenessValidationTest < ActiveRecord::TestCase fixtures :topics, 'warehouse-things', :developers @@ -80,6 +88,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase assert r3.valid?, "Saving r3" end + def test_validate_uniqueness_with_composed_attribute_scope + r1 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world" + assert r1.valid?, "Saving r1" + + r2 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world" + assert !r2.valid?, "Saving r2 first time" + end + def test_validate_uniqueness_scoped_to_defining_class t = Topic.create("title" => "What, me worry?") diff --git a/activerecord/test/models/possession.rb b/activerecord/test/models/possession.rb new file mode 100644 index 0000000000..ddf759113b --- /dev/null +++ b/activerecord/test/models/possession.rb @@ -0,0 +1,3 @@ +class Possession < ActiveRecord::Base + self.table_name = 'having' +end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 1a993fef11..ab62fb82b6 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -176,6 +176,7 @@ ActiveRecord::Schema.define do t.integer :client_of t.integer :rating, :default => 1 t.integer :account_id + t.string :description, :default => "" end add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index" @@ -281,6 +282,10 @@ ActiveRecord::Schema.define do t.string :info end + create_table :having, :force => true do |t| + t.string :where + end + create_table :guids, :force => true do |t| t.column :key, :string end diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md index 98a67b96a5..0ff884e809 100644 --- a/activeresource/CHANGELOG.md +++ b/activeresource/CHANGELOG.md @@ -1,4 +1,16 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* No changes. + + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## + +* No changes. + + +## Rails 3.2.12 (Feb 11, 2013) ## + +* No changes. ## Rails 3.2.11 (Jan 8, 2013) ## diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 500da6c137..0547dbe14d 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 = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 0511a7ac2d..528de79cf6 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,4 +1,13 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* No changes. + + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## + +* Fix DateTime comparison with DateTime::Infinity object. + + *Dan Kubb* * Remove surrogate unicode character encoding from ActiveSupport::JSON.encode The encoding scheme was broken for unicode characters outside the basic @@ -19,9 +28,11 @@ *Daniele Sluijters* -* Fix DateTime comparison with DateTime::Infinity object. - *Dan Kubb* +## Rails 3.2.12 (Feb 11, 2013) ## + +* No changes. + ## Rails 3.2.11 (Jan 8, 2012) ## @@ -37,6 +48,7 @@ * No changes. + ## Rails 3.2.9 (Nov 12, 2012) ## * Add logger.push_tags and .pop_tags to complement logger.tagged: diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index e6fc7bf571..fb3575b865 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -18,6 +18,6 @@ Gem::Specification.new do |s| s.rdoc_options.concat ['--encoding', 'UTF-8'] - s.add_dependency('i18n', '~> 0.6') + s.add_dependency('i18n', '~> 0.6', '>= 0.6.4') s.add_dependency('multi_json', '~> 1.0') end diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb index 7d54c9fae6..e5042c6c18 100644 --- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb +++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb @@ -1,7 +1,6 @@ require 'active_support/hash_with_indifferent_access' class Hash - # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver: # # {:a => 1}.with_indifferent_access["a"] # => 1 diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 0484d8e5d8..a983cae39e 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -13,7 +13,7 @@ class Hash # valid_keys = [:mass, :velocity, :time] # search(options.slice(*valid_keys)) def slice(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) hash = self.class.new keys.each { |k| hash[k] = self[k] if has_key?(k) } hash @@ -23,7 +23,7 @@ class Hash # Returns a hash contained the removed key/value pairs # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4} def slice!(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) omit = slice(*self.keys - keys) hash = slice(*keys) replace(hash) diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 526b8378a5..84986dff3c 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -1,4 +1,6 @@ require 'rbconfig' +require 'stringio' + module Kernel # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards. # diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 9e7cb76307..9dc93de5a9 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -6,7 +6,7 @@ require 'active_support/core_ext/hash/keys' module ActiveSupport class HashWithIndifferentAccess < Hash - + # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class. def extractable_options? true diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 6b29ba4c10..77c04758ba 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -12,8 +12,8 @@ module ActiveSupport end class ProxyTestResult - def initialize - @calls = [] + def initialize(calls = []) + @calls = calls end def add_error(e) @@ -27,6 +27,14 @@ module ActiveSupport end end + def marshal_dump + @calls + end + + def marshal_load(calls) + initialize(calls) + end + def method_missing(name, *args) @calls << [name, args] end diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb index 96718d4bd3..50c4852d4c 100644 --- a/activesupport/lib/active_support/testing/performance/ruby.rb +++ b/activesupport/lib/active_support/testing/performance/ruby.rb @@ -142,7 +142,7 @@ module ActiveSupport end end -if RUBY_VERSION.between?('1.9.2', '2.0') +if RUBY_VERSION.between?('1.9.2', '2.0.0') require 'active_support/testing/performance/ruby/yarv' elsif RUBY_VERSION.between?('1.8.6', '1.9') require 'active_support/testing/performance/ruby/mri' diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index e928403dd2..2230c5b78e 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 = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index c4c753caed..6db1746672 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -690,7 +690,6 @@ uses_memcached 'memcached backed store' do @data = @cache.instance_variable_get(:@data) @cache.clear @cache.silence! - @cache.logger = Logger.new("/dev/null") end include CacheStoreBehavior diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 0b5f912dc4..a7fd9402c8 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -221,7 +221,6 @@ class OrderedHashTest < Test::Unit::TestCase alternate = ActiveSupport::OrderedHash[ [ [1, 2], [3, 4], - "bad key value pair", [ 'missing value' ] ]] diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb index c4653b1ae6..ab74d579fc 100644 --- a/activesupport/test/test_case_test.rb +++ b/activesupport/test/test_case_test.rb @@ -16,6 +16,9 @@ module ActiveSupport def options nil end + + def record(*args) + end end if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 44533a579c..e558a7e564 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,4 +1,16 @@ -## Rails 3.2.12 (unreleased) ## +## unreleased ## + +* No changes. + + +## Rails 3.2.13.rc1 (Feb 17, 2013) ## + +* No changes. + + +## Rails 3.2.12 (Feb 11, 2013) ## + +* No changes. ## Rails 3.2.11 (Jan 8, 2013) ## diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb index 6c53d8bebb..1b7f5dee2a 100644 --- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb +++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb @@ -214,6 +214,18 @@ task :default => :test public_task :apply_rails_template, :run_bundle + def name + @name ||= begin + # same as ActiveSupport::Inflector#underscore except not replacing '-' + underscored = original_name.dup + underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') + underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2') + underscored.downcase! + + underscored + end + end + protected def app_templates_dir @@ -254,18 +266,6 @@ task :default => :test @original_name ||= File.basename(destination_root) end - def name - @name ||= begin - # same as ActiveSupport::Inflector#underscore except not replacing '-' - underscored = original_name.dup - underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') - underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2') - underscored.downcase! - - underscored - end - end - def camelized @camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize end diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index 352ecf45c0..ec1335ad34 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index 9e9702efb6..adec9533a9 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -2,6 +2,7 @@ require 'isolation/abstract_unit' require 'active_support/core_ext/kernel/reporting' require 'rack/test' +require 'yaml' module ApplicationTests class AssetsTest < Test::Unit::TestCase diff --git a/railties/test/application/initializers/active_record_test.rb b/railties/test/application/initializers/active_record_test.rb index edf78a8a0a..b62943a278 100644 --- a/railties/test/application/initializers/active_record_test.rb +++ b/railties/test/application/initializers/active_record_test.rb @@ -23,10 +23,17 @@ module ApplicationTests boot_rails simple_controller - get '/foo' - assert last_response.body.include?("We're sorry, but something went wrong (500)") + # ActiveSupport::LogSubscriber.flush_all! in lib/rails/rack/logger.rb blew up in Ruby 2.0 + # because it tries to open the database. This behavior doesn't happen in Ruby 1.9.3. + # However, regardless, the server blew up. + if RUBY_VERSION >= '2.0.0' + assert_raises (Errno::ENOENT) { get '/foo' } + else + get '/foo' + assert last_response.body.include?("We're sorry, but something went wrong (500)") + end end - + test "uses DATABASE_URL env var when config/database.yml doesn't exist" do database_path = "/db/foo.sqlite3" FileUtils.rm_rf("#{app_path}/config/database.yml") @@ -35,7 +42,7 @@ module ApplicationTests get '/foo' assert_equal 'foo', last_response.body - + # clean up FileUtils.rm("#{app_path}/#{database_path}") end diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index ab9084df55..107b54c0be 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -122,6 +122,18 @@ module ApplicationTests assert_equal 0, ::AppTemplate::Application::User.count end + def test_loading_only_yml_fixtures + Dir.chdir(app_path) do + `rake db:migrate` + end + + app_file "test/fixtures/products.csv", "" + + require "#{rails_root}/config/environment" + errormsg = Dir.chdir(app_path) { `rake db:fixtures:load` } + assert $?.success?, errormsg + end + def test_scaffold_tests_pass_by_default content = Dir.chdir(app_path) do `rails generate scaffold user username:string password:string` diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/route_inspect_test.rb index 5c920cb33a..dea0ee71c9 100644 --- a/railties/test/application/route_inspect_test.rb +++ b/railties/test/application/route_inspect_test.rb @@ -18,7 +18,7 @@ module ApplicationTests def test_displaying_routes_for_engines engine = Class.new(Rails::Engine) do - def self.to_s + def self.inspect "Blog::Engine" end end @@ -136,7 +136,7 @@ module ApplicationTests def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints constraint = Class.new do - def to_s + def inspect "( my custom constraint )" end end diff --git a/version.rb b/version.rb index 352ecf45c0..ec1335ad34 100644 --- a/version.rb +++ b/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 3 MINOR = 2 - TINY = 11 + TINY = 12 PRE = nil STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') |