class Model < ActiveRecord::Base
before_destroy :beforeDestroyMethod
def beforeDestroyMethod
return false
end
end
All operations on the model instances are contained within transactions. If any of the before_* callbacks return false, the transaction is rolled back. This is all well and good, provided you don't want to insert a record in a different table in the before_destroy method. I happened to want to log any attempt to destroy records from the database, and I wanted to use the database to store the log entry:
class Model < ActiveRecord::Base
before_destroy :beforeDestroyMethod
def beforeDestroyMethod
log = LogEntry.new
log.action_type = "Destroy Object"
log.save
return false
end
end
Unfortunately, when the before_destroy method returned false, it also rolled back the log entry insertion.
After looking around, I found a partial answer. If you call 'establish_connection' in your Rails model, you will get a new database connection for that class and all classes that inherit from it:
class LogEntry < ActiveRecord::Base establish_connection ENV['RAILS_ENV'] end
I am using ENV['RAILS_ENV'] so that it creates a connection for the development or the production database, depending on which environment it's in. You can also specify a different database entirely.
My other models can now work as I had intended before:
class Model < ActiveRecord::Base
before_destroy :beforeDestroyMethod
def beforeDestroyMethod
log = LogEntry.new
log.action_type = "Destroy Object"
log.save
return false
end
end
So, that seems to have solved the problem. Well, mostly. What happens if you want to add a log entry if someone tries to destroy a log entry? You have the exact same problem as before. This time, I used a different trick for this one case:class LogEntry < ActiveRecord::Base
establish_connection ENV['RAILS_ENV']
before_destroy :beforeDestroyMethod
def beforeDestroyMethod
query = "insert into log_entries(action_type) values ("Destroy Log Entry");"
ActiveRecord::Base.connection_pool.with_connection do |conn|
conn.execute(query)
end
return false
end
end
This time, I just grab a database connection from the connection pool. Unfortunately, you have to utilize raw queries to use the connection, but it gets the job done.
Alternatively, I could have also got a connection by using the checkout method:
class LogEntry < ActiveRecord::Base
establish_connection ENV['RAILS_ENV']
before_destroy :beforeDestroyMethod
def beforeDestroyMethod
query = "insert into log_entries(action_type) values ("Destroy Log Entry");"
conn = ActiveRecord::Base.connection_pool.checkout
conn.execute(query)
ActiveRecord::Base.connection_pool.checkin(conn)
return false
end
end
Thus, my problem was solved.
Lee, you are amazing! Who would of thought that would be a problem in the first place?... That was a way tough bug to figure out, since it seems common sense that the before_filter would work normally.
ReplyDelete