seems_rateable 1.0.13 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +20 -0
  3. data/Gemfile +3 -0
  4. data/README.md +41 -61
  5. data/Rakefile +8 -2
  6. data/app/assets/javascripts/seems_rateable/index.js +1 -0
  7. data/{lib/generators/seems_rateable/install/templates → app/assets/javascripts/seems_rateable}/jRating.js.erb +225 -225
  8. data/app/assets/stylesheets/seems_rateable/{application.css → index.css.erb} +2 -2
  9. data/app/controllers/seems_rateable/application_controller.rb +1 -3
  10. data/app/controllers/seems_rateable/rates_controller.rb +31 -0
  11. data/app/models/seems_rateable/rate.rb +2 -2
  12. data/bin/rails +8 -0
  13. data/config/routes.rb +1 -1
  14. data/lib/generators/seems_rateable/install/install_generator.rb +34 -28
  15. data/lib/generators/seems_rateable/install/templates/initializer.rb +5 -2
  16. data/lib/generators/seems_rateable/install/templates/rateable.js.erb +12 -22
  17. data/lib/generators/seems_rateable/install/templates/rates_migration.rb +6 -9
  18. data/lib/generators/seems_rateable/manifest_finder.rb +25 -0
  19. data/lib/generators/seems_rateable/migration_helpers.rb +21 -0
  20. data/lib/generators/seems_rateable/uninstall/templates/drop_seems_rateable_rates_table.rb +9 -0
  21. data/lib/generators/seems_rateable/uninstall/uninstall_generator.rb +50 -0
  22. data/lib/generators/seems_rateable/uninstall_old/templates/drop_seems_rateable_cached_ratings_table.rb +9 -0
  23. data/lib/generators/seems_rateable/uninstall_old/templates/drop_seems_rateable_rates_table.rb +9 -0
  24. data/lib/generators/seems_rateable/uninstall_old/uninstall_old_generator.rb +48 -0
  25. data/lib/seems_rateable.rb +22 -7
  26. data/lib/seems_rateable/builder.rb +35 -0
  27. data/lib/seems_rateable/builder/html_options.rb +37 -0
  28. data/lib/seems_rateable/configuration.rb +23 -0
  29. data/lib/seems_rateable/engine.rb +12 -7
  30. data/lib/seems_rateable/errors.rb +3 -17
  31. data/lib/seems_rateable/helpers/action_view_extension.rb +18 -0
  32. data/lib/seems_rateable/helpers/current_rater.rb +15 -0
  33. data/lib/seems_rateable/models/active_record_extension.rb +23 -0
  34. data/lib/seems_rateable/models/active_record_extension/rateable.rb +26 -0
  35. data/lib/seems_rateable/models/active_record_extension/rater.rb +11 -0
  36. data/lib/seems_rateable/rating.rb +19 -0
  37. data/lib/seems_rateable/routes.rb +1 -1
  38. data/lib/seems_rateable/version.rb +1 -1
  39. data/seems_rateable.gemspec +32 -0
  40. data/spec/builder_spec.rb +53 -0
  41. data/spec/controllers/rates_controller_spec.rb +56 -0
  42. data/spec/dummy/README.rdoc +28 -0
  43. data/spec/dummy/Rakefile +6 -0
  44. data/{app/assets/javascripts/seems_rateable → spec/dummy/app/assets/javascripts}/application.js +4 -3
  45. data/spec/dummy/app/assets/javascripts/posts.js +2 -0
  46. data/spec/dummy/app/assets/javascripts/rateable/rateable.js.erb +15 -0
  47. data/spec/dummy/app/assets/stylesheets/application.css +19 -0
  48. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  49. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  50. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  51. data/spec/dummy/app/controllers/posts_controller.rb +58 -0
  52. data/spec/dummy/app/models/.keep +0 -0
  53. data/spec/dummy/app/models/concerns/.keep +0 -0
  54. data/spec/dummy/app/models/post.rb +3 -0
  55. data/spec/dummy/app/models/user.rb +5 -0
  56. data/spec/dummy/app/views/layouts/application.html.erb +26 -0
  57. data/spec/dummy/app/views/posts/_form.html.erb +21 -0
  58. data/spec/dummy/app/views/posts/edit.html.erb +6 -0
  59. data/spec/dummy/app/views/posts/index.html.erb +28 -0
  60. data/spec/dummy/app/views/posts/new.html.erb +5 -0
  61. data/spec/dummy/app/views/posts/show.html.erb +9 -0
  62. data/spec/dummy/bin/bundle +3 -0
  63. data/spec/dummy/bin/rails +4 -0
  64. data/spec/dummy/bin/rake +4 -0
  65. data/spec/dummy/config.ru +4 -0
  66. data/spec/dummy/config/application.rb +42 -0
  67. data/spec/dummy/config/boot.rb +5 -0
  68. data/spec/dummy/config/database.yml +25 -0
  69. data/spec/dummy/config/environment.rb +5 -0
  70. data/spec/dummy/config/environments/development.rb +29 -0
  71. data/spec/dummy/config/environments/production.rb +80 -0
  72. data/spec/dummy/config/environments/test.rb +36 -0
  73. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  74. data/spec/dummy/config/initializers/devise.rb +254 -0
  75. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  76. data/spec/dummy/config/initializers/inflections.rb +16 -0
  77. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  78. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  79. data/spec/dummy/config/initializers/seems_rateable.rb +4 -0
  80. data/spec/dummy/config/initializers/session_store.rb +3 -0
  81. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  82. data/spec/dummy/config/locales/devise.en.yml +59 -0
  83. data/spec/dummy/config/locales/en.yml +23 -0
  84. data/spec/dummy/config/routes.rb +8 -0
  85. data/spec/dummy/db/migrate/20140506144141_create_posts.rb +9 -0
  86. data/spec/dummy/db/migrate/20140506144841_devise_create_users.rb +42 -0
  87. data/spec/dummy/db/migrate/20140520170316_create_seems_rateable_rates.rb +15 -0
  88. data/spec/dummy/db/schema.rb +54 -0
  89. data/spec/dummy/lib/assets/.keep +0 -0
  90. data/spec/dummy/public/404.html +58 -0
  91. data/spec/dummy/public/422.html +58 -0
  92. data/spec/dummy/public/500.html +57 -0
  93. data/spec/dummy/public/favicon.ico +0 -0
  94. data/spec/factories/posts.rb +5 -0
  95. data/spec/factories/rate.rb +7 -0
  96. data/spec/factories/users.rb +7 -0
  97. data/spec/features/rating_spec.rb +63 -0
  98. data/spec/helpers/action_view_extension_spec.rb +59 -0
  99. data/spec/models/active_record_extension_spec.rb +105 -0
  100. data/spec/models/rate_spec.rb +6 -0
  101. data/spec/rating_spec.rb +70 -0
  102. data/spec/spec_helper.rb +45 -0
  103. data/spec/support/dummies.rb +19 -0
  104. metadata +263 -25
  105. data/app/controllers/seems_rateable/ratings_controller.rb +0 -13
  106. data/app/models/seems_rateable/cached_rating.rb +0 -5
  107. data/lib/generators/seems_rateable/install/templates/cached_ratings_migration.rb +0 -17
  108. data/lib/seems_rateable/helpers.rb +0 -27
  109. data/lib/seems_rateable/model.rb +0 -91
