rateable 0.0.7 → 0.1.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 (74) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +5 -0
  3. data/README.rdoc +1 -1
  4. data/Rakefile +14 -0
  5. data/lib/rateable/helper.rb +3 -2
  6. data/lib/rateable/is_rateable.rb +18 -3
  7. data/lib/rateable/rate.rb +7 -1
  8. data/lib/rateable/rater.rb +1 -1
  9. data/lib/rateable/version.rb +1 -1
  10. data/rateable.gemspec +6 -0
  11. data/spec/controllers/application_controller_spec.rb +48 -0
  12. data/spec/controllers/pictures_controller_spec.rb +57 -0
  13. data/spec/factories.rb +8 -0
  14. data/spec/models/rateable_spec.rb +70 -0
  15. data/spec/models/rater_spec.rb +32 -0
  16. data/spec/requests/pictures_spec.rb +66 -0
  17. data/spec/spec.opts +2 -0
  18. data/spec/spec_helper.rb +3 -0
  19. data/spec/test_app/.gitignore +4 -0
  20. data/spec/test_app/.rspec +1 -0
  21. data/spec/test_app/Gemfile +37 -0
  22. data/spec/test_app/README +256 -0
  23. data/spec/test_app/Rakefile +7 -0
  24. data/spec/test_app/app/controllers/application_controller.rb +20 -0
  25. data/spec/test_app/app/controllers/pictures_controller.rb +19 -0
  26. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  27. data/spec/test_app/app/helpers/pictures_helper.rb +2 -0
  28. data/spec/test_app/app/models/picture.rb +3 -0
  29. data/spec/test_app/app/models/user.rb +3 -0
  30. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  31. data/spec/test_app/app/views/pictures/show.html.erb +3 -0
  32. data/spec/test_app/config.ru +4 -0
  33. data/spec/test_app/config/app_config.yml +2 -0
  34. data/spec/test_app/config/application.rb +42 -0
  35. data/spec/test_app/config/boot.rb +6 -0
  36. data/spec/test_app/config/database.yml +22 -0
  37. data/spec/test_app/config/environment.rb +5 -0
  38. data/spec/test_app/config/environments/development.rb +26 -0
  39. data/spec/test_app/config/environments/production.rb +49 -0
  40. data/spec/test_app/config/environments/test.rb +35 -0
  41. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  42. data/spec/test_app/config/initializers/inflections.rb +10 -0
  43. data/spec/test_app/config/initializers/mime_types.rb +5 -0
  44. data/spec/test_app/config/initializers/secret_token.rb +7 -0
  45. data/spec/test_app/config/initializers/session_store.rb +8 -0
  46. data/spec/test_app/config/locales/en.yml +5 -0
  47. data/spec/test_app/config/routes.rb +69 -0
  48. data/spec/test_app/db/migrate/20110806140922_create_users.rb +13 -0
  49. data/spec/test_app/db/migrate/20110806140953_create_pictures.rb +13 -0
  50. data/spec/test_app/db/migrate/20110806141552_rateable_migration.rb +15 -0
  51. data/spec/test_app/db/schema.rb +35 -0
  52. data/spec/test_app/db/seeds.rb +7 -0
  53. data/spec/test_app/doc/README_FOR_APP +2 -0
  54. data/spec/test_app/public/404.html +26 -0
  55. data/spec/test_app/public/422.html +26 -0
  56. data/spec/test_app/public/500.html +26 -0
  57. data/spec/test_app/public/favicon.ico +0 -0
  58. data/spec/test_app/public/images/rails.png +0 -0
  59. data/spec/test_app/public/images/rateable/star_filled.png +0 -0
  60. data/spec/test_app/public/images/rateable/star_unfilled.png +0 -0
  61. data/spec/test_app/public/javascripts/application.js +2 -0
  62. data/spec/test_app/public/javascripts/controls.js +965 -0
  63. data/spec/test_app/public/javascripts/dragdrop.js +974 -0
  64. data/spec/test_app/public/javascripts/effects.js +1123 -0
  65. data/spec/test_app/public/javascripts/jquery.js +8374 -0
  66. data/spec/test_app/public/javascripts/prototype.js +6001 -0
  67. data/spec/test_app/public/javascripts/rails.js +196 -0
  68. data/spec/test_app/public/javascripts/rateable.js +29 -0
  69. data/spec/test_app/public/robots.txt +5 -0
  70. data/spec/test_app/public/stylesheets/rateable.css +22 -0
  71. data/spec/test_app/script/rails +6 -0
  72. data/spec/test_app/spec/helpers/application_spec.rb +48 -0
  73. data/spec/test_app/spec/spec_helper.rb +42 -0
  74. metadata +154 -4
