Commit 899cf25d595bcca2b9d2594ba4351a40ef407e38

Authored by tcocca
1 parent 5a4f3582

pulled in arthurgeeks changes to remove trailing white space

MIT-LICENSE
... ... @@ -19,7 +19,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 19 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 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 23 Here is the original copyright notice for VoteFu:
24 24  
25 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 28 acts_as_followable
29 29 ...
30 30 end
31   -
  31 +
32 32 class Book < ActiveRecord::Base
33 33 ...
34 34 acts_as_followable
... ... @@ -64,7 +64,7 @@ To get follow records that have not been blocked use the following
64 64 user.all_follows # returns an array of Follow records
65 65  
66 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 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 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 82  
83 83 To get all the followers of a model that acts_as_followable
84 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 86 To get just the number of follows use
87 87 book.followers_count
88   -
  88 +
89 89 To see is a model that acts_as_followable is followed by a model that acts_as_follower use the following
90 90 book.followed_by?(user)
91   -
  91 +
92 92 # Returns true if the current instance is followed by the passed record
93 93 # Returns false if the current instance is blocked by the passed record or no follow is found
94 94  
95 95 To block a follower call the following
96   - book.block(user)
  96 + book.block(user)
97 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 99 To unblock is just as simple
... ... @@ -105,7 +105,7 @@ To get all blocked records
105 105 If you only need the number of blocks use the count method provided
106 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 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 114  
115 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 116 Follow.unblocked # returns all "unblocked" follow records
117   -
  117 +
118 118 Follow.blocked # returns all "blocked" follow records
119   -
  119 +
120 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 123 Follow.recent
124 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 145  
146 146  
147 147 == Note on Patches/Pull Requests
148   -
  148 +
149 149 * Fork the project.
150 150 * Make your feature addition or bug fix.
151 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 1 Description:
2 2 run ./script/generate acts_as_follower
3   -
  3 +
4 4 no need to specify a name after acts_as_follower as you can not change the model name from Follow
5 5 the acts_as_follower_migration file will be created in db/migrate
6 6 \ No newline at end of file
... ...
generators/acts_as_follower/acts_as_follower_generator.rb
... ... @@ -3,11 +3,11 @@ class ActsAsFollowerGenerator &lt; Rails::Generator::Base
3 3 record do |m|
4 4 m.directory "app/models"
5 5 m.template "model.rb", "app/models/follow.rb"
6   -
  6 +
7 7 m.migration_template 'migration.rb', 'db/migrate'
8 8 end
9 9 end
10   -
  10 +
11 11 def file_name
12 12 "acts_as_follower_migration"
13 13 end
... ...
generators/acts_as_follower/templates/migration.rb
... ... @@ -10,7 +10,7 @@ class ActsAsFollowerMigration &lt; ActiveRecord::Migration
10 10 add_index :follows, ["follower_id", "follower_type"], :name => "fk_follows"
11 11 add_index :follows, ["followable_id", "followable_type"], :name => "fk_followables"
12 12 end
13   -
  13 +
14 14 def self.down
15 15 drop_table :follows
16 16 end
... ...
generators/acts_as_follower/templates/model.rb
1 1 class Follow < ActiveRecord::Base
2   -
  2 +
3 3 named_scope :for_follower, lambda { |*args| {:conditions => ["follower_id = ? AND follower_type = ?", args.first.id, args.first.type.name]} }
4 4 named_scope :for_followable, lambda { |*args| {:conditions => ["followable_id = ? AND followable_type = ?", args.first.id, args.first.type.name]} }
5 5 named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || 2.weeks.ago).to_s(:db)]} }
6 6 named_scope :descending, :order => "created_at DESC"
7 7 named_scope :unblocked, :conditions => {:blocked => false}
8 8 named_scope :blocked, :conditions => {:blocked => true}
9   -
  9 +
10 10 # NOTE: Follows belong to the "followable" interface, and also to followers
11 11 belongs_to :followable, :polymorphic => true
12 12 belongs_to :follower, :polymorphic => true
13   -
  13 +
14 14 def block!
15 15 self.update_attribute(:blocked, true)
16 16 end
17   -
  17 +
18 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 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 29 def blocked_followers_count
30 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 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 38 def blocks
39 39 self.followings.blocked.all(:include => [:follower]).collect{|f| f.follower}
40   - end
41   -
  40 + end
  41 +
42 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 45 f = get_follow_for(follower)
46   - (f && !f.blocked?) ? true : false
  46 + (f && !f.blocked?) ? true : false
47 47 end
48   -
  48 +
49 49 def block(follower)
50 50 get_follow_for(follower) ? block_existing_follow(follower) : block_future_follow(follower)
51 51 end
52   -
  52 +
53 53 def unblock(follower)
54 54 get_follow_for(follower).try(:delete)
55 55 end
56   -
  56 +
57 57 private
58   -
  58 +
59 59 def get_follow_for(follower)
60 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 63 def block_future_follow(follower)
64 64 follows.create(:followable => self, :follower => follower, :blocked => true)
65 65 end
66   -
  66 +
67 67 def block_existing_follow(follower)
68 68 get_follow_for(follower).block!
69 69 end
70   -
71   - end
72   -
73   - end
74   - end
  70 +
  71 + end
  72 +
  73 + end
  74 + end
75 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 1 module FollowerLib
2   -
  2 +
3 3 private
4   -
  4 +
5 5 # Retrieves the parent class name if using STI.
6 6 def parent_class_name(obj)
7 7 if obj.class.superclass != ActiveRecord::Base
... ... @@ -9,5 +9,5 @@ module FollowerLib
9 9 end
10 10 return obj.class.name
11 11 end
12   -
  12 +
13 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 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 76 context "blocking a follower" do
77 77 setup do
78 78 @jon.block(@sam)
79 79 end
80   -
  80 +
81 81 should "remove him from followers" do
82 82 assert_equal 0, @jon.followers_count
83 83 end
84   -
  84 +
85 85 should "add him to the blocked followers" do
86 86 assert_equal 1, @jon.blocked_followers_count
87 87 end
88   -
  88 +
89 89 should "not be able to follow again" do
90 90 assert_equal 0, @jon.followers_count
91 91 end
92   -
  92 +
93 93 should "not be present when listing followers" do
94 94 assert_equal [], @jon.followers
95 95 end
96   -
  96 +
97 97 should "be in the list of blocks" do
98 98 assert_equal [@sam], @jon.blocks
99 99 end
100 100 end
101   -
  101 +
102 102 context "unblocking a blocked follow" do
103 103 setup do
104 104 @jon.block(@sam)
105 105 @jon.unblock(@sam)
106 106 end
107   -
  107 +
108 108 should "not include the unblocked user in the list of followers" do
109 109 assert_equal [], @jon.followers
110 110 end
111   -
  111 +
112 112 should "remove him from the blocked followers" do
113 113 assert_equal 0, @jon.blocked_followers_count
114 114 assert_equal [], @jon.blocks
115 115 end
116 116 end
117   -
  117 +
118 118 context "unblock a non-existent follow" do
119 119 setup do
120 120 @sam.stop_following(@jon)
121 121 @jon.unblock(@sam)
122 122 end
123   -
  123 +
124 124 should "not be in the list of followers" do
125 125 assert_equal [], @jon.followers
126 126 end
127   -
  127 +
128 128 should "not be in the blockked followers count" do
129 129 assert_equal 0, @jon.blocked_followers_count
130 130 end
131   -
  131 +
132 132 should "not be in the blocks list" do
133 133 assert_equal [], @jon.blocks
134 134 end
135 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
... ... @@ -2,7 +2,7 @@ mysql:
2 2 adapter: mysql
3 3 host: localhost
4 4 username: root
5   - password:
  5 + password:
6 6 database: rails_plugin_test
7 7  
8 8 sqlite3:
... ...
test/factories/bands.rb
1   -Factory.define :oasis, :class => Band do |b|
2   - b.name 'Oasis'
3   -end
  1 +Factory.define :oasis, :class => Band do |b|
  2 + b.name 'Oasis'
  3 +end
... ...
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
1   -class Band < ActiveRecord::Base
2   - validates_presence_of :name
3   - acts_as_followable
4   -end
  1 +class Band < ActiveRecord::Base
  2 + validates_presence_of :name
  3 + acts_as_followable
  4 +end
... ...
test/models/user.rb
1   -class User < ActiveRecord::Base
2   - validates_presence_of :name
3   - acts_as_follower
4   - acts_as_followable
5   -end
  1 +class User < ActiveRecord::Base
  2 + validates_presence_of :name
  3 + acts_as_follower
  4 + acts_as_followable
  5 +end
... ...
test/schema.rb
1 1 ActiveRecord::Schema.define :version => 0 do
2   -
  2 +
3 3 create_table :follows, :force => true do |t|
4 4 t.integer "followable_id", :null => false
5 5 t.string "followable_type", :null => false
... ... @@ -9,13 +9,13 @@ ActiveRecord::Schema.define :version =&gt; 0 do
9 9 t.datetime "created_at"
10 10 t.datetime "updated_at"
11 11 end
12   -
  12 +
13 13 create_table :users, :force => true do |t|
14 14 t.column :name, :string
15 15 end
16   -
  16 +
17 17 create_table :bands, :force => true do |t|
18 18 t.column :name, :string
19 19 end
20   -
  20 +
21 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')
... ...