diff options
20 files changed, 119 insertions, 34 deletions
@@ -8,7 +8,6 @@ gemspec gem 'mocha', '~> 0.14', require: false gem 'rack-cache', '~> 1.2' -# TODO: Release this gem gem 'jquery-rails', '~> 4.0.0.beta2' gem 'coffee-rails', '~> 4.0.0' # TODO: Release this gem diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index 9e8d005ec7..29733442c1 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -121,8 +121,8 @@ module ActionView # asset_path "application", type: :stylesheet # => /assets/application.css # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js def asset_path(source, options = {}) - return "" unless source.present? source = source.to_s + return "" unless source.present? return source if source =~ URI_REGEXP tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '') diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index d789a5ca27..df8547ef85 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -535,6 +535,17 @@ class AssetTagHelperTest < ActionView::TestCase assert_equal copy, source end + class PlaceholderImage + def blank?; true; end + def to_s; 'no-image-yet.png'; end + end + def test_image_tag_with_blank_placeholder + assert_equal '<img alt="" src="/images/no-image-yet.png" />', image_tag(PlaceholderImage.new, alt: "") + end + def test_image_path_with_blank_placeholder + assert_equal '/images/no-image-yet.png', image_path(PlaceholderImage.new) + end + def test_image_path_with_asset_host_proc_returning_nil @controller.config.asset_host = Proc.new do |source| unless source.end_with?("tiff") diff --git a/activejob/activejob.gemspec b/activejob/activejob.gemspec index 9944b2a1bd..a9be2a8f00 100644 --- a/activejob/activejob.gemspec +++ b/activejob/activejob.gemspec @@ -19,5 +19,5 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.add_dependency 'activesupport', version - s.add_dependency 'globalid', '>= 0.2.3' + s.add_dependency 'globalid', '>= 0.3.0' end diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb index 69a4ad928d..175a2f0956 100644 --- a/activejob/lib/active_job/arguments.rb +++ b/activejob/lib/active_job/arguments.rb @@ -38,7 +38,7 @@ module ActiveJob def serialize_argument(argument) case argument when GlobalID::Identification - argument.global_id.to_s + argument.to_global_id.to_s when *TYPE_WHITELIST argument when Array diff --git a/activejob/test/cases/parameters_test.rb b/activejob/test/cases/parameters_test.rb index 78853c51e1..92f835af5d 100644 --- a/activejob/test/cases/parameters_test.rb +++ b/activejob/test/cases/parameters_test.rb @@ -26,8 +26,8 @@ class ParameterSerializationTest < ActiveSupport::TestCase end test 'should dive deep into arrays or hashes' do - assert_equal [ { "a" => Person.find(5).gid.to_s }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ]) - assert_equal [ [ Person.find(5).gid.to_s ] ], ActiveJob::Arguments.serialize([ [ Person.find(5) ] ]) + assert_equal [ { "a" => Person.find(5).to_gid.to_s }.with_indifferent_access ], ActiveJob::Arguments.serialize([ { a: Person.find(5) } ]) + assert_equal [ [ Person.find(5).to_gid.to_s ] ], ActiveJob::Arguments.serialize([ [ Person.find(5) ] ]) end test 'should dive deep into arrays or hashes and raise exception on complex objects' do @@ -45,11 +45,11 @@ class ParameterSerializationTest < ActiveSupport::TestCase end test 'should serialize records with global id' do - assert_equal [ Person.find(5).gid.to_s ], ActiveJob::Arguments.serialize([ Person.find(5) ]) + assert_equal [ Person.find(5).to_gid.to_s ], ActiveJob::Arguments.serialize([ Person.find(5) ]) end test 'should serialize values and records together' do - assert_equal [ 3, Person.find(5).gid.to_s ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ]) + assert_equal [ 3, Person.find(5).to_gid.to_s ], ActiveJob::Arguments.serialize([ 3, Person.find(5) ]) end end @@ -59,19 +59,19 @@ class ParameterDeserializationTest < ActiveSupport::TestCase end test 'should deserialize records with global id' do - assert_equal [ Person.find(5) ], ActiveJob::Arguments.deserialize([ Person.find(5).gid ]) + assert_equal [ Person.find(5) ], ActiveJob::Arguments.deserialize([ Person.find(5).to_gid ]) end test 'should serialize values and records together' do - assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).gid ]) + assert_equal [ 3, Person.find(5) ], ActiveJob::Arguments.deserialize([ 3, Person.find(5).to_gid ]) end test 'should dive deep when deserialising arrays' do - assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).gid ] ]) + assert_equal [ [ 3, Person.find(5) ] ], ActiveJob::Arguments.deserialize([ [ 3, Person.find(5).to_gid ] ]) end test 'should dive deep when deserialising hashes' do - assert_equal [ { "5" => Person.find(5) } ], ActiveJob::Arguments.deserialize([ { "5" => Person.find(5).gid } ]) + assert_equal [ { "5" => Person.find(5) } ], ActiveJob::Arguments.deserialize([ { "5" => Person.find(5).to_gid } ]) end end diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb index 3386cef6f3..ce22833b11 100644 --- a/activejob/test/helper.rb +++ b/activejob/test/helper.rb @@ -27,4 +27,4 @@ end require 'active_support/testing/autorun' ActiveJob::Base.logger.level = Logger::DEBUG - +ActiveSupport::TestCase.test_order = :random diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index f6ad35769f..8f2a069ba3 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -75,7 +75,7 @@ module ActiveModel end validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED - validates_confirmation_of :password, if: ->{ password.present? } + validates_confirmation_of :password, allow_blank: true end # This code is necessary as long as the protected_attributes gem is supported. diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 13e8292954..40eb32c059 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* When a thread is killed, rollback the active transaction, instead of + committing it during the stack unwind. Previously, we could commit half- + completed work. This fix only works for Ruby 2.0+; on 1.9, we can't + distinguish a thread kill from an ordinary non-local (block) return, so must + default to committing. + + *Chris Hanks* + * A `NullRelation` should represent nothing. This fixes a bug where `Comment.where(post_id: Post.none)` returned a non-empty result. diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 36d0d63c71..f4a4e3f605 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -111,7 +111,7 @@ module ActiveRecord # # => false def instance_method_already_implemented?(method_name) if dangerous_attribute_method?(method_name) - raise DangerousAttributeError, "#{method_name} is defined by Active Record" + raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name." end if superclass == Base diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 90be835d8a..fd666c8c39 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -190,11 +190,17 @@ module ActiveRecord rollback_transaction if transaction raise ensure - begin - commit_transaction unless error - rescue Exception - transaction.rollback unless transaction.state.completed? - raise + unless error + if Thread.current.status == 'aborting' + rollback_transaction + else + begin + commit_transaction + rescue Exception + transaction.rollback unless transaction.state.completed? + raise + end + end end end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 9cfe041de2..5cccf2dda5 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -492,6 +492,37 @@ class TransactionTest < ActiveRecord::TestCase assert topic.frozen?, 'not frozen' end + # The behavior of killed threads having a status of "aborting" was changed + # in Ruby 2.0, so Thread#kill on 1.9 will prematurely commit the transaction + # and there's nothing we can do about it. + unless RUBY_VERSION.start_with? '1.9' + def test_rollback_when_thread_killed + queue = Queue.new + thread = Thread.new do + Topic.transaction do + @first.approved = true + @second.approved = false + @first.save + + queue.push nil + sleep + + @second.save + end + end + + queue.pop + thread.kill + thread.join + + assert @first.approved?, "First should still be changed in the objects" + assert !@second.approved?, "Second should still be changed in the objects" + + assert !Topic.find(1).approved?, "First shouldn't have been approved" + assert Topic.find(2).approved?, "Second should still be approved" + end + end + def test_restore_active_record_state_for_all_records_in_a_transaction topic_without_callbacks = Class.new(ActiveRecord::Base) do self.table_name = 'topics' diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 0e5a28e3fc..bc8d11a297 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,13 @@ +* Time#change can now change nanoseconds (:nsec) as a higher-precision + alternative to microseconds (:usec). + + *Agis Anastasooulos* + +* `MessageVerifier.new` raises an appropriate exception if the secret is `nil`. + This prevents `MessageVerifier#generate` from raising a cryptic error later on. + + *Kostiantyn Kahanskyi* + * Introduced new configuration option `active_support.test_order` for specifying the order test cases are executed. This option currently defaults to `:sorted` but will be changed to `:random` in Rails 5.0. diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 6a7bf7445a..ab8307429a 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -64,11 +64,12 @@ class Time # Returns a new Time where one or more of the elements have been changed according # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>, - # <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed, - # then minute, sec, and usec is set to 0. If the hour and minute is passed, then - # sec and usec is set to 0. The +options+ parameter takes a hash with any of these - # keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, - # <tt>:sec</tt>, <tt>:usec</tt>. + # <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only + # the hour is passed, then minute, sec, usec and nsec is set to 0. If the hour + # and minute is passed, then sec, usec and nsec is set to 0. The +options+ + # parameter takes a hash with any of these keys: <tt>:year</tt>, <tt>:month</tt>, + # <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>, <tt>:sec</tt>, <tt>:usec</tt> + # <tt>:nsec</tt>. Path either <tt>:usec</tt> or <tt>:nsec</tt>, not both. # # Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0) # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0) @@ -80,7 +81,13 @@ class Time new_hour = options.fetch(:hour, hour) new_min = options.fetch(:min, options[:hour] ? 0 : min) new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec) - new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000)) + + if new_nsec = options[:nsec] + raise ArgumentError, "Can't change both :nsec and :usec at the same time: #{options.inspect}" if options[:usec] + new_usec = Rational(new_nsec, 1000) + else + new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000)) + end if utc? ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 8e6e1dcfeb..6cb2884fb7 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -27,6 +27,7 @@ module ActiveSupport class InvalidSignature < StandardError; end def initialize(secret, options = {}) + raise ArgumentError, 'Secret should not be nil.' unless secret @secret = secret @digest = options[:digest] || 'SHA1' @serializer = options[:serializer] || Marshal diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 9a5bd19be2..d59775001b 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -387,6 +387,8 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,1,2,11, 6, 0, 0), Time.local(2005,1,2,11,22,33,44).change(:min => 6) assert_equal Time.local(2005,1,2,11,22, 7, 0), Time.local(2005,1,2,11,22,33,44).change(:sec => 7) assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,44).change(:usec => 8) + assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,2).change(:nsec => 8000) + assert_raise(ArgumentError) { Time.local(2005,1,2,11,22,33, 8).change(:usec => 1, :nsec => 1) } end def test_utc_change @@ -396,6 +398,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.utc(2005,2,22,16), Time.utc(2005,2,22,15,15,10).change(:hour => 16) assert_equal Time.utc(2005,2,22,16,45), Time.utc(2005,2,22,15,15,10).change(:hour => 16, :min => 45) assert_equal Time.utc(2005,2,22,15,45), Time.utc(2005,2,22,15,15,10).change(:min => 45) + assert_equal Time.utc(2005,1,2,11,22,33,8), Time.utc(2005,1,2,11,22,33,2).change(:nsec => 8000) end def test_offset_change @@ -405,7 +408,11 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.new(2005,2,22,16,0,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:hour => 16) assert_equal Time.new(2005,2,22,16,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:hour => 16, :min => 45) assert_equal Time.new(2005,2,22,15,45,0,"-08:00"), Time.new(2005,2,22,15,15,10,"-08:00").change(:min => 45) + assert_equal Time.new(2005,2,22,15,15,10,"-08:00"), Time.new(2005,2,22,15,15,0,"-08:00").change(:sec => 10) + assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(:usec => 10).usec + assert_equal 10, Time.new(2005,2,22,15,15,0,"-08:00").change(:nsec => 10).nsec assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(:usec => 1000000) } + assert_raise(ArgumentError) { Time.new(2005, 2, 22, 15, 15, 45, "-08:00").change(:nsec => 1000000000) } end def test_advance diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index a5748d28ba..28035bc428 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -69,6 +69,13 @@ class MessageVerifierTest < ActiveSupport::TestCase "undefined class/module MessageVerifierTest::AutoloadClass"], exception.message end + def test_raise_error_when_secret_is_nil + exception = assert_raise(ArgumentError) do + ActiveSupport::MessageVerifier.new(nil) + end + assert_equal exception.message, 'Secret should not be nil.' + end + def assert_not_verified(message) assert_raise(ActiveSupport::MessageVerifier::InvalidSignature) do @verifier.verify(message) diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index cb243c95f5..e1a465c64f 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -340,16 +340,14 @@ The `find_in_batches` method is similar to `find_each`, since both retrieve batc ```ruby # Give add_invoices an array of 1000 invoices at a time -Invoice.find_in_batches(include: :invoice_lines) do |invoices| +Invoice.find_in_batches do |invoices| export.add_invoices(invoices) end ``` -NOTE: The `:include` option allows you to name associations that should be loaded alongside with the models. - ##### Options for `find_in_batches` -The `find_in_batches` method accepts the same `:batch_size` and `:start` options as `find_each`, as well as most of the options allowed by the regular `find` method, except for `:order` and `:limit`, which are reserved for internal use by `find_in_batches`. +The `find_in_batches` method accepts the same `:batch_size` and `:start` options as `find_each`. Conditions ---------- diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md index 6f8584b3b7..7f084dd54c 100644 --- a/guides/source/maintenance_policy.md +++ b/guides/source/maintenance_policy.md @@ -39,7 +39,7 @@ Only the latest release series will receive bug fixes. When enough bugs are fixed and its deemed worthy to release a new gem, this is the branch it happens from. -**Currently included series:** `4.1.Z`, `4.0.Z`. +**Currently included series:** `4.2.Z`, `4.1.Z`. Security Issues --------------- @@ -54,7 +54,7 @@ be built from 1.2.2, and then added to the end of 1-2-stable. This means that security releases are easy to upgrade to if you're running the latest version of Rails. -**Currently included series:** `4.1.Z`, `4.0.Z`. +**Currently included series:** `4.2.Z`, `4.1.Z`. Severe Security Issues ---------------------- @@ -63,7 +63,7 @@ For severe security issues we will provide new versions as above, and also the last major release series will receive patches and new versions. The classification of the security issue is judged by the core team. -**Currently included series:** `4.1.Z`, `4.0.Z`, `3.2.Z`. +**Currently included series:** `4.2.Z`, `4.1.Z`, `3.2.Z`. Unsupported Release Series -------------------------- diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb index 05d9fde280..466b74bd12 100644 --- a/railties/lib/rails/generators/test_unit/job/job_generator.rb +++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb @@ -6,7 +6,7 @@ module TestUnit # :nodoc: check_class_collision suffix: 'JobTest' def create_test_file - template 'unit_test.rb.erb', File.join('test/job', class_path, "#{file_name}_test.rb") + template 'unit_test.rb.erb', File.join('test/jobs', class_path, "#{file_name}_test.rb") end end end |