data/.gitignore CHANGED
@@ -2,3 +2,6 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ *.sw*
6
+ coverage/
7
+ *.gitkeep
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in rateable.gemspec
4
4
  gemspec
5
+
6
+ gem "meta_where"
7
+ gem "launchy"
8
+ gem "database_cleaner"
9
+ gem "rcov"
data/README.rdoc CHANGED
@@ -62,7 +62,7 @@ Add a rate action to the controller of the model that should be rateable, it sho
62
62
 
63
63
  Add a post action called "rate" to the resource in your "config/routes.rb"
64
64
 
65
- resource :models do
65
+ resources :models do
66
66
  member do
67
67
  post :rate
68
68
  end
data/Rakefile CHANGED
@@ -1,2 +1,16 @@
1
1
  require 'bundler'
2
+ require 'rubygems'
3
+ require 'rspec/core/rake_task'
4
+
2
5
  Bundler::GemHelper.install_tasks
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = ['--options', "spec/spec.opts"]
9
+ t.pattern = "spec/**/*_spec.rb"
10
+ end
11
+
12
+ RSpec::Core::RakeTask.new(:spec_rcov) do |t|
13
+ t.rspec_opts = ['--options', "spec/spec.opts"]
14
+ t.pattern = "spec/**/*_spec.rb"
15
+ t.rcov = true
16
+ end
@@ -1,9 +1,10 @@
1
1
  module Rateable
2
2
  module Helper
3
3
  def rating_for(rateable, args = {})
4
- user = args[:user] || try(:current_user)
4
+ raise "The given model of class #{rateable.class} is not rateable" if rateable.nil? or not (rateable.respond_to?(:is_rateable?) and rateable.is_rateable?)
5
+ user = args[:user] || begin try(:current_user) rescue nil end
5
6
  if user and user.ratings.where(:rateable => rateable).empty?
6
- render :partial => "rateable/rate", :locals => {:url => args[:url] ? args[:url] : url_for(rateable), :stars => args[:stars]}
7
+ render :partial => "rateable/rate", :locals => {:url => args[:url] ? args[:url] : url_for([:rate, rateable]), :stars => args[:stars]}
7
8
  else
8
9
  stars_average = rateable.ratings.average("stars")
9
10
  ratings_count = rateable.ratings.count
@@ -5,7 +5,9 @@ module Rateable
5
5
  end
6
6
 
7
7
  module ClassMethods
8
- def is_rateable
8
+ def is_rateable(args = {})
9
+ raise ":stars must be an integer >= 1" if args[:stars] and not (args[:stars].is_a?(Integer) and args[:stars] >= 1)
10
+ @max_stars = args[:stars] || 5
9
11
  has_many :ratings, :as => :rateable, :class_name => "Rateable::Rate", :dependent => :destroy
10
12
  include Rateable::IsRateable::InstanceMethods
11
13
  end
@@ -13,6 +15,11 @@ module Rateable
13
15
  def is_rateable?
14
16
  true
15
17
  end
18
+
19
+ def max_stars
20
+ @max_stars
21
+ end
22
+
16
23
  end
17
24
 
18
25
  module InstanceMethods
@@ -20,11 +27,19 @@ module Rateable
20
27
  true
21
28
  end
22
29
 
30
+ def max_stars
31
+ self.class.max_stars
32
+ end
33
+
23
34
  # include instance methods that are needed
24
35
  def rate(user,stars)
