Need help with hound?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

leejarvis
122 Stars 4 Forks MIT License 42 Commits 2 Opened issues

Description

Track model activity

Services available

!
?

Need anything else?

Contributors list

# 83,025
Redis
elastic...
option-...
xslt
42 commits

Hound

Build Status

Hound is designed to track activity on models. It'll track your usual

create
update
and
destroy
actions as well as custom activity.

At Allur or more specifically the Spark CRM we've built, we use Hound for implementing activity tabs on our popular models. These activity tabs display the last x amount of actions taken out upon these models. We have a lot of custom associations so we can't simply track the

update
to models. With Hound we can build custom actions on the fly.

If you need something similar, Hound could be a good fit.

Installation

Add Hound to your

Gemfile
and run
bundle install
:
gem 'hound'

Hound expects a

hound_actions
table to exist in your schema, go ahead and run the generator provided:
rails generate hound:install

This will create a new migration file. Run

rake db:migrate
to add this table.

Usage

Hound will automatically track

create
update
and
destroy
methods that occur on your 'hounded' models. You can tell Hound which actions you'd like to track in the
hound
method:
class Article < ActiveRecord::Base
  hound actions: [:create, :update] # only track create/update
end

Hound also adds a

hound_action
helper method to your controllers. This allows you to set a custom action on any of your hounded models.
class ArticlesController < ApplicationController
  def add_to_frontpage
    # Does not update @article so no action will be created
    @frontpage << @article
    hound_action @article, 'added_to_frontpage'
  end
end

@article.actions.last.action #=> 'added_to_frontpage'

Hound will track the current user making these changes assuming your application controller responds to a

current_user
method. If you have a custom method, you can override
hound_user
to return this instead.
class ApplicationController < ActionController::Base
  def hound_user
    current_admin_user
  end
end

For this to work successfully you must tell Hound about your user class using the

hound_user
method. The
user
association on the
Hound::Action
class is polymorphic, so you can use
hound_user
in more than one class.
class User
  hound_user
end

class Admin hound_user end

Now

hound_user
can return either an instance of
User
or an instance of
Admin
.

You can also disable Hound on a model instance basis:

article = Article.new title: 'Hello, World!'
article.hound? #=> true
article.hound = false # disable hound
article.save
article.actions #=> []

Tracking Changes

Hound also tracks the changes made when updating your hounded records. You can access the change updates through the

changeset
attribute:
article = Article.create! title: 'Hello, World!'
article.update_attributes(title: 'Salut, World!')
article.actions.last.changeset
  #=> {"title" => ["Hello, World!", "Salut, World!"]}

Displaying model activity

Because Hound hooks into your existing user model as well as any models you tell it to track, you can display activity from either side. In fact, your user object doesn't even need to belong to the object you're tracking.

current_user.name #=> "Lee"
article = Article.create! title: 'Hello, World!'

article.actions.each do |action| puts "#{action.user.name} #{action + 'ed'} the "
"#{action.actionable_type} #{action.actionable.title}" end

current_user.actions.each do |action| puts "#{action.user.name} #{action + 'ed'} the "
"#{action.actionable_type} #{action.actionable.title}" end

Both of the above snippets will print the same thing:

Lee created the Article Hello, World!

Console

Hound implements a storage facility on the current thread for storing the id of the current user. This is how we make it available to the model data without sending it via the controller itself. This means when creating records via the console, there will be no

current_user
available.
>> Article.create! title: 'Foo'
>> _.actions.last.user
=> nil

You can solve this by setting

Hound.store[:current_user_id]
:
>> Hound.store[:current_user_id] = User.create!(name: 'Lee').id
>> Article.create! title: 'Foo'
>> _.actions.last.user.name
=> "Lee"

Cleaning Up

With all this action creating we're doing, your database is bound to start getting full quickly. You have two options for cleaning up after yourself, either create a rake task:

task :prune_hound_actions do
  Hound.actions.where('created_at < ?', 1.week.ago).delete_all
end

And run it as a cron job, or you can simply limit records on a per model basis

class Article < ActiveRecord::Base
  hound limit: 10
end

Now Hound will never store more than 10 actions for an Article. You can configure this globally through

Hound.config.limit
, too. Do note though that adding this functionality means whenever an action is tracked, Hound will not only create a new action, it will check and destroy any actions outside of this limit. This requires an extra call to the database, so if that could be an issue, using a rake task might be a better idea.

But we already have Paper Trail?

Yes, and Paper Trail is awesome. Hound is not designed to replace it. They do different things. Hound is designed to track activity (in the form of actions) on a model. Paper Trail does the same thing but it stores snapshots of your model at certain times (when they change). Hound is not just for tracking changes to your model, but you can attach custom activity to it, too.

  • Do I want custom actions and activity attached to my models? Use Hound.
  • Do I need to restore my model to an earlier time? Use Paper Trail.

At Allur we use them both for different things, and they work great.

TODO

  • Implement action grouping
  • Generate a config initializer on install?
  • Disable hound in test environment?

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.