horde-rails 0.0.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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +103 -0
- data/Rakefile +7 -0
- data/db/migrate/20120714150229_horde_setup.rb +56 -0
- data/db/migrate/20120714184427_test_models.rb +33 -0
- data/horde.gemspec +26 -0
- data/lib/horde.rb +16 -0
- data/lib/horde/actions.rb +14 -0
- data/lib/horde/actions/base.rb +11 -0
- data/lib/horde/actions/comment.rb +96 -0
- data/lib/horde/actions/favorite.rb +93 -0
- data/lib/horde/actions/follow.rb +93 -0
- data/lib/horde/actions/rate.rb +95 -0
- data/lib/horde/actor.rb +12 -0
- data/lib/horde/base.rb +0 -0
- data/lib/horde/core_ext/string.rb +13 -0
- data/lib/horde/setting.rb +7 -0
- data/lib/horde/version.rb +3 -0
- data/spec/comment_spec.rb +41 -0
- data/spec/favorite_spec.rb +41 -0
- data/spec/follow_spec.rb +55 -0
- data/spec/rate_spec.rb +41 -0
- data/spec/setting_spec.rb +11 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/string_ext_spec.rb +29 -0
- data/spec/support/database.yml +9 -0
- data/spec/support/db_config.rb +6 -0
- data/spec/support/test_models.rb +18 -0
- metadata +220 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Dorren Chen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Horde
|
2
|
+
|
3
|
+
Horde is a gem to help you implement social networking features in your app, like follow a user, favorite a photo, and comment on an article, etc.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
Say your app has typical models like User, Article, Category, etc.
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
# ----- setup models
|
10
|
+
class User
|
11
|
+
include Horde::Actor
|
12
|
+
|
13
|
+
include Horde::Actions::Follow # allow one user follow another user
|
14
|
+
end
|
15
|
+
|
16
|
+
class Article
|
17
|
+
include Horde::Actions::All # all actions that can be performed on this model.
|
18
|
+
|
19
|
+
# or selectively, a few of them
|
20
|
+
# include Horde::Actions::Comment
|
21
|
+
# include Horde::Actions::Favorite
|
22
|
+
# include Horde::Actions::Follow
|
23
|
+
# include Horde::Actions::Rate
|
24
|
+
end
|
25
|
+
|
26
|
+
# ----- case 1: favoriting
|
27
|
+
favorite = user.favorite(article) # saved in 'horde_favorites' table
|
28
|
+
favorite.favoriter # --> user
|
29
|
+
favorite.target # --> article
|
30
|
+
favorite.favorited_article # --> same article
|
31
|
+
|
32
|
+
article.favorites # --> [favorite]
|
33
|
+
article.favoriters # --> [user]
|
34
|
+
user.created_favorites # --> [favorite], could point to any target, article, photo.
|
35
|
+
user.favorited_articles # --> [article], this user's favorited articles
|
36
|
+
|
37
|
+
|
38
|
+
# ----- case 2: commenting
|
39
|
+
comment = user.comment(article, :comment => 'luv it') # saved in 'horde_comments' table
|
40
|
+
comment.commenter # --> user
|
41
|
+
comment.target # --> article
|
42
|
+
comment.commented_article # --> same article
|
43
|
+
|
44
|
+
article.comments # --> [comment]
|
45
|
+
article.commenters # --> [user]
|
46
|
+
user.created_comments # --> [comment]
|
47
|
+
user.commented_articles # --> [article]
|
48
|
+
|
49
|
+
|
50
|
+
# ----- case 3: rating
|
51
|
+
rate = user.rate(article, :score => 10)
|
52
|
+
rate.rater # --> user
|
53
|
+
rate.target # --> article
|
54
|
+
rate.rated_article # --> same article
|
55
|
+
|
56
|
+
article.rates # --> [rate]
|
57
|
+
article.raters # --> [user]
|
58
|
+
user.created_rates # --> [rate]
|
59
|
+
user.rated_articles # --> [article]
|
60
|
+
```
|
61
|
+
|
62
|
+
How do you notify commenters when new comment is posted for an article?
|
63
|
+
```ruby
|
64
|
+
class Article
|
65
|
+
after_comment :notify_commenters
|
66
|
+
after_favorite :do_something
|
67
|
+
after_rate :do_something_else
|
68
|
+
|
69
|
+
def notify_commenters(comment)
|
70
|
+
msg = "#{comment.commenter.login} just commented on article '#{self.title}'"
|
71
|
+
emails = commenters.map &:email
|
72
|
+
# email all users
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
|
78
|
+
## Installation
|
79
|
+
|
80
|
+
Add this line to your application's Gemfile:
|
81
|
+
|
82
|
+
gem 'horde'
|
83
|
+
|
84
|
+
And then execute:
|
85
|
+
|
86
|
+
$ bundle
|
87
|
+
|
88
|
+
Or install it yourself as:
|
89
|
+
|
90
|
+
$ gem install horde
|
91
|
+
|
92
|
+
|
93
|
+
copy db/migrate/timestamp_horde_setup.rb to your app. I haven't figure
|
94
|
+
out how to use generator.
|
95
|
+
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
1. Fork it
|
100
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
101
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
102
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
103
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
class HordeSetup < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :horde_favorites do |t|
|
4
|
+
t.string :actor_id, :limit => 64
|
5
|
+
t.string :target_type, :limit => 64
|
6
|
+
t.string :target_id, :limit => 64
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
add_index :horde_favorites, :actor_id
|
11
|
+
add_index :horde_favorites, [:target_id, :target_type]
|
12
|
+
|
13
|
+
|
14
|
+
create_table :horde_follows do |t|
|
15
|
+
t.string :actor_id, :limit => 64
|
16
|
+
t.string :target_type, :limit => 64
|
17
|
+
t.string :target_id, :limit => 64
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
add_index :horde_follows, :actor_id
|
22
|
+
add_index :horde_follows, [:target_id, :target_type]
|
23
|
+
|
24
|
+
|
25
|
+
create_table :horde_comments do |t|
|
26
|
+
t.string :actor_id, :limit => 64
|
27
|
+
t.string :target_type, :limit => 64
|
28
|
+
t.string :target_id, :limit => 64
|
29
|
+
t.string :comment, :limit => 2048
|
30
|
+
t.integer :abuse_count, :default => 0
|
31
|
+
|
32
|
+
t.timestamps
|
33
|
+
end
|
34
|
+
add_index :horde_comments, :actor_id
|
35
|
+
add_index :horde_comments, [:target_id, :target_type]
|
36
|
+
add_index :horde_comments, :abuse_count
|
37
|
+
|
38
|
+
|
39
|
+
create_table :horde_rates do |t|
|
40
|
+
t.string :actor_id, :limit => 64
|
41
|
+
t.string :target_type, :limit => 64
|
42
|
+
t.string :target_id, :limit => 64
|
43
|
+
t.integer :score
|
44
|
+
|
45
|
+
t.timestamps
|
46
|
+
end
|
47
|
+
add_index :horde_rates, :actor_id
|
48
|
+
add_index :horde_rates, [:target_id, :target_type]
|
49
|
+
end
|
50
|
+
|
51
|
+
def down
|
52
|
+
drop_table :horde_favorites
|
53
|
+
drop_table :horde_comments
|
54
|
+
drop_table :horde_rates
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class TestModels < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :users do |t|
|
4
|
+
t.string :login, :limit => 64
|
5
|
+
t.string :email, :limit => 64
|
6
|
+
t.string :first_name, :limit => 64
|
7
|
+
t.string :last_name, :limit => 64
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :articles do |t|
|
13
|
+
t.string :title
|
14
|
+
t.string :body
|
15
|
+
t.string :author_id
|
16
|
+
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :photos do |t|
|
21
|
+
t.string :name
|
22
|
+
t.string :author_id
|
23
|
+
|
24
|
+
t.timestamps
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def down
|
29
|
+
drop_table :users
|
30
|
+
drop_table :articles
|
31
|
+
drop_table :photos
|
32
|
+
end
|
33
|
+
end
|
data/horde.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/horde/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Dorren Chen"]
|
6
|
+
gem.email = ["dorrenchen@gmail.com"]
|
7
|
+
gem.description = %q{A ruby gem providing social networking features for rails app.}
|
8
|
+
gem.summary = %q{A ruby gem providing social networking features for rails app.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "horde-rails"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Horde::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "mysql2"
|
19
|
+
gem.add_dependency 'activerecord'
|
20
|
+
gem.add_dependency 'activesupport'
|
21
|
+
gem.add_dependency "sinatra-activerecord"
|
22
|
+
gem.add_dependency "hooks"
|
23
|
+
gem.add_development_dependency 'debugger'
|
24
|
+
gem.add_development_dependency 'rspec'
|
25
|
+
gem.add_development_dependency "database_cleaner"
|
26
|
+
end
|
data/lib/horde.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "hooks"
|
3
|
+
require "horde/version"
|
4
|
+
require "horde/core_ext/string"
|
5
|
+
require "horde/setting"
|
6
|
+
require "horde/actions/base"
|
7
|
+
require "horde/actions/comment"
|
8
|
+
require "horde/actions/favorite"
|
9
|
+
require "horde/actions/follow"
|
10
|
+
require "horde/actions/rate"
|
11
|
+
require "horde/base"
|
12
|
+
require "horde/actor"
|
13
|
+
require "horde/actions"
|
14
|
+
|
15
|
+
module Horde
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Horde
|
2
|
+
module Actions
|
3
|
+
module All
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
include ::Horde::Actions::Comment
|
7
|
+
include ::Horde::Actions::Favorite
|
8
|
+
include ::Horde::Actions::Follow
|
9
|
+
include ::Horde::Actions::Rate
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Horde
|
2
|
+
class Comment < Actions::Base
|
3
|
+
self.table_name = 'horde_comments'
|
4
|
+
|
5
|
+
validates_presence_of :comment
|
6
|
+
end
|
7
|
+
|
8
|
+
module Actions
|
9
|
+
module Comment
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
included do
|
12
|
+
include CommentMethods::TargetMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module CommentMethods
|
17
|
+
# methods for user, usually User class in your app.
|
18
|
+
module ActorMethods
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
included do
|
21
|
+
has_many :created_comments,
|
22
|
+
:class_name => "Horde::Comment",
|
23
|
+
:foreign_key => :actor_id
|
24
|
+
end
|
25
|
+
|
26
|
+
# user comment something.
|
27
|
+
def comment(target, options = {})
|
28
|
+
params = {:actor_id => self.id,
|
29
|
+
:target_id => target.id,
|
30
|
+
:target_type => target.class.name
|
31
|
+
}.merge(options)
|
32
|
+
comment = ::Horde::Comment.create(params)
|
33
|
+
target.run_hook(:after_comment, comment)
|
34
|
+
|
35
|
+
comment
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# methods for something to be commented, like article, photo, etc.
|
40
|
+
# if your model is Article. Then you'll get following methods
|
41
|
+
#
|
42
|
+
# article.comments # --> [comment]
|
43
|
+
# article.commenters # --> [user]
|
44
|
+
#
|
45
|
+
# comment.commenter # --> user
|
46
|
+
# comment.commented_article # --> article
|
47
|
+
#
|
48
|
+
# user.commented_articles # --> [article]
|
49
|
+
module TargetMethods
|
50
|
+
extend ActiveSupport::Concern
|
51
|
+
included do
|
52
|
+
has_many :comments,
|
53
|
+
:class_name => "Horde::Comment",
|
54
|
+
:as => :target,
|
55
|
+
:order => "created_at DESC"
|
56
|
+
|
57
|
+
has_many :commenters,
|
58
|
+
:through => :comments
|
59
|
+
|
60
|
+
create_comment_associations
|
61
|
+
|
62
|
+
include Hooks
|
63
|
+
define_hook :after_comment
|
64
|
+
end
|
65
|
+
|
66
|
+
module ClassMethods
|
67
|
+
def create_comment_associations
|
68
|
+
target_class_name = self.name # like "Article", "Photo"
|
69
|
+
assn_name = "commented_#{target_class_name.tableize}" # "commented_articles"
|
70
|
+
|
71
|
+
# define belongs_to here because Setting.actor_clas_name has not been set
|
72
|
+
::Horde::Comment.belongs_to :commenter,
|
73
|
+
:foreign_key => :actor_id,
|
74
|
+
:class_name => Horde::Setting.actor_class_name
|
75
|
+
|
76
|
+
# comment.commented_article, this is created for
|
77
|
+
# user.commented_articles to work.
|
78
|
+
::Horde::Comment.belongs_to assn_name.singularize.to_sym,
|
79
|
+
:foreign_key => :target_id,
|
80
|
+
:class_name => target_class_name
|
81
|
+
|
82
|
+
Horde::Setting.actor_class_name.constantize.instance_eval do
|
83
|
+
include ActorMethods
|
84
|
+
|
85
|
+
# user.commented_articles
|
86
|
+
has_many assn_name,
|
87
|
+
:through => :created_comments,
|
88
|
+
:source => assn_name.singularize,
|
89
|
+
:conditions => {:"horde_comments.target_type" => target_class_name}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Horde
|
2
|
+
class Favorite < Actions::Base
|
3
|
+
self.table_name = 'horde_favorites'
|
4
|
+
end
|
5
|
+
|
6
|
+
module Actions
|
7
|
+
module Favorite
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
included do
|
10
|
+
include FavoriteMethods::TargetMethods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module FavoriteMethods
|
15
|
+
# methods for user, usually User class in your app.
|
16
|
+
module ActorMethods
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
included do
|
19
|
+
has_many :created_favorites,
|
20
|
+
:class_name => "Horde::Favorite",
|
21
|
+
:foreign_key => :actor_id
|
22
|
+
end
|
23
|
+
|
24
|
+
# user favorite something.
|
25
|
+
def favorite(target, options = {})
|
26
|
+
params = {:actor_id => self.id,
|
27
|
+
:target_id => target.id,
|
28
|
+
:target_type => target.class.name
|
29
|
+
}.merge(options)
|
30
|
+
fav = ::Horde::Favorite.create(params)
|
31
|
+
target.run_hook(:after_favorite, fav)
|
32
|
+
|
33
|
+
fav
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# methods for something to be favorited, like article, photo, etc.
|
38
|
+
# if your model is Article. Then you'll get following methods
|
39
|
+
#
|
40
|
+
# article.favorites # --> [favorite]
|
41
|
+
# article.favoriters # --> [user]
|
42
|
+
#
|
43
|
+
# favorite.favoriter # --> user
|
44
|
+
# favorite.favorited_article # --> article
|
45
|
+
#
|
46
|
+
# user.favorited_articles # --> [article]
|
47
|
+
module TargetMethods
|
48
|
+
extend ActiveSupport::Concern
|
49
|
+
included do
|
50
|
+
has_many :favorites,
|
51
|
+
:class_name => "Horde::Favorite",
|
52
|
+
:as => :target
|
53
|
+
|
54
|
+
has_many :favoriters,
|
55
|
+
:through => :favorites
|
56
|
+
|
57
|
+
create_favorite_associations
|
58
|
+
|
59
|
+
include Hooks
|
60
|
+
define_hook :after_favorite
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
def create_favorite_associations
|
65
|
+
target_class_name = self.name # like "Article", "Photo"
|
66
|
+
assn_name = "favorited_#{target_class_name.tableize}" # "favorited_articles"
|
67
|
+
|
68
|
+
# define belongs_to here because Setting.actor_clas_name has not been set
|
69
|
+
::Horde::Favorite.belongs_to :favoriter,
|
70
|
+
:foreign_key => :actor_id,
|
71
|
+
:class_name => Horde::Setting.actor_class_name
|
72
|
+
|
73
|
+
# favorite.favorited_article, this is created for
|
74
|
+
# user.favorited_articles to work.
|
75
|
+
::Horde::Favorite.belongs_to assn_name.singularize.to_sym,
|
76
|
+
:foreign_key => :target_id,
|
77
|
+
:class_name => target_class_name
|
78
|
+
|
79
|
+
Horde::Setting.actor_class_name.constantize.instance_eval do
|
80
|
+
include ActorMethods
|
81
|
+
|
82
|
+
# user.favorited_articles
|
83
|
+
has_many assn_name,
|
84
|
+
:through => :created_favorites,
|
85
|
+
:source => assn_name.singularize,
|
86
|
+
:conditions => {:"horde_favorites.target_type" => target_class_name}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Horde
|
2
|
+
class Follow < Actions::Base
|
3
|
+
self.table_name = 'horde_follows'
|
4
|
+
end
|
5
|
+
|
6
|
+
module Actions
|
7
|
+
module Follow
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
included do
|
10
|
+
include FollowMethods::TargetMethods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module FollowMethods
|
15
|
+
# methods for user, usually User class in your app.
|
16
|
+
module ActorMethods
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
included do
|
19
|
+
has_many :created_follows,
|
20
|
+
:class_name => "Horde::Follow",
|
21
|
+
:foreign_key => :actor_id
|
22
|
+
end
|
23
|
+
|
24
|
+
# user follow something.
|
25
|
+
def follow(target, options = {})
|
26
|
+
params = {:actor_id => self.id,
|
27
|
+
:target_id => target.id,
|
28
|
+
:target_type => target.class.name
|
29
|
+
}.merge(options)
|
30
|
+
follow = ::Horde::Follow.create(params)
|
31
|
+
target.run_hook(:after_follow, follow)
|
32
|
+
|
33
|
+
follow
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# methods for something to be followed, like article, photo, etc.
|
38
|
+
# if your model is Article. Then you'll get following methods
|
39
|
+
#
|
40
|
+
# article.follows # --> [follow]
|
41
|
+
# article.followers # --> [user]
|
42
|
+
#
|
43
|
+
# follow.follower # --> user
|
44
|
+
# follow.followed_article # --> article
|
45
|
+
#
|
46
|
+
# user.followed_articles # --> [article]
|
47
|
+
module TargetMethods
|
48
|
+
extend ActiveSupport::Concern
|
49
|
+
included do
|
50
|
+
has_many :follows,
|
51
|
+
:class_name => "Horde::Follow",
|
52
|
+
:as => :target
|
53
|
+
|
54
|
+
has_many :followers,
|
55
|
+
:through => :follows
|
56
|
+
|
57
|
+
create_follow_associations
|
58
|
+
|
59
|
+
include Hooks
|
60
|
+
define_hook :after_follow
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
def create_follow_associations
|
65
|
+
target_class_name = self.name # like "Article", "Photo"
|
66
|
+
assn_name = "followed_#{target_class_name.tableize}" # "followed_articles"
|
67
|
+
|
68
|
+
# define belongs_to here because Setting.actor_clas_name has not been set
|
69
|
+
::Horde::Follow.belongs_to :follower,
|
70
|
+
:foreign_key => :actor_id,
|
71
|
+
:class_name => Horde::Setting.actor_class_name
|
72
|
+
|
73
|
+
# follow.followed_article, this is created for
|
74
|
+
# user.followed_articles to work.
|
75
|
+
::Horde::Follow.belongs_to assn_name.singularize.to_sym,
|
76
|
+
:foreign_key => :target_id,
|
77
|
+
:class_name => target_class_name
|
78
|
+
|
79
|
+
Horde::Setting.actor_class_name.constantize.instance_eval do
|
80
|
+
include ActorMethods
|
81
|
+
|
82
|
+
# user.followed_articles
|
83
|
+
has_many assn_name,
|
84
|
+
:through => :created_follows,
|
85
|
+
:source => assn_name.singularize,
|
86
|
+
:conditions => {:"horde_follows.target_type" => target_class_name}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Horde
|
2
|
+
class Rate < Actions::Base
|
3
|
+
self.table_name = 'horde_rates'
|
4
|
+
|
5
|
+
validates_presence_of :score
|
6
|
+
end
|
7
|
+
|
8
|
+
module Actions
|
9
|
+
module Rate
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
included do
|
12
|
+
include RateMethods::TargetMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module RateMethods
|
17
|
+
# methods for user, usually User class in your app.
|
18
|
+
module ActorMethods
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
included do
|
21
|
+
has_many :created_rates,
|
22
|
+
:class_name => "Horde::Rate",
|
23
|
+
:foreign_key => :actor_id
|
24
|
+
end
|
25
|
+
|
26
|
+
# user rate something.
|
27
|
+
def rate(target, options = {})
|
28
|
+
params = {:actor_id => self.id,
|
29
|
+
:target_id => target.id,
|
30
|
+
:target_type => target.class.name
|
31
|
+
}.merge(options)
|
32
|
+
rate = ::Horde::Rate.create(params)
|
33
|
+
target.run_hook(:after_rate, rate)
|
34
|
+
|
35
|
+
rate
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# methods for something to be rated, like article, photo, etc.
|
40
|
+
# if your model is Article. Then you'll get following methods
|
41
|
+
#
|
42
|
+
# article.rates # --> [rate]
|
43
|
+
# article.raters # --> [user]
|
44
|
+
#
|
45
|
+
# rate.rater # --> user
|
46
|
+
# rate.rated_article # --> article
|
47
|
+
#
|
48
|
+
# user.rated_articles # --> [article]
|
49
|
+
module TargetMethods
|
50
|
+
extend ActiveSupport::Concern
|
51
|
+
included do
|
52
|
+
has_many :rates,
|
53
|
+
:class_name => "Horde::Rate",
|
54
|
+
:as => :target
|
55
|
+
|
56
|
+
has_many :raters,
|
57
|
+
:through => :rates
|
58
|
+
|
59
|
+
create_rate_associations
|
60
|
+
|
61
|
+
include Hooks
|
62
|
+
define_hook :after_rate
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods
|
66
|
+
def create_rate_associations
|
67
|
+
target_class_name = self.name # like "Article", "Photo"
|
68
|
+
assn_name = "rated_#{target_class_name.tableize}" # "rated_articles"
|
69
|
+
|
70
|
+
# define belongs_to here because Setting.actor_clas_name has not been set
|
71
|
+
::Horde::Rate.belongs_to :rater,
|
72
|
+
:foreign_key => :actor_id,
|
73
|
+
:class_name => Horde::Setting.actor_class_name
|
74
|
+
|
75
|
+
# rate.rated_article, this is created for
|
76
|
+
# user.rated_articles to work.
|
77
|
+
::Horde::Rate.belongs_to assn_name.singularize.to_sym,
|
78
|
+
:foreign_key => :target_id,
|
79
|
+
:class_name => target_class_name
|
80
|
+
|
81
|
+
Horde::Setting.actor_class_name.constantize.instance_eval do
|
82
|
+
include ActorMethods
|
83
|
+
|
84
|
+
# user.rated_articles
|
85
|
+
has_many assn_name,
|
86
|
+
:through => :created_rates,
|
87
|
+
:source => assn_name.singularize,
|
88
|
+
:conditions => {:"horde_rates.target_type" => target_class_name}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/horde/actor.rb
ADDED
data/lib/horde/base.rb
ADDED
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class String
|
2
|
+
# actorize("comment") # --> commenter
|
3
|
+
# actorize("vote") # --> voter
|
4
|
+
def actorize
|
5
|
+
self =~ /e$/ ? self + "r" : self + "er"
|
6
|
+
end
|
7
|
+
|
8
|
+
# past_tense("comment") # --> commented
|
9
|
+
# past_tense("vote") # --> voted
|
10
|
+
def past_tense
|
11
|
+
self =~ /e$/ ? self + "d" : self + "ed"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "commenting" do
|
4
|
+
before(:each) do
|
5
|
+
@user = User.create(:login => 'john', :first_name => 'John', :last_name => 'Doe')
|
6
|
+
@user2 = User.create(:login => 'jane', :first_name => 'Jane', :last_name => 'Doe')
|
7
|
+
@article = Article.create(:title => 'article 1', :author_id => @user2.id)
|
8
|
+
@photo = Photo.create(:name => 'photo 1', :author_id => @user2.id)
|
9
|
+
@user.comment(@article, :comment => "great article")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should work for actor" do
|
13
|
+
comment = Horde::Comment.first
|
14
|
+
comment.should_not be_nil
|
15
|
+
|
16
|
+
comment.target.should == @article
|
17
|
+
@user.created_comments.should include comment
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should find comment by class" do
|
21
|
+
comment = Horde::Comment.first
|
22
|
+
comment.commented_article.should == @article
|
23
|
+
@user.commented_articles.should == [@article]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should comment photo" do
|
27
|
+
@user.comment(@photo, :comment => 'nic pic')
|
28
|
+
@user.commented_photos.should == [@photo]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should work for target" do
|
32
|
+
@article.comments.should_not be_empty
|
33
|
+
@article.commenters.should include @user
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should run callback" do
|
37
|
+
article2 = double("Article", :id => 2, :title => 'article 2', :author_id => @user2.id)
|
38
|
+
article2.should_receive(:run_hook)
|
39
|
+
@user.comment(article2)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "favoriting" do
|
4
|
+
before(:each) do
|
5
|
+
@user = User.create(:login => 'john', :first_name => 'John', :last_name => 'Doe')
|
6
|
+
@user2 = User.create(:login => 'jane', :first_name => 'Jane', :last_name => 'Doe')
|
7
|
+
@article = Article.create(:title => 'article 1', :author_id => @user2.id)
|
8
|
+
@photo = Photo.create(:name => 'photo 1', :author_id => @user2.id)
|
9
|
+
@user.favorite(@article)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should work for actor" do
|
13
|
+
fav = Horde::Favorite.first
|
14
|
+
fav.should_not be_nil
|
15
|
+
|
16
|
+
fav.target.should == @article
|
17
|
+
@user.created_favorites.should include fav
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should find favorite by class" do
|
21
|
+
fav = Horde::Favorite.first
|
22
|
+
fav.favorited_article.should == @article
|
23
|
+
@user.favorited_articles.should == [@article]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should favorite photo" do
|
27
|
+
@user.favorite(@photo)
|
28
|
+
@user.favorited_photos.should == [@photo]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should work for target" do
|
32
|
+
@article.favorites.should_not be_empty
|
33
|
+
@article.favoriters.should include @user
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should run callback" do
|
37
|
+
article2 = double("Article", :id => 2, :title => 'article 2', :author_id => @user2.id)
|
38
|
+
article2.should_receive(:run_hook)
|
39
|
+
@user.favorite(article2)
|
40
|
+
end
|
41
|
+
end
|
data/spec/follow_spec.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "following" do
|
4
|
+
before(:each) do
|
5
|
+
@user = User.create(:login => 'john', :first_name => 'John', :last_name => 'Doe')
|
6
|
+
@user2 = User.create(:login => 'jane', :first_name => 'Jane', :last_name => 'Doe')
|
7
|
+
@article = Article.create(:title => 'article 1', :author_id => @user2.id)
|
8
|
+
@photo = Photo.create(:name => 'photo 1', :author_id => @user2.id)
|
9
|
+
@user.follow(@article)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should work for actor" do
|
13
|
+
follow = Horde::Follow.first
|
14
|
+
follow.should_not be_nil
|
15
|
+
|
16
|
+
follow.target.should == @article
|
17
|
+
@user.created_follows.should include follow
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should find follow by class" do
|
21
|
+
follow = Horde::Follow.first
|
22
|
+
follow.followed_article.should == @article
|
23
|
+
@user.followed_articles.should == [@article]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should follow photo" do
|
27
|
+
@user.follow(@photo)
|
28
|
+
@user.followed_photos.should == [@photo]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should work for target" do
|
32
|
+
@article.follows.should_not be_empty
|
33
|
+
@article.followers.should include @user
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should run callback" do
|
37
|
+
article2 = double("Article", :id => 2, :title => 'article 2', :author_id => @user2.id)
|
38
|
+
article2.should_receive(:run_hook)
|
39
|
+
@user.follow(article2)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should follow user" do
|
43
|
+
follow = @user.follow(@user2)
|
44
|
+
|
45
|
+
follow.target.should == @user2
|
46
|
+
follow.followed_user.should == @user2
|
47
|
+
follow.follower.should == @user
|
48
|
+
|
49
|
+
@user.created_follows.should include follow
|
50
|
+
@user.followed_users.should == [@user2]
|
51
|
+
|
52
|
+
@user2.follows.should == [follow]
|
53
|
+
@user2.followers.should == [@user]
|
54
|
+
end
|
55
|
+
end
|
data/spec/rate_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "rating" do
|
4
|
+
before(:each) do
|
5
|
+
@user = User.create(:login => 'john', :first_name => 'John', :last_name => 'Doe')
|
6
|
+
@user2 = User.create(:login => 'jane', :first_name => 'Jane', :last_name => 'Doe')
|
7
|
+
@article = Article.create(:title => 'article 1', :author_id => @user2.id)
|
8
|
+
@photo = Photo.create(:name => 'photo 1', :author_id => @user2.id)
|
9
|
+
@user.rate(@article, :score => 5)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should work for actor" do
|
13
|
+
rate = Horde::Rate.first
|
14
|
+
rate.should_not be_nil
|
15
|
+
|
16
|
+
rate.target.should == @article
|
17
|
+
@user.created_rates.should include rate
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should find rate by class" do
|
21
|
+
rate = Horde::Rate.first
|
22
|
+
rate.rated_article.should == @article
|
23
|
+
@user.rated_articles.should == [@article]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should rate photo" do
|
27
|
+
@user.rate(@photo, :score => 5)
|
28
|
+
@user.rated_photos.should == [@photo]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should work for target" do
|
32
|
+
@article.rates.should_not be_empty
|
33
|
+
@article.raters.should include @user
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should run callback" do
|
37
|
+
article2 = double("Article", :id => 2, :title => 'article 2', :author_id => @user2.id)
|
38
|
+
article2.should_receive(:run_hook)
|
39
|
+
@user.rate(article2)
|
40
|
+
end
|
41
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'debugger'
|
2
|
+
require 'database_cleaner'
|
3
|
+
require 'horde'
|
4
|
+
|
5
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
6
|
+
|
7
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
8
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
9
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
10
|
+
# loaded once.
|
11
|
+
#
|
12
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
config.filter_run :focus
|
17
|
+
|
18
|
+
# Run specs in random order to surface order dependencies. If you find an
|
19
|
+
# order dependency and want to debug it, you can fix the order by providing
|
20
|
+
# the seed, which is printed after each run.
|
21
|
+
# --seed 1234
|
22
|
+
config.order = 'random'
|
23
|
+
|
24
|
+
# --- database cleaner config
|
25
|
+
config.before(:suite) do
|
26
|
+
ActiveRecord::Base.logger = nil
|
27
|
+
DatabaseCleaner.strategy = :truncation #:transaction doesn't clean
|
28
|
+
DatabaseCleaner.clean_with(:truncation)
|
29
|
+
end
|
30
|
+
|
31
|
+
config.before(:each) do
|
32
|
+
DatabaseCleaner.start
|
33
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
34
|
+
end
|
35
|
+
|
36
|
+
config.after(:each) do
|
37
|
+
ActiveRecord::Base.logger = nil
|
38
|
+
DatabaseCleaner.clean
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'actorize' do
|
4
|
+
it "should work on follow" do
|
5
|
+
"follow".actorize.should == "follower"
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should work on favorite" do
|
9
|
+
"favorite".actorize.should == "favoriter"
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should work on rate" do
|
13
|
+
"rate".actorize.should == "rater"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'past_tense' do
|
18
|
+
it "should work on follow" do
|
19
|
+
"follow".past_tense.should == "followed"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should work on favorite" do
|
23
|
+
"favorite".past_tense.should == "favorited"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should work on rate" do
|
27
|
+
"rate".past_tense.should == "rated"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
include Horde::Actor
|
3
|
+
include Horde::Actions::Follow
|
4
|
+
end
|
5
|
+
|
6
|
+
class Article < ActiveRecord::Base
|
7
|
+
include Horde::Actions::All
|
8
|
+
|
9
|
+
after_favorite :email_author
|
10
|
+
|
11
|
+
def email_author(fav)
|
12
|
+
"#{fav.favoriter.login} favorited article #{self.title}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Photo < ActiveRecord::Base
|
17
|
+
include Horde::Actions::All
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: horde-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dorren Chen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mysql2
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sinatra-activerecord
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: hooks
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: debugger
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: database_cleaner
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: A ruby gem providing social networking features for rails app.
|
143
|
+
email:
|
144
|
+
- dorrenchen@gmail.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- .gitignore
|
150
|
+
- .rspec
|
151
|
+
- Gemfile
|
152
|
+
- LICENSE
|
153
|
+
- README.md
|
154
|
+
- Rakefile
|
155
|
+
- db/migrate/20120714150229_horde_setup.rb
|
156
|
+
- db/migrate/20120714184427_test_models.rb
|
157
|
+
- horde.gemspec
|
158
|
+
- lib/horde.rb
|
159
|
+
- lib/horde/actions.rb
|
160
|
+
- lib/horde/actions/base.rb
|
161
|
+
- lib/horde/actions/comment.rb
|
162
|
+
- lib/horde/actions/favorite.rb
|
163
|
+
- lib/horde/actions/follow.rb
|
164
|
+
- lib/horde/actions/rate.rb
|
165
|
+
- lib/horde/actor.rb
|
166
|
+
- lib/horde/base.rb
|
167
|
+
- lib/horde/core_ext/string.rb
|
168
|
+
- lib/horde/setting.rb
|
169
|
+
- lib/horde/version.rb
|
170
|
+
- spec/comment_spec.rb
|
171
|
+
- spec/favorite_spec.rb
|
172
|
+
- spec/follow_spec.rb
|
173
|
+
- spec/rate_spec.rb
|
174
|
+
- spec/setting_spec.rb
|
175
|
+
- spec/spec_helper.rb
|
176
|
+
- spec/string_ext_spec.rb
|
177
|
+
- spec/support/database.yml
|
178
|
+
- spec/support/db_config.rb
|
179
|
+
- spec/support/test_models.rb
|
180
|
+
homepage: ''
|
181
|
+
licenses: []
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ! '>='
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
segments:
|
193
|
+
- 0
|
194
|
+
hash: 4029953517073986462
|
195
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
|
+
none: false
|
197
|
+
requirements:
|
198
|
+
- - ! '>='
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
segments:
|
202
|
+
- 0
|
203
|
+
hash: 4029953517073986462
|
204
|
+
requirements: []
|
205
|
+
rubyforge_project:
|
206
|
+
rubygems_version: 1.8.24
|
207
|
+
signing_key:
|
208
|
+
specification_version: 3
|
209
|
+
summary: A ruby gem providing social networking features for rails app.
|
210
|
+
test_files:
|
211
|
+
- spec/comment_spec.rb
|
212
|
+
- spec/favorite_spec.rb
|
213
|
+
- spec/follow_spec.rb
|
214
|
+
- spec/rate_spec.rb
|
215
|
+
- spec/setting_spec.rb
|
216
|
+
- spec/spec_helper.rb
|
217
|
+
- spec/string_ext_spec.rb
|
218
|
+
- spec/support/database.yml
|
219
|
+
- spec/support/db_config.rb
|
220
|
+
- spec/support/test_models.rb
|