36
+ raise "#{user.inspect} is not a rater" unless user.respond_to?(:is_rater?) and user.is_rater?
25
37
  rating = Rate.where(:rateable => self, :rater => user).first
26
- rating = Rate.create(:rateable => self, :rater => user) unless rating
27
- rating.stars = stars
38
+ unless rating
39
+ rating = Rate.create(:rateable => self, :rater => user, :stars => stars)
40
+ else
41
+ rating.stars = stars
42
+ end
28
43
  rating.save
29
44
  end
30
45
  end
data/lib/rateable/rate.rb CHANGED
@@ -5,6 +5,12 @@ module Rateable
5
5
 
6
6
  validates_presence_of :rateable
7
7
  validates_presence_of :rater
8
- validates_inclusion_of :stars, :in => (1..5).to_a
8
+ validate :stars_must_be_in_range
9
+
10
+ def stars_must_be_in_range
11
+ unless (1..rateable.max_stars).include? stars
12
+ errors.add :stars, "must be within 1 to #{rateable.max_stars}"
13
+ end
14
+ end
9
15
  end
10
16
  end
@@ -21,7 +21,7 @@ module Rateable
21
21
  end
22
22
 
23
23
  def rate(obj,stars)
24
- if obj.is_rateable?
24
+ if begin obj.is_rateable? rescue false end
25
25
  obj.rate(self,stars)
26
26
  else
27
27
  false
@@ -1,3 +1,3 @@
1
1
  module Rateable
2
- VERSION = "0.0.7"
2
+ VERSION = "0.1.0"
3
3
  end
data/rateable.gemspec CHANGED
@@ -14,6 +14,12 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.add_runtime_dependency 'rails', ">= 3.0.0"
16
16
  s.add_runtime_dependency 'activerecord', ">= 3.0.0"
17
+ s.add_runtime_dependency 'meta_where'
18
+ s.add_development_dependency "rspec"
19
+ s.add_development_dependency "capybara"
20
+ s.add_development_dependency "rspec-rails"
21
+ s.add_development_dependency "factory_girl_rails"
22
+ s.add_development_dependency "factory_girl"
17
23
 
18
24
  s.files = `git ls-files`.split("\n")
