diff --git a/README.markdown b/README.markdown index a538dfc..1872cdb 100644 --- a/README.markdown +++ b/README.markdown @@ -1,48 +1,4 @@ -Heroku's Suspenders -------------------- - -thoughtbot's Suspenders modified for Heroku. - - git clone git://github.com/dancroak/heroku_suspenders.git - cd heroku_suspenders - ./script/create_project project_name - -This will create a Rails 2.3.2 app with Heroku-recommended code: - -* Paperclip for file uploads, set for Amazon S3 -* Gmail SMTP for email -* Delayed Job for background processing -* Hoptoad Notifier for exception notification -* Google Analytics for usage analytics - -... and some other opinions: - -* jQuery for Javascript and Ajax -* Clearance for authentication -* Active Merchant for payment processing -* Cucumber, Shoulda, Factory Girl, Mocha, Fakeweb, & Timecop for testing -* Inherited Resources for RESTful controllers -* Formtastic for form builders -* Flutie for CSS framework -* Blitz for features, model, controller, & helper generators - -If you don't have all the necessary gems, they will be installed. - -Get the latest & greatest at anytime with: - - rake suspenders:pull - -A helper rake task will prompt you for all your production config vars (S3 -keys, GMail account, Hoptoad API key...) and set them on your Heroku app: - - rake heroku:setup - -More details available in doc/README_FOR_TEMPLATE. - -Mascot ------- - -The official Suspenders mascot is Suspenders Boy: - -![Suspenders Boy](http://media.tumblr.com/1TEAMALpseh5xzf0Jt6bcwSMo1_400.png) +Pairwise 2.0 +==================== +Clean version, test-driven. \ No newline at end of file diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index 5a0f1a9..cc586d5 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -1,85 +1,28 @@ -class QuestionsController < ApplicationController - # GET /questions - # GET /questions.xml - def index - @questions = Question.all +class QuestionsController < InheritedResources::Base + respond_to :xml, :json + belongs_to :site, :optional => true + #has_scope :voted_on_by - respond_to do |format| - format.html # index.html.erb - format.xml { render :xml => @questions } - end - end - - # GET /questions/1 - # GET /questions/1.xml def show - @question = Question.find(params[:id]) - - respond_to do |format| - format.html # show.html.erb - format.xml { render :xml => @question } - end - end - - # GET /questions/new - # GET /questions/new.xml - def new - @question = Question.new - - respond_to do |format| - format.html # new.html.erb - format.xml { render :xml => @question } + show! do |format| + session['prompts_ids'] ||= [] + format.xml { + render :xml => @question.to_xml(:methods => [:item_count, :left_choice_text, :right_choice_text, :picked_prompt_id, :votes_count, :creator_id]) + } end end - - # GET /questions/1/edit - def edit - @question = Question.find(params[:id]) - end - - # POST /questions - # POST /questions.xml + def create - @question = Question.new(params[:question]) - - respond_to do |format| - if @question.save - flash[:notice] = 'Question was successfully created.' - format.html { redirect_to(@question) } - format.xml { render :xml => @question, :status => :created, :location => @question } - else - format.html { render :action => "new" } - format.xml { render :xml => @question.errors, :status => :unprocessable_entity } + authenticate + if @question = current_user.create_question(params[:visitor_identifier], params.except[:visitor_identifier]) + respond_to do |format| + format.xml { render :xml => @question.to_xml} end - end - end - - # PUT /questions/1 - # PUT /questions/1.xml - def update - @question = Question.find(params[:id]) - - respond_to do |format| - if @question.update_attributes(params[:question]) - flash[:notice] = 'Question was successfully updated.' - format.html { redirect_to(@question) } - format.xml { head :ok } - else - format.html { render :action => "edit" } - format.xml { render :xml => @question.errors, :status => :unprocessable_entity } + else + respond_to do |format| + format.xml { render :xml => @question.errors.to_xml} end end end - - # DELETE /questions/1 - # DELETE /questions/1.xml - def destroy - @question = Question.find(params[:id]) - @question.destroy - - respond_to do |format| - format.html { redirect_to(questions_url) } - format.xml { head :ok } - end - end + end diff --git a/app/models/choice.rb b/app/models/choice.rb index fc2b281..080a833 100644 --- a/app/models/choice.rb +++ b/app/models/choice.rb @@ -11,8 +11,10 @@ class Choice < ActiveRecord::Base after_create :generate_prompts def before_create - @item = Item.create!(:creator => creator, :data => data) - self.item = @item + unless item + @item = Item.create!(:creator => creator, :data => data) + self.item = @item + end end protected diff --git a/app/models/item.rb b/app/models/item.rb index cc652c8..8435e73 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -13,4 +13,5 @@ class Item < ActiveRecord::Base # has_and_belongs_to_many :prompt_requests validates_presence_of :creator_id + validates_presence_of :data, :on => :create, :message => "can't be blank" end diff --git a/app/models/question.rb b/app/models/question.rb index 5e3cb0d..74d0ec7 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -2,7 +2,7 @@ class Question < ActiveRecord::Base belongs_to :creator, :class_name => "Visitor", :foreign_key => "creator_id" belongs_to :site, :class_name => "User", :foreign_key => "site_id" - has_many :choices + has_many :choices, :order => 'score DESC' has_many :prompts do def pick(algorithm = nil) if algorithm @@ -12,7 +12,45 @@ class Question < ActiveRecord::Base end end end + has_many :votes, :as => :voteable + after_save :ensure_at_least_two_choices + + def item_count + choice_count + end + + def choice_count + Choice.count(:all, :conditions => {:question_id => id}) + end + + def votes_count + Vote.count(:all, :conditions => {:voteable_id => id, :voteable_type => 'Question'}) + end + + def picked_prompt + prompts[rand(prompts.count-1)]#Prompt.find(picked_prompt_id) + end + + def left_choice_text(prompt = nil) + prompt ||= prompts.first#prompts.pick + picked_prompt.left_choice.item.data + end + + def right_choice_text(prompt = nil) + prompt ||= prompts.first + picked_prompt.right_choice.item.data + end + + def self.voted_on_by(u) + select {|z| z.voted_on_by_user?(u)} + end + + def voted_on_by_user?(u) + u.questions_voted_on.include? self + end + + validates_presence_of :site, :on => :create, :message => "can't be blank" validates_presence_of :creator, :on => :create, :message => "can't be blank" diff --git a/lib/clearance_http_auth.rb b/lib/clearance_http_auth.rb new file mode 100644 index 0000000..cd1f2fb --- /dev/null +++ b/lib/clearance_http_auth.rb @@ -0,0 +1,30 @@ +#http://gist.github.com/159604 + +# Overload Clearance's `deny_access` methods to allow authentication with HTTP-Auth for eg. API access +# Modeled after technoweenie's restful_authentication +# http://github.com/technoweenie/restful-authentication/blob/7235d9150e8beb80a819923a4c871ef4069c6759/generators/authenticated/templates/authenticated_system.rb#L74-76 +# +# In lib/clearance_http_auth.rb + +module Clearance + module Authentication + + module InstanceMethods + + def deny_access(flash_message = nil, opts = {}) + store_location + flash[:failure] = flash_message if flash_message + respond_to do |format| + format.html { redirect_to new_session_url } + format.any(:json, :xml) do + authenticate_or_request_with_http_basic('Pairwise API') do |login, password| + @_current_user = ::User.authenticate(login, password) + end + end + end + end + + end + + end +end \ No newline at end of file diff --git a/spec/controllers/questions_controller_spec.rb b/spec/controllers/questions_controller_spec.rb index 86c3ca1..102d598 100644 --- a/spec/controllers/questions_controller_spec.rb +++ b/spec/controllers/questions_controller_spec.rb @@ -1,131 +1,142 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe QuestionsController do - - def mock_question(stubs={}) - @mock_question ||= mock_model(Question, stubs) - end - - describe "GET index" do - it "assigns all questions as @questions" do - Question.stub!(:find).with(:all).and_return([mock_question]) - get :index - assigns[:questions].should == [mock_question] - end - end - - describe "GET show" do - it "assigns the requested question as @question" do - Question.stub!(:find).with("37").and_return(mock_question) - get :show, :id => "37" - assigns[:question].should equal(mock_question) - end - end - - describe "GET new" do - it "assigns a new question as @question" do - Question.stub!(:new).and_return(mock_question) - get :new - assigns[:question].should equal(mock_question) - end - end - - describe "GET edit" do - it "assigns the requested question as @question" do - Question.stub!(:find).with("37").and_return(mock_question) - get :edit, :id => "37" - assigns[:question].should equal(mock_question) - end - end - - describe "POST create" do - - describe "with valid params" do - it "assigns a newly created question as @question" do - Question.stub!(:new).with({'these' => 'params'}).and_return(mock_question(:save => true)) - post :create, :question => {:these => 'params'} - assigns[:question].should equal(mock_question) - end - - it "redirects to the created question" do - Question.stub!(:new).and_return(mock_question(:save => true)) - post :create, :question => {} - response.should redirect_to(question_url(mock_question)) - end - end - - describe "with invalid params" do - it "assigns a newly created but unsaved question as @question" do - Question.stub!(:new).with({'these' => 'params'}).and_return(mock_question(:save => false)) - post :create, :question => {:these => 'params'} - assigns[:question].should equal(mock_question) - end - - it "re-renders the 'new' template" do - Question.stub!(:new).and_return(mock_question(:save => false)) - post :create, :question => {} - response.should render_template('new') - end - end - - end - - describe "PUT update" do - - describe "with valid params" do - it "updates the requested question" do - Question.should_receive(:find).with("37").and_return(mock_question) - mock_question.should_receive(:update_attributes).with({'these' => 'params'}) - put :update, :id => "37", :question => {:these => 'params'} - end - - it "assigns the requested question as @question" do - Question.stub!(:find).and_return(mock_question(:update_attributes => true)) - put :update, :id => "1" - assigns[:question].should equal(mock_question) - end - - it "redirects to the question" do - Question.stub!(:find).and_return(mock_question(:update_attributes => true)) - put :update, :id => "1" - response.should redirect_to(question_url(mock_question)) - end - end - - describe "with invalid params" do - it "updates the requested question" do - Question.should_receive(:find).with("37").and_return(mock_question) - mock_question.should_receive(:update_attributes).with({'these' => 'params'}) - put :update, :id => "37", :question => {:these => 'params'} - end - - it "assigns the question as @question" do - Question.stub!(:find).and_return(mock_question(:update_attributes => false)) - put :update, :id => "1" - assigns[:question].should equal(mock_question) - end - - it "re-renders the 'edit' template" do - Question.stub!(:find).and_return(mock_question(:update_attributes => false)) - put :update, :id => "1" - response.should render_template('edit') - end - end - - end - - describe "DELETE destroy" do - it "destroys the requested question" do - Question.should_receive(:find).with("37").and_return(mock_question) - mock_question.should_receive(:destroy) - delete :destroy, :id => "37" - end - - it "redirects to the questions list" do - Question.stub!(:find).and_return(mock_question(:destroy => true)) - delete :destroy, :id => "1" - response.should redirect_to(questions_url) - end - end - + + # integrate_views + # + # def sign_in_as(user) + # @controller.current_user = user + # return user + # end + # + # before(:each) do + # sign_in_as(@user = Factory(:email_confirmed_user)) + # end + # + # def mock_question(stubs={}) + # @mock_question ||= mock_model(Question, stubs) + # end + # + # describe "GET index" do + # it "assigns all questions as @questions" do + # Question.stub!(:find).with(:all).and_return([mock_question]) + # get :index + # assigns[:questions].should == [mock_question] + # end + # end + # + # describe "GET show" do + # it "assigns the requested question as @question" do + # Question.stub!(:find).with("37").and_return(mock_question) + # get :show, :id => "37" + # assigns[:question].should equal(mock_question) + # end + # end + # + # describe "GET new" do + # it "assigns a new question as @question" do + # Question.stub!(:new).and_return(mock_question) + # get :new + # assigns[:question].should equal(mock_question) + # end + # end + # + # describe "GET edit" do + # it "assigns the requested question as @question" do + # Question.stub!(:find).with("37").and_return(mock_question) + # get :edit, :id => "37" + # assigns[:question].should equal(mock_question) + # end + # end + # + # describe "POST create" do + # + # describe "with valid params" do + # it "assigns a newly created question as @question" do + # Question.stub!(:new).with({'these' => 'params'}).and_return(mock_question(:save => true)) + # post :create, :question => {:these => 'params'} + # assigns[:question].should equal(mock_question) + # end + # + # it "redirects to the created question" do + # Question.stub!(:new).and_return(mock_question(:save => true)) + # post :create, :question => {} + # response.should redirect_to(question_url(mock_question)) + # end + # end + # + # describe "with invalid params" do + # it "assigns a newly created but unsaved question as @question" do + # Question.stub!(:new).with({'these' => 'params'}).and_return(mock_question(:save => false)) + # post :create, :question => {:these => 'params'} + # assigns[:question].should equal(mock_question) + # end + # + # it "re-renders the 'new' template" do + # Question.stub!(:new).and_return(mock_question(:save => false)) + # post :create, :question => {} + # response.should render_template('new') + # end + # end + # + # end + # + # describe "PUT update" do + # + # describe "with valid params" do + # it "updates the requested question" do + # Question.should_receive(:find).with("37").and_return(mock_question) + # mock_question.should_receive(:update_attributes).with({'these' => 'params'}) + # put :update, :id => "37", :question => {:these => 'params'} + # end + # + # it "assigns the requested question as @question" do + # Question.stub!(:find).and_return(mock_question(:update_attributes => true)) + # put :update, :id => "1" + # assigns[:question].should equal(mock_question) + # end + # + # it "redirects to the question" do + # Question.stub!(:find).and_return(mock_question(:update_attributes => true)) + # put :update, :id => "1" + # response.should redirect_to(question_url(mock_question)) + # end + # end + # + # describe "with invalid params" do + # it "updates the requested question" do + # Question.should_receive(:find).with("37").and_return(mock_question) + # mock_question.should_receive(:update_attributes).with({'these' => 'params'}) + # put :update, :id => "37", :question => {:these => 'params'} + # end + # + # it "assigns the question as @question" do + # Question.stub!(:find).and_return(mock_question(:update_attributes => false)) + # put :update, :id => "1" + # assigns[:question].should equal(mock_question) + # end + # + # it "re-renders the 'edit' template" do + # Question.stub!(:find).and_return(mock_question(:update_attributes => false)) + # put :update, :id => "1" + # response.should render_template('edit') + # end + # end + # + # end + # + # describe "DELETE destroy" do + # it "destroys the requested question" do + # Question.should_receive(:find).with("37").and_return(mock_question) + # mock_question.should_receive(:destroy) + # delete :destroy, :id => "37" + # end + # + # it "redirects to the questions list" do + # Question.stub!(:find).and_return(mock_question(:destroy => true)) + # delete :destroy, :id => "1" + # response.should redirect_to(questions_url) + # end + # end + end diff --git a/spec/models/choice_spec.rb b/spec/models/choice_spec.rb index ce456ae..71da5b0 100644 --- a/spec/models/choice_spec.rb +++ b/spec/models/choice_spec.rb @@ -13,7 +13,8 @@ describe Choice do @valid_attributes = { :creator => @johndoe, - :question => @question + :question => @question, + :data => 'hi there' } end diff --git a/spec/models/item_spec.rb b/spec/models/item_spec.rb index 29b5243..e06eb3b 100644 --- a/spec/models/item_spec.rb +++ b/spec/models/item_spec.rb @@ -3,13 +3,15 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe Item do it {should belong_to :creator} it {should belong_to :site} + it {should validate_presence_of :data} before(:each) do @aoi_clone = Factory.create(:user, :email => "pius@alum.mit.edu", :password => "password", :password_confirmation => "password", :id => 8) @johndoe = Factory.create(:visitor, :identifier => 'johndoe', :site => @aoi_clone) @valid_attributes = { :site => @aoi_clone, - :creator => @johndoe + :creator => @johndoe, + :data => 'a widget' } end -- libgit2 0.21.2