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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fa5428168c7b8609f60f208889cdc00039ec182e
4
+ data.tar.gz: 0c44c7a38d6a165069afa96cba0eeff74eee1d16
5
+ SHA512:
6
+ metadata.gz: 675ee97d8c2d37bd81627d738fa629eff5dff73558962b8e3e3862f4095ca420b278e000a066ad06cc8182d5d25a2143ba693f2a78e747b863b87d4c36d0a73a
7
+ data.tar.gz: 64e88864d50a179520792857dfcdb70f9b759f3097b92a23271f02b709d59bbd8d0722986872255613d2085f7a3de289aa4b13e6c34a05f15d1c4ecbcd273408
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ test/debug.log
2
+ coverage
3
+ rdoc
4
+ memory
5
+ *.gem
6
+ .rvmrc
7
+ Gemfile.lock
8
+ log
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.3
4
+ install: bundle install --without development --deployment --jobs=3 --retry=3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
4
+
5
+
6
+ gem 'activesupport', ENV['RAILS_VERSION'] if ENV['RAILS_VERSION']
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Slooob
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # acts_as_favoitor - Add Favorites to you Rails app
2
+
3
+ <img src="https://travis-ci.org/slooob/acts_as_favoritor.svg?branch=master" />
4
+
5
+ acts_as_favoritor is a Rubygem to allow any ActiveRecord model to favorite any other model. This is accomplished through a double polymorphic relationship on the Favorite model. There is also built in support for blocking/un-blocking favorite records.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ acts_as_favoritor works with Rails 4.0 onwards. You can add it to your `Gemfile` with:
12
+
13
+ ```ruby
14
+ gem 'acts_as_favoritor'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install acts_as_favoritor
24
+
25
+ If you always want to be up to date fetch the latest from GitHub in your `Gemfile`:
26
+
27
+ ```ruby
28
+ gem 'amphtml', github: 'slooob/acts_as_favoritor'
29
+ ```
30
+
31
+ Now run the generator:
32
+
33
+ $ rails g acts_as_favoritor
34
+
35
+ $ rails db:migrate
36
+
37
+ This will create a Favorite model as well as a migration file.
38
+
39
+ **Note:** Use `rake db:migrate` instead if you run Rails < 5.
40
+
41
+ ## Usage
42
+
43
+ ---
44
+
45
+ ## Contributors
46
+
47
+ Give the people some :heart: who are working on this project. Check them all at:
48
+
49
+ https://github.com/slooob/acts_as_favoritor/graphs/contributors
50
+
51
+ ## License
52
+
53
+ MIT License
54
+
55
+ Copyright (c) 2017 Jonas Hübotter
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining a copy
58
+ of this software and associated documentation files (the "Software"), to deal
59
+ in the Software without restriction, including without limitation the rights
60
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
61
+ copies of the Software, and to permit persons to whom the Software is
62
+ furnished to do so, subject to the following conditions:
63
+
64
+ The above copyright notice and this permission notice shall be included in all
65
+ copies or substantial portions of the Software.
66
+
67
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
73
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+ require 'rdoc/task'
7
+
8
+ desc 'Default: run unit tests.'
9
+ task default: :test
10
+
11
+ desc 'Test the acts_as_favoritor gem.'
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'lib'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+
18
+ desc 'Generate documentation for the acts_as_favoritor gem.'
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'Acts As Favoritor'
22
+ rdoc.main = 'README.rdoc'
23
+ rdoc.options << '--line-numbers' << '--inline-source'
24
+ rdoc.rdoc_files.include('README.rdoc', 'lib/**/*.rb')
25
+ end
26
+
27
+ namespace :rcov do
28
+ desc "Generate a coverage report in coverage/"
29
+ task :gen do
30
+ sh "rcov --output coverage test/*_test.rb --exclude 'gems/*'"
31
+ end
32
+
33
+ desc "Remove generated coverage files."
34
+ task :clobber do
35
+ sh "rm -rdf coverage"
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'acts_as_favoritor/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'acts_as_favoritor'
7
+ s.version = ActsAsFavoritor::VERSION
8
+ s.authors = ['Jonas Hübotter']
9
+ s.email = ['developer@slooob.com']
10
+ s.homepage = 'https://github.com/slooob/acts_as_favoritor'
11
+ s.summary = 'A Rubygem to add Favorite functionality for ActiveRecord models'
12
+ s.description = 'acts_as_favoritor is a Rubygem to allow any ActiveRecord model to favorite any other model. This is accomplished through a double polymorphic relationship on the Favorite model. There is also built in support for blocking/un-blocking favorite records.'
13
+ s.license = 'MIT'
14
+
15
+ s.rubyforge_project = 'acts_as_follower'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ['lib']
21
+
22
+ s.add_dependency 'activerecord', '>= 4.0'
23
+
24
+ s.add_development_dependency 'sqlite3', '~> 1'
25
+ s.add_development_dependency 'shoulda_create', '~> 0'
26
+ s.add_development_dependency 'shoulda', '~> 3'
27
+ s.add_development_dependency 'factory_girl', '~> 4'
28
+ s.add_development_dependency 'rails', '>= 4.0'
29
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'acts_as_favoritor'
@@ -0,0 +1,35 @@
1
+ require 'acts_as_favoritor/version'
2
+
3
+ module ActsAsFavoritor
4
+
5
+ autoload :Favoritor, 'acts_as_favoritor/favoritor'
6
+ autoload :Favoritable, 'acts_as_favoritor/favoritable'
7
+ autoload :FavoritorLib, 'acts_as_favoritor/favoritor_lib'
8
+ autoload :FavoriteScopes, 'acts_as_favoritor/favorite_scopes'
9
+
10
+ def self.setup
11
+ @configuration ||= Configuration.new
12
+ yield @configuration if block_given?
13
+ end
14
+
15
+ def self.method_missing method_name, *args, &block
16
+ if method_name == :custom_parent_classes=
17
+ ActiveSupport::Deprecation.warn 'Setting custom parent classes is deprecated and will be removed in future versions.'
18
+ end
19
+ @configuration.respond_to?(method_name) ?
20
+ @configuration.send(method_name, *args, &block) : super
21
+ end
22
+
23
+ class Configuration
24
+ attr_accessor :custom_parent_classes
25
+
26
+ def initialize
27
+ @custom_parent_classes = []
28
+ end
29
+ end
30
+
31
+ setup
32
+
33
+ require 'favoritor/railtie' if defined?(Rails) && Rails::VERSION::MAJOR >= 3
34
+
35
+ end
@@ -0,0 +1,115 @@
1
+ module ActsAsFavoritor #:nodoc:
2
+ module Favoritable
3
+
4
+ def self.included base
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_favoritable
10
+ has_many :favorited, as: :favoritable, dependent: :destroy, class_name: 'Favorite'
11
+ include ActsAsFavoritor::Favoritable::InstanceMethods
12
+ include ActsAsFavoritor::FavoritorLib
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+
18
+ # Returns the number of favoritors a record has.
19
+ def favoritors_count
20
+ self.favorited.unblocked.count
21
+ end
22
+
23
+ # Returns the favoritors by a given type
24
+ def favoritors_by_type favoritor_type, options = {}
25
+ favorites = favoritor_type.constantize.
26
+ joins(:favorites).
27
+ where('favorites.blocked': false,
28
+ 'favorites.followable_id': self.id,
29
+ 'favorites.followable_type': parent_class_name(self),
30
+ 'favorites.follower_type': favoritor_type)
31
+ if options.has_key? :limit
32
+ favorites = favorites.limit options[:limit]
33
+ end
34
+ if options.has_key? :includes
35
+ favorites = favorites.includes options[:includes]
36
+ end
37
+
38
+ favorites
39
+ end
40
+
41
+ def favoritors_by_type_count favoritor_type
42
+ self.favorited.unblocked.for_favoritor_type(favoritor_type).count
43
+ end
44
+
45
+ # Allows magic names on favoritors_by_type
46
+ # e.g. user_favoritors == favoritors_by_type 'User'
47
+ # Allows magic names on favoritors_by_type_count
48
+ # e.g. count_user_favoritors == favoritors_by_type_count 'User'
49
+ def method_missing m, *args
50
+ if m.to_s[/count_(.+)_favoritors/]
51
+ favoritors_by_type_count $1.singularize.classify
52
+ elsif m.to_s[/(.+)_favoritors/]
53
+ favoritors_by_type $1.singularize.classify
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ def respond_to? m, include_private = false
60
+ super || m.to_s[/count_(.+)_favoritors/] || m.to_s[/(.+)_favoritors/]
61
+ end
62
+
63
+ def blocked_favoritors_count
64
+ self.favorited.blocked.count
65
+ end
66
+
67
+ # Returns the favorited records scoped
68
+ def favoritors_scoped
69
+ self.favorited.includes :favoritor
70
+ end
71
+
72
+ def favoritors options = {}
73
+ favoritors_scope = favoritors_scoped.unblocked
74
+ favoritors_scope = apply_options_to_scope favoritors_scope, options
75
+ favoritors_scope.to_a.collect{ |f| f.favoritor }
76
+ end
77
+
78
+ def blocks options = {}
79
+ blocked_favoritors_scope = favoritors_scoped.blocked
80
+ blocked_favoritors_scope = apply_options_to_scope blocked_favoritors_scope, options
81
+ blocked_favoritors_scope.to_a.collect{ |f| f.favoritor }
82
+ end
83
+
84
+ # Returns true if the current instance is favorited by the passed record
85
+ # Returns false if the current instance is blocked by the passed record or no favorite is found
86
+ def favorited_by? favoritor
87
+ self.favorited.unblocked.for_favoritor(favoritor).first.present?
88
+ end
89
+
90
+ def block favoritor
91
+ get_favorite_for(favoritor) ? block_existing_favorite(favoritor) : block_future_favorite(favoritor)
92
+ end
93
+
94
+ def unblock favoritor
95
+ get_favorite_for(favoritor).try(:delete)
96
+ end
97
+
98
+ def get_favorite_for favoritor
99
+ self.favorited.for_favoritor(favoritor).first
100
+ end
101
+
102
+ private
103
+
104
+ def block_future_favorite favoritor
105
+ Favorite.create favoritable: self, favoritor: favoritor, blocked: true
106
+ end
107
+
108
+ def block_existing_favorite favoritor
109
+ get_favorite_for(favoritor).block!
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,45 @@
1
+ module ActsAsFavoritor #:nodoc:
2
+ module FavoriteScopes
3
+
4
+ # returns favorite records where favoritor is the record passed in.
5
+ def for_favoritor favoritor
6
+ where favoritor_id: favoritor.id, favoritor_type: parent_class_name(favoritor)
7
+ end
8
+
9
+ # returns favorite records where favoritable is the record passed in.
10
+ def for_favoritable favoritable
11
+ where favoritable_id: favoritable.id, favoritable_type: parent_class_name(favoritable)
12
+ end
13
+
14
+ # returns favorite records where favoritor_type is the record passed in.
15
+ def for_favoritor_type favoritor_type
16
+ where favoritor_type: favoritor_type
17
+ end
18
+
19
+ # returns favorite records where favoritable_type is the record passed in.
20
+ def for_favoritable_type favoritable_type
21
+ where favoritable_type: favoritable_type
22
+ end
23
+
24
+ # returns favorite records from past 2 weeks with default parameter.
25
+ def recent from
26
+ where ['created_at > ?', (from || 2.weeks.ago).to_s(:db)]
27
+ end
28
+
29
+ # returns favorite records in descending order.
30
+ def descending
31
+ order 'favorites.created_at desc'
32
+ end
33
+
34
+ # returns unblocked favorite records.
35
+ def unblocked
36
+ where blocked: false
37
+ end
38
+
39
+ # returns blocked favorite records.
40
+ def blocked
41
+ where blocked: true
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,113 @@
1
+ module ActsAsFavoritor #:nodoc:
2
+ module Follower
3
+
4
+ def self.included base
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_favoritor
10
+ has_many :favorites, as: :favoritor, dependent: :destroy
11
+ include ActsAsFavoritor::Favoritor::InstanceMethods
12
+ include ActsAsFavoritor::FavoritorLib
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 favorited? favoritable
20
+ 0 < Favorite.unblocked.for_favoritor(self).for_favoritable(favoritable).count
21
+ end
22
+
23
+ # Returns the number of objects this instance is following.
24
+ def favorites_count
25
+ Favorite.unblocked.for_favoritor(self).count
26
+ end
27
+
28
+ # Creates a new favorite record for this instance to favorite the passed object.
29
+ # Does not allow duplicate records to be created.
30
+ def favorite favoritable
31
+ if self != favoritable
32
+ params = {favoritable_id: favoritable.id, favoritable_type: parent_class_name(favoritable)}
33
+ self.favorites.where(params).first_or_create!
34
+ end
35
+ end
36
+
37
+ # Deletes the favorite record if it exists.
38
+ def remove_favorite favoritable
39
+ if favorite = get_Favoritor favoritable
40
+ favorite.destroy
41
+ end
42
+ end
43
+
44
+ # returns the favorite records to the current instance
45
+ def favorites_scoped
46
+ self.favorites.unblocked.includes :favoritable
47
+ end
48
+
49
+ # Returns the favorite records related to this instance by type.
50
+ def favorites_by_type favoritable_type, options={}
51
+ favorites_scope = favorites_scoped.for_favoritable_type favoritable_type
52
+ favorites_scope = apply_options_to_scope favorites_scope, options
53
+ end
54
+
55
+ # Returns the favorite records related to this instance with the favoritable included.
56
+ def all_favorites options = {}
57
+ favorites_scope = favorites_scoped
58
+ favorites_scope = apply_options_to_scope favorites_scope, options
59
+ end
60
+
61
+ # Returns the actual records which this instance has favorited.
62
+ def all_favorited options = {}
63
+ all_favorites(options).collect{ |f| f.favoritable }
64
+ end
65
+
66
+ # Returns the actual records of a particular type which this record has fovarited.
67
+ def favorited_by_type favoritable_type, options = {}
68
+ favoritables = favoritable_type.constantize.
69
+ joins(:favorited).
70
+ where('favorites.blocked': false,
71
+ 'favorites.favoritor_id': self.id,
72
+ 'favorites.favoritor_type': parent_class_name(self),
73
+ 'favorites.favoritable_type': favoritable_type)
74
+ if options.has_key? :limit
75
+ favoritables = favoritables.limit options[:limit]
76
+ end
77
+ if options.has_key? :includes
78
+ favoritables = favoritables.includes options[:includes]
79
+ end
80
+ favoritables
81
+ end
82
+
83
+ def favorited_by_type_count favoritable_type
84
+ favorites.unblocked.for_favoritable_type(favoritable_type).count
85
+ end
86
+
87
+ # Allows magic names on favorited_by_type
88
+ # e.g. favorited_users == favorited_by_type 'User'
89
+ # Allows magic names on favorited_by_type_count
90
+ # e.g. favorited_users_count == favorited_by_type_count 'User'
91
+ def method_missing m, *args
92
+ if m.to_s[/favorited_(.+)_count/]
93
+ favorited_by_type_count $1.singularize.classify
94
+ elsif m.to_s[/favorited_(.+)/]
95
+ favorited_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[/favorited_(.+)_count/] || m.to_s[/favorited_(.+)/]
103
+ end
104
+
105
+ # Returns a favorite record for the current instance and favoritable object.
106
+ def get_favorite favoritable
107
+ self.favorites.unblocked.for_favoritable(favoritable).first
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+ end