@@ -0,0 +1,35 @@
1
+ require_relative 'builder/html_options'
2
+
3
+ module SeemsRateable
4
+ class Builder
5
+ include ActionView::Helpers::TagHelper
6
+
7
+ attr_reader :rateable, :options
8
+
9
+ def self.build(rateable, options)
10
+ new(rateable, options).build
11
+ end
12
+
13
+ def initialize(rateable, options)
14
+ @rateable, @options = rateable, options
15
+ end
16
+
17
+ def build
18
+ content_tag :div, nil, html_options
19
+ end
20
+
21
+ private
22
+
23
+ def html_options
24
+ HtmlOptions.new(rating, options).to_h
25
+ end
26
+
27
+ def rating
28
+ rateable.rating dimension
29
+ end
30
+
31
+ def dimension
32
+ options.delete(:dimension)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ module SeemsRateable
2
+ class Builder
3
+ class HtmlOptions
4
+ attr_reader :rating, :options
5
+
6
+ def initialize(rating, options)
7
+ @rating, @options = rating, options
8
+ end
9
+
10
+ def to_h
11
+ options[:class] = "#{klass} #{disabled}".strip
12
+ options[:data] = options.fetch(:data, {}).merge(rating_attributes)
13
+ options
14
+ end
15
+
16
+ private
17
+
18
+ def rating_attributes
19
+ rate_params.merge average: rating.average
20
+ end
21
+
22
+ # Avoids passing nil dimension to the data html attrbitutes
23
+ # because it gets processed as data-dimension="null"
24
+ def rate_params
25
+ rating.rates.where_values_hash.keep_if { |k, v| v }
26
+ end
27
+
28
+ def klass
29
+ options[:class] || SeemsRateable.config.default_selector_class
30
+ end
31
+
32
+ def disabled
33
+ 'jDisabled' if options[:disabled]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module SeemsRateable
2
+ def self.config
3
+ @config ||= Configuration.new
4
+ end
5
+
6
+ def self.configure(&block)
7
+ yield config if block_given?
8
+ end
9
+
10
+ class Configuration
11
+ include ActiveSupport::Configurable
12
+
13
+ config_accessor :rate_owner_class
14
+ config_accessor :current_rater_method
15
+ config_accessor :default_selector_class
16
+ end
17
+
18
+ configure do |config|
19
+ config.rate_owner_class = 'User'
20
+ config.current_rater_method = :current_user
21
+ config.default_selector_class = 'rateable'
22
+ end
23
+ end
@@ -2,15 +2,20 @@ module SeemsRateable
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace SeemsRateable
4
4
 
