acts_as_follower1 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +6 -0
  5. data/MIT-LICENSE +44 -0
  6. data/README.rdoc +247 -0
  7. data/Rakefile +37 -0
  8. data/acts_as_follower.gemspec +29 -0
  9. data/init.rb +1 -0
  10. data/lib/acts_as_follower/follow_scopes.rb +45 -0
  11. data/lib/acts_as_follower/followable.rb +113 -0
  12. data/lib/acts_as_follower/follower.rb +112 -0
  13. data/lib/acts_as_follower/follower_lib.rb +42 -0
  14. data/lib/acts_as_follower/railtie.rb +14 -0
  15. data/lib/acts_as_follower/version.rb +3 -0
  16. data/lib/acts_as_follower1.rb +33 -0
  17. data/lib/generators/USAGE +5 -0
  18. data/lib/generators/acts_as_follower_generator.rb +30 -0
  19. data/lib/generators/templates/migration.rb +17 -0
  20. data/lib/generators/templates/model.rb +14 -0
  21. data/test/README +24 -0
  22. data/test/acts_as_followable_test.rb +283 -0
  23. data/test/acts_as_follower_test.rb +224 -0
  24. data/test/dummy30/Gemfile +1 -0
  25. data/test/dummy30/Rakefile +7 -0
  26. data/test/dummy30/app/models/application_record.rb +3 -0
  27. data/test/dummy30/app/models/band.rb +4 -0
  28. data/test/dummy30/app/models/band/punk.rb +4 -0
  29. data/test/dummy30/app/models/band/punk/pop_punk.rb +4 -0
  30. data/test/dummy30/app/models/custom_record.rb +3 -0
  31. data/test/dummy30/app/models/some.rb +5 -0
  32. data/test/dummy30/app/models/user.rb +5 -0
  33. data/test/dummy30/config.ru +4 -0
  34. data/test/dummy30/config/application.rb +42 -0
  35. data/test/dummy30/config/boot.rb +10 -0
  36. data/test/dummy30/config/database.yml +7 -0
  37. data/test/dummy30/config/environment.rb +5 -0
  38. data/test/dummy30/config/environments/development.rb +19 -0
  39. data/test/dummy30/config/environments/test.rb +20 -0
  40. data/test/dummy30/config/initializers/backtrace_silencers.rb +7 -0
  41. data/test/dummy30/config/initializers/inflections.rb +10 -0
  42. data/test/dummy30/config/initializers/secret_token.rb +7 -0
  43. data/test/dummy30/config/initializers/session_store.rb +8 -0
  44. data/test/dummy30/config/locales/en.yml +5 -0
  45. data/test/dummy30/config/routes.rb +2 -0
  46. data/test/factories/bands.rb +17 -0
  47. data/test/factories/somes.rb +9 -0
  48. data/test/factories/users.rb +13 -0
  49. data/test/follow_test.rb +28 -0
  50. data/test/schema.rb +25 -0
  51. data/test/test_helper.rb +18 -0
  52. metadata +213 -0