19
25
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationController do
4
+ let(:user){ Factory(:user) }
5
+
6
+ describe :login do
7
+ it "it saves the given user_id in the session" do
8
+ get :login, :user_id => user.id
9
+ session[:user_id].should == user.id
10
+ end
11
+
12
+ it "saves nil in the session if no id is given" do
13
+ get :login
14
+ session[:user_id].should == nil
15
+ end
16
+ end
17
+
18
+ describe :logout do
19
+ it "sets the user in the session to nil if user is logged in" do
20
+ session[:user_id] = user.id
21
+ get :logout
22
+ session[:user_id].should == nil
23
+ end
24
+
25
+ it "sets the user in the session to nil if no user logged in" do
26
+ get :logout
27
+ session[:user_id].should == nil
28
+ end
29
+ end
30
+
31
+ describe :current_user do
32
+ it "returns nil if session[:user_id] is nil and no user_id is given as param" do
33
+ session[:user_id] = nil
34
+ controller.current_user.should == nil
35
+ end
36
+
37
+ it "return user if session nil and user_id is given as param" do
38
+ session[:user_id] = nil
39
+ get :logout, :user_id => user.id
40
+ controller.current_user.should == user
41
+ end
42
+
43
+ it "returns the User with session[:user_id] as it's id" do
44
+ session[:user_id] = user.id
45
+ controller.current_user.should == user
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe PicturesController, "rating a picture" do
4
+
5
+ it "should rate the picture" do
6
+ picture = Factory(:picture)
7
+ user = Factory(:user)
8
+ post 'rate', :user_id => user.id, :id => picture.id, :stars => 1
9
+ ratings = user.reload.ratings
10
+ ratings.count.should == 1
11
+ ratings.first.rateable.should == picture
12
+ ratings.first.stars.should == 1
13
+ response.should be_success
14
+ end
15
+
16
+ it "is not possible unless logged in" do
17
+ picture = Factory(:picture)
18
+ post 'rate', :id => picture.id, :stars => 1
19
+ picture.ratings.count.should == 0
20
+ response.should_not be_success
21
+ response.body.should == "You are not allowed to vote for this item!"
22
+ end
23
+
24
+ it "is not possible if already voted" do
25
+ picture = Factory(:picture)
26
+ user = Factory(:user)
27
+ user.rate(picture, 1)
28
+ post 'rate', :id => picture.id, :user_id => user.id, :stars => 1
29
+ response.should_not be_success
30
+ picture.ratings.count.should == 1
31
+ end
32
+
33
+ it "is not possible to vote if no stars are given" do
34
+ picture = Factory(:picture)
35
+ user = Factory(:user)
36
+ post 'rate', :id => picture.id, :user_id => user.id
37
+ response.should_not be_success
38
+ picture.ratings.count.should == 0
39
+ end
40
+
41
+ it "should not be possible with to many stars" do
42
+ picture = Factory(:picture)
43
+ user = Factory(:user)
44
+ post 'rate', :id => picture.id, :user_id => user.id, :stars => picture.max_stars+1
45
+ response.should_not be_success
46
+ picture.ratings.count.should == 0
47
+ end
48
+
49
+ it "should not be possible with to less stars" do
50
+ picture = Factory(:picture)
51
+ user = Factory(:user)
52
+ post 'rate', :id => picture.id, :user_id => user.id, :stars => 0
53
+ response.should_not be_success
54
+ picture.ratings.count.should == 0
55
+ end
56
+
57
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'factory_girl'
2
+ Factory.define :picture do |f|
3
+ f.sequence(:name) { |n| "Picture #{n}"}
4
+ end
5
+
6
+ Factory.define :user do |f|
7
+ f.sequence(:email) { |n| "#{n}@example.com" }
8
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Picture do
4
+
5
+ it "should have many ratings" do
6
+ Factory(:picture).should respond_to(:ratings)
7
+ end
8
+
9
+ describe :is_rateable? do
10
+ it "as instance method returns true" do
11
+ picture = Factory(:picture)
12
+ assert picture.is_rateable?
13
+ end
14
+
15
+ it "as class method returns true" do
16
+ assert Picture.is_rateable?
17
+ end
18
+ end
19
+
20
+ describe :rate do
21
+ let(:picture) { Factory(:picture) }
22
+ let(:user) { Factory(:user) }
23
+
24
+ it "raises an exception if no valid rater is given" do
25
+ lambda{picture.rate(picture,1)}.should raise_error
26
+ end
27
+
28
+ it "changes the rating of the given rater if already voted and stars are valid" do
29
+ rating = Rateable::Rate.create(:rateable => picture, :rater => user, :stars => 1)
30
+ picture.rate(user,2).should == true
31
+ rating.id.should == picture.ratings.last.id
32
+ picture.ratings.last.stars.should == 2
33
+ end
34
+
35
+ it "does not change the rating of the given rater if already voted and stars are not valid" do
36
+ Rateable::Rate.create(:rateable => picture, :rater => user, :stars => 1)
37
+ picture.rate(user,0).should == false
38
+ picture.ratings.last.stars.should == 1
39
+ picture.rate(user,100000).should == false
40
+ picture.ratings.last.stars.should == 1
41
+ end
42
+
43
+ it "creates a new rating if valid rater and stars are given and not already rated" do
44
+ user.ratings.empty?.should be_true
45
+ picture.rate(user,1).should be_true
46
+ rating = picture.ratings.last
47
+ rating.rater.should == user
48
+ rating.rateable.should == picture
49
+ rating.stars.should == 1
50
+ end
51
+
52
+ it "does not create a new rating if valid rater and invalid stars are given" do
53
+ picture.rate(user,0).should be_false
54
+ picture.ratings.last.should be_nil
55
+ picture.rate(user,10000).should be_false
56
+ picture.ratings.last.should be_nil
57
+ end
58
+ end
59
+
60
+ describe :max_stars do
61
+ it "returns the value of the class-method max_stars" do
62
+ Factory(:picture).max_stars.should == Picture.max_stars
63
+ end
64
+
65
+ it "returns an integer" do
66
+ Picture.max_stars.should be_an(Integer)
67
+ Picture.max_stars.should >= 1
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe User do
4
+ describe :is_rater? do
5
+ it "as instance method returns true" do
6
+ user = Factory(:user)
7
+ assert user.is_rater?
8
+ end
9
+
10
+ it "as class method returns true" do
11
+ assert User.is_rater?
12
+ end
13
+ end
14
+
15
+ describe :rate do
16
+ let(:user) { Factory(:user) }
17
+
18
+ it "should call Rateable.rate if object is rateable" do
19
+ picture = Factory(:picture)
20
+ picture.should_receive(:rate).with(user,1)
21
+ user.rate(picture,1)
22
+ end
23
+
24
+ it "returns false if the object does not respond to is_rateable?" do
25
+ user.rate(user,1).should == false
26
+ end
27
+
28
+ it "returns false if the object is nil" do
29
+ user.rate(nil,1).should == false
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Pictures" do
4
+ it "shows the picture when visiting the show action" do
5
+ picture = Factory(:picture)
6
+ visit picture_path(picture)
7
+ page.should have_content(picture.name)
8
+ end
9
+
10
+ it "should not set current_user when no user_id is given" do
11
+ user = Factory(:user)
12
+ picture = Factory(:picture)
13
+ visit picture_path(picture)
14
+ User.all.each do |u|
15
+ page.should_not have_content(u.email)
16
+ end
17
+ end
18
+
19
+ it "should set current_user when a user_id is given" do
20
+ picture = Factory(:picture)
21
+ user = Factory(:user)
22
+ visit picture_path(picture, :user_id => user.id)
23
+ page.should have_content(user.email)
24
+ end
25
+
26
+ it "should show star images when not logged in" do
27
+ picture = Factory(:picture)
28
+ visit picture_path(picture)
29
+ page.should_not have_xpath('//div[@class="rating"]/a')
30
+ page.should have_xpath('//div[@class="rating"]/img')
31
+ end
32
+
33
+ it "should show a star links when logged in and not already voted" do
34
+ picture = Factory(:picture)
35
+ user = Factory(:user)
36
+ user.ratings.destroy_all
37
+ visit picture_path(picture, :user_id => user.id)
38
+ page.should have_xpath('//div[@class="rating"]/a[@class="rate"]')
39
+ page.should_not have_xpath('//div[@class="rating"]/img')
40
+ end
41
+
42
+ it "shows star images when logged in and already voted" do
43
+ picture = Factory(:picture)
44
+ user = Factory(:user)
45
+ user.rate(picture,1)
46
+ visit picture_path(picture, :user_id => user.id)
47
+ page.should_not have_xpath('//div[@class="rating"]/a[@class="rate"]')
48
+ page.should have_xpath('//div[@class="rating"]/img')
49
+ end
50
+
51
+ it "should be possible to vote", :js => true do
52
+ picture = Factory(:picture)
53
+ user = Factory(:user)
54
+ user.ratings.destroy_all
55
+ visit login_path(:user_id => user.id)
56
+ visit picture_path(picture)
57
+ page.should have_xpath('//div[@class="rating"]/a[@class="rate"]')
58
+ find(:xpath, '//div[@class="rating"]/a[@class="rate"][3]').click
59
+ page.should have_no_xpath('//div[@class="rating"]/a[@class="rate"]')
60
+ page.should have_xpath('//div[@class="rating"]/img[@alt="Star_filled"][1]')
61
+ page.should have_xpath('//div[@class="rating"]/img[@alt="Star_filled"][2]')
62
+ page.should have_xpath('//div[@class="rating"]/img[@alt="Star_filled"][3]')
63
+ page.should have_no_xpath('//div[@class="rating"]/img[@alt="Star_filled"][4]')
64
+ end
65
+ end
66
+
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress