acts_as_favoritor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +4 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +73 -0
- data/Rakefile +37 -0
- data/acts_as_favoritor.gemspec +29 -0
- data/init.rb +1 -0
- data/lib/acts_as_favoritor.rb +35 -0
- data/lib/acts_as_favoritor/favoritable.rb +115 -0
- data/lib/acts_as_favoritor/favorite_scopes.rb +45 -0
- data/lib/acts_as_favoritor/favoritor.rb +113 -0
- data/lib/acts_as_favoritor/favoritor_lib.rb +43 -0
- data/lib/acts_as_favoritor/railtie.rb +16 -0
- data/lib/acts_as_favoritor/version.rb +5 -0
- data/lib/generators/USAGE +5 -0
- data/lib/generators/acts_as_follower_generator.rb +28 -0
- data/lib/generators/templates/migration.rb.erb +18 -0
- data/lib/generators/templates/model.rb +16 -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 +6 -0
- data/test/dummy30/config/environment.rb +5 -0
- data/test/dummy30/config/environments/development.rb +18 -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 +181 -0
@@ -0,0 +1,43 @@
|
|
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 ActsAsFavoritor.custom_parent_classes
|
37
|
+
|
38
|
+
ActiveSupport::Deprecation.warn('Setting custom parent classes is deprecated and will be removed in future versions.')
|
39
|
+
ActsAsFavoritor.custom_parent_classes + DEFAULT_PARENTS
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module ActsAsFavoritor
|
4
|
+
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
|
7
|
+
initializer 'acts_as_favoritor.active_record' do |app|
|
8
|
+
ActiveSupport.on_load :active_record do
|
9
|
+
include ActsAsFavoritor::Favoritor
|
10
|
+
include ActsAsFavoritor::Favoritable
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
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
|
+
def self.next_migration_number dirname
|
13
|
+
if ActiveRecord::Base.timestamped_migrations
|
14
|
+
Time.now.utc.strftime '%Y%m%d%H%M%S'
|
15
|
+
else
|
16
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_migration_file
|
21
|
+
migration_template 'migration.rb.erb', 'db/migrate/acts_as_favoritor_migration.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_model
|
25
|
+
template 'model.rb', 'app/models/follow.rb'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class ActsAsFavoritorMigration < ActiveRecord::Migration<% if Rails::VERSION::MAJOR >= 5 %>[<%= Rails::VERSION %>]<% end %>
|
2
|
+
def self.up
|
3
|
+
create_table :favorites, force: true do |t|
|
4
|
+
t.references :favoritable, polymorphic: true, null: false
|
5
|
+
t.references :favoritor, polymorphic: true, null: false
|
6
|
+
t.string :types, default: [:favorite].to_yaml, null: false
|
7
|
+
t.boolean :blocked, default: false, null: false
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :favorites, ['favoritor_id', 'favoritor_type'], name: 'fk_favorites'
|
12
|
+
add_index :favorites, ['favoritable_id', 'favoritable_type'], name: 'fk_favoritables'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :favorites
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Favorite < ActiveRecord::Base
|
2
|
+
|
3
|
+
extend ActsAsFavoritor::FavoritorLib
|
4
|
+
extend ActsAsFavoritor::FavoriteScopes
|
5
|
+
|
6
|
+
serialize :types
|
7
|
+
|
8
|
+
# NOTE: Follows belong to the 'favoritable' and 'favoritor' interface
|
9
|
+
belongs_to :favoritable, polymorphic: true
|
10
|
+
belongs_to :favoritor, polymorphic: true
|
11
|
+
|
12
|
+
def block!
|
13
|
+
self.update_attribute :blocked, true
|
14
|
+
end
|
15
|
+
|
16
|
+
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_favoritor 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.remove_favorite(@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
|