acts_as_favoritor 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +21 -0
  6. data/README.md +73 -0
  7. data/Rakefile +37 -0
  8. data/acts_as_favoritor.gemspec +29 -0
  9. data/init.rb +1 -0
  10. data/lib/acts_as_favoritor.rb +35 -0
  11. data/lib/acts_as_favoritor/favoritable.rb +115 -0
  12. data/lib/acts_as_favoritor/favorite_scopes.rb +45 -0
  13. data/lib/acts_as_favoritor/favoritor.rb +113 -0
  14. data/lib/acts_as_favoritor/favoritor_lib.rb +43 -0
  15. data/lib/acts_as_favoritor/railtie.rb +16 -0
  16. data/lib/acts_as_favoritor/version.rb +5 -0
  17. data/lib/generators/USAGE +5 -0
  18. data/lib/generators/acts_as_follower_generator.rb +28 -0
  19. data/lib/generators/templates/migration.rb.erb +18 -0
  20. data/lib/generators/templates/model.rb +16 -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 +6 -0
  37. data/test/dummy30/config/environment.rb +5 -0
  38. data/test/dummy30/config/environments/development.rb +18 -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 +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,5 @@
1
+ module ActsAsFavoritor
2
+
3
+ VERSION = '1.0.0'
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ rails generate acts_as_favoritor
3
+
4
+ no need to specify a name after acts_as_favoritor as you can not change the model name from Favorite
5
+ the acts_as_favoritor_migration file will be created in db/migrate
@@ -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