socialization 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.travis.yml +8 -0
  2. data/Appraisals +4 -4
  3. data/README.rdoc +68 -8
  4. data/demo/demo_app/Gemfile +2 -2
  5. data/demo/demo_app/app/models/comment.rb +5 -0
  6. data/demo/demo_app/app/models/mention.rb +3 -0
  7. data/demo/demo_app/app/models/user.rb +1 -0
  8. data/demo/demo_app/db/migrate/20120221200644_create_mentions.rb +14 -0
  9. data/demo/demo_app/db/migrate/20120221202703_create_comments.rb +9 -0
  10. data/demo/demo_app/db/schema.rb +18 -1
  11. data/demo/demo_app/db/seeds.rb +3 -0
  12. data/gemfiles/activerecord30.gemfile +6 -0
  13. data/gemfiles/activerecord30.gemfile.lock +23 -0
  14. data/gemfiles/activerecord31.gemfile +6 -0
  15. data/gemfiles/activerecord31.gemfile.lock +25 -0
  16. data/gemfiles/activerecord32.gemfile +6 -0
  17. data/gemfiles/activerecord32.gemfile.lock +25 -0
  18. data/generators/socialization/USAGE +1 -1
  19. data/generators/socialization/socialization_generator.rb +8 -4
  20. data/generators/socialization/templates/migration_mentions.rb +14 -0
  21. data/generators/socialization/templates/model_mention.rb +3 -0
  22. data/lib/generators/socialization/socialization_generator.rb +8 -4
  23. data/lib/socialization/follow_store.rb +8 -8
  24. data/lib/socialization/followable.rb +26 -15
  25. data/lib/socialization/follower.rb +32 -21
  26. data/lib/socialization/hello.rb +21 -5
  27. data/lib/socialization/like_store.rb +8 -8
  28. data/lib/socialization/likeable.rb +26 -15
  29. data/lib/socialization/liker.rb +36 -21
  30. data/lib/socialization/mention_store.rb +14 -0
  31. data/lib/socialization/mentionable.rb +36 -0
  32. data/lib/socialization/mentionner.rb +47 -0
  33. data/lib/socialization/version.rb +1 -1
  34. data/test/follow_test.rb +36 -6
  35. data/test/like_test.rb +36 -6
  36. data/test/mention_test.rb +107 -0
  37. data/test/test_helper.rb +61 -0
  38. data/test/world_test.rb +34 -24
  39. 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
- def self.included(base)
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
- def is_followable?
9
- true
10
- end
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
- def followed_by?(follower)
13
- raise ArgumentError, "#{follower} is not a follower!" unless follower.respond_to?(:is_follower?) && follower.is_follower?
14
- !self.followings.where(:follower_type => follower.class.to_s, :follower_id => follower.id).empty?
15
- end
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
- def followers
18
- self.followings.map { |f| f.follower }
19
- end
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
- def self.included(base)
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
- def is_follower?
9
- true
10
- end
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
- def follow!(followable)
13
- raise ArgumentError, "#{followable} is not followable!" unless followable.respond_to?(:is_followable?)
14
- raise ArgumentError, "#{self} cannot follow itself!" unless self != followable
15
- Follow.create! :follower => self, :followable => followable
16
- end
17
+ def is_follower?
18
+ true
19
+ end
17
20
 
18
- def unfollow!(followable)
19
- followable.followings.where(:follower_type => self.class.to_s, :follower_id => self.id).each do |f|
20
- f.destroy
21
- end
22
- end
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
- def follows?(followable)
25
- raise ArgumentError, "#{followable} is not followable!" unless followable.respond_to?(:is_followable?) && followable.is_followable?
26
- !self.follows.where(:followable_type => followable.class.to_s, :followable_id => followable.id).empty?
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
@@ -1,14 +1,15 @@
1
- %w{followable follower follow_store likeable liker like_store}.each do |f|
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
- def self.included(klass)
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
- def self.included(base)
4
- base.class_eval do
5
- belongs_to :liker, :polymorphic => true
6
- belongs_to :likeable, :polymorphic => true
3
+ extend ActiveSupport::Concern
7
4
 
8
- validates_uniqueness_of :likeable_type, :scope => [:likeable_id, :liker_type, :liker_id], :message => 'You cannot like the same thing twice.'
5
+ included do
6
+ belongs_to :liker, :polymorphic => true
7
+ belongs_to :likeable, :polymorphic => true
9
8
 
10
- def self.human_attribute_name(*args); ''; end
11
- end
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
- def self.included(base)
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
- def is_likeable?
9
- true
10
- end
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
- def liked_by?(liker)
13
- raise ArgumentError, "#{liker} is not a liker!" unless liker.respond_to?(:is_liker?) && liker.is_liker?
14
- !self.likings.where(:liker_type => liker.class.to_s, :liker_id => liker.id).empty?
15
- end
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
- def likers
18
- self.likings.map { |l| l.liker }
19
- end
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
@@ -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
- def self.included(base)
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
- def is_liker?
9
- true
10
- end
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
- def like!(likeable)
13
- raise ArgumentError, "#{likeable} is not likeable!" unless likeable.respond_to?(:is_likeable?)
14
- raise ArgumentError, "#{self} cannot like itself!" unless self != likeable
15
- Like.create! :liker => self, :likeable => likeable
16
- end
17
+ def is_liker?
18
+ true
19
+ end
17
20
 
18
- def unlike!(likeable)
19
- likeable.likings.where(:liker_type => self.class.to_s, :liker_id => self.id).each do |l|
20
- l.destroy
21
- end
22
- end
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
- def likes?(likeable)
25
- raise ArgumentError, "#{likeable} is not likeable!" unless likeable.respond_to?(:is_likeable?) && likeable.is_likeable?
26
- !self.likes.where(:likeable_type => likeable.class.to_s, :likeable_id => likeable.id).empty?
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
@@ -1,3 +1,3 @@
1
1
  module Socialization
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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 "respond to is_follower?" do
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 "respond to is_followable?" do
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
- assert_equal [@follower1], @followable1.followers
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