diff options
25 files changed, 198 insertions, 206 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index ea5028a7c0..f64cff8394 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -444,9 +444,10 @@ module ActionDispatch raise "A rack application must be specified" unless path - options[:as] ||= app_name(app) + options[:as] ||= app_name(app) + options[:via] ||= :all - match(path, options.merge(:to => app, :anchor => false, :format => false, :via => :all)) + match(path, options.merge(:to => app, :anchor => false, :format => false)) define_generate_prefix(app, options[:as]) self diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 6cc1370105..57ab325683 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -197,7 +197,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase end def test_regexp_precidence - @rs.draw do + rs.draw do get '/whois/:domain', :constraints => { :domain => /\w+\.[\w\.]+/ }, :to => lambda { |env| [200, {}, %w{regexp}] } @@ -216,7 +216,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase end } - @rs.draw do + rs.draw do get '/', :constraints => subdomain.new, :to => lambda { |env| [200, {}, %w{default}] } get '/', :constraints => { :subdomain => 'clients' }, @@ -228,7 +228,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase end def test_lambda_constraints - @rs.draw do + rs.draw do get '/', :constraints => lambda { |req| req.subdomain.present? and req.subdomain != "clients" }, :to => lambda { |env| [200, {}, %w{default}] } @@ -266,22 +266,22 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_draw_with_block_arity_one_raises assert_raise(RuntimeError) do - @rs.draw { |map| map.match '/:controller(/:action(/:id))' } + rs.draw { |map| map.match '/:controller(/:action(/:id))' } end end def test_specific_controller_action_failure - @rs.draw do + rs.draw do mount lambda {} => "/foo" end assert_raises(ActionController::RoutingError) do - url_for(@rs, :controller => "omg", :action => "lol") + url_for(rs, :controller => "omg", :action => "lol") end end def test_default_setup - @rs.draw { get '/:controller(/:action(/:id))' } + rs.draw { get '/:controller(/:action(/:id))' } assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content")) assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list")) assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/content/show/10")) @@ -298,8 +298,8 @@ class LegacyRouteSetTests < ActiveSupport::TestCase end def test_ignores_leading_slash - @rs.clear! - @rs.draw { get '/:controller(/:action(/:id))'} + rs.clear! + rs.draw { get '/:controller(/:action(/:id))'} test_default_setup end @@ -470,7 +470,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase end def test_changing_controller - @rs.draw { get ':controller/:action/:id' } + rs.draw { get ':controller/:action/:id' } assert_equal '/admin/stuff/show/10', url_for(rs, {:controller => 'stuff', :action => 'show', :id => 10}, @@ -583,7 +583,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase end def test_action_expiry - @rs.draw { get ':controller(/:action(/:id))' } + rs.draw { get ':controller(/:action(/:id))' } assert_equal '/content', url_for(rs, { :controller => 'content' }, { :controller => 'content', :action => 'show' }) end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 536e35ab2e..3b008fdff0 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -22,6 +22,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest mount SprocketsApp => "/shorthand" mount FakeEngine, :at => "/fakeengine" + mount FakeEngine, :at => "/getfake", :via => :get scope "/its_a" do mount SprocketsApp, :at => "/sprocket" @@ -52,6 +53,14 @@ class TestRoutingMount < ActionDispatch::IntegrationTest assert_equal "/shorthand -- /omg", response.body end + def test_mounting_works_with_via + get "/getfake" + assert_equal "OK", response.body + + post "/getfake" + assert_response :not_found + end + def test_with_fake_engine_does_not_call_invalid_method get "/fakeengine" assert_equal "OK", response.body diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index bcf2fcb5ec..da31da3269 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,14 @@ ## Rails 4.0.0 (unreleased) ## +* `serialized_attributes` and `_attr_readonly` become class method only. Instance reader methods are deprecated. + + *kennyj* + +* Round usec when comparing timestamp attributes in the dirty tracking. + Fixes #6975. + + *kennyj* + * Use inversed parent for first and last child of has_many association. *Ravil Bayramgalin* diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 33d7cc7f34..bdda5bc009 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -6,7 +6,7 @@ module ActiveRecord included do # Returns a hash of all the attributes that have been specified for serialization as # keys and their class restriction as values. - class_attribute :serialized_attributes, instance_writer: false + class_attribute :serialized_attributes, instance_accessor: false self.serialized_attributes = {} end @@ -41,6 +41,11 @@ module ActiveRecord end end + def serialized_attributes + ActiveSupport::Deprecation.warn("Instance level serialized_attributes method is deprecated, please use class level method.") + defined?(@serialized_attributes) ? @serialized_attributes : self.class.serialized_attributes + end + class Type # :nodoc: def initialize(column) @column = column @@ -114,7 +119,7 @@ module ActiveRecord end def read_attribute_before_type_cast(attr_name) - if serialized_attributes.include?(attr_name) + if self.class.serialized_attributes.include?(attr_name) super.unserialized_value else super 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 fa5b2ef336..d1e9d2de0e 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -59,11 +59,14 @@ module ActiveRecord unless time.acts_like?(:time) time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time end - time = time.in_time_zone rescue nil if time - changed = read_attribute(:#{attr_name}) != time - write_attribute(:#{attr_name}, original_time) - #{attr_name}_will_change! if changed - @attributes_cache["#{attr_name}"] = time + 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 end EOV generated_attribute_methods.module_eval(method_body, __FILE__, line) @@ -79,6 +82,12 @@ module ActiveRecord [:datetime, :timestamp].include?(column.type) 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/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index b0b51f540c..02459763f7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -1,6 +1,12 @@ module ActiveRecord module ConnectionAdapters # :nodoc: module DatabaseStatements + def initialize + super + @_current_transaction_records = [] + @transaction_joinable = nil + end + # Converts an arel AST to SQL def to_sql(arel, binds = []) if arel.respond_to?(:ast) @@ -167,7 +173,7 @@ module ActiveRecord def transaction(options = {}) options.assert_valid_keys :requires_new, :joinable - last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil + last_transaction_joinable = @transaction_joinable if options.has_key?(:joinable) @transaction_joinable = options[:joinable] else @@ -176,22 +182,19 @@ module ActiveRecord requires_new = options[:requires_new] || !last_transaction_joinable transaction_open = false - @_current_transaction_records ||= [] begin - if block_given? - if requires_new || open_transactions == 0 - if open_transactions == 0 - begin_db_transaction - elsif requires_new - create_savepoint - end - increment_open_transactions - transaction_open = true - @_current_transaction_records.push([]) + if requires_new || open_transactions == 0 + if open_transactions == 0 + begin_db_transaction + elsif requires_new + create_savepoint end - yield + increment_open_transactions + transaction_open = true + @_current_transaction_records.push([]) end + yield rescue Exception => database_transaction_rollback if transaction_open && !outside_transaction? transaction_open = false @@ -225,7 +228,7 @@ module ActiveRecord @_current_transaction_records.last.concat(save_point_records) end end - rescue Exception => database_transaction_rollback + rescue Exception if open_transactions == 0 rollback_db_transaction rollback_transaction_records(true) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index cde3325919..aad21b8e37 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -393,6 +393,7 @@ module ActiveRecord @marked_for_destruction = false @new_record = true @mass_assignment_options = nil + @_start_transaction_state = {} end end end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index ff7c996648..99de16cd33 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -225,7 +225,7 @@ module ActiveRecord def decorate_columns(columns_hash) # :nodoc: return if columns_hash.empty? - serialized_attributes.keys.each do |key| + serialized_attributes.each_key do |key| columns_hash[key] = AttributeMethods::Serialization::Type.new(columns_hash[key]) end @@ -259,13 +259,12 @@ module ActiveRecord # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute # is available. def column_methods_hash #:nodoc: - @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr| + @dynamic_methods_hash ||= column_names.each_with_object(Hash.new(false)) do |attr, methods| attr_name = attr.to_s methods[attr.to_sym] = attr_name methods["#{attr}=".to_sym] = attr_name methods["#{attr}?".to_sym] = attr_name methods["#{attr}_before_type_cast".to_sym] = attr_name - methods end end @@ -324,8 +323,7 @@ module ActiveRecord # Guesses the table name, but does not decorate it with prefix and suffix information. def undecorated_table_name(class_name = base_class.name) table_name = class_name.to_s.demodulize.underscore - table_name = table_name.pluralize if pluralize_table_names - table_name + pluralize_table_names ? table_name.pluralize : table_name end # Computes and returns a table name according to default conventions. diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index 1d8c566e40..b3c20c4aff 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -4,7 +4,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_attribute :_attr_readonly, instance_writer: false + class_attribute :_attr_readonly, instance_accessor: false self._attr_readonly = [] end @@ -20,5 +20,10 @@ module ActiveRecord self._attr_readonly end end + + def _attr_readonly + ActiveSupport::Deprecation.warn("Instance level _attr_readonly method is deprecated, please use class level method.") + defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly + end end end diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 5151f349b7..b4013ecc1e 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -41,7 +41,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_attribute :stored_attributes, instance_writer: false + class_attribute :stored_attributes, instance_accessor: false self.stored_attributes = {} end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 5c3399e2aa..9cec791faf 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -293,12 +293,12 @@ module ActiveRecord begin status = yield rescue ActiveRecord::Rollback - if defined?(@_start_transaction_state) + if defined?(@_start_transaction_state) @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 end status = nil end - + raise ActiveRecord::Rollback unless status end status @@ -308,7 +308,6 @@ module ActiveRecord # Save the new record state and id of a record so it can be restored later if a transaction fails. def remember_transaction_record_state #:nodoc: - @_start_transaction_state ||= {} @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) @_start_transaction_state[:new_record] = @new_record @_start_transaction_state[:destroyed] = @destroyed @@ -317,18 +316,16 @@ module ActiveRecord # Clear the new record state and id of a record. def clear_transaction_record_state #:nodoc: - if defined?(@_start_transaction_state) - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1 - end + @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 + @_start_transaction_state.clear if @_start_transaction_state[:level] < 1 end # Restore the new record state and id of a record that was previously saved by a call to save_record_state. def restore_transaction_record_state(force = false) #:nodoc: - if defined?(@_start_transaction_state) + unless @_start_transaction_state.empty? @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 if @_start_transaction_state[:level] < 1 || force - restore_state = remove_instance_variable(:@_start_transaction_state) + restore_state = @_start_transaction_state was_frozen = @attributes.frozen? @attributes = @attributes.dup if was_frozen @new_record = restore_state[:new_record] @@ -340,13 +337,14 @@ module ActiveRecord @attributes_cache.delete(self.class.primary_key) end @attributes.freeze if was_frozen + @_start_transaction_state.clear end end end # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed. def transaction_record_state(state) #:nodoc: - @_start_transaction_state[state] if defined?(@_start_transaction_state) + @_start_transaction_state[state] end # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks. diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 04b1d75e3e..63981a68a9 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -604,6 +604,12 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "changed", post.body end + def test_attr_readonly_is_class_level_setting + post = ReadonlyTitlePost.new + assert_raise(NoMethodError) { post._attr_readonly = [:title] } + assert_deprecated { post._attr_readonly } + end + def test_non_valid_identifier_column_name weird = Weird.create('a$b' => 'value') weird.reload diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 248f4efe3e..92677b9926 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -525,6 +525,21 @@ 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 + in_time_zone 'Paris' do + target = Class.new(ActiveRecord::Base) + target.table_name = 'pirates' + + created_on = Time.now + + pirate = target.create(:created_on => created_on) + pirate.reload # Here mysql truncate the usec value to 0 + + pirate.created_on = created_on + assert !pirate.created_on_changed? + end + end + private def with_partial_updates(klass, on = true) old = klass.partial_updates? diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb index ce167509c1..10d8ccc711 100644 --- a/activerecord/test/cases/serialization_test.rb +++ b/activerecord/test/cases/serialization_test.rb @@ -53,9 +53,8 @@ class SerializationTest < ActiveRecord::TestCase end def test_serialized_attributes_are_class_level_settings - assert_raise NoMethodError do - topic = Topic.new - topic.serialized_attributes = [] - end + topic = Topic.new + assert_raise(NoMethodError) { topic.serialized_attributes = [] } + assert_deprecated { topic.serialized_attributes } end end diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index 3e60b62fd5..fb0d116c08 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -122,9 +122,8 @@ class StoreTest < ActiveRecord::TestCase end test "stores_attributes are class level settings" do - assert_raise NoMethodError do - @john.stored_attributes = {} - end + assert_raise(NoMethodError) { @john.stored_attributes = Hash.new } + assert_raise(NoMethodError) { @john.stored_attributes } end end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index d5597a68ad..0d0de455b3 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -91,18 +91,14 @@ class TransactionTest < ActiveRecord::TestCase end def test_raising_exception_in_callback_rollbacks_in_save - add_exception_raising_after_save_callback_to_topic - - begin - @first.approved = true - @first.save - flunk - rescue => e - assert_equal "Make the transaction rollback", e.message - assert !Topic.find(1).approved? - ensure - remove_exception_raising_after_save_callback_to_topic + def @first.after_save_for_transaction + raise 'Make the transaction rollback' end + + @first.approved = true + e = assert_raises(RuntimeError) { @first.save } + assert_equal "Make the transaction rollback", e.message + assert !Topic.find(1).approved? end def test_update_attributes_should_rollback_on_failure @@ -125,100 +121,83 @@ class TransactionTest < ActiveRecord::TestCase end def test_cancellation_from_before_destroy_rollbacks_in_destroy - add_cancelling_before_destroy_with_db_side_effect_to_topic - begin - nbooks_before_destroy = Book.count - status = @first.destroy - assert !status - assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload } - assert_equal nbooks_before_destroy, Book.count - ensure - remove_cancelling_before_destroy_with_db_side_effect_to_topic - end + add_cancelling_before_destroy_with_db_side_effect_to_topic @first + nbooks_before_destroy = Book.count + status = @first.destroy + assert !status + @first.reload + assert_equal nbooks_before_destroy, Book.count end - def test_cancellation_from_before_filters_rollbacks_in_save - %w(validation save).each do |filter| - send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") - begin - nbooks_before_save = Book.count - original_author_name = @first.author_name - @first.author_name += '_this_should_not_end_up_in_the_db' - status = @first.save - assert !status - assert_equal original_author_name, @first.reload.author_name - assert_equal nbooks_before_save, Book.count - ensure - send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") - end + %w(validation save).each do |filter| + define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}") do + send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first) + nbooks_before_save = Book.count + original_author_name = @first.author_name + @first.author_name += '_this_should_not_end_up_in_the_db' + status = @first.save + assert !status + assert_equal original_author_name, @first.reload.author_name + assert_equal nbooks_before_save, Book.count end - end - def test_cancellation_from_before_filters_rollbacks_in_save! - %w(validation save).each do |filter| - send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") + define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}!") do + send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first) + nbooks_before_save = Book.count + original_author_name = @first.author_name + @first.author_name += '_this_should_not_end_up_in_the_db' + begin - nbooks_before_save = Book.count - original_author_name = @first.author_name - @first.author_name += '_this_should_not_end_up_in_the_db' @first.save! - flunk - rescue - assert_equal original_author_name, @first.reload.author_name - assert_equal nbooks_before_save, Book.count - ensure - send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") + rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved end + + assert_equal original_author_name, @first.reload.author_name + assert_equal nbooks_before_save, Book.count end end def test_callback_rollback_in_create - new_topic = Topic.new( - :title => "A new topic", - :author_name => "Ben", - :author_email_address => "ben@example.com", - :written_on => "2003-07-16t15:28:11.2233+01:00", - :last_read => "2004-04-15", - :bonus_time => "2005-01-30t15:28:00.00+01:00", - :content => "Have a nice day", - :approved => false) + topic = Class.new(Topic) { + def after_create_for_transaction + raise 'Make the transaction rollback' + end + } + + new_topic = topic.new(:title => "A new topic", + :author_name => "Ben", + :author_email_address => "ben@example.com", + :written_on => "2003-07-16t15:28:11.2233+01:00", + :last_read => "2004-04-15", + :bonus_time => "2005-01-30t15:28:00.00+01:00", + :content => "Have a nice day", + :approved => false) + new_record_snapshot = !new_topic.persisted? id_present = new_topic.has_attribute?(Topic.primary_key) id_snapshot = new_topic.id # Make sure the second save gets the after_create callback called. 2.times do - begin - add_exception_raising_after_create_callback_to_topic - new_topic.approved = true - new_topic.save - flunk - rescue => e - assert_equal "Make the transaction rollback", e.message - assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value" - assert_equal id_snapshot, new_topic.id, "The topic should have its old id" - assert_equal id_present, new_topic.has_attribute?(Topic.primary_key) - ensure - remove_exception_raising_after_create_callback_to_topic - end + new_topic.approved = true + e = assert_raises(RuntimeError) { new_topic.save } + assert_equal "Make the transaction rollback", e.message + assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value" + assert_equal id_snapshot, new_topic.id, "The topic should have its old id" + assert_equal id_present, new_topic.has_attribute?(Topic.primary_key) end end def test_callback_rollback_in_create_with_record_invalid_exception - begin - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method(:after_create_for_transaction) - def after_create_for_transaction - raise ActiveRecord::RecordInvalid.new(Author.new) - end - eoruby + topic = Class.new(Topic) { + def after_create_for_transaction + raise ActiveRecord::RecordInvalid.new(Author.new) + end + } - new_topic = Topic.create(:title => "A new topic") - assert !new_topic.persisted?, "The topic should not be persisted" - assert_nil new_topic.id, "The topic should not have an ID" - ensure - remove_exception_raising_after_create_callback_to_topic - end + new_topic = topic.create(:title => "A new topic") + assert !new_topic.persisted?, "The topic should not be persisted" + assert_nil new_topic.id, "The topic should not have an ID" end def test_nested_explicit_transactions @@ -478,62 +457,16 @@ class TransactionTest < ActiveRecord::TestCase end private - def define_callback_method(callback_method) - define_method(callback_method) do - self.history << [callback_method, :method] - end - end - def add_exception_raising_after_save_callback_to_topic - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method(:after_save_for_transaction) - def after_save_for_transaction - raise 'Make the transaction rollback' - end - eoruby - end - - def remove_exception_raising_after_save_callback_to_topic - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method :after_save_for_transaction - def after_save_for_transaction; end - eoruby - end - - def add_exception_raising_after_create_callback_to_topic - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method(:after_create_for_transaction) - def after_create_for_transaction - raise 'Make the transaction rollback' - end - eoruby - end - - def remove_exception_raising_after_create_callback_to_topic - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method :after_create_for_transaction - def after_create_for_transaction; end - eoruby - end - - %w(validation save destroy).each do |filter| - define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method :before_#{filter}_for_transaction - def before_#{filter}_for_transaction - Book.create - false - end - eoruby - end - - define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do - Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1 - remove_method :before_#{filter}_for_transaction - def before_#{filter}_for_transaction; end - eoruby + %w(validation save destroy).each do |filter| + define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic| + meta = class << topic; self; end + meta.send("define_method", "before_#{filter}_for_transaction") do + Book.create + false end end + end end class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase diff --git a/guides/source/upgrading_ruby_on_rails.textile b/guides/source/upgrading_ruby_on_rails.textile index 5024bc4c37..680beb5dfe 100644 --- a/guides/source/upgrading_ruby_on_rails.textile +++ b/guides/source/upgrading_ruby_on_rails.textile @@ -44,6 +44,8 @@ The <tt>delete</tt> method in collection associations can now receive <tt>Fixnum Rails 4.0 has changed how orders get stacked in +ActiveRecord::Relation+. In previous versions of rails new order was applied after previous defined order. But this is no long true. Check "ActiveRecord Query guide":active_record_querying.html#ordering for more information. +Rails 4.0 has changed <tt>serialized_attributes</tt> and <tt>_attr_readonly</tt> to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. <tt>self.serialized_attributes</tt> to <tt>self.class.serialized_attributes</tt>. + h4(#active_model4_0). Active Model Rails 4.0 has changed how errors attach with the <tt>ActiveModel::Validations::ConfirmationValidator</tt>. Now when confirmation validations fail the error will be attached to <tt>:#{attribute}_confirmation</tt> instead of <tt>attribute</tt>. diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index b2b760ee7b..1ac0248bcf 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -60,7 +60,7 @@ module <%= app_const_base %> config.assets.version = '1.0' <% end -%> - # Enable app-wide asynchronous ActionMailer + # Enable app-wide asynchronous ActionMailer. # config.action_mailer.async = true end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb index 1684986a59..e080ebd74e 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb @@ -1,5 +1,5 @@ -# Load the rails application +# Load the rails application. require File.expand_path('../application', __FILE__) -# Initialize the rails application +# Initialize the rails application. <%= app_const %>.initialize! diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index fee9eb9456..2016eb8b05 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -39,6 +39,6 @@ config.assets.debug = true <%- end -%> - # In development, use an in-memory queue for queueing + # In development, use an in-memory queue for queueing. config.queue = Rails::Queueing::Queue end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 2ffae66a57..80413be0d3 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -71,10 +71,10 @@ # Disable automatic flushing of the log to improve performance. # config.autoflush_log = false - # Use default logging formatter so that PID and timestamp are not suppressed + # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new # Default the production mode queue to an in-memory queue. You will probably - # want to replace this with an out-of-process queueing solution + # want to replace this with an out-of-process queueing solution. config.queue = Rails::Queueing::Queue end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index b27b88a3c6..878deb49d7 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -34,6 +34,6 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Use the testing queue + # Use the testing queue. config.queue = Rails::Queueing::TestQueue end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt index ade0c4f78c..ff676280cb 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt @@ -4,5 +4,5 @@ # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information -# (create the session table with "rails generate session_migration") +# (create the session table with "rails generate session_migration"). # <%= app_const %>.config.session_store :active_record_store diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index 85acac6725..f6b1ef1feb 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -8,11 +8,11 @@ # Sample of regular route: # get 'products/:id' => 'catalog#view' - # Keep in mind you can assign values other than :controller and :action + # Keep in mind you can assign values other than :controller and :action. # Sample of named route: # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase - # This route can be invoked with purchase_url(id: product.id) + # This route can be invoked with purchase_url(id: product.id). # Sample resource route (maps HTTP verbs to controller actions automatically): # resources :products @@ -35,7 +35,7 @@ # resource :seller # end - # Sample resource route with more complex sub-resources + # Sample resource route with more complex sub-resources: # resources :products do # resources :comments # resources :sales do @@ -51,5 +51,5 @@ # end - # See how all your routes lay out with "rake routes" + # See how all your routes lay out with "rake routes". end |