narrator 0.0.1.alpha → 0.0.1.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +2 -3
- data/README.rdoc +66 -1
- data/Rakefile +2 -1
- data/lib/generators/narrator/install_generator.rb +41 -0
- data/lib/generators/narrator/templates/active_record/migration.rb +31 -0
- data/lib/narrator/activity.rb +25 -1
- data/lib/narrator/controller_additions.rb +24 -10
- data/lib/narrator/controller_resource.rb +23 -7
- data/lib/narrator/model_additions.rb +34 -0
- data/lib/narrator/model_resources.rb +9 -0
- data/lib/narrator/narrator_railtie.rb +9 -0
- data/lib/narrator/version.rb +1 -1
- data/lib/narrator.rb +6 -8
- data/narrator.gemspec +3 -0
- data/spec/narrator/narrator_spec.rb +4 -1
- data/spec/spec_helper.rb +1 -1
- metadata +42 -16
- data/lib/generators/narrator/activity/USAGE +0 -4
- data/lib/generators/narrator/activity/activity_generator.rb +0 -11
- data/lib/generators/narrator/activity/templates/activity.rb +0 -30
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fa1bb48cf586a6f4f347ed23bad29ef890b6a970
|
4
|
+
data.tar.gz: 61fc99168cf94e4bf24b68652747e4df0822d6da
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fa6d4ce60133cca0a0c8596fb5bc9dfc314ddb46f278c52d2c470bac44a5d0457a4f87b29a210093bbf4191f46c64897e2f4463ffccd2920146d455a9396a3e5
|
7
|
+
data.tar.gz: a264d17ae149cb7713ffed1b36f1884b62103ff442431f4539bebaaada182964ba6acc4b753ca3691440c5082d4298a65c45a1466395691dc0038854eb4ae58e
|
data/.travis.yml
CHANGED
data/README.rdoc
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
= This gem is currently unstable as it is being developed.
|
1
2
|
= Narrator {<img src="https://secure.travis-ci.org/jweakley/narrator.png?branch=master" />}[http://travis-ci.org/jweakley/narrator]
|
2
3
|
|
3
4
|
Narrator is an activity library for Ruby on Rails that allow you to track what is happening on models and later use to build an activity or news stream. Tracking is done at the controller level with a simple narrate_resource command (which was inspired by the simplicity of Ryan Bates' CanCan gem).
|
@@ -8,9 +9,73 @@ In <b> Rails 3 </b> add this line to your application's Gemfile and run the +bun
|
|
8
9
|
|
9
10
|
gem "narrator"
|
10
11
|
|
12
|
+
Once you have the Narrator gem installed you will need to run the generator:
|
13
|
+
|
14
|
+
rails generate narrator:install
|
15
|
+
|
16
|
+
This generator will add the migrations needed for the activity tracking. Currently this only works for ActiveRecord.
|
17
|
+
|
18
|
+
After the migration has been added you will need to migrate your database to run the new migration.
|
19
|
+
|
20
|
+
rake db:migrate
|
21
|
+
|
11
22
|
== Getting Started
|
12
23
|
|
13
|
-
|
24
|
+
Narrator expects a +current_user+ method to exist in the controller. First, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]).
|
25
|
+
|
26
|
+
=== 1. Mark a Model as Narratable
|
27
|
+
|
28
|
+
Any model that we would like to have tracked by narrator needs to have <tt>narratable_model</tt> added to it.
|
29
|
+
|
30
|
+
class Article
|
31
|
+
narratable_model
|
32
|
+
...
|
33
|
+
end
|
34
|
+
|
35
|
+
See {Narrated Model}[https://github.com/jweakley/narrator/wiki/Narrated-Model] for more details and features.
|
36
|
+
|
37
|
+
=== 1. Mark a Model as Narratable
|
38
|
+
|
39
|
+
Any model that we would like to create activites (normally just User) needs to have <tt>narratable_actor</tt> added to it.
|
40
|
+
|
41
|
+
class User
|
42
|
+
narratable_actor
|
43
|
+
...
|
44
|
+
end
|
45
|
+
|
46
|
+
See {Narrated Actor}[https://github.com/jweakley/narrator/wiki/Narrated-ACtor] for more details and features.
|
47
|
+
|
48
|
+
|
49
|
+
=== 3. Narrate the Model's activities from the controller
|
50
|
+
|
51
|
+
The <tt>narrate</tt> method in the controller will narrate something important.
|
52
|
+
|
53
|
+
def create
|
54
|
+
...
|
55
|
+
@article = Article.new(params[:article])
|
56
|
+
...
|
57
|
+
narrate @article if @article.save
|
58
|
+
....
|
59
|
+
end
|
60
|
+
|
61
|
+
In this example, if the article is successfully saved, an activity gets logged for the article.
|
62
|
+
|
63
|
+
See {Tracked Activities}[https://github.com/jweakley/narrator/wiki/Tracked-Activities] for more details and features with tracked activities.
|
64
|
+
|
65
|
+
== Future Features
|
66
|
+
|
67
|
+
Automagical Narration
|
68
|
+
|
69
|
+
class ArticlesController < ApplicationController
|
70
|
+
narrate_resource
|
71
|
+
|
72
|
+
def create
|
73
|
+
...
|
74
|
+
respond_with @article
|
75
|
+
# @article is automaticly tracked if it is valid
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
14
79
|
|
15
80
|
== Wiki Docs
|
16
81
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module Narrator
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
desc 'Generates migration for Activity model'
|
8
|
+
|
9
|
+
def self.orm
|
10
|
+
Rails::Generators.options[:rails][:orm]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.source_root
|
14
|
+
File.join(
|
15
|
+
File.dirname(__FILE__),
|
16
|
+
'templates',
|
17
|
+
(orm.to_s unless orm.class.eql?(String))
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.orm_has_migration?
|
22
|
+
[:active_record].include? orm
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.next_migration_number(dirname)
|
26
|
+
if ActiveRecord::Base.timestamped_migrations
|
27
|
+
migration_number = Time.now.utc.strftime('%Y%m%d%H%M%S').to_i
|
28
|
+
migration_number += 1
|
29
|
+
migration_number.to_s
|
30
|
+
else
|
31
|
+
sprintf '%.3d' , (current_migration_number(dirname) + 1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_migration_file
|
36
|
+
if self.class.orm_has_migration?
|
37
|
+
migration_template 'migration.rb', 'db/migrate/narrator_migration'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class NarratorMigration < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
if table_exists?(:activities)
|
4
|
+
say 'Activities table already exists! Narrator was unable to install.'
|
5
|
+
raise 'Narrator was unable to resolve clashing table names.'
|
6
|
+
else
|
7
|
+
suppress_messages do
|
8
|
+
create_table :activities do |t|
|
9
|
+
t.belongs_to :owner
|
10
|
+
t.string :owner_type
|
11
|
+
t.belongs_to :actor
|
12
|
+
t.string :actor_type
|
13
|
+
t.text :context
|
14
|
+
t.belongs_to :subject
|
15
|
+
t.string :subject_type
|
16
|
+
t.belongs_to :target
|
17
|
+
t.string :target_type
|
18
|
+
t.string :verb
|
19
|
+
t.boolean :has_seen, default: false
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
add_index :activities, [:owner_id, :owner_type]
|
24
|
+
add_index :activities, [:actor_id, :actor_type]
|
25
|
+
add_index :activities, [:subject_id, :subject_type]
|
26
|
+
add_index :activities, [:target_id, :target_type]
|
27
|
+
end
|
28
|
+
say 'Created tables for Narrator'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/narrator/activity.rb
CHANGED
@@ -1,4 +1,28 @@
|
|
1
1
|
module Narrator
|
2
|
-
|
2
|
+
class Activity < ::ActiveRecord::Base
|
3
|
+
###
|
4
|
+
# Attribute accessible
|
5
|
+
###
|
6
|
+
attr_accessible :owner, :subject, :actor, :target, :verb, :context, :has_seen
|
7
|
+
|
8
|
+
###
|
9
|
+
# Associations
|
10
|
+
###
|
11
|
+
belongs_to :owner, polymorphic: true
|
12
|
+
belongs_to :actor, polymorphic: true
|
13
|
+
belongs_to :subject, polymorphic: true
|
14
|
+
belongs_to :target, polymorphic: true
|
15
|
+
|
16
|
+
###
|
17
|
+
# Validate
|
18
|
+
###
|
19
|
+
validates :subject, presence: true
|
20
|
+
validates :verb, presence: true
|
21
|
+
|
22
|
+
###
|
23
|
+
# Scopes
|
24
|
+
###
|
25
|
+
scope :unseen, where(has_seen: false)
|
26
|
+
scope :seen, where(has_seen: true)
|
3
27
|
end
|
4
28
|
end
|
@@ -3,23 +3,37 @@ module Narrator
|
|
3
3
|
module ClassMethods
|
4
4
|
|
5
5
|
def narrate_resource(*args)
|
6
|
-
narrator_resource_class.
|
7
|
-
end
|
8
|
-
|
9
|
-
def narrate(subject, target = nil, verb = params[:action], actor_id = current_user.user_profile_id)
|
10
|
-
if user_signed_in?
|
11
|
-
subject.class.delay.track_user_activity(subject.id, actor_id, target.class.to_s, (target.blank? ? nil : target.id), verb.to_s)
|
12
|
-
end
|
6
|
+
narrator_resource_class.add_after_filter(self, :narrate_resource, *args)
|
13
7
|
end
|
14
8
|
|
15
9
|
def narrator_resource_class
|
10
|
+
if ancestors.map(&:to_s).include? 'InheritedResources::Actions'
|
11
|
+
InheritedResource
|
12
|
+
else
|
16
13
|
ControllerResource
|
14
|
+
end
|
17
15
|
end
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def self.included(base)
|
19
|
+
base.extend ClassMethods
|
20
|
+
base.helper_method :narrate
|
21
|
+
end
|
22
22
|
|
23
|
+
def narrate(subject, actor = current_user, target = nil, verb = params[:action], context = nil)
|
24
|
+
if subject.class.respond_to? :track_user_activity
|
25
|
+
# Add some config way of determining to use delayed jobs.
|
26
|
+
subject.class.track_user_activity(
|
27
|
+
(subject.blank? ? nil : subject.id),
|
28
|
+
subject.class.to_s,
|
29
|
+
(actor.blank? ? nil : actor.id),
|
30
|
+
actor.class.to_s,
|
31
|
+
(target.blank? ? nil : target.id),
|
32
|
+
target.class.to_s,
|
33
|
+
verb,
|
34
|
+
context
|
35
|
+
)
|
36
|
+
end
|
23
37
|
end
|
24
38
|
end
|
25
39
|
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
module Narrator
|
2
2
|
class ControllerResource
|
3
3
|
|
4
|
+
def self.add_after_filter(controller_class, method, *args)
|
5
|
+
options = args.extract_options!
|
6
|
+
resource_name = args.first
|
7
|
+
after_filter_method = options.delete(:prepend) ? :prepend_after_filter : :after_filter
|
8
|
+
controller_class.send(after_filter_method, options.slice(:only, :except, :if, :unless)) do |controller|
|
9
|
+
controller.class.narrator_resource_class.new(controller, resource_name, options.except(:only, :except, :if, :unless)).send(method)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
def initialize(controller, *args)
|
5
14
|
@controller = controller
|
6
15
|
@params = controller.params
|
@@ -9,8 +18,7 @@ module Narrator
|
|
9
18
|
end
|
10
19
|
|
11
20
|
def narrate_resource
|
12
|
-
|
13
|
-
@controller.narrate(subject, nil, params[:action], current_user)
|
21
|
+
@controller.narrate(resource_instance) if resource_instance.valid?
|
14
22
|
end
|
15
23
|
|
16
24
|
protected
|
@@ -19,12 +27,20 @@ module Narrator
|
|
19
27
|
@controller.send(:current_user)
|
20
28
|
end
|
21
29
|
|
22
|
-
def
|
23
|
-
|
24
|
-
when String then @options[:class].constantize
|
25
|
-
else @options[:class]
|
26
|
-
end
|
30
|
+
def resource_instance
|
31
|
+
@controller.instance_variable_get("@#{instance_name}")
|
27
32
|
end
|
28
33
|
|
34
|
+
def name
|
35
|
+
@name || name_from_controller
|
36
|
+
end
|
37
|
+
|
38
|
+
def name_from_controller
|
39
|
+
@params[:controller].sub('Controller', '').underscore.split('/').last.singularize
|
40
|
+
end
|
41
|
+
|
42
|
+
def instance_name
|
43
|
+
@options[:instance_name] || name
|
44
|
+
end
|
29
45
|
end
|
30
46
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Narrator
|
2
|
+
module ModelAdditions
|
3
|
+
include ::ActiveSupport::Inflector
|
4
|
+
def narratable_model
|
5
|
+
###
|
6
|
+
# Includes
|
7
|
+
###
|
8
|
+
include Narrator::ModelResources
|
9
|
+
|
10
|
+
###
|
11
|
+
# Associations
|
12
|
+
###
|
13
|
+
has_many :activities, as: :subject, class_name: 'Narrator::Activity', dependent: :destroy
|
14
|
+
|
15
|
+
###
|
16
|
+
# Instance Methods
|
17
|
+
###
|
18
|
+
|
19
|
+
# TODO Add documentation once format is determined.
|
20
|
+
def track_user_activity(subject_id, subject_class, actor_id, actor_class, target_id, target_class, verb, context)
|
21
|
+
actor = actor_id.blank? ? nil : actor_class.constantize.find_by_id(actor_id)
|
22
|
+
subject = subject_id.blank? ? nil : subject_class.constantize.find_by_id(subject_id)
|
23
|
+
target = target_id.blank? ? nil : target_class.constantize.find_by_id(target_id)
|
24
|
+
Narrator::Activity.create!(owner: nil, subject: subject, actor: actor, target: target, verb: verb, context: context)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def narratable_actor
|
28
|
+
###
|
29
|
+
# Associations
|
30
|
+
###
|
31
|
+
has_many :activities, as: :actor, class_name: 'Narrator::Activity', dependent: :destroy
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/narrator/version.rb
CHANGED
data/lib/narrator.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
require 'narrator/
|
2
|
-
require 'narrator/
|
1
|
+
require 'narrator/version'
|
2
|
+
require 'narrator/activity'
|
3
|
+
require 'narrator/model_additions'
|
4
|
+
require 'narrator/model_resources'
|
3
5
|
require 'narrator/controller_additions'
|
4
|
-
|
5
|
-
require
|
6
|
-
|
7
|
-
module Narrator
|
8
|
-
# Your code goes here...
|
9
|
-
end
|
6
|
+
require 'narrator/controller_resource'
|
7
|
+
require 'narrator/narrator_railtie.rb' if defined?(Rails)
|
data/narrator.gemspec
CHANGED
@@ -17,5 +17,8 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
+
gem.add_dependency('rails', '~>3.0')
|
21
|
+
|
20
22
|
gem.add_development_dependency "rspec"
|
23
|
+
gem.add_development_dependency "rake"
|
21
24
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require 'narrator'
|
metadata
CHANGED
@@ -1,30 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: narrator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
5
|
-
prerelease: 6
|
4
|
+
version: 0.0.1.beta
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Joe Weakley
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-10-23 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
14
27
|
- !ruby/object:Gem::Dependency
|
15
28
|
name: rspec
|
16
29
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
30
|
requirements:
|
19
|
-
- -
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
20
46
|
- !ruby/object:Gem::Version
|
21
47
|
version: '0'
|
22
48
|
type: :development
|
23
49
|
prerelease: false
|
24
50
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
51
|
requirements:
|
27
|
-
- -
|
52
|
+
- - '>='
|
28
53
|
- !ruby/object:Gem::Version
|
29
54
|
version: '0'
|
30
55
|
description: Straightforward and flexable activity tracking for rails that is designed
|
@@ -43,40 +68,41 @@ files:
|
|
43
68
|
- LICENSE.txt
|
44
69
|
- README.rdoc
|
45
70
|
- Rakefile
|
46
|
-
- lib/generators/narrator/
|
47
|
-
- lib/generators/narrator/
|
48
|
-
- lib/generators/narrator/activity/templates/activity.rb
|
71
|
+
- lib/generators/narrator/install_generator.rb
|
72
|
+
- lib/generators/narrator/templates/active_record/migration.rb
|
49
73
|
- lib/narrator.rb
|
50
74
|
- lib/narrator/activity.rb
|
51
75
|
- lib/narrator/controller_additions.rb
|
52
76
|
- lib/narrator/controller_resource.rb
|
77
|
+
- lib/narrator/model_additions.rb
|
78
|
+
- lib/narrator/model_resources.rb
|
79
|
+
- lib/narrator/narrator_railtie.rb
|
53
80
|
- lib/narrator/version.rb
|
54
81
|
- narrator.gemspec
|
55
82
|
- spec/narrator/narrator_spec.rb
|
56
83
|
- spec/spec_helper.rb
|
57
84
|
homepage: https://github.com/jweakley/narrator
|
58
85
|
licenses: []
|
86
|
+
metadata: {}
|
59
87
|
post_install_message:
|
60
88
|
rdoc_options: []
|
61
89
|
require_paths:
|
62
90
|
- lib
|
63
91
|
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
-
none: false
|
65
92
|
requirements:
|
66
|
-
- -
|
93
|
+
- - '>='
|
67
94
|
- !ruby/object:Gem::Version
|
68
95
|
version: '0'
|
69
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
-
none: false
|
71
97
|
requirements:
|
72
|
-
- -
|
98
|
+
- - '>'
|
73
99
|
- !ruby/object:Gem::Version
|
74
100
|
version: 1.3.1
|
75
101
|
requirements: []
|
76
102
|
rubyforge_project:
|
77
|
-
rubygems_version:
|
103
|
+
rubygems_version: 2.0.3
|
78
104
|
signing_key:
|
79
|
-
specification_version:
|
105
|
+
specification_version: 4
|
80
106
|
summary: Straightforward and flexable activity tracking for rails.
|
81
107
|
test_files:
|
82
108
|
- spec/narrator/narrator_spec.rb
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class Activity
|
2
|
-
include Narrator::Activity
|
3
|
-
###
|
4
|
-
# Attribute accessible
|
5
|
-
###
|
6
|
-
attr_accessible :owner, :subject, :actor, :target, :target_id, :target_type, :verb, :context, :has_seen
|
7
|
-
|
8
|
-
###
|
9
|
-
# Associations
|
10
|
-
###
|
11
|
-
belongs_to :owner, class_name: 'UserProfile'
|
12
|
-
belongs_to :actor, class_name: 'UserProfile'
|
13
|
-
belongs_to :subject, polymorphic: true
|
14
|
-
belongs_to :target, polymorphic: true
|
15
|
-
|
16
|
-
###
|
17
|
-
# Validate
|
18
|
-
###
|
19
|
-
validates :owner, presence: true
|
20
|
-
validates :subject, presence: true
|
21
|
-
validates :verb, presence: true
|
22
|
-
|
23
|
-
###
|
24
|
-
# Scopes
|
25
|
-
###
|
26
|
-
scope :unseen, where{has_seen.eq false}
|
27
|
-
scope :seen, where{has_seen.eq true}
|
28
|
-
scope :recent, order('created_at DESC')
|
29
|
-
|
30
|
-
end
|