5
- config.generators do |g|
6
- g.test_framework :rspec, :fixture => false
7
- g.fixture_replacement :factory_girl, :dir => 'spec/factories'
8
- end
9
-
10
5
  initializer :seems_rateable do
11
- ActiveRecord::Base.send :include, SeemsRateable::Model
12
- ActionView::Base.send :include, SeemsRateable::Helpers
6
+ ::ActiveSupport.on_load(:active_record) { extend SeemsRateable::Models::ActiveRecordExtension }
7
+ ::ActiveSupport.on_load(:action_view) { include SeemsRateable::Helpers::ActionViewExtension }
8
+ ::ActiveSupport.on_load(:action_controller) { include SeemsRateable::Helpers::CurrentRater }
13
9
  ActionDispatch::Routing::Mapper.send :include, SeemsRateable::Routes
14
10
  end
11
+
12
+ config.after_initialize do
13
+ require 'seems_rateable/application_controller'
14
+ end
15
+
16
+ config.generators do |g|
17
+ g.test_framework :rspec, fixture: false
18
+ g.fixture_replacement :factory_girl, dir: 'spec/factories'
19
+ end
15
20
  end
16
21
  end
@@ -1,21 +1,7 @@
1
1
  module SeemsRateable
2
2
  module Errors
3
- class InvalidRateableObjectError < StandardError
4
- def to_s
5
- "Stated object is not rateable. Add 'seems_rateable' to your object's class model."
6
- end
7
- end
8
-
9
- class NoCurrentUserInstanceError < StandardError
10
- def to_s
11
- "User instance current_user is not available."
12
- end
13
- end
14
-
15
- class AlreadyRatedError < StandardError
16
- def to_s
17
- "User has already rated an object."
18
- end
19
- end
3
+ class InvalidRateableError < StandardError;end
4
+ class NonExistentDimension < StandardError;end
5
+ class NoCurrentRaterError < StandardError;end
20
6
  end
21
7
  end
@@ -0,0 +1,18 @@
1
+ module SeemsRateable
2
+ module Helpers
3
+ module ActionViewExtension
4
+ def rating_for(rateable, options={})
5
+ unless rateable.kind_of? Models::ActiveRecordExtension::Rateable
6
+ raise Errors::InvalidRateableError, "#{rateable.inspect} is not a rateable" +
7
+ " object, try adding 'seems_rateable' into your object's model"
8
+ end
9
+
10
+ if !current_rater || rateable.rated_by?(current_rater, options[:dimension])
11
+ options[:disabled] = true
12
+ end
13
+
14
+ SeemsRateable::Builder.build(rateable, options)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module SeemsRateable
2
+ module Helpers
3
+ module CurrentRater
4
+ extend ActiveSupport::Concern
5
+
6
+ def current_rater
7
+ send SeemsRateable.config.current_rater_method
8
+ end
9
+
10
+ included do
11
+ helper_method :current_rater
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module SeemsRateable
2
+ module Models
3
+ module ActiveRecordExtension
4
+ def seems_rateable(*dimensions)
5
+ has_many :_rates, -> { where dimension: nil }, as: :rateable, class_name: SeemsRateable::Rate, dependent: :destroy
6
+ has_many :_raters, through: :_rates, source: :rater, dependent: :destroy
7
+
8
+ dimensions.each do |dimension|
9
+ has_many :"#{dimension}_rates", -> { where dimension: dimension }, as: :rateable, class_name: SeemsRateable::Rate, dependent: :destroy
10
+ has_many :"#{dimension}_raters", through: :"#{dimension}_rates", source: :rater, dependent: :destroy
11
+ end
12
+
13
+ include Rateable
14
+ end
15
+
16
+ def seems_rateable_rater
17
+ has_many :rates_given, class_name: SeemsRateable::Rate, foreign_key: :rater_id
18
+
19
+ include Rater
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ module SeemsRateable
2
+ module Models
3
+ module ActiveRecordExtension
4
+ module Rateable
5
+ %w[rates raters].each do |m|
6
+ define_method m do |dimension=nil|
7
+ if respond_to? "#{dimension}_#{m}"
8
+ send "#{dimension}_#{m}"
9
+ else
10
+ raise Errors::NonExistentDimension, "Dimension '#{dimension.inspect}' does not exist for #{self.class}"
11
+ end
12
+ end
13
+ end
14
+
15
+ def rating(dimension=nil)
16
+ SeemsRateable::Rating.new rates(dimension)
17
+ end
18
+
19
+ def rated_by?(rater, dimension=nil)
20
+ return unless rater
21
+ raters(dimension).include? rater
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module SeemsRateable
2
+ module Models
3
+ module ActiveRecordExtension
4
+ module Rater
5
+ def rates_given(dimension=nil)
6
+ super.where(dimension: dimension)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ module SeemsRateable
2
+ class Rating < Struct.new(:rates)
3
+ def average
4
+ sum/(count.nonzero? || 1)
5
+ end
6
+
7
+ def sum
8
+ stars.inject(:+).to_f
9
+ end
10
+
11
+ def count
12
+ stars.length
13
+ end
14
+
15
+ def stars
16
+ @stars ||= rates.pluck(:stars)
17
+ end
18
+ end
19
+ end
@@ -1,7 +1,7 @@
1
1
  module SeemsRateable
2
2
  module Routes
3
3
  def seems_rateable
4
- mount SeemsRateable::Engine => '/rateable', :as => :rateable
4
+ mount SeemsRateable::Engine => '/rateable', as: 'seems_rateable'
5
5
  end
6
6
  end
7
7
  end
@@ -1,3 +1,3 @@
1
1
  module SeemsRateable
