simple_activity 0.1.1
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +142 -0
- data/Rakefile +38 -0
- data/lib/generators/simple_activity/install_generator.rb +32 -0
- data/lib/generators/simple_activity/templates/activity.rb +2 -0
- data/lib/generators/simple_activity/templates/create_activity.rb +15 -0
- data/lib/generators/simple_activity/templates/rules.yml +35 -0
- data/lib/generators/simple_activity/templates/simple_activity.rb +8 -0
- data/lib/simple_activity/controller_methods.rb +69 -0
- data/lib/simple_activity/helpers.rb +12 -0
- data/lib/simple_activity/hooks.rb +20 -0
- data/lib/simple_activity/models/activity.rb +79 -0
- data/lib/simple_activity/models/extenders.rb +29 -0
- data/lib/simple_activity/railtie.rb +7 -0
- data/lib/simple_activity/rule.rb +31 -0
- data/lib/simple_activity/services/activity_processor.rb +118 -0
- data/lib/simple_activity/services/callbacks.rb +45 -0
- data/lib/simple_activity/version.rb +3 -0
- data/lib/simple_activity.rb +21 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9d868ca86145e7defedd43fbda7cf7a9ef9119c4
|
4
|
+
data.tar.gz: fe60f2894728a517af4f2348e815ee366a93d60e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 83459aca9b69474c188714a199c442839f58194d44a7bdbbad47f389114cd6f739471317629c143159af1586a0a91fe73b3f078fd12a62002adccb716caa61d5
|
7
|
+
data.tar.gz: e1f9846693efb02da8aca434c95050fd09db96937fe3a1e02892fd426d62099e5fdd0e27703da7bea71328897d2fcb49a7d7201ead59125762cfaffa9fb65394
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 Billy Chan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# SimpleActivity
|
2
|
+
|
3
|
+
SimpleActivity is a gem to record, display and reuse users activities for
|
4
|
+
Rails app, in a fast, flexible and extendable way.
|
5
|
+
|
6
|
+
## Features
|
7
|
+
* **Fast** - SimpleActivity is built with speed in mind. Normally activities involve
|
8
|
+
several models and hard to eager load. SimpleActivity allows you define cache
|
9
|
+
rules beforehand so there is no JOIN on displaying activities.
|
10
|
+
|
11
|
+
* **Flexible** - Raw data is here, you are free to do anything you like, display them
|
12
|
+
in any fashion.
|
13
|
+
|
14
|
+
* **Extendable** - Actually activity records are useful not only for displaying as timeline. There could be lots of other functionality based on activity such as assining reputation
|
15
|
+
to models, assign points to users, send notifications, granting badges etc. It would be
|
16
|
+
redudant if every feature build its own logic to record activity which is basically the same.
|
17
|
+
|
18
|
+
To solve this problem, this gem provides a hook for other libs or your app code to work
|
19
|
+
on the raw data once activity created, either within the request or at
|
20
|
+
backend(recommended). This will remove code repetition and increase app performance.
|
21
|
+
|
22
|
+
## How it works
|
23
|
+
* Record key data of user activities(create, update, destroy and custom) in
|
24
|
+
controler level, automaticly or manually.
|
25
|
+
* Read and process cache rules to allow showing activities with minimal db load.
|
26
|
+
* Provide helper to load partials with defined names
|
27
|
+
* Provide barebone model to allow your customization if needed.
|
28
|
+
* Provide hook to further working on the activity created.
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
* Add the gem to Gemfile and `bundle`
|
33
|
+
|
34
|
+
```bash
|
35
|
+
gem 'simple_activity'
|
36
|
+
```
|
37
|
+
|
38
|
+
* Install the custom files and migration
|
39
|
+
|
40
|
+
```bash
|
41
|
+
$ rails generate simple_activity:install
|
42
|
+
$ rake db:migrate
|
43
|
+
```
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
### Record Activities
|
48
|
+
By default you don't need to do anything to get activities recorded.
|
49
|
+
|
50
|
+
Activity record will be created automaticly at basic RESTful actions :create, :update
|
51
|
+
and :destroy, with filtering out some controllers such as "registration", "session",
|
52
|
+
"admin" etc. You can also customize them. See more at Configurations.
|
53
|
+
|
54
|
+
### Display Activities: Define Rules
|
55
|
+
|
56
|
+
The rules file `rules.yml` resides in `app/models/activity/rules.yml`. One purpose of
|
57
|
+
rule.yml is to helper displaying - customizing displaying logic and define things to cache.
|
58
|
+
|
59
|
+
Here is an example of rules.yml
|
60
|
+
```yml
|
61
|
+
Comment:
|
62
|
+
_cache:
|
63
|
+
actor:
|
64
|
+
- nick_name
|
65
|
+
target:
|
66
|
+
- commentable_title
|
67
|
+
- excerpt
|
68
|
+
Article:
|
69
|
+
...
|
70
|
+
```
|
71
|
+
|
72
|
+
### Display Activities: By partial
|
73
|
+
|
74
|
+
A helper `render_activity(activity)` is shipped. This helper will render partial
|
75
|
+
at default place according to the activity.
|
76
|
+
|
77
|
+
At first you need to write partials at right place. By default the helper will
|
78
|
+
look up `app/views/activities` folder. The partial name need to be a combination
|
79
|
+
of model name and action key with underscore before. e.g. `_comment_create.html.erb`,
|
80
|
+
`_article_destroy.html.slim`
|
81
|
+
|
82
|
+
You can call any attributes defined in cache rules within the partial, by concating
|
83
|
+
actor/target and the method name. e.g. `activity.actore_name`, `activity.target_title`,
|
84
|
+
`activity.target_commentable_title`
|
85
|
+
|
86
|
+
`actor_id` and `target_id` is available natively. No need cache rules.
|
87
|
+
|
88
|
+
Also `actor` and `target` object is available if you really need them. But it's not
|
89
|
+
recommended to use them to reduce db load. `activity.actor.name` is less preferred to
|
90
|
+
`activity.actore_name` which is a cached value.
|
91
|
+
|
92
|
+
As said above, to define a link, you don't need the instance itself. Instead, id will work
|
93
|
+
as well. So, instead of `link_to activity.actor`, it's recommended to use
|
94
|
+
`link_to user_path(activity.actor_id)`
|
95
|
+
|
96
|
+
## Configurations
|
97
|
+
|
98
|
+
#### To customize the actions to record
|
99
|
+
|
100
|
+
The default actions to record activiies are `:create`, `:update` and `:destroy`.
|
101
|
+
Customize them as per your need.
|
102
|
+
```ruby
|
103
|
+
config.filtered_controllers = /(user|admin|session|registration)/i
|
104
|
+
```
|
105
|
+
|
106
|
+
#### To filter out more controllers
|
107
|
+
|
108
|
+
You don't need activity to be recorded in every controller. Below is the
|
109
|
+
default settings. You can customize them as you like.
|
110
|
+
```ruby
|
111
|
+
config.filtered_controllers = /(user|admin|session|registration)/i
|
112
|
+
```
|
113
|
+
|
114
|
+
## Extend
|
115
|
+
|
116
|
+
Reuse activity in third party libs.
|
117
|
+
|
118
|
+
Docs and demo coming soon.
|
119
|
+
|
120
|
+
## Compatability
|
121
|
+
|
122
|
+
SimpleActivity is built to work under both Rails 3 and Rails 4. The demo is on Rails 4.
|
123
|
+
The current tests are based on Rails 3, and Rails 4 ones will come later.
|
124
|
+
|
125
|
+
The current ORM is ActiveRecord. Other ORMS should be able to added in the future without
|
126
|
+
too much sweat(or coffee?)
|
127
|
+
|
128
|
+
## Contributing
|
129
|
+
|
130
|
+
This gem is still at its early stage. Very likely there are bugs and unconsidered
|
131
|
+
situations. Thansk for your patience and trust to use it. Your bug reporting and
|
132
|
+
pull requests will be appreciated.
|
133
|
+
|
134
|
+
1. Fork it
|
135
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
136
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
137
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
138
|
+
5. Create new Pull Request
|
139
|
+
|
140
|
+
## License
|
141
|
+
|
142
|
+
MIT. See MIT-LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'SimpleActivity'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task :default => :test
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
require 'rails/generators/active_record'
|
4
|
+
|
5
|
+
module SimpleActivity
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
include Rails::Generators::Migration
|
9
|
+
|
10
|
+
desc "Description: Create yml template for rules. Generate migration file for activities table. Create custom activity model(default to Activity)"
|
11
|
+
|
12
|
+
self.source_paths << File.join(File.dirname(__FILE__), 'templates')
|
13
|
+
|
14
|
+
def self.next_migration_number(path)
|
15
|
+
ActiveRecord::Generators::Base.next_migration_number(path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_migration_file
|
19
|
+
migration_template "create_activity.rb", "db/migrate/create_activity.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
def copy_rules
|
23
|
+
copy_file "rules.yml", "app/models/activity/rules.yml"
|
24
|
+
end
|
25
|
+
|
26
|
+
def copy_activity_model
|
27
|
+
copy_file "activity.rb", "app/models/activity.rb"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateActivity < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :simple_activity_activities do |t|
|
4
|
+
t.string :actor_type
|
5
|
+
t.integer :actor_id
|
6
|
+
t.string :target_type
|
7
|
+
t.integer :target_id
|
8
|
+
t.string :action_key
|
9
|
+
t.boolean :display
|
10
|
+
t.text :cache
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Sample Activity settings
|
2
|
+
#
|
3
|
+
# How to write rules
|
4
|
+
# * Model class name at first level
|
5
|
+
# * '_cache` at second level. It's for defining things to cache
|
6
|
+
# * The third level allows only keys as `actor` or `target`.
|
7
|
+
# - actor is the user who initiate this action. For example, the
|
8
|
+
# man who wrote an article, the man who liked a comment.
|
9
|
+
# - target is the resource itself. It represents the only instance variable
|
10
|
+
# you processed in controller. e.g. the article wrote, the comment
|
11
|
+
# be liked, or the like itself if "Like" is the model
|
12
|
+
# - Any other things are based on actor or target.
|
13
|
+
# * The fourth level represents the method available in actor's or taget's
|
14
|
+
# model. The value will be stored for caching.
|
15
|
+
# - Item(s) here must be in array(with dash and one space before it)
|
16
|
+
# - The model must respond to such methods.
|
17
|
+
|
18
|
+
# Comment:
|
19
|
+
# _cache:
|
20
|
+
# actor:
|
21
|
+
# - name
|
22
|
+
# target:
|
23
|
+
# - body
|
24
|
+
# create:
|
25
|
+
# # future possibility to extend
|
26
|
+
# # points:
|
27
|
+
# # - user: 10
|
28
|
+
# # condition: within_1_day_of_article
|
29
|
+
# # - commentable_user: 2
|
30
|
+
# Article:
|
31
|
+
# _cache:
|
32
|
+
# actor:
|
33
|
+
# - name
|
34
|
+
# target:
|
35
|
+
# - title
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# SimpleActivity Intializers
|
2
|
+
SimpleActivity.setup do |config|
|
3
|
+
# Setup controllers not to record activities
|
4
|
+
# config.filtered_controllers = /(user|admin|session|registration)/i
|
5
|
+
|
6
|
+
# Setup actions to record activities
|
7
|
+
# config.allowed_actions = [:create, :update, :destroy]
|
8
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
|
3
|
+
# Public: To be used on controller. The module is supposed to be
|
4
|
+
# included in ActionController::Base
|
5
|
+
|
6
|
+
module ControllerMethods
|
7
|
+
|
8
|
+
# extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
# included do
|
11
|
+
# after_filter :record_activity, only: [:create, :update, :destroy]
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
def self.included(base)
|
15
|
+
base.after_filter :record_activity, only: SimpleActivity.allowed_actions
|
16
|
+
end
|
17
|
+
|
18
|
+
# The main method to log activity.
|
19
|
+
#
|
20
|
+
# By default it is used as an after_filter
|
21
|
+
#
|
22
|
+
# If after_filter disabled, it can be called without arguments
|
23
|
+
#
|
24
|
+
# # ArticlesController
|
25
|
+
# def create
|
26
|
+
# @article = Article.create(params[:article])
|
27
|
+
# if @article.save
|
28
|
+
# record_activity
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# target argument is needed if the instance is not the convention
|
33
|
+
# (the sigularize of controller name)
|
34
|
+
#
|
35
|
+
# # ArticlesController
|
36
|
+
# def create
|
37
|
+
# @article_in_other_name = Article.create(params[:article])
|
38
|
+
# if @article_in_other_name.save
|
39
|
+
# record_activity(@article_in_other_name)
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
#
|
44
|
+
# @param target [Object] the target instance variable. If nil, the processor
|
45
|
+
# will build it according to mathcing instance variable in controller
|
46
|
+
# automatically
|
47
|
+
private
|
48
|
+
def record_activity(target=nil)
|
49
|
+
unless controller_name.match SimpleActivity.filtered_controllers
|
50
|
+
process_activity(target)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def process_activity(target)
|
55
|
+
activity = ::SimpleActivity::ActivityProcessor.new(self, target)
|
56
|
+
activity.save
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# ActiveSupport.on_load(:action_controller) do
|
63
|
+
# ActionController::Base.send :include, SimpleActivity::ControllerMethods
|
64
|
+
# end
|
65
|
+
# ActionController::Base.send :include, SimpleActivity::ControllerMethods
|
66
|
+
# ActionController::Base.class_eval do
|
67
|
+
# include SimpleActivity::ControllerMethods
|
68
|
+
# end
|
69
|
+
# end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
module Helpers
|
3
|
+
def render_activity(activity, mode="actor")
|
4
|
+
target = activity.target_type.underscore
|
5
|
+
action_key = activity.action_key
|
6
|
+
template = "activities/#{mode}/#{target}_#{action_key}"
|
7
|
+
if lookup_context.template_exists? template, [], true
|
8
|
+
render partial: template, locals: {activity: activity}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
class Hooks
|
3
|
+
def self.init
|
4
|
+
ActiveSupport.on_load(:action_controller) do
|
5
|
+
require 'simple_activity/controller_methods'
|
6
|
+
ActionController::Base.send :include, SimpleActivity::ControllerMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
ActiveSupport.on_load(:active_record) do
|
10
|
+
require 'simple_activity/models/extenders'
|
11
|
+
ActiveRecord::Base.send :include, SimpleActivity::ModelExtenders
|
12
|
+
end
|
13
|
+
|
14
|
+
ActiveSupport.on_load(:action_view) do
|
15
|
+
require 'simple_activity/helpers'
|
16
|
+
ActionView::Base.send :include, SimpleActivity::Helpers
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
class Activity < ActiveRecord::Base
|
3
|
+
self.table_name = "simple_activity_activities"
|
4
|
+
|
5
|
+
# cache can cache rule when necessary, for third party lib speeding
|
6
|
+
# us processing.
|
7
|
+
if Rails::VERSION::MAJOR == 3
|
8
|
+
attr_accessible :actor_type, :actor_id, :target_type, :target_id, :action_key, :display, :cache
|
9
|
+
end
|
10
|
+
|
11
|
+
serialize :cache
|
12
|
+
|
13
|
+
# Show activities belongs to an actor
|
14
|
+
def self.actor_activities(obj)
|
15
|
+
type = obj.class.to_s
|
16
|
+
id = obj.id
|
17
|
+
self.where(actor_type: type, actor_id: id)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Show activities belongs to a target
|
21
|
+
def self.target_activities(obj)
|
22
|
+
type = obj.class.to_s
|
23
|
+
id = obj.id
|
24
|
+
self.where(target_type: type, target_id: id)
|
25
|
+
end
|
26
|
+
|
27
|
+
def cache
|
28
|
+
read_attribute(:cache) || []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Delegate the methods start with "actor_" or "target_" to
|
32
|
+
# cached result
|
33
|
+
def method_missing(method_name, *arguments, &block)
|
34
|
+
if method_name.to_s =~ /(actor|target)_(?!type|id).*/
|
35
|
+
self.cache.try(:[], method_name.to_s)
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(method_name, include_private = false)
|
42
|
+
method_name.to_s.match /(actor|target)_.*/ || super
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO: Untested
|
46
|
+
def update_cache(cache_rule)
|
47
|
+
cache_rule.each do |type, type_methods|
|
48
|
+
type_methods.each do |method|
|
49
|
+
value = self.send(type).send(method)
|
50
|
+
self.cache["#{type}_#{method}"] = value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
save
|
54
|
+
end
|
55
|
+
|
56
|
+
def actor
|
57
|
+
actor_type.capitalize.constantize.find(actor_id)
|
58
|
+
end
|
59
|
+
|
60
|
+
def target
|
61
|
+
target_type.capitalize.constantize.find(target_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def can_display?
|
65
|
+
display
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def self.cache_methods
|
71
|
+
Rule.get_rules_set self.class.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def past_form(action)
|
75
|
+
action.last == 'e' ?
|
76
|
+
"#{action}d" : "#{action}ed"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module SimpleActivity
|
4
|
+
module ModelExtenders
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def acts_as_activity_actor
|
9
|
+
include ::SimpleActivity::ActorMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
def acts_as_activity_target
|
13
|
+
include ::SimpleActivity::TargetMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ActorMethods
|
19
|
+
def activities
|
20
|
+
::Activity.actor_activities(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module TargetMethods
|
25
|
+
def activities
|
26
|
+
::Activity.target_activities(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
class Rule
|
3
|
+
|
4
|
+
# Get a set of rules for specific target, action, and type.
|
5
|
+
# The type is normally "cache". It can also be other things once
|
6
|
+
# needed in external libs.
|
7
|
+
#
|
8
|
+
# @param target [String] model class name as string. e.g. "Article"
|
9
|
+
#
|
10
|
+
# @param type [String] the rule type, default as "cache". or
|
11
|
+
# other types defined by third parties
|
12
|
+
#
|
13
|
+
# @return set of rule when matched. Returns nil when unmatching
|
14
|
+
def self.get_rules_set(target, type='_cache')
|
15
|
+
all_rules.try(:[], target).try(:[], type)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.all_rules
|
21
|
+
@@_all_rules ||= load_rules
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return rules or blank array
|
25
|
+
def self.load_rules
|
26
|
+
File.open("#{Rails.root}/app/models/activity/rules.yml") do |rules|
|
27
|
+
YAML.load rules || []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
class ActivityProcessor
|
3
|
+
|
4
|
+
# This class is for internal usage. No need to initialize this manually,
|
5
|
+
# instead use controller methods provided.
|
6
|
+
#
|
7
|
+
# When being used as automatical way in controller, e.g. as
|
8
|
+
# after_filter, supply the controller
|
9
|
+
#
|
10
|
+
# ActivityProcessor.new(self)
|
11
|
+
#
|
12
|
+
# If cache options needs to be attached, ensure the second argument
|
13
|
+
#
|
14
|
+
# ActivityProcessor.new(self, nil, foo: 'bar')
|
15
|
+
#
|
16
|
+
# When being used manually, normally the target would be provided
|
17
|
+
#
|
18
|
+
# ActivityProcessor.new(self, @article, foo: 'bar')
|
19
|
+
#
|
20
|
+
# @param controller [Object] The controller object, often self in controller
|
21
|
+
#
|
22
|
+
# @param target [Object] The target instance. If nil, it will be found based on
|
23
|
+
# controller.
|
24
|
+
#
|
25
|
+
# When supplied manually, target should be free of error, aka,
|
26
|
+
# after controller actiton(create, update etc) success
|
27
|
+
#
|
28
|
+
# @param reference [Hash] When required, the second arg "target" should be
|
29
|
+
# there, either object or nil
|
30
|
+
#
|
31
|
+
# @return warning
|
32
|
+
# If the target object has any error, the fact is it has not
|
33
|
+
# passed validation yet. Return a warning in logger.
|
34
|
+
#
|
35
|
+
# If there is any other errors say absence of current_user, warning
|
36
|
+
# will also be returned from create_activity
|
37
|
+
#
|
38
|
+
# @return Valid activity object from create_activity if everything okay
|
39
|
+
#
|
40
|
+
def initialize(controller, target=nil)
|
41
|
+
@controller = controller
|
42
|
+
@target = target ? target : get_target
|
43
|
+
@target_type = @target.class.to_s
|
44
|
+
@actor = get_actor
|
45
|
+
@actor_type = @actor.class.to_s
|
46
|
+
@action_key = @controller.action_name
|
47
|
+
@cache = get_cache
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return nil at this level, but not at #initialize. The reason is not to
|
51
|
+
# throw error on `nil.create_activity`, for validation error of
|
52
|
+
# target is common case. SimpleActivity should let it pass.
|
53
|
+
def save
|
54
|
+
if validate_attrs
|
55
|
+
Activity.create(activity_attrs).tap do |activity|
|
56
|
+
Callbacks.run_callbacks(activity)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
warning
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def warning
|
64
|
+
Rails.logger.warn "SimpleActivity: failed to create activity"
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Do it here but not model level because target having error is very
|
71
|
+
# common. Catch it so that such info could be logged.
|
72
|
+
def validate_attrs
|
73
|
+
@target.present? && !@target.errors.any? && @actor.present? && @action_key.present?
|
74
|
+
end
|
75
|
+
|
76
|
+
def activity_attrs
|
77
|
+
{ actor_type: @actor_type,
|
78
|
+
actor_id: @actor.id,
|
79
|
+
target_type: @target_type,
|
80
|
+
target_id: @target.id,
|
81
|
+
action_key: @action_key,
|
82
|
+
display: true,
|
83
|
+
cache: @cache
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_target
|
88
|
+
@controller.instance_variable_get "@#{@controller.controller_name.singularize}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def get_actor
|
92
|
+
@controller.current_user
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_cache
|
96
|
+
return nil unless cache_methods
|
97
|
+
set_cache
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_cache
|
101
|
+
cache = {}
|
102
|
+
%w{actor target}.each do |type|
|
103
|
+
if cache_methods[type]
|
104
|
+
cache_methods[type].each do |method|
|
105
|
+
value = instance_variable_get("@#{type}").send method
|
106
|
+
cache["#{type}_#{method}"] = value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
cache
|
111
|
+
end
|
112
|
+
|
113
|
+
def cache_methods
|
114
|
+
@cache_methods ||= Rule.get_rules_set(@target_type)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
# The callback would be the module or class name.
|
3
|
+
# It must be responsed to `process` method with argument of
|
4
|
+
# activity.
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Add option if need to process the callback in the backend.
|
8
|
+
# The callback constant need to be responsible to add its own
|
9
|
+
# backend worker which respond to `delay` as per backend_jobs
|
10
|
+
# and sidekiq.
|
11
|
+
#
|
12
|
+
# @param const - the third party lib name in constant or string
|
13
|
+
#
|
14
|
+
# SimpleActivity::Callbacks.add_callback('Foo')
|
15
|
+
# SimpleActivity::Callbacks.add_callback(Foo)
|
16
|
+
#
|
17
|
+
# @option backend: true
|
18
|
+
# if true, will process the callback in backend by its own works
|
19
|
+
class Callbacks
|
20
|
+
@@callbacks = []
|
21
|
+
|
22
|
+
def self.add_callback(name, options={})
|
23
|
+
name = name.to_string unless name.kind_of?(String)
|
24
|
+
@@callbacks << {name: name}.merge!(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.run_callbacks(activity)
|
28
|
+
@@callbacks.each do |callback|
|
29
|
+
const = Object.const_get callback[:name]
|
30
|
+
if callback[:backend]
|
31
|
+
const.delay.process(activity)
|
32
|
+
else
|
33
|
+
const.process(activity)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.delete_callback(name)
|
39
|
+
@@callbacks.delete_if do |callback|
|
40
|
+
callback[:name] == name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SimpleActivity
|
2
|
+
mattr_accessor :filtered_controllers
|
3
|
+
@@filtered_controllers = /(user|admin|session|registration)/i
|
4
|
+
|
5
|
+
mattr_accessor :allowed_actions
|
6
|
+
@@allowed_actions = [:create, :update, :destroy]
|
7
|
+
|
8
|
+
def self.setup
|
9
|
+
yield self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'active_support/dependencies'
|
14
|
+
require 'simple_activity/version'
|
15
|
+
require 'simple_activity/rule'
|
16
|
+
require 'simple_activity/models/activity'
|
17
|
+
require 'simple_activity/models/extenders'
|
18
|
+
require 'simple_activity/services/activity_processor'
|
19
|
+
require 'simple_activity/services/callbacks'
|
20
|
+
require 'simple_activity/hooks'
|
21
|
+
require 'simple_activity/railtie'
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_activity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Billy Chan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-09 00:00:00.000000000 Z
|
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.2.0
|
20
|
+
- - <
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.0
|
30
|
+
- - <
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sqlite3
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec-rails
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: database_cleaner
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.9.1
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 0.9.1
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: pry-rails
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: pry-highlight
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
description: SimpleActivity is a gem to record, display and reuse users activities
|
104
|
+
for Rails app, in a fast, flexible and extendable way.
|
105
|
+
email: billychanpublic@gmail.com
|
106
|
+
executables: []
|
107
|
+
extensions: []
|
108
|
+
extra_rdoc_files: []
|
109
|
+
files:
|
110
|
+
- lib/generators/simple_activity/install_generator.rb
|
111
|
+
- lib/generators/simple_activity/templates/rules.yml
|
112
|
+
- lib/generators/simple_activity/templates/create_activity.rb
|
113
|
+
- lib/generators/simple_activity/templates/activity.rb
|
114
|
+
- lib/generators/simple_activity/templates/simple_activity.rb
|
115
|
+
- lib/simple_activity/railtie.rb
|
116
|
+
- lib/simple_activity/controller_methods.rb
|
117
|
+
- lib/simple_activity/version.rb
|
118
|
+
- lib/simple_activity/hooks.rb
|
119
|
+
- lib/simple_activity/services/activity_processor.rb
|
120
|
+
- lib/simple_activity/services/callbacks.rb
|
121
|
+
- lib/simple_activity/rule.rb
|
122
|
+
- lib/simple_activity/helpers.rb
|
123
|
+
- lib/simple_activity/models/activity.rb
|
124
|
+
- lib/simple_activity/models/extenders.rb
|
125
|
+
- lib/simple_activity.rb
|
126
|
+
- MIT-LICENSE
|
127
|
+
- Rakefile
|
128
|
+
- README.md
|
129
|
+
homepage: https://github.com/billychan/simple_activity
|
130
|
+
licenses:
|
131
|
+
- MIT
|
132
|
+
metadata: {}
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
require_paths:
|
136
|
+
- lib
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
requirements: []
|
148
|
+
rubyforge_project:
|
149
|
+
rubygems_version: 2.1.11
|
150
|
+
signing_key:
|
151
|
+
specification_version: 4
|
152
|
+
summary: Record, display and reuse users activities for Rails app
|
153
|
+
test_files: []
|
154
|
+
has_rdoc:
|