Herstory is made to record changes to ActiveRecord models, including all of their associations.
It's a two component system:
This component registers all the changes and passes it on to the next component. It's a class method logs_changes
, which in turn defines some after_save
callbacks to do its work. See Fine Print for a more detailed explanation.
class Person < ActiveRecord::Base
includes Herstory
has_many :addresses
has_many :phone_numbers, through: :addresses
has_and_belongs_to_many :meetups
logs_changes includes: [:addresses, :phone_numbers,
{meetups: {superordinate: :other_record}]
end
A logger class that gets called from the extension with a minimal interface. You can (and probably will) replace this component to your heart's content. Herstory enables you to take note of all changes to an AR model and its associations, but it's up to you how you log them.
Herstory ships with a default logger class, which is unceremoniously called ChangeLogger
. It saves an Event
model to persist change information to the DB. You could do anything though, as long as it can deal with Herstory's interface methods.
See Implementing a custom logger for details.
So much beta warning here. Although it's used in production software. But it might blow up and spew poisonous lizards LOOKING AND ACTING like docile puppies EVERYWHERE until it is TOO LATE.
Herstory does what it does by injecting before_save
and before_destroy
callbacks on the model you're watching and all models that you're watching through associations. It monitors the belongs_to
part of any association.
Great idea! Go right ahead. Make a class, any class, and have it conform to the following interface (it's all named arguments):
log_creation(record:, user: nil)
log_destruction(record: user: nil)
log_attribute_changes(record:, user: nil)
log_association_changes (
change:, # Can be :addition or :deletion
record:, # The record that has the association that's being logged
superordinate:, # See below
other_record:, # The record that was added or deleted
user: nil # The user who done did it
)
About superordinate:
- This information can be used to make sense of the relationship / hierarchy between two objects, which can't necessarily be inferred from the association definition. It serves to express what is assigned to what. The included logger uses it to do some linguistic adjustments, e.g. sort out whether to save an Event á la person_attached_to_contact
or contact_attached_to_person
.
Possible values are:
:record
- This end of the association is the superordinate, e.g.Organization (record) has_and_belongs_to_many persons (other_record)
:other_record
- The other end of the association is the superordinate, e.g.Person (record) has_and_belongs_to_many organizations (other_record)
:none
- They're both subordinate:both
(Default) - They're both equally superordinate, e.g.House has_and_belongs_to_many neighboring_houses
The log_association_changes
method will only be called once, even if you define logs_changes
on both ends of the association.
When you're done, register it with Herstory like so:
Herstory.logger = YourLoggerClass
Done and done.
Define logs_changes on your polymorphic model, done.
Yeah, sure! All I care about (except basic manners) are SPECS for any bugs you report or changes you want to introduce. The actual fix/implementation is a distant second to good SPECS!