2
- VERSION = "1.0.13"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -0,0 +1,32 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require "seems_rateable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "seems_rateable"
7
+ s.version = SeemsRateable::VERSION
8
+ s.authors = ["Peter Toth"]
9
+ s.email = ["peter@petertoth.me"]
10
+ s.homepage = "http://rateable.herokuapp.com"
11
+ s.summary = "Star Rating Engine"
12
+ s.description = "Star rating engine using jQuery plugin jRating for Rails applications"
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- spec/**/*`.split("\n")
16
+
17
+ s.add_dependency "rails", ">= 4.0.0"
18
+ s.add_dependency "jquery-rails"
19
+
20
+ s.add_development_dependency "pry-rails"
21
+ s.add_development_dependency "sqlite3"
22
+ s.add_development_dependency "devise"
23
+
24
+ s.add_development_dependency "rspec-rails"
25
+ s.add_development_dependency "factory_girl_rails"
26
+ s.add_development_dependency "database_cleaner"
27
+ s.add_development_dependency "capybara"
28
+ s.add_development_dependency "selenium-webdriver"
29
+ s.add_development_dependency "shoulda-matchers"
30
+ s.add_development_dependency "simplecov"
31
+ s.add_development_dependency "generator_spec"
32
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ feature SeemsRateable::Builder do
4
+ let(:rateable) { DummyModel.seems_rateable.create }
5
+ let(:options) { {} }
6
+
7
+ describe ".build" do
8
+ it "initializes a new builder object and builds it" do
9
+ SeemsRateable::Builder.should_receive(:new).with(rateable, kind_of(Hash)).and_call_original
10
+ SeemsRateable::Builder.any_instance.should_receive(:build)
11
+ SeemsRateable::Builder.build(rateable, options)
12
+ end
13
+ end
14
+
15
+ describe "#build" do
16
+ def rating(rateable, options)
17
+ Capybara.string(SeemsRateable::Builder.new(rateable, options).build).first('div')
18
+ end
19
+
20
+ subject { rating(rateable, options) }
21
+
22
+ describe "attributes" do
23
+ its(['class']) { should include(SeemsRateable.config.default_selector_class) }
24
+ its(['data-rateable-type']) { should eq(rateable.class.name) }
25
+ its(['data-rateable-id']) { should eq(rateable.id.to_s) }
26
+ its(['data-average']) { should_not be_nil }
27
+ its(['data-dimension']) { should be_nil }
28
+
29
+ context "with explicit class passed" do
30
+ let(:klass) { 'stuff' }
31
+
32
+ subject { rating(rateable, class: klass) }
33
+
34
+ its(['class']) { should include(klass) }
35
+ end
36
+
37
+ context "with dimension" do
38
+ let(:dimension) { 'quality' }
39
+ let(:rateable) { DummyModel.seems_rateable(dimension).create }
40
+
41
+ subject { rating(rateable, dimension: dimension) }
42
+
43
+ its(['data-dimension']) { should eq(dimension) }
44
+ end
45
+
46
+ context "when disabled" do
47
+ subject { rating(rateable, disabled: true) }
48
+
49
+ its(['class']) { should include('jDisabled') }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe SeemsRateable::RatesController do
4
+ routes { SeemsRateable::Engine.routes }
5
+
6
+ let(:user) { FactoryGirl.create(:user) }
7
+ let(:article) { FactoryGirl.create(:post) }
8
+
9
+ describe "POST #create" do
10
+ def create_rate
11
+ xhr :post, :create, valid_attributes(article)
12
+ end
13
+
14
+ def valid_attributes(rateable)
15
+ {
16
+ rate: {
17
+ rateable_type: rateable.class.name,
18
+ rateable_id: rateable.id,
19
+ stars: rand(1..5),
20
+ dimension: ['speed', 'quality', nil].sample
21
+ }
22
+ }
23
+ end
24
+
25
+ context "when valid" do
26
+ before do
27
+ controller.stub(:current_user).and_return(user)
28
+ end
29
+
30
+ it "creates a new rate" do
31
+ expect { create_rate }.to change(SeemsRateable::Rate, :count).by(1)
32
+ end
33
+
34
+ it "assigns a newly created rate as @rate" do
35
+ create_rate
36
+ assigns(:rate).should be_a(SeemsRateable::Rate)
37
+ assigns(:rate).should be_persisted
38
+ end
39
+
40
+ it "renders average rating as json" do
41
+ create_rate
42
+ expect(response.content_type).to eq('application/json')
43
+ expect(response.status).to eq(200)
44
+ expect(JSON.parse(response.body)).to have_key("average")
45
+ end
46
+ end
47
+
48
+ context "when invalid" do
49
+ context "when user is not signed in" do
50
+ it "raises a NoCurrentRaterError error" do
51
+ expect { create_rate }.to raise_error
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.