hound 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](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
|