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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +44 -0
- data/README.rdoc +247 -0
- data/Rakefile +37 -0
- data/acts_as_follower.gemspec +29 -0
- data/init.rb +1 -0
- data/lib/acts_as_follower/follow_scopes.rb +45 -0
- data/lib/acts_as_follower/followable.rb +113 -0
- data/lib/acts_as_follower/follower.rb +112 -0
- data/lib/acts_as_follower/follower_lib.rb +42 -0
- data/lib/acts_as_follower/railtie.rb +14 -0
- data/lib/acts_as_follower/version.rb +3 -0
- data/lib/acts_as_follower1.rb +33 -0
- data/lib/generators/USAGE +5 -0
- data/lib/generators/acts_as_follower_generator.rb +30 -0
- data/lib/generators/templates/migration.rb +17 -0
- data/lib/generators/templates/model.rb +14 -0
- data/test/README +24 -0
- data/test/acts_as_followable_test.rb +283 -0
- data/test/acts_as_follower_test.rb +224 -0
- data/test/dummy30/Gemfile +1 -0
- data/test/dummy30/Rakefile +7 -0
- data/test/dummy30/app/models/application_record.rb +3 -0
- data/test/dummy30/app/models/band.rb +4 -0
- data/test/dummy30/app/models/band/punk.rb +4 -0
- data/test/dummy30/app/models/band/punk/pop_punk.rb +4 -0
- data/test/dummy30/app/models/custom_record.rb +3 -0
- data/test/dummy30/app/models/some.rb +5 -0
- data/test/dummy30/app/models/user.rb +5 -0
- data/test/dummy30/config.ru +4 -0
- data/test/dummy30/config/application.rb +42 -0
- data/test/dummy30/config/boot.rb +10 -0
- data/test/dummy30/config/database.yml +7 -0
- data/test/dummy30/config/environment.rb +5 -0
- data/test/dummy30/config/environments/development.rb +19 -0
- data/test/dummy30/config/environments/test.rb +20 -0
- data/test/dummy30/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy30/config/initializers/inflections.rb +10 -0
- data/test/dummy30/config/initializers/secret_token.rb +7 -0
- data/test/dummy30/config/initializers/session_store.rb +8 -0
- data/test/dummy30/config/locales/en.yml +5 -0
- data/test/dummy30/config/routes.rb +2 -0
- data/test/factories/bands.rb +17 -0
- data/test/factories/somes.rb +9 -0
- data/test/factories/users.rb +13 -0
- data/test/follow_test.rb +28 -0
- data/test/schema.rb +25 -0
- data/test/test_helper.rb +18 -0
- 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,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,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
|