@@ -0,0 +1,113 @@
1
+ module ActsAsFollower #:nodoc:
2
+ module Followable
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_followable
10
+ has_many :followings, as: :followable, dependent: :destroy, class_name: 'Follow'
11
+ include ActsAsFollower::Followable::InstanceMethods
12
+ include ActsAsFollower::FollowerLib
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+
18
+ # Returns the number of followers a record has.
19
+ def followers_count
20
+ self.followings.unblocked.count
21
+ end
22
+
23
+ # Returns the followers by a given type
24
+ def followers_by_type(follower_type, options={})
25
+ follows = follower_type.constantize.
26
+ joins(:follows).
27
+ where('follows.blocked' => false,
28
+ 'follows.followable_id' => self.id,
29
+ 'follows.followable_type' => parent_class_name(self),
30
+ 'follows.follower_type' => follower_type)
31
+ if options.has_key?(:limit)
32
+ follows = follows.limit(options[:limit])
33
+ end
34
+ if options.has_key?(:includes)
35
+ follows = follows.includes(options[:includes])
36
+ end
37
+ follows
38
+ end
39
+
40
+ def followers_by_type_count(follower_type)
41
+ self.followings.unblocked.for_follower_type(follower_type).count
42
+ end
43
+
44
+ # Allows magic names on followers_by_type
45
+ # e.g. user_followers == followers_by_type('User')
46
+ # Allows magic names on followers_by_type_count
47
+ # e.g. count_user_followers == followers_by_type_count('User')
48
+ def method_missing(m, *args)
49
+ if m.to_s[/count_(.+)_followers/]
50
+ followers_by_type_count($1.singularize.classify)
51
+ elsif m.to_s[/(.+)_followers/]
52
+ followers_by_type($1.singularize.classify)
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def respond_to?(m, include_private = false)
59
+ super || m.to_s[/count_(.+)_followers/] || m.to_s[/(.+)_followers/]
60
+ end
61
+
62
+ def blocked_followers_count
63
+ self.followings.blocked.count
64
+ end
65
+
66
+ # Returns the followings records scoped
67
+ def followers_scoped
68
+ self.followings.includes(:follower)
69
+ end
70
+
71
+ def followers(options={})
72
+ followers_scope = followers_scoped.unblocked
73
+ followers_scope = apply_options_to_scope(followers_scope, options)
74
+ followers_scope.to_a.collect{|f| f.follower}
75
+ end
76
+
77
+ def blocks(options={})
78
+ blocked_followers_scope = followers_scoped.blocked
79
+ blocked_followers_scope = apply_options_to_scope(blocked_followers_scope, options)
80
+ blocked_followers_scope.to_a.collect{|f| f.follower}
81
+ end
82
+
83
+ # Returns true if the current instance is followed by the passed record
84
+ # Returns false if the current instance is blocked by the passed record or no follow is found
85
+ def followed_by?(follower)
86
+ self.followings.unblocked.for_follower(follower).first.present?
87
+ end
88
+
89
+ def block(follower)
90
+ get_follow_for(follower) ? block_existing_follow(follower) : block_future_follow(follower)
91
+ end
92
+
93
+ def unblock(follower)
94
+ get_follow_for(follower).try(:delete)
95
+ end
96
+
97
+ def get_follow_for(follower)
98
+ self.followings.for_follower(follower).first
99
+ end
100
+
101
+ private
102
+
103
+ def block_future_follow(follower)
104
+ Follow.create(followable: self, follower: follower, blocked: true)
105
+ end
106
+
107
+ def block_existing_follow(follower)
108
+ get_follow_for(follower).block!
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,112 @@
1
+ module ActsAsFollower #:nodoc:
2
+ module Follower
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_follower
10
+ has_many :follows, as: :follower, dependent: :destroy
11
+ include ActsAsFollower::Follower::InstanceMethods
12
+ include ActsAsFollower::FollowerLib
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+
18
+ # Returns true if this instance is following the object passed as an argument.
19
+ def following?(followable)
20
+ 0 < Follow.unblocked.for_follower(self).for_followable(followable).count
21
+ end
22
+
23
+ # Returns the number of objects this instance is following.
24
+ def follow_count
25
+ Follow.unblocked.for_follower(self).count
26
+ end
27
+
28
+ # Creates a new follow record for this instance to follow the passed object.
29
+ # Does not allow duplicate records to be created.
30
+ def follow(followable)
31
+ if self != followable
32
+ params = {followable_id: followable.id, followable_type: parent_class_name(followable)}
33
+ self.follows.where(params).first_or_create!
34
+ end
35
+ end
36
+
37
+ # Deletes the follow record if it exists.
38
+ def stop_following(followable)
39
+ if follow = get_follow(followable)
40
+ follow.destroy
41
+ end
42
+ end
43
+
44
+ # returns the follows records to the current instance
45
+ def follows_scoped
46
+ self.follows.unblocked.includes(:followable)
47
+ end
48
+
49
+ # Returns the follow records related to this instance by type.
50
+ def follows_by_type(followable_type, options={})
51
+ follows_scope = follows_scoped.for_followable_type(followable_type)
52
+ follows_scope = apply_options_to_scope(follows_scope, options)
53
+ end
54
+
55
+ # Returns the follow records related to this instance with the followable included.
56
+ def all_follows(options={})
57
+ follows_scope = follows_scoped
58
+ follows_scope = apply_options_to_scope(follows_scope, options)
59
+ end
60
+
61
+ # Returns the actual records which this instance is following.
62
+ def all_following(options={})
63
+ all_follows(options).collect{ |f| f.followable }
64
+ end
65
+
66
+ # Returns the actual records of a particular type which this record is following.
67
+ def following_by_type(followable_type, options={})
68
+ followables = followable_type.constantize.
69
+ joins(:followings).
70
+ where('follows.blocked' => false,
71
+ 'follows.follower_id' => self.id,
72
+ 'follows.follower_type' => parent_class_name(self),
73
+ 'follows.followable_type' => followable_type)
74
+ if options.has_key?(:limit)
75
+ followables = followables.limit(options[:limit])
76
+ end
77
+ if options.has_key?(:includes)
78
+ followables = followables.includes(options[:includes])
79
+ end
80
+ followables
81
+ end
82
+
83
+ def following_by_type_count(followable_type)
84
+ follows.unblocked.for_followable_type(followable_type).count
85
+ end
86
+
87
+ # Allows magic names on following_by_type
88
+ # e.g. following_users == following_by_type('User')
89
+ # Allows magic names on following_by_type_count
90
+ # e.g. following_users_count == following_by_type_count('User')
91
+ def method_missing(m, *args)
92
+ if m.to_s[/following_(.+)_count/]
93
+ following_by_type_count($1.singularize.classify)
94
+ elsif m.to_s[/following_(.+)/]
95
+ following_by_type($1.singularize.classify)
96
+ else
97
+ super
98
+ end
99
+ end
100
+
101
+ def respond_to?(m, include_private = false)
102
+ super || m.to_s[/following_(.+)_count/] || m.to_s[/following_(.+)/]
103
+ end
104
+
105
+ # Returns a follow record for the current instance and followable object.
106
+ def get_follow(followable)
107
+ self.follows.unblocked.for_followable(followable).first
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,42 @@
1
+ module ActsAsFollower
2
+ module FollowerLib
3
+
4
+ private
5
+
6
+ DEFAULT_PARENTS = [ApplicationRecord, ActiveRecord::Base]
7
+
8
+ # Retrieves the parent class name if using STI.
9
+ def parent_class_name(obj)
10
+ unless parent_classes.include?(obj.class.superclass)
11
+ return obj.class.base_class.name
12
+ end
13
+ obj.class.name
14
+ end
15
+
16
+ def apply_options_to_scope(scope, options = {})
17
+ if options.has_key?(:limit)
18
+ scope = scope.limit(options[:limit])
19
+ end
20
+ if options.has_key?(:includes)
21
+ scope = scope.includes(options[:includes])
22
+ end
23
+ if options.has_key?(:joins)
24
+ scope = scope.joins(options[:joins])
25
+ end
26
+ if options.has_key?(:where)
27
+ scope = scope.where(options[:where])
28
+ end
29
+ if options.has_key?(:order)
30
+ scope = scope.order(options[:order])
31
+ end
32
+ scope
33
+ end
34
+
35
+ def parent_classes
36
+ return DEFAULT_PARENTS unless ActsAsFollower.custom_parent_classes
37
+
38
+ ActiveSupport::Deprecation.warn("Setting custom parent classes is deprecated and will be removed in future versions.")
39
+ ActsAsFollower.custom_parent_classes + DEFAULT_PARENTS
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails'
2
+
3
+ module ActsAsFollower
4
+ class Railtie < Rails::Railtie
5
+
6
+ initializer "acts_as_follower.active_record" do |app|
7
+ ActiveSupport.on_load :active_record do
8
+ include ActsAsFollower::Follower
9
+ include ActsAsFollower::Followable
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module ActsAsFollower
2
+ VERSION = "1.0.9"
3
+ end
@@ -0,0 +1,33 @@
1
+ require "acts_as_follower/version"
2
+
3
+ module ActsAsFollower
4
+ autoload :Follower, 'acts_as_follower/follower'
5
+ autoload :Followable, 'acts_as_follower/followable'
6
+ autoload :FollowerLib, 'acts_as_follower/follower_lib'
7
+ autoload :FollowScopes, 'acts_as_follower/follow_scopes'
8
+
9
+ def self.setup
10
+ @configuration ||= Configuration.new
11
+ yield @configuration if block_given?
12
+ end
13
+
14
+ def self.method_missing(method_name, *args, &block)
15
+ if method_name == :custom_parent_classes=
16
+ ActiveSupport::Deprecation.warn("Setting custom parent classes is deprecated and will be removed in future versions.")
17
+ end
18
+ @configuration.respond_to?(method_name) ?
19
+ @configuration.send(method_name, *args, &block) : super
20
+ end
21
+
22
+ class Configuration
23
+ attr_accessor :custom_parent_classes
24
+
25
+ def initialize
26
+ @custom_parent_classes = []
27
+ end
28
+ end
29
+
30
+ setup
31
+
32
+ require 'acts_as_follower/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
33
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ rails generate acts_as_follower
3
+
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
@@ -0,0 +1,30 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class ActsAsFollowerGenerator < Rails::Generators::Base
5
+
6
+ include Rails::Generators::Migration
7
+
8
+ def self.source_root
9
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
10
+ end
11
+
12
+ # Implement the required interface for Rails::Generators::Migration.
13
+ # taken from https://github.com/rails/rails/blob/master/activerecord/lib/rails/generators/active_record.rb
14
+ def self.next_migration_number(dirname)
15
+ if ActiveRecord::Base.timestamped_migrations
16
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
17
+ else
18
+ "%.3d" % (current_migration_number(dirname) + 1)
19
+ end
20
+ end
21
+
22
+ def create_migration_file
23
+ migration_template 'migration.rb', 'db/migrate/acts_as_follower_migration.rb'
24
+ end
25
+
26
+ def create_model
27
+ template "model.rb", File.join('app/models', "follow.rb")
28
+ end
29
+
30
+ end
@@ -0,0 +1,17 @@
1
+ class ActsAsFollowerMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :follows, force: true do |t|
4
+ t.references :followable, polymorphic: true, null: false
5
+ t.references :follower, polymorphic: true, null: false
6
+ t.boolean :blocked, default: false, null: false
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :follows, ["follower_id", "follower_type"], name: "fk_follows"
11
+ add_index :follows, ["followable_id", "followable_type"], name: "fk_followables"
12
+ end
13
+
14
+ def self.down
15
+ drop_table :follows
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ class Follow < ActiveRecord::Base
2
+
3
+ extend ActsAsFollower::FollowerLib
4
+ extend ActsAsFollower::FollowScopes
5
+
6
+ # NOTE: Follows belong to the "followable" and "follower" interface
7
+ belongs_to :followable, polymorphic: true
8
+ belongs_to :follower, polymorphic: true
9
+
10
+ def block!
11
+ self.update_attribute(:blocked, true)
12
+ end
13
+
14
+ end
data/test/README ADDED
@@ -0,0 +1,24 @@
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
+
6
+ 1. Clone your fork git locally.
7
+ 2. Install the dependencies
8
+ $ bundle install
9
+ 3. Run the tests:
10
+ rake test
11
+
12
+
13
+ Coverage
14
+ =======
15
+
16
+ Test coverage can be calculated using Rcov. Make sure you have the rcov gem installed.
17
+
18
+ Again in the acts_as_follower directory:
19
+
20
+ rake rcov:gen DB=sqlite3 # For sqlite
21
+
22
+ The coverage will now be available in the test/coverage directory.
23
+
24
+ rake rcov:clobber will delete the coverage directory.
@@ -0,0 +1,283 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ActsAsFollowableTest < ActiveSupport::TestCase
4
+
5
+ context "instance methods" do
6
+ setup do
7
+ @sam = FactoryGirl.create(: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 = FactoryGirl.create(:sam)
20
+ @jon = FactoryGirl.create(:jon)
21
+ @oasis = FactoryGirl.create(:oasis)
22
+ @metallica = FactoryGirl.create(:metallica)
23
+ @green_day = FactoryGirl.create(:green_day)
24
+ @blink_182 = FactoryGirl.create(:blink_182)
25
+ @sam.follow(@jon)
26
+ end
27
+
28
+ context "followers_count" do
29
+ should "return the number of followers" do
30
+ assert_equal 0, @sam.followers_count
31
+ assert_equal 1, @jon.followers_count
32
+ end
33
+
34
+ should "return the proper number of multiple followers" do
35
+ @bob = FactoryGirl.create(:bob)
36
+ @sam.follow(@bob)
37
+ assert_equal 0, @sam.followers_count
38
+ assert_equal 1, @jon.followers_count
39
+ assert_equal 1, @bob.followers_count
40
+ end
41
+ end
42
+
43
+ context "followers" do
44
+ should "return users" do
45
+ assert_equal [], @sam.followers
46
+ assert_equal [@sam], @jon.followers
47
+ end
48
+
49
+ should "return users (multiple followers)" do
50
+ @bob = FactoryGirl.create(:bob)
51
+ @sam.follow(@bob)
52
+ assert_equal [], @sam.followers
53
+ assert_equal [@sam], @jon.followers
54
+ assert_equal [@sam], @bob.followers
55
+ end
56
+
57
+ should "return users (multiple followers, complex)" do
58
+ @bob = FactoryGirl.create(:bob)
59
+ @sam.follow(@bob)
60
+ @jon.follow(@bob)
61
+ assert_equal [], @sam.followers
62
+ assert_equal [@sam], @jon.followers
63
+ assert_equal [@sam, @jon], @bob.followers
64
+ end
65
+
66
+ should "accept AR options" do
67
+ @bob = FactoryGirl.create(:bob)
68
+ @bob.follow(@jon)
69
+ assert_equal 1, @jon.followers(limit: 1).count
70
+ end
71
+ end
72
+
73
+ context "followed_by" do
74
+ should "return_follower_status" do
75
+ assert_equal true, @jon.followed_by?(@sam)
76
+ assert_equal false, @sam.followed_by?(@jon)
77
+ end
78
+ end
79
+
80
+ context "destroying a followable" do
81
+ setup do
82
+ @jon.destroy
83
+ end
84
+
85
+ should_change("follow count", by: -1) { Follow.count }
86
+ should_change("@sam.all_following.size", by: -1) { @sam.all_following.size }
87
+ end
88
+
89
+ context "get follow record" do
90
+ setup do
91
+ @bob = FactoryGirl.create(:bob)
92
+ @follow = @bob.follow(@sam)
93
+ end
94
+
95
+ should "return follow record" do
96
+ assert_equal @follow, @sam.get_follow_for(@bob)
97
+ end
98
+
99
+ should "return nil" do
100
+ assert_nil @sam.get_follow_for(@jon)
101
+ end
102
+ end
103
+
104
+ context "blocks" do
105
+ setup do
106
+ @bob = FactoryGirl.create(:bob)
107
+ @jon.block(@sam)
108
+ @jon.block(@bob)
109
+ end
110
+
111
+ should "accept AR options" do
112
+ assert_equal 1, @jon.blocks(limit: 1).count
113
+ end
114
+ end
115
+
116
+ context "blocking a follower" do
117
+ context "in my following list" do
118
+ setup do
119
+ @jon.block(@sam)
120
+ end
121
+
122
+ should "remove him from followers" do
123
+ assert_equal 0, @jon.followers_count
124
+ end
125
+
126
+ should "add him to the blocked followers" do
127
+ assert_equal 1, @jon.blocked_followers_count
128
+ end
129
+
130
+ should "not be able to follow again" do
131
+ @jon.follow(@sam)
132
+ assert_equal 0, @jon.followers_count
133
+ end
134
+
135
+ should "not be present when listing followers" do
136
+ assert_equal [], @jon.followers
137
+ end
138
+
139
+ should "be in the list of blocks" do
140
+ assert_equal [@sam], @jon.blocks
141
+ end
142
+ end
143
+
144
+ context "not in my following list" do
145
+ setup do
146
+ @sam.block(@jon)
147
+ end
148
+
149
+ should "add him to the blocked followers" do
150
+ assert_equal 1, @sam.blocked_followers_count
151
+ end
152
+
153
+ should "not be able to follow again" do
154
+ @sam.follow(@jon)
155
+ assert_equal 0, @sam.followers_count
156
+ end
157
+
158
+ should "not be present when listing followers" do
159
+ assert_equal [], @sam.followers
160
+ end
161
+
162
+ should "be in the list of blocks" do
163
+ assert_equal [@jon], @sam.blocks
164
+ end
165
+ end
166
+ end
167
+
168
+ context "unblocking a blocked follow" do
169
+ setup do
170
+ @jon.block(@sam)
171
+ @jon.unblock(@sam)
172
+ end
173
+
174
+ should "not include the unblocked user in the list of followers" do
175
+ assert_equal [], @jon.followers
176
+ end
177
+
178
+ should "remove him from the blocked followers" do
179
+ assert_equal 0, @jon.blocked_followers_count
180
+ assert_equal [], @jon.blocks
181
+ end
182
+ end
183
+
184
+ context "unblock a non-existent follow" do
185
+ setup do
186
+ @sam.stop_following(@jon)
187
+ @jon.unblock(@sam)
188
+ end
189
+
190
+ should "not be in the list of followers" do
191
+ assert_equal [], @jon.followers
192
+ end
193
+
194
+ should "not be in the blocked followers count" do
195
+ assert_equal 0, @jon.blocked_followers_count
196
+ end
197
+
198
+ should "not be in the blocks list" do
199
+ assert_equal [], @jon.blocks
200
+ end
201
+ end
202
+
203
+ context "followers_by_type" do
204
+ setup do
205
+ @sam.follow(@oasis)
206
+ @jon.follow(@oasis)
207
+ end
208
+
209
+ should "return the followers for given type" do
210
+ assert_equal [@sam], @jon.followers_by_type('User')
211
+ assert_equal [@sam, @jon], @oasis.followers_by_type('User')
212
+ end
213
+
214
+ should "not return block followers in the followers for a given type" do
215
+ @oasis.block(@jon)
216
+ assert_equal [@sam], @oasis.followers_by_type('User')
217
+ end
218
+
219
+ should "return the count for followers_by_type_count for a given type" do
220
+ assert_equal 1, @jon.followers_by_type_count('User')
221
+ assert_equal 2, @oasis.followers_by_type_count('User')
222
+ end
223
+
224
+ should "not count blocked follows in the count" do
225
+ @oasis.block(@sam)
226
+ assert_equal 1, @oasis.followers_by_type_count('User')
227
+ end
228
+ end
229
+
230
+ context "followers_with_sti" do
231
+ setup do
232
+ @sam.follow(@green_day)
233
+ @sam.follow(@blink_182)
234
+ end
235
+
236
+ should "return the followers for given type" do
237
+ assert_equal @sam.follows_by_type('Band').first.followable, @green_day.becomes(Band)
238
+ assert_equal @sam.follows_by_type('Band').second.followable, @blink_182.becomes(Band)
239
+ assert @green_day.followers_by_type('User').include?(@sam)
240
+ assert @blink_182.followers_by_type('User').include?(@sam)
241
+ end
242
+ end
243
+
244
+ context "method_missing" do
245
+ setup do
246
+ @sam.follow(@oasis)
247
+ @jon.follow(@oasis)
248
+ end
249
+
250
+ should "return the followers for given type" do
251
+ assert_equal [@sam], @jon.user_followers
252
+ assert_equal [@sam, @jon], @oasis.user_followers
253
+ end
254
+
255
+ should "not return block followers in the followers for a given type" do
256
+ @oasis.block(@jon)
257
+ assert_equal [@sam], @oasis.user_followers
258
+ end
259
+
260
+ should "return the count for followers_by_type_count for a given type" do
261
+ assert_equal 1, @jon.count_user_followers
262
+ assert_equal 2, @oasis.count_user_followers
263
+ end
264
+
265
+ should "not count blocked follows in the count" do
266
+ @oasis.block(@sam)
267
+ assert_equal 1, @oasis.count_user_followers
268
+ end
269
+ end
270
+
271
+ context "respond_to?" do
272
+ should "advertise that it responds to following methods" do
273
+ assert @oasis.respond_to?(:user_followers)
274
+ assert @oasis.respond_to?(:user_followers_count)
275
+ end
276
+
277
+ should "return false when called with a nonexistent method" do
278
+ assert (not @oasis.respond_to?(:foobar))
279
+ end
280
+ end
281
+
282
+ end
283
+ end