socialization 0.2.2 → 0.3.0

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.
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