Commit 94018746 by Rocky Duan

use thumbs_up

parent 3d8d780d
...@@ -12,6 +12,8 @@ gem 'sqlite3' ...@@ -12,6 +12,8 @@ gem 'sqlite3'
gem 'ampex' gem 'ampex'
gem 'thumbs_up', :git => "git@github.com:dementrock/thumbs_up.git"
group :test do group :test do
gem 'rspec' gem 'rspec'
gem 'rack-test', :require => "rack/test" gem 'rack-test', :require => "rack/test"
......
...@@ -2,10 +2,12 @@ require 'rubygems' ...@@ -2,10 +2,12 @@ require 'rubygems'
require 'yajl' require 'yajl'
require 'active_record' require 'active_record'
require 'sinatra' require 'sinatra'
require 'thumbs_up'
require_relative 'models/comment' require_relative 'models/comment'
require_relative 'models/comment_thread' require_relative 'models/comment_thread'
require_relative 'models/vote' require_relative 'models/vote'
require_relative 'models/user'
env_index = ARGV.index("-e") env_index = ARGV.index("-e")
env_arg = ARGV[env_index + 1] if env_index env_arg = ARGV[env_index + 1] if env_index
...@@ -103,11 +105,12 @@ put '/api/v1/votes/comments/:comment_id/users/:user_id' do |comment_id, user_id| ...@@ -103,11 +105,12 @@ put '/api/v1/votes/comments/:comment_id/users/:user_id' do |comment_id, user_id|
if comment.nil? if comment.nil?
error 400, {:error => "invalid comment id"}.to_json error 400, {:error => "invalid comment id"}.to_json
else else
vote = Vote.create_or_update :user_id => user_id, :comment_id => comment_id, :value => params["value"] if %w[up down].include? params["value"]
if vote user = User.find_or_create_by_id(user_id)
vote = user.vote(comment, { :direction => (params["value"] == "up" ? :up : :down ), :exclusive => :true})
vote.to_json vote.to_json
else else
error 400, vote.errors.to_json error 400, {:error => "value must be up or down"}
end end
end end
end end
...@@ -115,12 +118,13 @@ end ...@@ -115,12 +118,13 @@ end
# undo the vote on the comment by the user # undo the vote on the comment by the user
delete '/api/v1/votes/comments/:comment_id/users/:user_id' do |comment_id, user_id| delete '/api/v1/votes/comments/:comment_id/users/:user_id' do |comment_id, user_id|
vote = Vote.find_by_comment_id_and_user_id(comment_id, user_id) user = User.find_by_id(user_id.to_i)
if vote.nil? comment = Comment.find_by_id(comment_id)
error 400, {:error => "vote does not exist"}.to_json if user and comment and not comment.is_root?
else vote = user.unvote_for(comment)
vote.destroy
vote.to_json vote.to_json
else
error 400, {:error => "invalid user or comment id"}
end end
end end
......
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
end
end
def self.down
drop_table :users
end
end
class CreateVotes < ActiveRecord::Migration class CreateVotes < ActiveRecord::Migration
def self.up def self.up
create_table :votes do |t| create_table :votes, :force => true do |t|
t.integer :user_id
t.integer :comment_id t.boolean :vote, :default => false
t.string :value t.references :voteable, :polymorphic => true, :null => false
t.references :voter, :polymorphic => true
t.timestamps t.timestamps
end end
add_index :votes, :comment_id add_index :votes, [:voter_id, :voter_type]
add_index :votes, :user_id add_index :votes, [:voteable_id, :voteable_type]
add_index :votes, [:comment_id, :user_id]
add_index :votes, [:voter_id, :voter_type, :voteable_id, :voteable_type], :unique => true, :name => 'fk_one_vote_per_user_per_entity'
end end
def self.down def self.down
drop_table :votes drop_table :votes
end end
end end
require 'active_record' require 'active_record'
require 'ancestry' require 'ancestry'
require 'thumbs_up'
class Comment < ActiveRecord::Base class Comment < ActiveRecord::Base
...@@ -7,10 +8,10 @@ class Comment < ActiveRecord::Base ...@@ -7,10 +8,10 @@ class Comment < ActiveRecord::Base
has_ancestry has_ancestry
has_many :votes
belongs_to :comment_thread belongs_to :comment_thread
acts_as_voteable
validates_presence_of :body, :unless => :is_root? validates_presence_of :body, :unless => :is_root?
validates_presence_of :user_id, :unless => :is_root? validates_presence_of :user_id, :unless => :is_root?
validates_presence_of :course_id, :unless => :is_root? validates_presence_of :course_id, :unless => :is_root?
...@@ -25,7 +26,7 @@ class Comment < ActiveRecord::Base ...@@ -25,7 +26,7 @@ class Comment < ActiveRecord::Base
end end
def to_hash def to_hash
attributes.merge(:votes => {:up => Vote.comment_id(id).up.count, :down => Vote.comment_id(id).down.count}) attributes.merge(:votes => {:up => votes_for, :down => votes_against})
end end
def to_json def to_json
......
require 'active_record'
require 'thumbs_up'
class User < ActiveRecord::Base
acts_as_voter
end
require 'active_record' require 'active_record'
# Adapted from "Service-Oriented Design with Ruby and Rails"
class Vote < ActiveRecord::Base class Vote < ActiveRecord::Base
attr_accessible :value, :user_id, :comment_id scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.base_class.name]) }
belongs_to :comment scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
scope :descending, order("created_at DESC")
validates_inclusion_of :value, :in => %w{up down} belongs_to :voteable, :polymorphic => true
validates_uniqueness_of :user_id, :scope => :comment_id belongs_to :voter, :polymorphic => true
validates_presence_of :comment_id, :user_id
scope :up, :conditions => ["value = ?", "up"] attr_accessible :vote, :voter, :voteable
scope :down, :conditions => ["value = ?", "down"]
scope :user_id, lambda {|user_id| {:conditions => ["user_id = ?", user_id]}}
scope :comment_id, lambda {|comment_id| {:conditions => ["comment_id = ?", comment_id]}}
def self.create_or_update(attributes) # Comment out the line below to allow multiple votes per user.
vote = Vote.find_by_comment_id_and_user_id(attributes[:comment_id], attributes[:user_id]) validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
if vote
vote.value = attributes[:value]
vote.save
vote
else
Vote.create(attributes)
end
end
end end
...@@ -69,13 +69,14 @@ describe "app" do ...@@ -69,13 +69,14 @@ describe "app" do
comment << (comment_thread.root_comments.create :body => "top comment", :title => "top 0", :user_id => 1, :course_id => 1, :comment_thread_id => comment_thread.id) comment << (comment_thread.root_comments.create :body => "top comment", :title => "top 0", :user_id => 1, :course_id => 1, :comment_thread_id => comment_thread.id)
sub_comment << (comment[0].children.create :body => "comment body", :title => "comment title 0", :user_id => 1, :course_id => 1, :comment_thread_id => comment_thread.id) sub_comment << (comment[0].children.create :body => "comment body", :title => "comment title 0", :user_id => 1, :course_id => 1, :comment_thread_id => comment_thread.id)
sub_comment << (comment[0].children.create :body => "comment body", :title => "comment title 1", :user_id => 1, :course_id => 1, :comment_thread_id => comment_thread.id) sub_comment << (comment[0].children.create :body => "comment body", :title => "comment title 1", :user_id => 1, :course_id => 1, :comment_thread_id => comment_thread.id)
Vote.create! :value => "up", :comment_id => comment[0].id, :user_id => 1 (1..4).each do |id|
Vote.create! :value => "up", :comment_id => comment[0].id, :user_id => 2 user = User.find_or_create_by_id(id)
Vote.create! :value => "up", :comment_id => comment[0].id, :user_id => 3 user.vote_for(comment[0])
Vote.create! :value => "up", :comment_id => comment[0].id, :user_id => 4 end
Vote.create! :value => "down", :comment_id => comment[0].id, :user_id => 5 (5..7).each do |id|
Vote.create! :value => "down", :comment_id => comment[0].id, :user_id => 6 user = User.find_or_create_by_id(id)
Vote.create! :value => "down", :comment_id => comment[0].id, :user_id => 7 user.vote_against(comment[0])
end
get "/api/v1/commentables/questions/1/comments" get "/api/v1/commentables/questions/1/comments"
last_response.should be_ok last_response.should be_ok
comments = Yajl::Parser.parse last_response.body comments = Yajl::Parser.parse last_response.body
...@@ -196,16 +197,18 @@ describe "app" do ...@@ -196,16 +197,18 @@ describe "app" do
comment = CommentThread.first.root_comments.create :body => "top comment", :title => "top", :user_id => 1, :course_id => 1 comment = CommentThread.first.root_comments.create :body => "top comment", :title => "top", :user_id => 1, :course_id => 1
comment.comment_thread = comment_thread comment.comment_thread = comment_thread
comment.save! comment.save!
Vote.delete_all
end end
it "votes up on a comment" do it "votes up on a comment" do
comment = CommentThread.first.comments.first comment = CommentThread.first.comments.first
put "/api/v1/votes/comments/#{comment.id}/users/1", :value => "up" put "/api/v1/votes/comments/#{comment.id}/users/1", :value => "up"
last_response.should be_ok last_response.should be_ok
vote = Vote.first vote = Vote.first
puts vote
vote.should_not be_nil vote.should_not be_nil
vote.user_id.should == 1 vote.voter_id.should == 1
vote.comment_id.should == comment.id vote.voteable_id.should == comment.id
vote.value.should == "up" vote.vote.should be_true
end end
it "votes down on a comment" do it "votes down on a comment" do
comment = CommentThread.first.comments.first comment = CommentThread.first.comments.first
...@@ -213,9 +216,10 @@ describe "app" do ...@@ -213,9 +216,10 @@ describe "app" do
last_response.should be_ok last_response.should be_ok
vote = Vote.first vote = Vote.first
vote.should_not be_nil vote.should_not be_nil
vote.user_id.should == 1 vote.voter_id.should == 1
vote.comment_id.should == comment.id vote.voteable_id.should == comment.id
vote.value.should == "down" vote.vote.should be_false
end end
it "rejects invalid vote value" do it "rejects invalid vote value" do
comment = CommentThread.first.comments.first comment = CommentThread.first.comments.first
...@@ -229,10 +233,11 @@ describe "app" do ...@@ -229,10 +233,11 @@ describe "app" do
end end
it "change vote on comment" do it "change vote on comment" do
comment = CommentThread.first.comments.first comment = CommentThread.first.comments.first
Vote.create! :value => "up", :user_id => 1, :comment_id => comment.id user = User.find_or_create_by_id(1)
user.vote_for(comment)
put "/api/v1/votes/comments/#{comment.id}/users/1", :value => "down" put "/api/v1/votes/comments/#{comment.id}/users/1", :value => "down"
last_response.should be_ok last_response.should be_ok
Vote.first.value.should == "down" Vote.first.vote.should be_false
end end
end end
describe "DELETE on /api/v1/votes/comments/:comment_id/users/:user_id" do describe "DELETE on /api/v1/votes/comments/:comment_id/users/:user_id" do
...@@ -242,15 +247,16 @@ describe "app" do ...@@ -242,15 +247,16 @@ describe "app" do
end end
it "deletes vote" do it "deletes vote" do
comment = CommentThread.first.comments.first comment = CommentThread.first.comments.first
Vote.create! :value => "up", :user_id => 1, :comment_id => comment.id user = User.find_or_create_by_id(1)
user.vote_for(comment)
delete "/api/v1/votes/comments/#{comment.id}/users/1" delete "/api/v1/votes/comments/#{comment.id}/users/1"
last_response.should be_ok last_response.should be_ok
Vote.count.should == 0 Vote.count.should == 0
end end
it "returns 400 for nonexisted vote" do it "does nothing for nonexisted vote" do
comment = CommentThread.first.comments.first comment = CommentThread.first.comments.first
delete "/api/v1/votes/comments/#{comment.id}/users/1" delete "/api/v1/votes/comments/#{comment.id}/users/1"
last_response.status.should == 400 last_response.should be_ok
end end
end end
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment