socialization 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +8 -0
- data/Appraisals +4 -4
- data/README.rdoc +68 -8
- data/demo/demo_app/Gemfile +2 -2
- data/demo/demo_app/app/models/comment.rb +5 -0
- data/demo/demo_app/app/models/mention.rb +3 -0
- data/demo/demo_app/app/models/user.rb +1 -0
- data/demo/demo_app/db/migrate/20120221200644_create_mentions.rb +14 -0
- data/demo/demo_app/db/migrate/20120221202703_create_comments.rb +9 -0
- data/demo/demo_app/db/schema.rb +18 -1
- data/demo/demo_app/db/seeds.rb +3 -0
- data/gemfiles/activerecord30.gemfile +6 -0
- data/gemfiles/activerecord30.gemfile.lock +23 -0
- data/gemfiles/activerecord31.gemfile +6 -0
- data/gemfiles/activerecord31.gemfile.lock +25 -0
- data/gemfiles/activerecord32.gemfile +6 -0
- data/gemfiles/activerecord32.gemfile.lock +25 -0
- data/generators/socialization/USAGE +1 -1
- data/generators/socialization/socialization_generator.rb +8 -4
- data/generators/socialization/templates/migration_mentions.rb +14 -0
- data/generators/socialization/templates/model_mention.rb +3 -0
- data/lib/generators/socialization/socialization_generator.rb +8 -4
- data/lib/socialization/follow_store.rb +8 -8
- data/lib/socialization/followable.rb +26 -15
- data/lib/socialization/follower.rb +32 -21
- data/lib/socialization/hello.rb +21 -5
- data/lib/socialization/like_store.rb +8 -8
- data/lib/socialization/likeable.rb +26 -15
- data/lib/socialization/liker.rb +36 -21
- data/lib/socialization/mention_store.rb +14 -0
- data/lib/socialization/mentionable.rb +36 -0
- data/lib/socialization/mentionner.rb +47 -0
- data/lib/socialization/version.rb +1 -1
- data/test/follow_test.rb +36 -6
- data/test/like_test.rb +36 -6
- data/test/mention_test.rb +107 -0
- data/test/test_helper.rb +61 -0
- data/test/world_test.rb +34 -24
- metadata +33 -16
@@ -1,23 +1,34 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_followable?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
1
9
|
module Socialization
|
2
10
|
module Followable
|
3
|
-
|
4
|
-
base.class_eval do
|
5
|
-
# A following is the Follow record of the follower following self.
|
6
|
-
has_many :followings, :as => :followable, :dependent => :destroy, :class_name => 'Follow'
|
11
|
+
extend ActiveSupport::Concern
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
included do
|
14
|
+
# A following is the Follow record of the follower following self.
|
15
|
+
has_many :followings, :as => :followable, :dependent => :destroy, :class_name => 'Follow'
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
def is_followable?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def followed_by?(follower)
|
22
|
+
raise ArgumentError, "#{follower} is not a follower!" unless follower.is_follower?
|
23
|
+
!self.followings.where(:follower_type => follower.class.to_s, :follower_id => follower.id).empty?
|
24
|
+
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
26
|
+
def followers(klass)
|
27
|
+
klass = klass.to_s.singularize.camelize.constantize unless klass.is_a?(Class)
|
28
|
+
klass.joins("INNER JOIN follows ON follows.follower_id = #{klass.to_s.tableize}.id AND follows.follower_type = '#{klass.to_s}'").
|
29
|
+
where("follows.followable_type = '#{self.class.to_s}'").
|
30
|
+
where("follows.followable_id = #{self.id}")
|
20
31
|
end
|
21
32
|
end
|
22
33
|
end
|
23
|
-
end
|
34
|
+
end
|
@@ -1,32 +1,43 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_follower?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
1
9
|
module Socialization
|
2
10
|
module Follower
|
3
|
-
|
4
|
-
base.class_eval do
|
5
|
-
# A follow is the Follow record of self following a followable record.
|
6
|
-
has_many :follows, :as => :follower, :dependent => :destroy, :class_name => 'Follow'
|
11
|
+
extend ActiveSupport::Concern
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
included do
|
14
|
+
# A follow is the Follow record of self following a followable record.
|
15
|
+
has_many :follows, :as => :follower, :dependent => :destroy, :class_name => 'Follow'
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Follow.create! :follower => self, :followable => followable
|
16
|
-
end
|
17
|
+
def is_follower?
|
18
|
+
true
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def follow!(followable)
|
22
|
+
raise ArgumentError, "#{followable} is not followable!" unless followable.is_followable?
|
23
|
+
raise ArgumentError, "#{self} cannot follow itself!" unless self != followable
|
24
|
+
Follow.create!({ :follower => self, :followable => followable }, :without_protection => true)
|
25
|
+
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
def unfollow!(followable)
|
28
|
+
ff = followable.followings.where(:follower_type => self.class.to_s, :follower_id => self.id)
|
29
|
+
unless ff.empty?
|
30
|
+
ff.each { |f| f.destroy }
|
31
|
+
else
|
32
|
+
raise ActiveRecord::RecordNotFound
|
27
33
|
end
|
34
|
+
end
|
28
35
|
|
36
|
+
def follows?(followable)
|
37
|
+
raise ArgumentError, "#{followable} is not followable!" unless followable.is_followable?
|
38
|
+
!self.follows.where(:followable_type => followable.class.to_s, :followable_id => followable.id).empty?
|
29
39
|
end
|
40
|
+
|
30
41
|
end
|
31
42
|
end
|
32
|
-
end
|
43
|
+
end
|
data/lib/socialization/hello.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
%w{followable follower follow_store likeable liker like_store mentionable mentionner mention_store}.each do |f|
|
2
4
|
require "#{File.dirname(__FILE__)}/#{f}"
|
3
5
|
end
|
4
6
|
|
5
7
|
module Socialization
|
6
8
|
module Hello
|
7
|
-
|
8
|
-
klass.send(:extend, ClassMethods)
|
9
|
-
end
|
9
|
+
extend ActiveSupport::Concern
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
+
## Follow
|
12
13
|
def acts_as_follower(opts = nil)
|
13
14
|
include Socialization::Follower
|
14
15
|
end
|
@@ -21,6 +22,7 @@ module Socialization
|
|
21
22
|
include Socialization::FollowStore
|
22
23
|
end
|
23
24
|
|
25
|
+
## Like
|
24
26
|
def acts_as_liker(opts = nil)
|
25
27
|
include Socialization::Liker
|
26
28
|
end
|
@@ -32,6 +34,20 @@ module Socialization
|
|
32
34
|
def acts_as_like_store(opts = nil)
|
33
35
|
include Socialization::LikeStore
|
34
36
|
end
|
37
|
+
|
38
|
+
## Mention
|
39
|
+
def acts_as_mentionner(opts = nil)
|
40
|
+
include Socialization::Mentionner
|
41
|
+
end
|
42
|
+
|
43
|
+
def acts_as_mentionable(opts = nil)
|
44
|
+
include Socialization::Mentionable
|
45
|
+
end
|
46
|
+
|
47
|
+
def acts_as_mention_store(opts = nil)
|
48
|
+
include Socialization::MentionStore
|
49
|
+
end
|
50
|
+
|
35
51
|
end
|
36
52
|
end
|
37
|
-
end
|
53
|
+
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module Socialization
|
2
2
|
module LikeStore
|
3
|
-
|
4
|
-
base.class_eval do
|
5
|
-
belongs_to :liker, :polymorphic => true
|
6
|
-
belongs_to :likeable, :polymorphic => true
|
3
|
+
extend ActiveSupport::Concern
|
7
4
|
|
8
|
-
|
5
|
+
included do
|
6
|
+
belongs_to :liker, :polymorphic => true
|
7
|
+
belongs_to :likeable, :polymorphic => true
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
validates_uniqueness_of :likeable_type, :scope => [:likeable_id, :liker_type, :liker_id], :message => 'You cannot like the same thing twice.'
|
10
|
+
|
11
|
+
def self.human_attribute_name(*args); ''; end
|
12
12
|
end
|
13
13
|
end
|
14
|
-
end
|
14
|
+
end
|
@@ -1,23 +1,34 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_likeable?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
1
9
|
module Socialization
|
2
10
|
module Likeable
|
3
|
-
|
4
|
-
base.class_eval do
|
5
|
-
# A liking is the Like record of the liker liking self.
|
6
|
-
has_many :likings, :as => :likeable, :dependent => :destroy, :class_name => 'Like'
|
11
|
+
extend ActiveSupport::Concern
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
included do
|
14
|
+
# A liking is the Like record of the liker liking self.
|
15
|
+
has_many :likings, :as => :likeable, :dependent => :destroy, :class_name => 'Like'
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
def is_likeable?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def liked_by?(liker)
|
22
|
+
raise ArgumentError, "#{liker} is not a liker!" unless liker.is_liker?
|
23
|
+
!self.likings.where(:liker_type => liker.class.to_s, :liker_id => liker.id).empty?
|
24
|
+
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
26
|
+
def likers(klass)
|
27
|
+
klass = klass.to_s.singularize.camelize.constantize unless klass.is_a?(Class)
|
28
|
+
klass.joins("INNER JOIN likes ON likes.liker_id = #{klass.to_s.tableize}.id AND likes.liker_type = '#{klass.to_s}'").
|
29
|
+
where("likes.likeable_type = '#{self.class.to_s}'").
|
30
|
+
where("likes.likeable_id = #{self.id}")
|
20
31
|
end
|
21
32
|
end
|
22
33
|
end
|
23
|
-
end
|
34
|
+
end
|
data/lib/socialization/liker.rb
CHANGED
@@ -1,32 +1,47 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_liker?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
1
9
|
module Socialization
|
2
10
|
module Liker
|
3
|
-
|
4
|
-
base.class_eval do
|
5
|
-
# A like is the Like record of self liking a likeable record.
|
6
|
-
has_many :likes, :as => :liker, :dependent => :destroy, :class_name => 'Like'
|
11
|
+
extend ActiveSupport::Concern
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
included do
|
14
|
+
# A like is the Like record of self liking a likeable record.
|
15
|
+
has_many :likes, :as => :liker, :dependent => :destroy, :class_name => 'Like'
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Like.create! :liker => self, :likeable => likeable
|
16
|
-
end
|
17
|
+
def is_liker?
|
18
|
+
true
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def like!(likeable)
|
22
|
+
ensure_likeable!(likeable)
|
23
|
+
raise ArgumentError, "#{self} cannot like itself!" unless self != likeable
|
24
|
+
Like.create!({ :liker => self, :likeable => likeable }, :without_protection => true)
|
25
|
+
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
def unlike!(likeable)
|
28
|
+
ll = likeable.likings.where(:liker_type => self.class.to_s, :liker_id => self.id)
|
29
|
+
unless ll.empty?
|
30
|
+
ll.each { |l| l.destroy }
|
31
|
+
else
|
32
|
+
raise ActiveRecord::RecordNotFound
|
27
33
|
end
|
34
|
+
end
|
28
35
|
|
36
|
+
def likes?(likeable)
|
37
|
+
ensure_likeable!(likeable)
|
38
|
+
!self.likes.where(:likeable_type => likeable.class.to_s, :likeable_id => likeable.id).empty?
|
29
39
|
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def ensure_likeable!(likeable)
|
43
|
+
raise ArgumentError, "#{likeable} is not likeable!" unless likeable.is_likeable?
|
44
|
+
end
|
30
45
|
end
|
31
46
|
end
|
32
|
-
end
|
47
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Socialization
|
2
|
+
module MentionStore
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
belongs_to :mentionner, :polymorphic => true
|
7
|
+
belongs_to :mentionable, :polymorphic => true
|
8
|
+
|
9
|
+
validates_uniqueness_of :mentionable_type, :scope => [:mentionable_id, :mentionner_type, :mentionner_id], :message => 'You cannot mention the same thing twice in a given object.'
|
10
|
+
|
11
|
+
def self.human_attribute_name(*args); ''; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_mentionable?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Socialization
|
10
|
+
module Mentionable
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do
|
14
|
+
# A mentioning is the Mention record of describing the mention relationship between
|
15
|
+
# the mentionner and the mentionable (self).
|
16
|
+
has_many :mentionings, :as => :mentionable, :dependent => :destroy, :class_name => 'Mention'
|
17
|
+
|
18
|
+
def is_mentionable?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def mentioned_by?(mentionner)
|
23
|
+
raise ArgumentError, "#{mentionner} is not a mentionner!" unless mentionner.is_mentionner?
|
24
|
+
!self.mentionings.where(:mentionner_type => mentionner.class.to_s, :mentionner_id => mentionner.id).empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def mentionners(klass)
|
28
|
+
klass = klass.to_s.singularize.camelize.constantize unless klass.is_a?(Class)
|
29
|
+
klass.joins("INNER JOIN mentions ON mentions.mentionner_id = #{klass.to_s.tableize}.id AND mentions.mentionner_type = '#{klass.to_s}'").
|
30
|
+
where("mentions.mentionable_type = '#{self.class.to_s}'").
|
31
|
+
where("mentions.mentionable_id = #{self.id}")
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
def is_mentionner?
|
4
|
+
false
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Socialization
|
10
|
+
module Mentionner
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do
|
14
|
+
# A mention is the Mention record (self) mentionning a mentionable record.
|
15
|
+
has_many :mentions, :as => :mentionner, :dependent => :destroy, :class_name => 'Mention'
|
16
|
+
|
17
|
+
def is_mentionner?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def mention!(mentionable)
|
22
|
+
ensure_mentionable!(mentionable)
|
23
|
+
Mention.create!({ :mentionner => self, :mentionable => mentionable }, :without_protection => true)
|
24
|
+
end
|
25
|
+
|
26
|
+
def unmention!(mentionable)
|
27
|
+
mm = mentionable.mentionings.where(:mentionner_type => self.class.to_s, :mentionner_id => self.id)
|
28
|
+
unless mm.empty?
|
29
|
+
mm.each { |m| m.destroy }
|
30
|
+
else
|
31
|
+
raise ActiveRecord::RecordNotFound
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def mentions?(mentionable)
|
36
|
+
ensure_mentionable!(mentionable)
|
37
|
+
!self.mentions.where(:mentionable_type => mentionable.class.to_s, :mentionable_id => mentionable.id).empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def ensure_mentionable!(mentionable)
|
42
|
+
raise ArgumentError, "#{mentionable} is not mentionable!" unless mentionable.is_mentionable?
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/test/follow_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__)+'/test_helper'
|
1
|
+
require File.expand_path(File.dirname(__FILE__))+'/test_helper'
|
2
2
|
|
3
3
|
class FollowTest < Test::Unit::TestCase
|
4
4
|
context "a Follower" do
|
@@ -6,8 +6,7 @@ class FollowTest < Test::Unit::TestCase
|
|
6
6
|
seed
|
7
7
|
end
|
8
8
|
|
9
|
-
should "
|
10
|
-
assert_equal true, @follower1.respond_to?(:is_follower?)
|
9
|
+
should "be follower" do
|
11
10
|
assert_equal true, @follower1.is_follower?
|
12
11
|
end
|
13
12
|
|
@@ -22,6 +21,20 @@ class FollowTest < Test::Unit::TestCase
|
|
22
21
|
assert @follower1.unfollow!(@followable1)
|
23
22
|
assert_equal false, @follower1.follows?(@followable1)
|
24
23
|
end
|
24
|
+
|
25
|
+
should "not be able to follow the same thing twice" do
|
26
|
+
assert @follower1.follow!(@followable1)
|
27
|
+
|
28
|
+
assert_raise ActiveRecord::RecordInvalid do
|
29
|
+
@follower1.follow!(@followable1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
should "not be able to unfollow something that is not followed" do
|
34
|
+
assert_raise ActiveRecord::RecordNotFound do
|
35
|
+
@follower1.unfollow!(@followable1)
|
36
|
+
end
|
37
|
+
end
|
25
38
|
end
|
26
39
|
|
27
40
|
context "a Followable" do
|
@@ -29,8 +42,7 @@ class FollowTest < Test::Unit::TestCase
|
|
29
42
|
seed
|
30
43
|
end
|
31
44
|
|
32
|
-
should "
|
33
|
-
assert_equal true, @followable1.respond_to?(:is_followable?)
|
45
|
+
should "be followable" do
|
34
46
|
assert_equal true, @followable1.is_followable?
|
35
47
|
end
|
36
48
|
|
@@ -42,7 +54,11 @@ class FollowTest < Test::Unit::TestCase
|
|
42
54
|
|
43
55
|
should "expose a list of its followers" do
|
44
56
|
Follow.create :follower => @follower1, :followable => @followable1
|
45
|
-
|
57
|
+
assert @followable1.followers(ImAFollower).is_a?(ActiveRecord::Relation)
|
58
|
+
assert_equal [@follower1], @followable1.followers(ImAFollower).all
|
59
|
+
|
60
|
+
assert_equal @followable1.followers(ImAFollower), @followable1.followers(:im_a_followers)
|
61
|
+
assert_equal @followable1.followers(ImAFollower), @followable1.followers("im_a_follower")
|
46
62
|
end
|
47
63
|
|
48
64
|
should "expose followings" do
|
@@ -77,6 +93,20 @@ class FollowTest < Test::Unit::TestCase
|
|
77
93
|
end
|
78
94
|
end
|
79
95
|
|
96
|
+
context "Virgin ActiveRecord::Base objects" do
|
97
|
+
setup do
|
98
|
+
@foo = Vanilla.new
|
99
|
+
end
|
100
|
+
|
101
|
+
should "not be follower" do
|
102
|
+
assert_equal false, @foo.is_follower?
|
103
|
+
end
|
104
|
+
|
105
|
+
should "not be followable" do
|
106
|
+
assert_equal false, @foo.is_followable?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
80
110
|
def seed
|
81
111
|
@follower1 = ImAFollower.create
|
82
112
|
@follower2 = ImAFollower.create
|