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.
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