🚂 TrainTrack 🚂
TrainTrack is a gem that makes it easier to track changes in your rails projects. It doesn't make any decisions on how to do that. That part is up to you. All it does is provide some nice helpers to make that task easier.
TrainTrack may gain features in the future that also manage the actual tracking side. If you want to help develop that, shoot us a pull request!
Why not just use ActiveRecord callbacks?
Well, you could use those. In fact, for very simple tracking, those are probably better. However, that has some limitations:
-
Doesn't fully work with collections: a
has_many
is going to cause severe trouble for your code, since some methods will triggerbefore_update
andafter_update
, while others won't. -
Will run on records updated by automated tasks, when you probably care more about what users are doing
Installation
Add this line to your application's Gemfile:
gem 'train_track'
And then execute:
$ bundle
Or install it yourself as:
$ gem install train_track
Usage
Include TrainTrack in your controller:
class ImagesController < ApplicationController
include TrainTrack
end
Call the record
method in the controller when there's a state you wish to track.
What do I mean by this?
Well, in the create method, you probably only want to track that it has been created
def create
i = Image.new(image_params)
if i.save
track i
redirect_to i
else
# handle error
end
end
Whereas in an update action, you probably want to manage multiple states:
def update
i = Image.find(params[:id])
track i
if i.update(image_params)
track i
redirect_to i
else
# handle error
end
end
Trackers
Now, all those calls to track are pretty useless right now.
In order to make them do something, you have to specify a tracker.
We recommend you put those in /app/trackers/
A tracker looks like this:
class ImageTracker < ApplicationTracker
# user is taken from the `current_user` method on your controller
def initialize(user, record)
@user = user
@record = record
end
# Called on the create action
def create
# assuming we have a model called "ImageEdit" that tracks modifications of Images
# also assume that this uses Postgres' arrays
ImageEdit.create(user: @user,
image: @record,
action: :created,
old_tags: [],
new_tags: @record.tags.pluck(:id))
end
# Called on the first call to "track" in the edit action
def update_before
@old_tags = @record.tags.pluck(:id)
end
def update_after
@new_tags = @record.tags.reload.pluck(:id)
# Assuming ImageEdit makes use of Postgres' arrays
ImageEdit.create(user: @user,
image: @record,
action: :edited,
old_tags: @old_tags,
new_tags: @new_tags)
end
end
Much like in Pundit, a tracker is a PORO. This gives TrainTracks a higher level of flexibility.
Also like in pundit, we infer the name of the tracker from the class name.
If you want to over-ride this behavior, simple include a method called train_tracker_class
on the class.
class Noided < ActiveRecord::Base
def self.train_tracker_class
ParanoidTracker
end
end
You can also define it as an instance method. This lets you do fancy things:
class ElfmanSong < ActiveRecord::Base
def train_tracker_class
if band == "Oingo Boingo"
BoingoTracker
else
SoloTracker
end
end
Contributing
- Fork it ( https://github.com/AnthonySuper/train_track/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request