ChiliProject is not maintained anymore. Please be advised that there will be no more updates.
We do not recommend that you setup new ChiliProject instances and we urge all existing users to migrate their data to a maintained system, e.g. Redmine. We will provide a migration script later. In the meantime, you can use the instructions by Christian Daehn.
acts_as_journalized¶
Development and API documentation for using the acts_as_journalized
plugin to create a version history for ActiveRecord objects.
Archetecture overview¶
acts_as_journalized
replaces JournalDetails
and acts_as_versioned
in ChiliProject 2.0.0. It does this by tracking changes on the journaled
object and creating an STI'd Journal
when that object is saved. So by changing the name of an Issue (journaled object) from "Old subject" to "New subject", an IssueJournal is created that has shows the changes. The changes are stored in the changes
field, automatically serialized to YAML.
Versions¶
Each Journal has a unique version field based on it's Journaled object. So Issue 100 can have a version 1 (initial journal), version 2, version 3, etc. This is automatically incremented, you don't need to do anything to update it.
Initial versions¶
Each Journaled object has an initial journal created when it's first created. This Journal will have the version of 1 and will store the original attributes of the Journaled object in it's changes. This means you can get a full history of a Journaled object by going through it's Journals and "walking" the changes hashes.
Changes format¶
The changes
field is automatically serialzied to YAML but once it's loaded into Rails it's a hash of the changes:
1>> issue.changes
2{
3 "attribute_name" => [
4 "old value",
5 "new value"
6 ],
7 "relations_too_id" => [
8 0,
9 5
10 ]
11}
12
13>> issue.changes
14{
15 "subject" => [
16 "Old subject",
17 "New subject"
18 ]
19}
Public API¶
Previously Journals were only created for Issues and used the init_journal
method. While init_journal
is still around, it should not be used directly anymore. It is called by the journaled class's callbacks automatically (e.g. after_save).
Instead specific setter methods should be used to set up a Journal:
Journaled#journal_user¶
Data type: User
Will set the creator of the Journal. Defaults to the the Journaled#updated_by
user or the current user (User.current
).
Journaled#journal_notes¶
Data type: String
Will set the notes for the journal. Defaults to an empty string.
Journaled#extra_journal_attributes¶
Data type: Hash
Will set extra attributes on the automatically created Journal object. This is used because the Journals are created automatically and you are unable to access them before they are created (see Acts::As::Journalized::Creation#create_journal
and Acts::As::Journalized::Creation#journal_attributes
for details). The extra_journal_attributes
field will override any of the default attributes and let you have more control over the final Journal.
Upgrade plugin to acts_as_journalized¶
The standard way to provide activities in ChiliProject 2 onward has changed from acts_as_activity_provider
to acts_as_journalized
. This paragraph describes how to upgrade a plugin to use acts_as_journalized
instead of acts_as_activity_provider
. In the following, it is assumed the model providing an activity that is upgraded to acts_as_journalized
is Foo
.
Configure acts_as_journalized¶
acts_as_journalized
replaces acts_as_event
as well as acts_as_activity_provider
but can take options for both as needed. Options prefixed with activity_
will be passed to acts_as_activity_provider
, those prefixed with event_
will be passed to acts_as_event
.
Assuming Foo had the following:
1acts_as_event :title => Proc.new {|o| "#{l(:label_foo)} ##{o.id}: #{o.title}"},
2 :url => Proc.new {|o| {:controller => 'foos', :action => 'show', :id => o.id}}
3acts_as_activity_provider :find_options => {:include => [:project, :author]},
4 :author_key => :author_id
New journals are created by default with the current user as the author, i.e. you don't need the author_key
parameter anymore if you had any, and you don't even need to save the author in your object if you needed it for the activity only.
acts_as_journalized
will also automatically include the :project
in the find_options
if the journalized model has a project, so no need to put it in the activity_find_options
anymore. One important thing to note: Procs in the options are passed the journal, not (as was the case before) the object, use .journaled
on a journal to get the object.
All in all, this gives us the following acts_as_journalized
line for the above example:
1acts_as_journalized :activity_find_options => {:include => :author},
2 :event_title => Proc.new {|o| "#{l(:label_foo)} ##{o.journaled.id}: #{o.journaled.title}"},
3 :event_url => Proc.new {|o| {:controller => 'foos', :action => 'show', :id => o.journaled.id}}
Create initial journals¶
Initial journals will have to be back-created for each existing Foo
. acts_as_journalized
adds a utility method recreate_initial_journal!
to journalized objects to guess and create the initial journal. The migration to back-create the initial journals db/migrate/20110906203247_create_initial_foo_journals.rb
could be like:
1class CreateInitialFooJournals < ActiveRecord::Migration
2 def self.up
3 journaled_class = Foo
4
5 say_with_time("Building initial journals for #{journaled_class.class_name}") do
6
7 # avoid touching the journaled object on journal creation
8 journaled_class.journal_class.class_exec {def touch_journaled_after_creation; end}
9
10 activity_type = journaled_class.activity_provider_options.keys.first
11
12 # Create initial journals
13 journaled_class.find(:all).each do |o|
14 begin
15 o.recreate_initial_journal!
16 rescue ActiveRecord::RecordInvalid => e
17 puts "ERROR: errors creating the initial journal for #{o.class.to_s}##{o.id.to_s}:"
18 puts " #{e.message}"
19 end
20 end
21 end
22 end
23
24 def self.down
25 # no-op
26 # (well, in theory we should delete the FooJournals…)
27 end
28end