Commit 899cf25d595bcca2b9d2594ba4351a40ef407e38
1 parent
5a4f3582
Exists in
master
and in
3 other branches
pulled in arthurgeeks changes to remove trailing white space
Showing
20 changed files
with
511 additions
and
535 deletions
Show diff stats
MIT-LICENSE
@@ -19,7 +19,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | @@ -19,7 +19,7 @@ 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 | 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. | 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | 21 | ||
22 | -The major design pattern of this plugin was abstracted from Peter Jackson's VoteFu, which is subject to the same license. | 22 | +The major design pattern of this plugin was abstracted from Peter Jackson's VoteFu, which is subject to the same license. |
23 | Here is the original copyright notice for VoteFu: | 23 | Here is the original copyright notice for VoteFu: |
24 | 24 | ||
25 | Copyright (c) 2008 Peter Jackson (peteonrails.com) | 25 | Copyright (c) 2008 Peter Jackson (peteonrails.com) |
README.rdoc
@@ -28,7 +28,7 @@ Make your model(s) that you want to allow to be followed acts_as_followable, jus | @@ -28,7 +28,7 @@ Make your model(s) that you want to allow to be followed acts_as_followable, jus | ||
28 | acts_as_followable | 28 | acts_as_followable |
29 | ... | 29 | ... |
30 | end | 30 | end |
31 | - | 31 | + |
32 | class Book < ActiveRecord::Base | 32 | class Book < ActiveRecord::Base |
33 | ... | 33 | ... |
34 | acts_as_followable | 34 | acts_as_followable |
@@ -64,7 +64,7 @@ To get follow records that have not been blocked use the following | @@ -64,7 +64,7 @@ To get follow records that have not been blocked use the following | ||
64 | user.all_follows # returns an array of Follow records | 64 | user.all_follows # returns an array of Follow records |
65 | 65 | ||
66 | To get all of the records that an object is following that have not been blocked use the following | 66 | To get all of the records that an object is following that have not been blocked use the following |
67 | - user.all_following | 67 | + user.all_following |
68 | # Returns an array of every followed object for the user, this can be a collection of different object types, eg: User, Book | 68 | # Returns an array of every followed object for the user, this can be a collection of different object types, eg: User, Book |
69 | 69 | ||
70 | To get all Follow records by a certain type use the following | 70 | To get all Follow records by a certain type use the following |
@@ -82,18 +82,18 @@ There is also a method_missing to accomplish the exact same thing a following_by | @@ -82,18 +82,18 @@ There is also a method_missing to accomplish the exact same thing a following_by | ||
82 | 82 | ||
83 | To get all the followers of a model that acts_as_followable | 83 | To get all the followers of a model that acts_as_followable |
84 | book.followers # Returns an array of all the followers for that book, a collection of different object types (eg. type User or type Book) | 84 | book.followers # Returns an array of all the followers for that book, a collection of different object types (eg. type User or type Book) |
85 | - | 85 | + |
86 | To get just the number of follows use | 86 | To get just the number of follows use |
87 | book.followers_count | 87 | book.followers_count |
88 | - | 88 | + |
89 | To see is a model that acts_as_followable is followed by a model that acts_as_follower use the following | 89 | To see is a model that acts_as_followable is followed by a model that acts_as_follower use the following |
90 | book.followed_by?(user) | 90 | book.followed_by?(user) |
91 | - | 91 | + |
92 | # Returns true if the current instance is followed by the passed record | 92 | # Returns true if the current instance is followed by the passed record |
93 | # Returns false if the current instance is blocked by the passed record or no follow is found | 93 | # Returns false if the current instance is blocked by the passed record or no follow is found |
94 | 94 | ||
95 | To block a follower call the following | 95 | To block a follower call the following |
96 | - book.block(user) | 96 | + book.block(user) |
97 | # Blocks the user from appearing in the followers list, and blocks the book from appearing in the user.all_follows or user.all_following lists | 97 | # Blocks the user from appearing in the followers list, and blocks the book from appearing in the user.all_follows or user.all_following lists |
98 | 98 | ||
99 | To unblock is just as simple | 99 | To unblock is just as simple |
@@ -105,7 +105,7 @@ To get all blocked records | @@ -105,7 +105,7 @@ To get all blocked records | ||
105 | If you only need the number of blocks use the count method provided | 105 | If you only need the number of blocks use the count method provided |
106 | book.blocked_followers_count | 106 | book.blocked_followers_count |
107 | 107 | ||
108 | -Unblocking deletes all records of that follow, instead of just the :blocked attribute => false the follow is deleted. So, a user would need to try and follow the book again. | 108 | +Unblocking deletes all records of that follow, instead of just the :blocked attribute => false the follow is deleted. So, a user would need to try and follow the book again. |
109 | I would like to hear thoughts on this, I may change this to make the follow as :blocked => false instead of deleting the record. | 109 | I would like to hear thoughts on this, I may change this to make the follow as :blocked => false instead of deleting the record. |
110 | 110 | ||
111 | --- | 111 | --- |
@@ -114,12 +114,12 @@ I would like to hear thoughts on this, I may change this to make the follow as : | @@ -114,12 +114,12 @@ I would like to hear thoughts on this, I may change this to make the follow as : | ||
114 | 114 | ||
115 | The Follow model has a set of named_scope's. In case you want to interface directly with the Follow model you can use them. | 115 | The Follow model has a set of named_scope's. In case you want to interface directly with the Follow model you can use them. |
116 | Follow.unblocked # returns all "unblocked" follow records | 116 | Follow.unblocked # returns all "unblocked" follow records |
117 | - | 117 | + |
118 | Follow.blocked # returns all "blocked" follow records | 118 | Follow.blocked # returns all "blocked" follow records |
119 | - | 119 | + |
120 | Follow.descending # returns all records in a descending order based on created_at datetime | 120 | Follow.descending # returns all records in a descending order based on created_at datetime |
121 | 121 | ||
122 | -This method pulls all records created after a certain date. The default is 2 weeks but it takes an optional parameter. | 122 | +This method pulls all records created after a certain date. The default is 2 weeks but it takes an optional parameter. |
123 | Follow.recent | 123 | Follow.recent |
124 | Follow.recent(4.weeks.ago) | 124 | Follow.recent(4.weeks.ago) |
125 | 125 | ||
@@ -145,7 +145,7 @@ If you have updates or patches or want to contribute I would love to see what yo | @@ -145,7 +145,7 @@ If you have updates or patches or want to contribute I would love to see what yo | ||
145 | 145 | ||
146 | 146 | ||
147 | == Note on Patches/Pull Requests | 147 | == Note on Patches/Pull Requests |
148 | - | 148 | + |
149 | * Fork the project. | 149 | * Fork the project. |
150 | * Make your feature addition or bug fix. | 150 | * Make your feature addition or bug fix. |
151 | * Add tests for it. This is important so I don't break it in a future version unintentionally (acts_as_follower uses Shoulda and Factory Girl) | 151 | * Add tests for it. This is important so I don't break it in a future version unintentionally (acts_as_follower uses Shoulda and Factory Girl) |
generators/acts_as_follower/USAGE
1 | Description: | 1 | Description: |
2 | run ./script/generate acts_as_follower | 2 | run ./script/generate acts_as_follower |
3 | - | 3 | + |
4 | no need to specify a name after acts_as_follower as you can not change the model name from Follow | 4 | no need to specify a name after acts_as_follower as you can not change the model name from Follow |
5 | the acts_as_follower_migration file will be created in db/migrate | 5 | the acts_as_follower_migration file will be created in db/migrate |
6 | \ No newline at end of file | 6 | \ No newline at end of file |
generators/acts_as_follower/acts_as_follower_generator.rb
@@ -3,11 +3,11 @@ class ActsAsFollowerGenerator < Rails::Generator::Base | @@ -3,11 +3,11 @@ class ActsAsFollowerGenerator < Rails::Generator::Base | ||
3 | record do |m| | 3 | record do |m| |
4 | m.directory "app/models" | 4 | m.directory "app/models" |
5 | m.template "model.rb", "app/models/follow.rb" | 5 | m.template "model.rb", "app/models/follow.rb" |
6 | - | 6 | + |
7 | m.migration_template 'migration.rb', 'db/migrate' | 7 | m.migration_template 'migration.rb', 'db/migrate' |
8 | end | 8 | end |
9 | end | 9 | end |
10 | - | 10 | + |
11 | def file_name | 11 | def file_name |
12 | "acts_as_follower_migration" | 12 | "acts_as_follower_migration" |
13 | end | 13 | end |
generators/acts_as_follower/templates/migration.rb
@@ -10,7 +10,7 @@ class ActsAsFollowerMigration < ActiveRecord::Migration | @@ -10,7 +10,7 @@ class ActsAsFollowerMigration < ActiveRecord::Migration | ||
10 | add_index :follows, ["follower_id", "follower_type"], :name => "fk_follows" | 10 | add_index :follows, ["follower_id", "follower_type"], :name => "fk_follows" |
11 | add_index :follows, ["followable_id", "followable_type"], :name => "fk_followables" | 11 | add_index :follows, ["followable_id", "followable_type"], :name => "fk_followables" |
12 | end | 12 | end |
13 | - | 13 | + |
14 | def self.down | 14 | def self.down |
15 | drop_table :follows | 15 | drop_table :follows |
16 | end | 16 | end |
generators/acts_as_follower/templates/model.rb
1 | class Follow < ActiveRecord::Base | 1 | class Follow < ActiveRecord::Base |
2 | - | 2 | + |
3 | named_scope :for_follower, lambda { |*args| {:conditions => ["follower_id = ? AND follower_type = ?", args.first.id, args.first.type.name]} } | 3 | named_scope :for_follower, lambda { |*args| {:conditions => ["follower_id = ? AND follower_type = ?", args.first.id, args.first.type.name]} } |
4 | named_scope :for_followable, lambda { |*args| {:conditions => ["followable_id = ? AND followable_type = ?", args.first.id, args.first.type.name]} } | 4 | named_scope :for_followable, lambda { |*args| {:conditions => ["followable_id = ? AND followable_type = ?", args.first.id, args.first.type.name]} } |
5 | named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || 2.weeks.ago).to_s(:db)]} } | 5 | named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || 2.weeks.ago).to_s(:db)]} } |
6 | named_scope :descending, :order => "created_at DESC" | 6 | named_scope :descending, :order => "created_at DESC" |
7 | named_scope :unblocked, :conditions => {:blocked => false} | 7 | named_scope :unblocked, :conditions => {:blocked => false} |
8 | named_scope :blocked, :conditions => {:blocked => true} | 8 | named_scope :blocked, :conditions => {:blocked => true} |
9 | - | 9 | + |
10 | # NOTE: Follows belong to the "followable" interface, and also to followers | 10 | # NOTE: Follows belong to the "followable" interface, and also to followers |
11 | belongs_to :followable, :polymorphic => true | 11 | belongs_to :followable, :polymorphic => true |
12 | belongs_to :follower, :polymorphic => true | 12 | belongs_to :follower, :polymorphic => true |
13 | - | 13 | + |
14 | def block! | 14 | def block! |
15 | self.update_attribute(:blocked, true) | 15 | self.update_attribute(:blocked, true) |
16 | end | 16 | end |
17 | - | 17 | + |
18 | end | 18 | end |
19 | 19 |
lib/acts_as_followable.rb
1 | -require File.dirname(__FILE__) + '/follower_lib' | ||
2 | - | ||
3 | -module ActiveRecord #:nodoc: | ||
4 | - module Acts #:nodoc: | ||
5 | - module Followable | ||
6 | - | ||
7 | - def self.included(base) | ||
8 | - base.extend ClassMethods | ||
9 | - base.class_eval do | ||
10 | - include FollowerLib | ||
11 | - end | ||
12 | - end | ||
13 | - | ||
14 | - module ClassMethods | ||
15 | - def acts_as_followable | ||
16 | - has_many :followings, :as => :followable, :dependent => :destroy, :class_name => 'Follow' | ||
17 | - include ActiveRecord::Acts::Followable::InstanceMethods | ||
18 | - end | ||
19 | - end | ||
20 | - | ||
21 | - | ||
22 | - module InstanceMethods | ||
23 | - | ||
24 | - # Returns the number of followers a record has. | ||
25 | - def followers_count | ||
26 | - self.followings.unblocked.count | 1 | +require File.dirname(__FILE__) + '/follower_lib' |
2 | + | ||
3 | +module ActiveRecord #:nodoc: | ||
4 | + module Acts #:nodoc: | ||
5 | + module Followable | ||
6 | + | ||
7 | + def self.included(base) | ||
8 | + base.extend ClassMethods | ||
9 | + base.class_eval do | ||
10 | + include FollowerLib | ||
27 | end | 11 | end |
28 | - | 12 | + end |
13 | + | ||
14 | + module ClassMethods | ||
15 | + def acts_as_followable | ||
16 | + has_many :followings, :as => :followable, :dependent => :destroy, :class_name => 'Follow' | ||
17 | + include ActiveRecord::Acts::Followable::InstanceMethods | ||
18 | + end | ||
19 | + end | ||
20 | + | ||
21 | + | ||
22 | + module InstanceMethods | ||
23 | + | ||
24 | + # Returns the number of followers a record has. | ||
25 | + def followers_count | ||
26 | + self.followings.unblocked.count | ||
27 | + end | ||
28 | + | ||
29 | def blocked_followers_count | 29 | def blocked_followers_count |
30 | self.followings.blocked.count | 30 | self.followings.blocked.count |
31 | - end | ||
32 | - | ||
33 | - # Returns the following records. | ||
34 | - def followers | ||
35 | - self.followings.unblocked.all(:include => [:follower]).collect{|f| f.follower} | ||
36 | end | 31 | end |
37 | - | 32 | + |
33 | + # Returns the following records. | ||
34 | + def followers | ||
35 | + self.followings.unblocked.all(:include => [:follower]).collect{|f| f.follower} | ||
36 | + end | ||
37 | + | ||
38 | def blocks | 38 | def blocks |
39 | self.followings.blocked.all(:include => [:follower]).collect{|f| f.follower} | 39 | self.followings.blocked.all(:include => [:follower]).collect{|f| f.follower} |
40 | - end | ||
41 | - | 40 | + end |
41 | + | ||
42 | # Returns true if the current instance is followed by the passed record | 42 | # Returns true if the current instance is followed by the passed record |
43 | - # Returns false if the current instance is blocked by the passed record or no follow is found | ||
44 | - def followed_by?(follower) | 43 | + # Returns false if the current instance is blocked by the passed record or no follow is found |
44 | + def followed_by?(follower) | ||
45 | f = get_follow_for(follower) | 45 | f = get_follow_for(follower) |
46 | - (f && !f.blocked?) ? true : false | 46 | + (f && !f.blocked?) ? true : false |
47 | end | 47 | end |
48 | - | 48 | + |
49 | def block(follower) | 49 | def block(follower) |
50 | get_follow_for(follower) ? block_existing_follow(follower) : block_future_follow(follower) | 50 | get_follow_for(follower) ? block_existing_follow(follower) : block_future_follow(follower) |
51 | end | 51 | end |
52 | - | 52 | + |
53 | def unblock(follower) | 53 | def unblock(follower) |
54 | get_follow_for(follower).try(:delete) | 54 | get_follow_for(follower).try(:delete) |
55 | end | 55 | end |
56 | - | 56 | + |
57 | private | 57 | private |
58 | - | 58 | + |
59 | def get_follow_for(follower) | 59 | def get_follow_for(follower) |
60 | Follow.find(:first, :conditions => ["followable_id = ? AND followable_type = ? AND follower_id = ? AND follower_type = ?", self.id, parent_class_name(self), follower.id, parent_class_name(follower)]) | 60 | Follow.find(:first, :conditions => ["followable_id = ? AND followable_type = ? AND follower_id = ? AND follower_type = ?", self.id, parent_class_name(self), follower.id, parent_class_name(follower)]) |
61 | - end | ||
62 | - | 61 | + end |
62 | + | ||
63 | def block_future_follow(follower) | 63 | def block_future_follow(follower) |
64 | follows.create(:followable => self, :follower => follower, :blocked => true) | 64 | follows.create(:followable => self, :follower => follower, :blocked => true) |
65 | end | 65 | end |
66 | - | 66 | + |
67 | def block_existing_follow(follower) | 67 | def block_existing_follow(follower) |
68 | get_follow_for(follower).block! | 68 | get_follow_for(follower).block! |
69 | end | 69 | end |
70 | - | ||
71 | - end | ||
72 | - | ||
73 | - end | ||
74 | - end | 70 | + |
71 | + end | ||
72 | + | ||
73 | + end | ||
74 | + end | ||
75 | end | 75 | end |
lib/acts_as_follower.rb
1 | -require File.dirname(__FILE__) + '/follower_lib' | ||
2 | - | ||
3 | -module ActiveRecord #:nodoc: | ||
4 | - module Acts #:nodoc: | ||
5 | - module Follower | ||
6 | - | ||
7 | - def self.included(base) | ||
8 | - base.extend ClassMethods | ||
9 | - base.class_eval do | ||
10 | - include FollowerLib | ||
11 | - end | ||
12 | - end | ||
13 | - | ||
14 | - module ClassMethods | ||
15 | - def acts_as_follower | ||
16 | - has_many :follows, :as => :follower, :dependent => :destroy | ||
17 | - include ActiveRecord::Acts::Follower::InstanceMethods | ||
18 | - end | ||
19 | - end | ||
20 | - | ||
21 | - module InstanceMethods | ||
22 | - | ||
23 | - # Returns true if this instance is following the object passed as an argument. | ||
24 | - def following?(followable) | ||
25 | - 0 < Follow.unblocked.count(:all, :conditions => [ | ||
26 | - "follower_id = ? AND follower_type = ? AND followable_id = ? AND followable_type = ?", | ||
27 | - self.id, parent_class_name(self), followable.id, parent_class_name(followable) | ||
28 | - ]) | ||
29 | - end | ||
30 | - | ||
31 | - # Returns the number of objects this instance is following. | ||
32 | - def follow_count | ||
33 | - Follow.unblocked.count(:all, :conditions => ["follower_id = ? AND follower_type = ?", self.id, parent_class_name(self)]) | ||
34 | - end | ||
35 | - | ||
36 | - # Creates a new follow record for this instance to follow the passed object. | ||
37 | - # Does not allow duplicate records to be created. | ||
38 | - def follow(followable) | ||
39 | - follow = get_follow(followable) | ||
40 | - unless follow | ||
41 | - Follow.create(:followable => followable, :follower => self) | ||
42 | - end | ||
43 | - end | ||
44 | - | ||
45 | - # Deletes the follow record if it exists. | ||
46 | - def stop_following(followable) | ||
47 | - follow = get_follow(followable) | ||
48 | - if follow | ||
49 | - follow.destroy | ||
50 | - end | ||
51 | - end | ||
52 | - | ||
53 | - # Returns the follow records related to this instance by type. | ||
54 | - def follows_by_type(followable_type) | ||
55 | - Follow.unblocked.find(:all, :include => [:followable], :conditions => ["follower_id = ? AND follower_type = ? AND followable_type = ?", self.id, parent_class_name(self), followable_type]) | ||
56 | - end | ||
57 | - | ||
58 | - # Returns the follow records related to this instance with the followable included. | ||
59 | - def all_follows | ||
60 | - self.follows.unblocked.all(:include => :followable) | ||
61 | - end | ||
62 | - | ||
63 | - # Returns the actual records which this instance is following. | ||
64 | - def all_following | ||
65 | - all_follows.collect{ |f| f.followable } | ||
66 | - end | ||
67 | - | ||
68 | - # Returns the actual records of a particular type which this record is following. | ||
69 | - def following_by_type(followable_type) | ||
70 | - follows_by_type(followable_type).collect{ |f| f.followable } | ||
71 | - end | ||
72 | - | ||
73 | - def following_by_type_count(followable_type) | ||
74 | - Follow.unblocked.count(:all, :conditions => ["follower_id = ? AND follower_type = ? AND followable_type = ?", self.id, parent_class_name(self), followable_type]) | ||
75 | - end | ||
76 | - | ||
77 | - # Allows magic names on following_by_type | ||
78 | - # e.g. following_users == following_by_type('User') | ||
79 | - def method_missing(m, *args) | ||
80 | - if m.to_s[/following_(.+)_count/] | ||
81 | - following_by_type_count($1.singularize.classify) | ||
82 | - elsif m.to_s[/following_(.+)/] | ||
83 | - following_by_type($1.singularize.classify) | ||
84 | - else | ||
85 | - super | ||
86 | - end | ||
87 | - end | ||
88 | - | ||
89 | - private | ||
90 | - | ||
91 | - # Returns a follow record for the current instance and followable object. | ||
92 | - def get_follow(followable) | ||
93 | - Follow.unblocked.find(:first, :conditions => ["follower_id = ? AND follower_type = ? AND followable_id = ? AND followable_type = ?", self.id, parent_class_name(self), followable.id, parent_class_name(followable)]) | ||
94 | - end | ||
95 | - | ||
96 | - end | ||
97 | - | ||
98 | - end | ||
99 | - end | ||
100 | -end | 1 | +require File.dirname(__FILE__) + '/follower_lib' |
2 | + | ||
3 | +module ActiveRecord #:nodoc: | ||
4 | + module Acts #:nodoc: | ||
5 | + module Follower | ||
6 | + | ||
7 | + def self.included(base) | ||
8 | + base.extend ClassMethods | ||
9 | + base.class_eval do | ||
10 | + include FollowerLib | ||
11 | + end | ||
12 | + end | ||
13 | + | ||
14 | + module ClassMethods | ||
15 | + def acts_as_follower | ||
16 | + has_many :follows, :as => :follower, :dependent => :destroy | ||
17 | + include ActiveRecord::Acts::Follower::InstanceMethods | ||
18 | + end | ||
19 | + end | ||
20 | + | ||
21 | + module InstanceMethods | ||
22 | + | ||
23 | + # Returns true if this instance is following the object passed as an argument. | ||
24 | + def following?(followable) | ||
25 | + 0 < Follow.unblocked.count(:all, :conditions => [ | ||
26 | + "follower_id = ? AND follower_type = ? AND followable_id = ? AND followable_type = ?", | ||
27 | + self.id, parent_class_name(self), followable.id, parent_class_name(followable) | ||
28 | + ]) | ||
29 | + end | ||
30 | + | ||
31 | + # Returns the number of objects this instance is following. | ||
32 | + def follow_count | ||
33 | + Follow.unblocked.count(:all, :conditions => ["follower_id = ? AND follower_type = ?", self.id, parent_class_name(self)]) | ||
34 | + end | ||
35 | + | ||
36 | + # Creates a new follow record for this instance to follow the passed object. | ||
37 | + # Does not allow duplicate records to be created. | ||
38 | + def follow(followable) | ||
39 | + follow = get_follow(followable) | ||
40 | + unless follow | ||
41 | + Follow.create(:followable => followable, :follower => self) | ||
42 | + end | ||
43 | + end | ||
44 | + | ||
45 | + # Deletes the follow record if it exists. | ||
46 | + def stop_following(followable) | ||
47 | + follow = get_follow(followable) | ||
48 | + if follow | ||
49 | + follow.destroy | ||
50 | + end | ||
51 | + end | ||
52 | + | ||
53 | + # Returns the follow records related to this instance by type. | ||
54 | + def follows_by_type(followable_type) | ||
55 | + Follow.unblocked.find(:all, :include => [:followable], :conditions => ["follower_id = ? AND follower_type = ? AND followable_type = ?", self.id, parent_class_name(self), followable_type]) | ||
56 | + end | ||
57 | + | ||
58 | + # Returns the follow records related to this instance with the followable included. | ||
59 | + def all_follows | ||
60 | + self.follows.unblocked.all(:include => :followable) | ||
61 | + end | ||
62 | + | ||
63 | + # Returns the actual records which this instance is following. | ||
64 | + def all_following | ||
65 | + all_follows.collect{ |f| f.followable } | ||
66 | + end | ||
67 | + | ||
68 | + # Returns the actual records of a particular type which this record is following. | ||
69 | + def following_by_type(followable_type) | ||
70 | + follows_by_type(followable_type).collect{ |f| f.followable } | ||
71 | + end | ||
72 | + | ||
73 | + # Allows magic names on following_by_type | ||
74 | + # e.g. following_users == following_by_type('User') | ||
75 | + def method_missing(m, *args) | ||
76 | + if m.to_s[/following_(.+)/] | ||
77 | + following_by_type($1.singularize.classify) | ||
78 | + else | ||
79 | + super | ||
80 | + end | ||
81 | + end | ||
82 | + | ||
83 | + private | ||
84 | + | ||
85 | + # Returns a follow record for the current instance and followable object. | ||
86 | + def get_follow(followable) | ||
87 | + Follow.unblocked.find(:first, :conditions => ["follower_id = ? AND follower_type = ? AND followable_id = ? AND followable_type = ?", self.id, parent_class_name(self), followable.id, parent_class_name(followable)]) | ||
88 | + end | ||
89 | + | ||
90 | + end | ||
91 | + | ||
92 | + end | ||
93 | + end | ||
94 | +end |
lib/follower_lib.rb
1 | module FollowerLib | 1 | module FollowerLib |
2 | - | 2 | + |
3 | private | 3 | private |
4 | - | 4 | + |
5 | # Retrieves the parent class name if using STI. | 5 | # Retrieves the parent class name if using STI. |
6 | def parent_class_name(obj) | 6 | def parent_class_name(obj) |
7 | if obj.class.superclass != ActiveRecord::Base | 7 | if obj.class.superclass != ActiveRecord::Base |
@@ -9,5 +9,5 @@ module FollowerLib | @@ -9,5 +9,5 @@ module FollowerLib | ||
9 | end | 9 | end |
10 | return obj.class.name | 10 | return obj.class.name |
11 | end | 11 | end |
12 | - | 12 | + |
13 | end | 13 | end |
test/TESTING
1 | -Testing | ||
2 | -============== | ||
3 | - | ||
4 | -Tests are written with Shoulda on top of Test::Unit and Factory Girl is used instead of fixtures. Tests are run using rake. | ||
5 | -Test can either be run against a MySQL database or the faster in-memory SQLite3. | ||
6 | - | ||
7 | - | ||
8 | -MySQL | ||
9 | -======= | ||
10 | - | ||
11 | -1. Create a new Rails app. | ||
12 | -2. Install acts_as_follower as a plugin. | ||
13 | -3. Copy the database config within the plugin: | ||
14 | - cp test/database.yml.example test/database.yml | ||
15 | -4. Create a database as specified in test/database.yml. | ||
16 | -5. Run the tests: | ||
17 | - rake test | ||
18 | - | ||
19 | - | ||
20 | -SQLite3 | ||
21 | -======= | ||
22 | - | ||
23 | -1. Create a new Rails app. | ||
24 | -2. Install acts_as_follower as a plugin. | ||
25 | -3. Copy the database config within the plugin: | ||
26 | - cp test/database.yml.example test/database.yml | ||
27 | -4. Install the sqlite3 library (if you don't have it already): | ||
28 | - sudo gem install sqlite3-ruby | ||
29 | -5. Run the tests: | ||
30 | - DB=sqlite3 rake test | ||
31 | - | ||
32 | - | ||
33 | -Coverage | ||
34 | -======= | ||
35 | - | ||
36 | -Test coverage can be calculated using Rcov. Make sure you have the rcov gem installed. | ||
37 | - | ||
38 | -Again in the acts_as_follower directory: | ||
39 | - | ||
40 | -rake rcov:gen # For mysql | ||
41 | - | ||
42 | -or: | ||
43 | - | ||
44 | -rake rcov:gen DB=sqlite3 # For sqlite | ||
45 | - | ||
46 | -The coverage will now be available in the test/coverage directory. | ||
47 | - | ||
48 | -rake rcov:clobber will delete the coverage directory. | 1 | +Testing |
2 | +============== | ||
3 | + | ||
4 | +Tests are written with Shoulda on top of Test::Unit and Factory Girl is used instead of fixtures. Tests are run using rake. | ||
5 | +Test can either be run against a MySQL database or the faster in-memory SQLite3. | ||
6 | + | ||
7 | + | ||
8 | +MySQL | ||
9 | +======= | ||
10 | + | ||
11 | +1. Create a new Rails app. | ||
12 | +2. Install acts_as_follower as a plugin. | ||
13 | +3. Copy the database config within the plugin: | ||
14 | + cp test/database.yml.example test/database.yml | ||
15 | +4. Create a database as specified in test/database.yml. | ||
16 | +5. Run the tests: | ||
17 | + rake test | ||
18 | + | ||
19 | + | ||
20 | +SQLite3 | ||
21 | +======= | ||
22 | + | ||
23 | +1. Create a new Rails app. | ||
24 | +2. Install acts_as_follower as a plugin. | ||
25 | +3. Copy the database config within the plugin: | ||
26 | + cp test/database.yml.example test/database.yml | ||
27 | +4. Install the sqlite3 library (if you don't have it already): | ||
28 | + sudo gem install sqlite3-ruby | ||
29 | +5. Run the tests: | ||
30 | + DB=sqlite3 rake test | ||
31 | + | ||
32 | + | ||
33 | +Coverage | ||
34 | +======= | ||
35 | + | ||
36 | +Test coverage can be calculated using Rcov. Make sure you have the rcov gem installed. | ||
37 | + | ||
38 | +Again in the acts_as_follower directory: | ||
39 | + | ||
40 | +rake rcov:gen # For mysql | ||
41 | + | ||
42 | +or: | ||
43 | + | ||
44 | +rake rcov:gen DB=sqlite3 # For sqlite | ||
45 | + | ||
46 | +The coverage will now be available in the test/coverage directory. | ||
47 | + | ||
48 | +rake rcov:clobber will delete the coverage directory. |
test/acts_as_followable_test.rb
1 | -require File.dirname(__FILE__) + '/test_helper' | ||
2 | - | ||
3 | -class ActsAsFollowableTest < Test::Unit::TestCase | ||
4 | - | ||
5 | - context "instance methods" do | ||
6 | - setup do | ||
7 | - @sam = Factory(:sam) | ||
8 | - end | ||
9 | - | ||
10 | - should "be defined" do | ||
11 | - assert @sam.respond_to?(:followers_count) | ||
12 | - assert @sam.respond_to?(:followers) | ||
13 | - assert @sam.respond_to?(:followed_by?) | ||
14 | - end | ||
15 | - end | ||
16 | - | ||
17 | - context "acts_as_followable" do | ||
18 | - setup do | ||
19 | - @sam = Factory(:sam) | ||
20 | - @jon = Factory(:jon) | ||
21 | - @sam.follow(@jon) | ||
22 | - end | ||
23 | - | ||
24 | - context "followers_count" do | ||
25 | - should "return the number of followers" do | ||
26 | - assert_equal 0, @sam.followers_count | ||
27 | - assert_equal 1, @jon.followers_count | ||
28 | - end | ||
29 | - should "return the proper number of multiple followers" do | ||
30 | - @bob = Factory(:bob) | ||
31 | - @sam.follow(@bob) | ||
32 | - assert_equal 0, @sam.followers_count | ||
33 | - assert_equal 1, @jon.followers_count | ||
34 | - assert_equal 1, @bob.followers_count | ||
35 | - end | ||
36 | - end | ||
37 | - | ||
38 | - context "followers" do | ||
39 | - should "return users" do | ||
40 | - assert_equal [], @sam.followers | ||
41 | - assert_equal [@sam], @jon.followers | ||
42 | - end | ||
43 | - should "return users (multiple followers)" do | ||
44 | - @bob = Factory(:bob) | ||
45 | - @sam.follow(@bob) | ||
46 | - assert_equal [], @sam.followers | ||
47 | - assert_equal [@sam], @jon.followers | ||
48 | - assert_equal [@sam], @bob.followers | ||
49 | - end | ||
50 | - should "return users (multiple followers, complex)" do | ||
51 | - @bob = Factory(:bob) | ||
52 | - @sam.follow(@bob) | ||
53 | - @jon.follow(@bob) | ||
54 | - assert_equal [], @sam.followers | ||
55 | - assert_equal [@sam], @jon.followers | ||
56 | - assert_equal [@sam, @jon], @bob.followers | ||
57 | - end | ||
58 | - end | ||
59 | - | ||
60 | - context "followed_by" do | ||
61 | - should "return_follower_status" do | ||
62 | - assert_equal true, @jon.followed_by?(@sam) | ||
63 | - assert_equal false, @sam.followed_by?(@jon) | ||
64 | - end | ||
65 | - end | ||
66 | - | ||
67 | - context "destroying a followable" do | ||
68 | - setup do | ||
69 | - @jon.destroy | ||
70 | - end | ||
71 | - | ||
72 | - should_change("follow count", :by => -1) { Follow.count } | ||
73 | - should_change("@sam.all_following.size", :by => -1) { @sam.all_following.size } | 1 | +require File.dirname(__FILE__) + '/test_helper' |
2 | + | ||
3 | +class ActsAsFollowableTest < Test::Unit::TestCase | ||
4 | + | ||
5 | + context "instance methods" do | ||
6 | + setup do | ||
7 | + @sam = Factory(:sam) | ||
74 | end | 8 | end |
75 | - | 9 | + |
10 | + should "be defined" do | ||
11 | + assert @sam.respond_to?(:followers_count) | ||
12 | + assert @sam.respond_to?(:followers) | ||
13 | + assert @sam.respond_to?(:followed_by?) | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + context "acts_as_followable" do | ||
18 | + setup do | ||
19 | + @sam = Factory(:sam) | ||
20 | + @jon = Factory(:jon) | ||
21 | + @sam.follow(@jon) | ||
22 | + end | ||
23 | + | ||
24 | + context "followers_count" do | ||
25 | + should "return the number of followers" do | ||
26 | + assert_equal 0, @sam.followers_count | ||
27 | + assert_equal 1, @jon.followers_count | ||
28 | + end | ||
29 | + should "return the proper number of multiple followers" do | ||
30 | + @bob = Factory(:bob) | ||
31 | + @sam.follow(@bob) | ||
32 | + assert_equal 0, @sam.followers_count | ||
33 | + assert_equal 1, @jon.followers_count | ||
34 | + assert_equal 1, @bob.followers_count | ||
35 | + end | ||
36 | + end | ||
37 | + | ||
38 | + context "followers" do | ||
39 | + should "return users" do | ||
40 | + assert_equal [], @sam.followers | ||
41 | + assert_equal [@sam], @jon.followers | ||
42 | + end | ||
43 | + should "return users (multiple followers)" do | ||
44 | + @bob = Factory(:bob) | ||
45 | + @sam.follow(@bob) | ||
46 | + assert_equal [], @sam.followers | ||
47 | + assert_equal [@sam], @jon.followers | ||
48 | + assert_equal [@sam], @bob.followers | ||
49 | + end | ||
50 | + should "return users (multiple followers, complex)" do | ||
51 | + @bob = Factory(:bob) | ||
52 | + @sam.follow(@bob) | ||
53 | + @jon.follow(@bob) | ||
54 | + assert_equal [], @sam.followers | ||
55 | + assert_equal [@sam], @jon.followers | ||
56 | + assert_equal [@sam, @jon], @bob.followers | ||
57 | + end | ||
58 | + end | ||
59 | + | ||
60 | + context "followed_by" do | ||
61 | + should "return_follower_status" do | ||
62 | + assert_equal true, @jon.followed_by?(@sam) | ||
63 | + assert_equal false, @sam.followed_by?(@jon) | ||
64 | + end | ||
65 | + end | ||
66 | + | ||
67 | + context "destroying a followable" do | ||
68 | + setup do | ||
69 | + @jon.destroy | ||
70 | + end | ||
71 | + | ||
72 | + should_change("follow count", :by => -1) { Follow.count } | ||
73 | + should_change("@sam.all_following.size", :by => -1) { @sam.all_following.size } | ||
74 | + end | ||
75 | + | ||
76 | context "blocking a follower" do | 76 | context "blocking a follower" do |
77 | setup do | 77 | setup do |
78 | @jon.block(@sam) | 78 | @jon.block(@sam) |
79 | end | 79 | end |
80 | - | 80 | + |
81 | should "remove him from followers" do | 81 | should "remove him from followers" do |
82 | assert_equal 0, @jon.followers_count | 82 | assert_equal 0, @jon.followers_count |
83 | end | 83 | end |
84 | - | 84 | + |
85 | should "add him to the blocked followers" do | 85 | should "add him to the blocked followers" do |
86 | assert_equal 1, @jon.blocked_followers_count | 86 | assert_equal 1, @jon.blocked_followers_count |
87 | end | 87 | end |
88 | - | 88 | + |
89 | should "not be able to follow again" do | 89 | should "not be able to follow again" do |
90 | assert_equal 0, @jon.followers_count | 90 | assert_equal 0, @jon.followers_count |
91 | end | 91 | end |
92 | - | 92 | + |
93 | should "not be present when listing followers" do | 93 | should "not be present when listing followers" do |
94 | assert_equal [], @jon.followers | 94 | assert_equal [], @jon.followers |
95 | end | 95 | end |
96 | - | 96 | + |
97 | should "be in the list of blocks" do | 97 | should "be in the list of blocks" do |
98 | assert_equal [@sam], @jon.blocks | 98 | assert_equal [@sam], @jon.blocks |
99 | end | 99 | end |
100 | end | 100 | end |
101 | - | 101 | + |
102 | context "unblocking a blocked follow" do | 102 | context "unblocking a blocked follow" do |
103 | setup do | 103 | setup do |
104 | @jon.block(@sam) | 104 | @jon.block(@sam) |
105 | @jon.unblock(@sam) | 105 | @jon.unblock(@sam) |
106 | end | 106 | end |
107 | - | 107 | + |
108 | should "not include the unblocked user in the list of followers" do | 108 | should "not include the unblocked user in the list of followers" do |
109 | assert_equal [], @jon.followers | 109 | assert_equal [], @jon.followers |
110 | end | 110 | end |
111 | - | 111 | + |
112 | should "remove him from the blocked followers" do | 112 | should "remove him from the blocked followers" do |
113 | assert_equal 0, @jon.blocked_followers_count | 113 | assert_equal 0, @jon.blocked_followers_count |
114 | assert_equal [], @jon.blocks | 114 | assert_equal [], @jon.blocks |
115 | end | 115 | end |
116 | end | 116 | end |
117 | - | 117 | + |
118 | context "unblock a non-existent follow" do | 118 | context "unblock a non-existent follow" do |
119 | setup do | 119 | setup do |
120 | @sam.stop_following(@jon) | 120 | @sam.stop_following(@jon) |
121 | @jon.unblock(@sam) | 121 | @jon.unblock(@sam) |
122 | end | 122 | end |
123 | - | 123 | + |
124 | should "not be in the list of followers" do | 124 | should "not be in the list of followers" do |
125 | assert_equal [], @jon.followers | 125 | assert_equal [], @jon.followers |
126 | end | 126 | end |
127 | - | 127 | + |
128 | should "not be in the blockked followers count" do | 128 | should "not be in the blockked followers count" do |
129 | assert_equal 0, @jon.blocked_followers_count | 129 | assert_equal 0, @jon.blocked_followers_count |
130 | end | 130 | end |
131 | - | 131 | + |
132 | should "not be in the blocks list" do | 132 | should "not be in the blocks list" do |
133 | assert_equal [], @jon.blocks | 133 | assert_equal [], @jon.blocks |
134 | end | 134 | end |
135 | end | 135 | end |
136 | - | ||
137 | - end | ||
138 | - | ||
139 | -end | 136 | + |
137 | + end | ||
138 | + | ||
139 | +end |
test/acts_as_follower_test.rb
1 | -require File.dirname(__FILE__) + '/test_helper' | ||
2 | - | ||
3 | -class ActsAsFollowerTest < Test::Unit::TestCase | ||
4 | - | ||
5 | - context "instance methods" do | ||
6 | - setup do | ||
7 | - @sam = Factory(:sam) | ||
8 | - end | ||
9 | - | ||
10 | - should "be defined" do | ||
11 | - assert @sam.respond_to?(:following?) | ||
12 | - assert @sam.respond_to?(:follow_count) | ||
13 | - assert @sam.respond_to?(:follow) | ||
14 | - assert @sam.respond_to?(:stop_following) | ||
15 | - assert @sam.respond_to?(:follows_by_type) | ||
16 | - assert @sam.respond_to?(:all_follows) | ||
17 | - end | ||
18 | - end | ||
19 | - | ||
20 | - context "acts_as_follower" do | ||
21 | - setup do | ||
22 | - @sam = Factory(:sam) | ||
23 | - @jon = Factory(:jon) | ||
24 | - @oasis = Factory(:oasis) | ||
25 | - @sam.follow(@jon) | ||
26 | - @sam.follow(@oasis) | ||
27 | - end | ||
28 | - | ||
29 | - context "following" do | ||
30 | - should "return following_status" do | ||
31 | - assert_equal true, @sam.following?(@jon) | ||
32 | - assert_equal false, @jon.following?(@sam) | ||
33 | - end | ||
34 | - | ||
35 | - should "return follow_count" do | ||
36 | - assert_equal 2, @sam.follow_count | ||
37 | - assert_equal 0, @jon.follow_count | ||
38 | - end | ||
39 | - end | ||
40 | - | ||
41 | - context "follow" do | ||
42 | - setup do | ||
43 | - @jon.follow(@sam) | ||
44 | - end | ||
45 | - | ||
46 | - should_change("Follow count", :by => 1) { Follow.count } | ||
47 | - should_change("@jon.follow_count", :by => 1) { @jon.follow_count } | ||
48 | - | ||
49 | - should "set the follower" do | ||
50 | - assert_equal @jon, Follow.last.follower | ||
51 | - end | ||
52 | - | ||
53 | - should "set the followable" do | ||
54 | - assert_equal @sam, Follow.last.followable | ||
55 | - end | ||
56 | - end | ||
57 | - | ||
58 | - context "stop_following" do | ||
59 | - setup do | ||
60 | - @sam.stop_following(@jon) | ||
61 | - end | ||
62 | - | ||
63 | - should_change("Follow count", :by => -1) { Follow.count } | ||
64 | - should_change("@sam.follow_count", :by => -1) { @sam.follow_count } | ||
65 | - end | ||
66 | - | ||
67 | - context "follows" do | ||
68 | - setup do | ||
69 | - @band_follow = Follow.find(:first, :conditions => ["follower_id = ? and follower_type = 'User' and followable_id = ? and followable_type = 'Band'", @sam.id, @oasis.id]) | ||
70 | - @user_follow = Follow.find(:first, :conditions => ["follower_id = ? and follower_type = 'User' and followable_id = ? and followable_type = 'User'", @sam.id, @jon.id]) | ||
71 | - end | ||
72 | - | ||
73 | - context "follows_by_type" do | ||
74 | - should "only return requested follows" do | ||
75 | - assert_equal [@band_follow], @sam.follows_by_type('Band') | ||
76 | - assert_equal [@user_follow], @sam.follows_by_type('User') | ||
77 | - end | ||
78 | - end | ||
79 | - | ||
80 | - context "all_follows" do | ||
81 | - should "return all follows" do | ||
82 | - assert_equal 2, @sam.all_follows.size | ||
83 | - assert @sam.all_follows.include?(@band_follow) | ||
84 | - assert @sam.all_follows.include?(@user_follow) | ||
85 | - assert_equal [], @jon.all_follows | ||
86 | - end | ||
87 | - end | ||
88 | - end | ||
89 | - | ||
90 | - context "all_following" do | ||
91 | - should "return the actual follow records" do | ||
92 | - assert_equal 2, @sam.all_following.size | ||
93 | - assert @sam.all_following.include?(@oasis) | ||
94 | - assert @sam.all_following.include?(@jon) | ||
95 | - assert_equal [], @jon.all_following | ||
96 | - end | ||
97 | - end | ||
98 | - | ||
99 | - context "following_by_type" do | ||
100 | - should "return only requested records" do | ||
101 | - assert_equal [@oasis], @sam.following_by_type('Band') | ||
102 | - assert_equal [@jon], @sam.following_by_type('User') | ||
103 | - end | ||
104 | - end | ||
105 | - | ||
106 | - context "following_by_type_count" do | ||
107 | - should "return the count of the requested type" do | ||
108 | - assert_equal 1, @sam.following_by_type_count('Band') | ||
109 | - assert_equal 1, @sam.following_by_type_count('User') | ||
110 | - assert_equal 0, @jon.following_by_type_count('Band') | ||
111 | - @jon.block(@sam) | ||
112 | - assert_equal 0, @sam.following_by_type_count('User') | ||
113 | - end | ||
114 | - end | ||
115 | - | ||
116 | - context "method_missing" do | ||
117 | - should "call following_by_type" do | ||
118 | - assert_equal [@oasis], @sam.following_bands | ||
119 | - assert_equal [@jon], @sam.following_users | ||
120 | - end | ||
121 | - | ||
122 | - should "call following_by_type_count" do | ||
123 | - assert_equal 1, @sam.following_bands_count | ||
124 | - assert_equal 1, @sam.following_users_count | ||
125 | - assert_equal 0, @jon.following_bands_count | ||
126 | - @jon.block(@sam) | ||
127 | - assert_equal 0, @sam.following_users_count | ||
128 | - end | ||
129 | - | ||
130 | - should "raise on no method" do | ||
131 | - assert_raises (NoMethodError){ @sam.foobar } | ||
132 | - end | ||
133 | - end | ||
134 | - | ||
135 | - context "destroying follower" do | ||
136 | - setup do | ||
137 | - @jon.destroy | ||
138 | - end | ||
139 | - | ||
140 | - should_change("Follow.count", :by => -1) { Follow.count } | ||
141 | - should_change("@sam.follow_count", :by => -1) { @sam.follow_count } | ||
142 | - end | ||
143 | - | ||
144 | - context "blocked by followable" do | ||
145 | - setup do | ||
146 | - @jon.block(@sam) | ||
147 | - end | ||
148 | - | ||
149 | - should "return following_status" do | ||
150 | - assert_equal false, @sam.following?(@jon) | ||
151 | - end | ||
152 | - | ||
153 | - should "return follow_count" do | ||
154 | - assert_equal 1, @sam.follow_count | ||
155 | - end | ||
156 | - | ||
157 | - should "not return record of the blocked follows" do | ||
158 | - assert_equal 1, @sam.all_follows.size | ||
159 | - assert !@sam.all_follows.include?(@user_follow) | ||
160 | - assert !@sam.all_following.include?(@jon) | ||
161 | - assert_equal [], @sam.following_by_type('User') | ||
162 | - assert_equal [], @sam.follows_by_type('User') | ||
163 | - assert_equal [], @sam.following_users | ||
164 | - end | ||
165 | - end | ||
166 | - end | ||
167 | - | ||
168 | -end | 1 | +require File.dirname(__FILE__) + '/test_helper' |
2 | + | ||
3 | +class ActsAsFollowerTest < Test::Unit::TestCase | ||
4 | + | ||
5 | + context "instance methods" do | ||
6 | + setup do | ||
7 | + @sam = Factory(:sam) | ||
8 | + end | ||
9 | + | ||
10 | + should "be defined" do | ||
11 | + assert @sam.respond_to?(:following?) | ||
12 | + assert @sam.respond_to?(:follow_count) | ||
13 | + assert @sam.respond_to?(:follow) | ||
14 | + assert @sam.respond_to?(:stop_following) | ||
15 | + assert @sam.respond_to?(:follows_by_type) | ||
16 | + assert @sam.respond_to?(:all_follows) | ||
17 | + end | ||
18 | + end | ||
19 | + | ||
20 | + context "acts_as_follower" do | ||
21 | + setup do | ||
22 | + @sam = Factory(:sam) | ||
23 | + @jon = Factory(:jon) | ||
24 | + @oasis = Factory(:oasis) | ||
25 | + @sam.follow(@jon) | ||
26 | + @sam.follow(@oasis) | ||
27 | + end | ||
28 | + | ||
29 | + context "following" do | ||
30 | + should "return following_status" do | ||
31 | + assert_equal true, @sam.following?(@jon) | ||
32 | + assert_equal false, @jon.following?(@sam) | ||
33 | + end | ||
34 | + | ||
35 | + should "return follow_count" do | ||
36 | + assert_equal 2, @sam.follow_count | ||
37 | + assert_equal 0, @jon.follow_count | ||
38 | + end | ||
39 | + end | ||
40 | + | ||
41 | + context "follow" do | ||
42 | + setup do | ||
43 | + @jon.follow(@sam) | ||
44 | + end | ||
45 | + | ||
46 | + should_change("Follow count", :by => 1) { Follow.count } | ||
47 | + should_change("@jon.follow_count", :by => 1) { @jon.follow_count } | ||
48 | + | ||
49 | + should "set the follower" do | ||
50 | + assert_equal @jon, Follow.last.follower | ||
51 | + end | ||
52 | + | ||
53 | + should "set the followable" do | ||
54 | + assert_equal @sam, Follow.last.followable | ||
55 | + end | ||
56 | + end | ||
57 | + | ||
58 | + context "stop_following" do | ||
59 | + setup do | ||
60 | + @sam.stop_following(@jon) | ||
61 | + end | ||
62 | + | ||
63 | + should_change("Follow count", :by => -1) { Follow.count } | ||
64 | + should_change("@sam.follow_count", :by => -1) { @sam.follow_count } | ||
65 | + end | ||
66 | + | ||
67 | + context "follows" do | ||
68 | + setup do | ||
69 | + @band_follow = Follow.find(:first, :conditions => ["follower_id = ? and follower_type = 'User' and followable_id = ? and followable_type = 'Band'", @sam.id, @oasis.id]) | ||
70 | + @user_follow = Follow.find(:first, :conditions => ["follower_id = ? and follower_type = 'User' and followable_id = ? and followable_type = 'User'", @sam.id, @jon.id]) | ||
71 | + end | ||
72 | + | ||
73 | + context "follows_by_type" do | ||
74 | + should "only return requested follows" do | ||
75 | + assert_equal [@band_follow], @sam.follows_by_type('Band') | ||
76 | + assert_equal [@user_follow], @sam.follows_by_type('User') | ||
77 | + end | ||
78 | + end | ||
79 | + | ||
80 | + context "all_follows" do | ||
81 | + should "return all follows" do | ||
82 | + assert_equal 2, @sam.all_follows.size | ||
83 | + assert @sam.all_follows.include?(@band_follow) | ||
84 | + assert @sam.all_follows.include?(@user_follow) | ||
85 | + assert_equal [], @jon.all_follows | ||
86 | + end | ||
87 | + end | ||
88 | + end | ||
89 | + | ||
90 | + context "all_following" do | ||
91 | + should "return the actual follow records" do | ||
92 | + assert_equal 2, @sam.all_following.size | ||
93 | + assert @sam.all_following.include?(@oasis) | ||
94 | + assert @sam.all_following.include?(@jon) | ||
95 | + assert_equal [], @jon.all_following | ||
96 | + end | ||
97 | + end | ||
98 | + | ||
99 | + context "following_by_type" do | ||
100 | + should "return only requested records" do | ||
101 | + assert_equal [@oasis], @sam.following_by_type('Band') | ||
102 | + assert_equal [@jon], @sam.following_by_type('User') | ||
103 | + end | ||
104 | + end | ||
105 | + | ||
106 | + context "method_missing" do | ||
107 | + should "call following_by_type" do | ||
108 | + assert_equal [@oasis], @sam.following_bands | ||
109 | + assert_equal [@jon], @sam.following_users | ||
110 | + end | ||
111 | + | ||
112 | + should "raise on no method" do | ||
113 | + assert_raises (NoMethodError){ @sam.foobar } | ||
114 | + end | ||
115 | + end | ||
116 | + | ||
117 | + context "destroying follower" do | ||
118 | + setup do | ||
119 | + @jon.destroy | ||
120 | + end | ||
121 | + | ||
122 | + should_change("Follow.count", :by => -1) { Follow.count } | ||
123 | + should_change("@sam.follow_count", :by => -1) { @sam.follow_count } | ||
124 | + end | ||
125 | + | ||
126 | + context "blocked by followable" do | ||
127 | + setup do | ||
128 | + @jon.block(@sam) | ||
129 | + end | ||
130 | + | ||
131 | + should "return following_status" do | ||
132 | + assert_equal false, @sam.following?(@jon) | ||
133 | + end | ||
134 | + | ||
135 | + should "return follow_count" do | ||
136 | + assert_equal 1, @sam.follow_count | ||
137 | + end | ||
138 | + | ||
139 | + should "not return record of the blocked follows" do | ||
140 | + assert_equal 1, @sam.all_follows.size | ||
141 | + assert !@sam.all_follows.include?(@user_follow) | ||
142 | + assert !@sam.all_following.include?(@jon) | ||
143 | + assert_equal [], @sam.following_by_type('User') | ||
144 | + assert_equal [], @sam.follows_by_type('User') | ||
145 | + assert_equal [], @sam.following_users | ||
146 | + end | ||
147 | + end | ||
148 | + end | ||
149 | + | ||
150 | +end |
test/database.yml.example
test/factories/bands.rb
test/factories/users.rb
1 | -Factory.define :jon, :class => User do |u| | ||
2 | - u.name 'Jon' | ||
3 | -end | ||
4 | - | ||
5 | -Factory.define :sam, :class => User do |u| | ||
6 | - u.name 'Sam' | ||
7 | -end | ||
8 | - | ||
9 | -Factory.define :bob, :class => User do |u| | ||
10 | - u.name 'Bob' | ||
11 | -end | 1 | +Factory.define :jon, :class => User do |u| |
2 | + u.name 'Jon' | ||
3 | +end | ||
4 | + | ||
5 | +Factory.define :sam, :class => User do |u| | ||
6 | + u.name 'Sam' | ||
7 | +end | ||
8 | + | ||
9 | +Factory.define :bob, :class => User do |u| | ||
10 | + u.name 'Bob' | ||
11 | +end |
test/follow_test.rb
1 | -require File.dirname(__FILE__) + '/test_helper' | ||
2 | - | ||
3 | -class FollowTest < Test::Unit::TestCase | ||
4 | - | ||
5 | - # Replace with real tests | ||
6 | - def test_assert_true_should_be_true | ||
7 | - assert true | ||
8 | - end | ||
9 | - | ||
10 | -end | 1 | +require File.dirname(__FILE__) + '/test_helper' |
2 | + | ||
3 | +class FollowTest < Test::Unit::TestCase | ||
4 | + | ||
5 | + # Replace with real tests | ||
6 | + def test_assert_true_should_be_true | ||
7 | + assert true | ||
8 | + end | ||
9 | + | ||
10 | +end |
test/models/band.rb
test/models/user.rb
test/schema.rb
1 | ActiveRecord::Schema.define :version => 0 do | 1 | ActiveRecord::Schema.define :version => 0 do |
2 | - | 2 | + |
3 | create_table :follows, :force => true do |t| | 3 | create_table :follows, :force => true do |t| |
4 | t.integer "followable_id", :null => false | 4 | t.integer "followable_id", :null => false |
5 | t.string "followable_type", :null => false | 5 | t.string "followable_type", :null => false |
@@ -9,13 +9,13 @@ ActiveRecord::Schema.define :version => 0 do | @@ -9,13 +9,13 @@ ActiveRecord::Schema.define :version => 0 do | ||
9 | t.datetime "created_at" | 9 | t.datetime "created_at" |
10 | t.datetime "updated_at" | 10 | t.datetime "updated_at" |
11 | end | 11 | end |
12 | - | 12 | + |
13 | create_table :users, :force => true do |t| | 13 | create_table :users, :force => true do |t| |
14 | t.column :name, :string | 14 | t.column :name, :string |
15 | end | 15 | end |
16 | - | 16 | + |
17 | create_table :bands, :force => true do |t| | 17 | create_table :bands, :force => true do |t| |
18 | t.column :name, :string | 18 | t.column :name, :string |
19 | end | 19 | end |
20 | - | 20 | + |
21 | end | 21 | end |
test/test_helper.rb
1 | -require 'rubygems' | ||
2 | -require 'active_record' | ||
3 | -require 'active_record/base' | ||
4 | - | ||
5 | -require File.dirname(__FILE__) + '/../init.rb' | ||
6 | -require File.dirname(__FILE__) + '/models/band' | ||
7 | -require File.dirname(__FILE__) + '/models/user' | ||
8 | -require File.dirname(__FILE__) + '/../generators/acts_as_follower/templates/model.rb' | ||
9 | - | ||
10 | -require 'test/unit' | ||
11 | -require 'shoulda' | ||
12 | -require 'factory_girl' | ||
13 | - | ||
14 | -ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log') | ||
15 | -ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) | ||
16 | -ActiveRecord::Base.establish_connection(ENV['DB'] || 'mysql') | ||
17 | - | ||
18 | -load(File.dirname(__FILE__) + '/schema.rb') | 1 | +require 'rubygems' |
2 | +require 'active_record' | ||
3 | +require 'active_record/base' | ||
4 | + | ||
5 | +require File.dirname(__FILE__) + '/../init.rb' | ||
6 | +require File.dirname(__FILE__) + '/models/band' | ||
7 | +require File.dirname(__FILE__) + '/models/user' | ||
8 | +require File.dirname(__FILE__) + '/../generators/acts_as_follower/templates/model.rb' | ||
9 | + | ||
10 | +require 'test/unit' | ||
11 | +require 'shoulda' | ||
12 | +require 'factory_girl' | ||
13 | + | ||
14 | +ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log') | ||
15 | +ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) | ||
16 | +ActiveRecord::Base.establish_connection(ENV['DB'] || 'mysql') | ||
17 | + | ||
18 | +load(File.dirname(__FILE__) + '/schema.rb') |