hound 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -6
- data/CHANGELOG.md +21 -0
- data/Gemfile +5 -0
- data/README.md +90 -4
- data/Rakefile +2 -0
- data/lib/generators/hound/templates/create_hound_actions.rb +3 -0
- data/lib/hound/action.rb +6 -2
- data/lib/hound/config.rb +0 -8
- data/lib/hound/controller.rb +7 -1
- data/lib/hound/model.rb +32 -23
- data/lib/hound/version.rb +1 -1
- data/test/config_test.rb +0 -7
- data/test/dummy/app/models/user.rb +1 -0
- data/test/dummy/db/migrate/{20130308110847_create_hound_actions.rb → 20130314120938_create_hound_actions.rb} +4 -0
- data/test/dummy/db/schema.rb +3 -1
- data/test/hound_test.rb +10 -0
- metadata +6 -7
- data/test/dummy/db/migrate/20130310103454_add_changeset_to_hound_actions.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17ce6502740de85ef5dcb728028205418d81af11
|
4
|
+
data.tar.gz: 142481253159ae0d10042948213a469f4cc763e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4d5e99ef5d62b26d0a88cb67a773438e1fe311ab11f49a06b3b50a5b427e1fdb2e2652268f58b34671467f5cbd3f453a1a91666564fc919fdf374861a1791d9
|
7
|
+
data.tar.gz: 05c6ce2f13cccce70557a3cc855bb57e3f5a3c7ea5894e4db90d83dc0d4c0887b420ed4cebd25601bdb6a3feba78ea71588a4873355d69d2c20da1b760becafd
|
data/.travis.yml
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
2
|
+
|
3
3
|
rvm:
|
4
|
-
- 1.9.2
|
5
4
|
- 1.9.3
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
|
6
|
+
env:
|
7
|
+
- DB=sqlite
|
8
|
+
- DB=mysql
|
9
|
+
- DB=postgresql
|
10
|
+
|
11
|
+
before_script:
|
12
|
+
- rake app:db:create
|
13
|
+
- rake app:db:migrate
|
14
|
+
- rake app:db:test:prepare
|
15
|
+
|
16
|
+
after_script:
|
17
|
+
- rake app:db:rollback
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
## v0.3.0 - March 14, 2013
|
2
|
+
|
3
|
+
* Implement `hound_user` and polymorphic user association (#3).
|
4
|
+
* Hook into user model and add `has_many :actions`.
|
5
|
+
* Ensure Hound does not raise an exception if a user class does not exist.
|
6
|
+
|
7
|
+
## v0.2.0 - March 10, 2013
|
8
|
+
|
9
|
+
* Support tracking changes made to hounded models.
|
10
|
+
|
11
|
+
## v0.1.0 - March 8, 2013
|
12
|
+
|
13
|
+
* Support limiting actions.
|
14
|
+
* Added `hound_action` helper to controllers.
|
15
|
+
* Support for a custom User class.
|
16
|
+
* Support travis CI.
|
17
|
+
* Lower Rails dependency version.
|
18
|
+
|
19
|
+
## v0.0.1 - March 7, 2013
|
20
|
+
|
21
|
+
* Initial Release of Hound. Support tracking actions made to models.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Hound
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/injekt/hound.png?branch=master)](https://travis-ci.org/injekt/hound)
|
4
|
+
|
5
|
+
Hound is designed to track activity on models. It'll track your usual
|
6
|
+
`create` `update` and `destroy` actions as well as custom activity.
|
7
|
+
|
8
|
+
At [Allur](https://www.allur.com) or more specifically the
|
9
|
+
[Spark CRM](http://allurspark.com) we've built, we use Hound for implementing
|
10
|
+
activity tabs on our popular models. These activity tabs display
|
11
|
+
the last x amount of actions taken out upon these models. We have a lot
|
12
|
+
of custom associations so we can't simply track the `update` to models.
|
13
|
+
With Hound we can build custom actions on the fly.
|
14
|
+
|
15
|
+
If you need something similar, Hound could be a good fit.
|
16
|
+
|
3
17
|
## Installation
|
4
18
|
|
5
19
|
Add Hound to your `Gemfile` and run `bundle install`:
|
@@ -58,13 +72,22 @@ end
|
|
58
72
|
```
|
59
73
|
|
60
74
|
For this to work successfully you must tell Hound about your user class
|
61
|
-
|
75
|
+
using the `hound_user` method. The `user` association on the `Hound::Action`
|
76
|
+
class is polymorphic, so you can use `hound_user` in more than one class.
|
62
77
|
|
63
78
|
```ruby
|
64
|
-
|
65
|
-
|
79
|
+
class User
|
80
|
+
hound_user
|
81
|
+
end
|
82
|
+
|
83
|
+
class Admin
|
84
|
+
hound_user
|
85
|
+
end
|
66
86
|
```
|
67
87
|
|
88
|
+
Now `hound_user` can return either an instance of `User` or an instance of
|
89
|
+
`Admin`.
|
90
|
+
|
68
91
|
You can also disable Hound on a model instance basis:
|
69
92
|
|
70
93
|
```ruby
|
@@ -75,7 +98,7 @@ article.save
|
|
75
98
|
article.actions #=> []
|
76
99
|
```
|
77
100
|
|
78
|
-
## Changes
|
101
|
+
## Tracking Changes
|
79
102
|
|
80
103
|
Hound also tracks the changes made when updating your hounded records. You
|
81
104
|
can access the change updates through the `changeset` attribute:
|
@@ -87,6 +110,55 @@ article.actions.last.changeset
|
|
87
110
|
#=> {"title" => ["Hello, World!", "Salut, World!"]}
|
88
111
|
```
|
89
112
|
|
113
|
+
## Displaying model activity
|
114
|
+
|
115
|
+
Because Hound hooks into your existing user model as well as any models
|
116
|
+
you tell it to track, you can display activity from either side. In fact,
|
117
|
+
your user object doesn't even need to belong to the object you're tracking.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
current_user.name #=> "Lee"
|
121
|
+
article = Article.create! title: 'Hello, World!'
|
122
|
+
|
123
|
+
article.actions.each do |action|
|
124
|
+
puts "#{action.user.name} #{action + 'ed'} the " \
|
125
|
+
"#{action.actionable_type} #{action.actionable.title}"
|
126
|
+
end
|
127
|
+
|
128
|
+
current_user.actions.each do |action|
|
129
|
+
puts "#{action.user.name} #{action + 'ed'} the " \
|
130
|
+
"#{action.actionable_type} #{action.actionable.title}"
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Both of the above snippets will print the same thing:
|
135
|
+
|
136
|
+
```
|
137
|
+
Lee created the Article Hello, World!
|
138
|
+
```
|
139
|
+
|
140
|
+
## Console
|
141
|
+
|
142
|
+
Hound implements a storage facility on the current thread for storing
|
143
|
+
the id of the current user. This is how we make it available to the model
|
144
|
+
data without sending it via the controller itself. This means when
|
145
|
+
creating records via the console, there will be no `current_user` available.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
>> Article.create! title: 'Foo'
|
149
|
+
>> _.actions.last.user
|
150
|
+
=> nil
|
151
|
+
```
|
152
|
+
|
153
|
+
You can solve this by setting `Hound.store[:current_user_id]`:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
>> Hound.store[:current_user_id] = User.create!(name: 'Lee').id
|
157
|
+
>> Article.create! title: 'Foo'
|
158
|
+
>> _.actions.last.user.name
|
159
|
+
=> "Lee"
|
160
|
+
```
|
161
|
+
|
90
162
|
## Cleaning Up
|
91
163
|
|
92
164
|
With all this action creating we're doing, your database is bound to start
|
@@ -114,6 +186,20 @@ will not only create a new action, it will check and destroy any actions
|
|
114
186
|
outside of this limit. This requires an extra call to the database, so if
|
115
187
|
that could be an issue, using a rake task might be a better idea.
|
116
188
|
|
189
|
+
## But we already have Paper Trail?
|
190
|
+
|
191
|
+
Yes, and Paper Trail is awesome. Hound is not designed to replace it. They
|
192
|
+
do different things. Hound is designed to track activity (in the form of
|
193
|
+
actions) on a model. Paper Trail does the same thing but it stores snapshots
|
194
|
+
of your model at certain times (when they change). Hound is not just for
|
195
|
+
tracking changes to your model, but you can attach custom activity to it, too.
|
196
|
+
|
197
|
+
* Do I want custom actions and activity attached to my models? Use Hound.
|
198
|
+
* Do I need to restore my model to an earlier time? Use Paper Trail.
|
199
|
+
|
200
|
+
At [Allur](https://www.allur.com) we use them both for different things,
|
201
|
+
and they work great.
|
202
|
+
|
117
203
|
## TODO
|
118
204
|
|
119
205
|
* Implement action grouping
|
data/Rakefile
CHANGED
@@ -5,14 +5,17 @@ class CreateHoundActions < ActiveRecord::Migration
|
|
5
5
|
t.string :actionable_type, null: false
|
6
6
|
t.integer :actionable_id, null: false
|
7
7
|
t.integer :user_id
|
8
|
+
t.string :user_type
|
8
9
|
t.datetime :created_at
|
9
10
|
t.text :changeset
|
10
11
|
end
|
11
12
|
add_index :hound_actions, [:actionable_type, :actionable_id]
|
13
|
+
add_index :hound_actions, [:user_type, :user_id]
|
12
14
|
end
|
13
15
|
|
14
16
|
def self.down
|
15
17
|
remove_index :hound_actions, [:actionable_type, :actionable_id]
|
18
|
+
remove_index :hound_actions, [:user_type, :user_id]
|
16
19
|
drop_table :hound_actions
|
17
20
|
end
|
18
21
|
end
|
data/lib/hound/action.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
module Hound
|
2
2
|
class Action < ActiveRecord::Base
|
3
3
|
self.table_name = 'hound_actions'
|
4
|
-
|
4
|
+
|
5
|
+
attr_accessible :action, :actionable_id, :actionable_type,
|
6
|
+
:user_id, :user_type, :changeset
|
7
|
+
|
5
8
|
belongs_to :actionable, polymorphic: true
|
6
|
-
belongs_to :user,
|
9
|
+
belongs_to :user, polymorphic: true
|
10
|
+
|
7
11
|
serialize :changeset, Hash
|
8
12
|
|
9
13
|
scope :created, -> { where(action: 'create') }
|
data/lib/hound/config.rb
CHANGED
@@ -7,21 +7,13 @@ module Hound
|
|
7
7
|
# An Array of actions Hound should track.
|
8
8
|
attr_accessor :actions
|
9
9
|
|
10
|
-
# The String used to represent a User class (defaults to 'User').
|
11
|
-
attr_reader :user_class
|
12
|
-
|
13
10
|
# Limit actions on a global basis.
|
14
11
|
attr_accessor :limit
|
15
12
|
|
16
13
|
def initialize
|
17
14
|
@actions = %w'create update destroy'
|
18
|
-
@user_class = 'User'
|
19
15
|
@limit = nil
|
20
16
|
end
|
21
17
|
|
22
|
-
def user_class=(klassname)
|
23
|
-
@user_class = klassname.to_s
|
24
|
-
end
|
25
|
-
|
26
18
|
end
|
27
19
|
end
|
data/lib/hound/controller.rb
CHANGED
@@ -19,14 +19,20 @@ module Hound
|
|
19
19
|
hound_user.try(:id)
|
20
20
|
end
|
21
21
|
|
22
|
+
def hound_user_type
|
23
|
+
hound_user.class.base_class.name if hound_user
|
24
|
+
end
|
25
|
+
|
22
26
|
private
|
23
27
|
|
24
28
|
def hound_action(object, action = params[:action])
|
25
|
-
object.actions.create(action: action,
|
29
|
+
object.actions.create(action: action,
|
30
|
+
user_id: hound_user_id, user_type: hound_user_type)
|
26
31
|
end
|
27
32
|
|
28
33
|
def set_hound_user
|
29
34
|
Hound.store[:current_user_id] = hound_user_id
|
35
|
+
Hound.store[:current_user_type] = hound_user_type
|
30
36
|
end
|
31
37
|
|
32
38
|
end
|
data/lib/hound/model.rb
CHANGED
@@ -9,13 +9,15 @@ module Hound
|
|
9
9
|
|
10
10
|
# Tell Hound to track this models actions.
|
11
11
|
#
|
12
|
-
# options - a Hash of configuration options.
|
12
|
+
# options - a Hash of configuration options (default: {}).
|
13
|
+
# :limit - An Integer limit to restrict the maximum actions
|
14
|
+
# stored (optional, default: nil (no limit)).
|
15
|
+
# :actions - An Array of actions to track.
|
16
|
+
# (default: [:create, :update, :destroy])
|
13
17
|
def hound(options = {})
|
14
18
|
send :include, InstanceMethods
|
15
19
|
|
16
|
-
has_many :actions,
|
17
|
-
as: 'actionable',
|
18
|
-
class_name: 'Hound::Action'
|
20
|
+
has_many :actions, as: :actionable, class_name: 'Hound::Action'
|
19
21
|
|
20
22
|
options[:actions] ||= Hound.config.actions
|
21
23
|
options[:actions] = Array(options[:actions]).map(&:to_s)
|
@@ -26,9 +28,13 @@ module Hound
|
|
26
28
|
attr_accessor :hound
|
27
29
|
|
28
30
|
# Add action hooks
|
29
|
-
after_create :hound_create if options[:actions].include?('create')
|
30
|
-
before_update :hound_update if options[:actions].include?('update')
|
31
|
-
after_destroy :hound_destroy if options[:actions].include?('destroy')
|
31
|
+
after_create :hound_create, if: :hound? if options[:actions].include?('create')
|
32
|
+
before_update :hound_update, if: :hound? if options[:actions].include?('update')
|
33
|
+
after_destroy :hound_destroy, if: :hound? if options[:actions].include?('destroy')
|
34
|
+
end
|
35
|
+
|
36
|
+
def hound_user(options = {})
|
37
|
+
has_many :actions, as: :user, class_name: 'Hound::Action'
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
@@ -47,33 +53,36 @@ module Hound
|
|
47
53
|
private
|
48
54
|
|
49
55
|
def hound_create
|
50
|
-
|
51
|
-
attributes = default_attributes.merge(action: 'create')
|
52
|
-
actions.create! attributes
|
53
|
-
enforce_limit
|
56
|
+
create_action(action: 'create')
|
54
57
|
end
|
55
58
|
|
56
59
|
def hound_update
|
57
|
-
|
58
|
-
attributes = default_attributes.merge(action: 'update')
|
59
|
-
attributes.merge!(changeset: changes)
|
60
|
-
actions.create! attributes
|
61
|
-
enforce_limit
|
60
|
+
create_action(action: 'update')
|
62
61
|
end
|
63
62
|
|
64
63
|
def hound_destroy
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
64
|
+
create_action({action: 'destroy'}, false)
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_action(attributes, scoped = true)
|
68
|
+
attributes = default_attributes.merge(attributes)
|
69
|
+
if scoped
|
70
|
+
actions.create!(attributes)
|
71
|
+
else
|
72
|
+
# unscoped actions should still always save the associated data
|
73
|
+
Hound.actions.create(attributes.merge({
|
74
|
+
actionable_id: self.id,
|
75
|
+
actionable_type: self.class.base_class.name
|
76
|
+
}))
|
77
|
+
end
|
71
78
|
enforce_limit
|
72
79
|
end
|
73
80
|
|
74
81
|
def default_attributes
|
75
82
|
{
|
76
|
-
user_id: Hound.store[:current_user_id]
|
83
|
+
user_id: Hound.store[:current_user_id],
|
84
|
+
user_type: Hound.store[:current_user_type],
|
85
|
+
changeset: changes
|
77
86
|
}
|
78
87
|
end
|
79
88
|
|
data/lib/hound/version.rb
CHANGED
data/test/config_test.rb
CHANGED
@@ -4,11 +4,4 @@ class TestUserClass; end
|
|
4
4
|
|
5
5
|
class ConfigTest < ActiveSupport::TestCase
|
6
6
|
|
7
|
-
test 'user_class' do
|
8
|
-
assert_equal 'User', Hound.config.user_class
|
9
|
-
|
10
|
-
Hound.config.user_class = TestUserClass
|
11
|
-
assert_equal 'TestUserClass', Hound.config.user_class
|
12
|
-
end
|
13
|
-
|
14
7
|
end
|
@@ -5,13 +5,17 @@ class CreateHoundActions < ActiveRecord::Migration
|
|
5
5
|
t.string :actionable_type, null: false
|
6
6
|
t.integer :actionable_id, null: false
|
7
7
|
t.integer :user_id
|
8
|
+
t.string :user_type
|
8
9
|
t.datetime :created_at
|
10
|
+
t.text :changeset
|
9
11
|
end
|
10
12
|
add_index :hound_actions, [:actionable_type, :actionable_id]
|
13
|
+
add_index :hound_actions, [:user_type, :user_id]
|
11
14
|
end
|
12
15
|
|
13
16
|
def self.down
|
14
17
|
remove_index :hound_actions, [:actionable_type, :actionable_id]
|
18
|
+
remove_index :hound_actions, [:user_type, :user_id]
|
15
19
|
drop_table :hound_actions
|
16
20
|
end
|
17
21
|
end
|
data/test/dummy/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20130314120938) do
|
15
15
|
|
16
16
|
create_table "articles", :force => true do |t|
|
17
17
|
t.string "title"
|
@@ -25,11 +25,13 @@ ActiveRecord::Schema.define(:version => 20130310103454) do
|
|
25
25
|
t.string "actionable_type", :null => false
|
26
26
|
t.integer "actionable_id", :null => false
|
27
27
|
t.integer "user_id"
|
28
|
+
t.string "user_type"
|
28
29
|
t.datetime "created_at"
|
29
30
|
t.text "changeset"
|
30
31
|
end
|
31
32
|
|
32
33
|
add_index "hound_actions", ["actionable_type", "actionable_id"], :name => "index_hound_actions_on_actionable_type_and_actionable_id"
|
34
|
+
add_index "hound_actions", ["user_type", "user_id"], :name => "index_hound_actions_on_user_type_and_user_id"
|
33
35
|
|
34
36
|
create_table "posts", :force => true do |t|
|
35
37
|
t.text "text"
|
data/test/hound_test.rb
CHANGED
@@ -36,4 +36,14 @@ class HoundTest < ActiveSupport::TestCase
|
|
36
36
|
end
|
37
37
|
assert_equal 3, @post.actions.size
|
38
38
|
end
|
39
|
+
|
40
|
+
test 'user has many actions' do
|
41
|
+
user = User.create! name: 'Lee'
|
42
|
+
assert user.respond_to?(:actions)
|
43
|
+
article = Article.create! title: 'Hello, World!'
|
44
|
+
action = article.actions.last
|
45
|
+
action.user = user
|
46
|
+
assert action.save
|
47
|
+
assert_equal article.actions, user.actions
|
48
|
+
end
|
39
49
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Jarvis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-03-
|
11
|
+
date: 2013-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -47,6 +47,7 @@ extra_rdoc_files: []
|
|
47
47
|
files:
|
48
48
|
- .gitignore
|
49
49
|
- .travis.yml
|
50
|
+
- CHANGELOG.md
|
50
51
|
- Gemfile
|
51
52
|
- LICENSE
|
52
53
|
- README.md
|
@@ -98,8 +99,7 @@ files:
|
|
98
99
|
- test/dummy/db/migrate/20130307165752_create_users.rb
|
99
100
|
- test/dummy/db/migrate/20130307165837_create_articles.rb
|
100
101
|
- test/dummy/db/migrate/20130307183451_create_posts.rb
|
101
|
-
- test/dummy/db/migrate/
|
102
|
-
- test/dummy/db/migrate/20130310103454_add_changeset_to_hound_actions.rb
|
102
|
+
- test/dummy/db/migrate/20130314120938_create_hound_actions.rb
|
103
103
|
- test/dummy/db/schema.rb
|
104
104
|
- test/dummy/lib/assets/.gitkeep
|
105
105
|
- test/dummy/log/.gitkeep
|
@@ -139,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
139
|
version: '0'
|
140
140
|
requirements: []
|
141
141
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.0.
|
142
|
+
rubygems_version: 2.0.3
|
143
143
|
signing_key:
|
144
144
|
specification_version: 4
|
145
145
|
summary: Trace changes to your models
|
@@ -180,8 +180,7 @@ test_files:
|
|
180
180
|
- test/dummy/db/migrate/20130307165752_create_users.rb
|
181
181
|
- test/dummy/db/migrate/20130307165837_create_articles.rb
|
182
182
|
- test/dummy/db/migrate/20130307183451_create_posts.rb
|
183
|
-
- test/dummy/db/migrate/
|
184
|
-
- test/dummy/db/migrate/20130310103454_add_changeset_to_hound_actions.rb
|
183
|
+
- test/dummy/db/migrate/20130314120938_create_hound_actions.rb
|
185
184
|
- test/dummy/db/schema.rb
|
186
185
|
- test/dummy/lib/assets/.gitkeep
|
187
186
|
- test/dummy/log/.gitkeep
|