aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md9
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb31
-rw-r--r--activerecord/lib/active_record/core.rb1
-rw-r--r--activerecord/lib/active_record/model_schema.rb8
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb7
-rw-r--r--activerecord/lib/active_record/store.rb2
-rw-r--r--activerecord/lib/active_record/transactions.rb18
-rw-r--r--activerecord/test/cases/base_test.rb6
-rw-r--r--activerecord/test/cases/dirty_test.rb15
-rw-r--r--activerecord/test/cases/serialization_test.rb7
-rw-r--r--activerecord/test/cases/store_test.rb5
-rw-r--r--activerecord/test/cases/transactions_test.rb205
14 files changed, 161 insertions, 181 deletions
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