diff --git a/Gemfile b/Gemfile
index a9b355c..278c7b1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -23,6 +23,7 @@ gem 'omniauth-twitter'
# Misc
gem 'activeadmin', github: 'activeadmin/activeadmin'
+gem 'acts_as_votable', '~> 0.11.0'
gem 'paperclip'
gem 'acts-as-taggable-on'
diff --git a/Gemfile.lock b/Gemfile.lock
index 72d7d8f..1faf831 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -55,14 +55,15 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
- acts-as-taggable-on (4.0.0)
- activerecord (>= 4.0)
- addressable (2.5.2)
- public_suffix (>= 2.0.2, < 4.0)
- annotate (2.7.2)
- activerecord (>= 3.2, < 6.0)
- rake (>= 10.4, < 13.0)
- arbre (1.1.1)
+ add-event-voting
+ acts-as-taggable-on (3.4.4)
+ activerecord (>= 3.2, < 5)
+ acts_as_votable (0.11.0)
+ addressable (2.3.8)
+ annotate (2.6.5)
+ activerecord (>= 2.3.0)
+ rake (>= 0.8.7)
+ arbre (1.0.2)
activesupport (>= 3.0.0)
arel (6.0.4)
ast (2.3.0)
@@ -382,6 +383,7 @@ DEPENDENCIES
active_record-acts_as
activeadmin!
acts-as-taggable-on
+ acts_as_votable (~> 0.11.0)
annotate
bootstrap-sass (~> 3.3.3)
bootstrap3-datetimepicker-rails (~> 4.14.30)
diff --git a/app/assets/javascripts/votes.js.coffee b/app/assets/javascripts/votes.js.coffee
new file mode 100644
index 0000000..da6d3c0
--- /dev/null
+++ b/app/assets/javascripts/votes.js.coffee
@@ -0,0 +1,5 @@
+$ ->
+ $('.vote-container').on 'ajax:success', 'form.vote-form', (event, data) ->
+ form = event.target
+ container = form.closest('.vote-container')
+ container.innerHTML = data
diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss
index 416cb29..065f1ed 100644
--- a/app/assets/stylesheets/application.css.scss
+++ b/app/assets/stylesheets/application.css.scss
@@ -28,6 +28,7 @@
@import "pages";
@import "people";
@import "scaffolds";
+@import "votes";
.font-heading { font-family: $font-heading; }
.font-body { font-family: $font-body; }
diff --git a/app/assets/stylesheets/votes.css.scss b/app/assets/stylesheets/votes.css.scss
new file mode 100644
index 0000000..59f0d30
--- /dev/null
+++ b/app/assets/stylesheets/votes.css.scss
@@ -0,0 +1,8 @@
+.vote-header {
+ display: inline-block;
+ margin-right: 0.5em;
+}
+
+.vote-form {
+ display: inline-block;
+}
diff --git a/app/controllers/votes_controller.rb b/app/controllers/votes_controller.rb
new file mode 100644
index 0000000..82e7657
--- /dev/null
+++ b/app/controllers/votes_controller.rb
@@ -0,0 +1,42 @@
+class VotesController < ApplicationController
+ before_filter :authenticate_user!
+ before_filter :set_votable
+ before_filter :ensure_votable
+
+ def create
+ @votable.liked_by current_user
+
+ if @votable.vote_registered?
+ render partial: 'votes/form', locals: { votable: @votable }
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ def destroy
+ if current_user.voted_for?(@votable)
+ @votable.unvote_by current_user
+ end
+
+ render partial: 'votes/form', locals: { votable: @votable }
+ end
+
+ private
+
+ def set_votable
+ model = if params[:votable_type] == "Event"
+ Event
+ end
+
+ if model
+ @votable = model.find_by_id(params[:votable_id])
+ end
+ end
+
+ def ensure_votable
+ return head(:not_found) unless @votable
+
+ # Disallow users to vote on their own items:
+ head :forbidden if can_edit?(@votable)
+ end
+end
diff --git a/app/helpers/votes_helper.rb b/app/helpers/votes_helper.rb
new file mode 100644
index 0000000..eaed9ac
--- /dev/null
+++ b/app/helpers/votes_helper.rb
@@ -0,0 +1,17 @@
+module VotesHelper
+ def upvote_button_class(votable)
+ if current_user.voted_up_on?(votable)
+ "btn bg-orange"
+ else
+ "btn bg-grey"
+ end
+ end
+
+ def upvote_form_method(votable)
+ if current_user.voted_up_on?(votable)
+ :delete
+ else
+ :post
+ end
+ end
+end
diff --git a/app/models/event.rb b/app/models/event.rb
index 2e79adf..a22488c 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -11,6 +11,7 @@
class Event < ActiveRecord::Base
acts_as :topic
acts_as_taggable
+ acts_as_votable
has_and_belongs_to_many :people
has_many :indications, as: :questionable
diff --git a/app/models/user.rb b/app/models/user.rb
index c990186..c2bd608 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -27,6 +27,7 @@ class User < ActiveRecord::Base
has_one :person
has_many :topics
accepts_nested_attributes_for :person
+ acts_as_voter
scope :admins, -> { where('admin = ?', true).all }
@@ -53,5 +54,4 @@ def self.new_with_session(params, session)
end
end
end
-
end
diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb
index adfc94f..e13ed61 100644
--- a/app/views/events/show.html.erb
+++ b/app/views/events/show.html.erb
@@ -19,7 +19,7 @@
<% end %>
<%= link_to indications_path(id: @event.id, type: :event), :method => :post, :class => "btn" do %>
- Flag For Spam
+ Flag For Spam
<% end %>
<% end %>
@@ -54,6 +54,11 @@
+ <% if user_signed_in? && !can_edit?(@event) %>
+
+ <%= render partial: "votes/form", locals: { votable: @event } %>
+
+ <% end %>
<%= render partial: 'topics/disqus' %>
diff --git a/app/views/news/show.html.erb b/app/views/news/show.html.erb
index 597d5ab..bdfb2a7 100644
--- a/app/views/news/show.html.erb
+++ b/app/views/news/show.html.erb
@@ -19,7 +19,7 @@
<% end %>
<%= link_to indications_path(id: @news.id, type: :news), :method => :post, :class => "btn" do %>
- Flag For Spam
+ Flag For Spam
<% end %>
<% end %>
diff --git a/app/views/resources/show.html.erb b/app/views/resources/show.html.erb
index 72bac35..38ede21 100644
--- a/app/views/resources/show.html.erb
+++ b/app/views/resources/show.html.erb
@@ -17,7 +17,7 @@
<% end %>
<%= link_to indications_path(id: @resource.id, type: :resource), :method => :post, :class => "btn" do %>
- Flag For Spam
+ Flag For Spam
<% end %>
<% end %>
diff --git a/app/views/votes/_form.html.erb b/app/views/votes/_form.html.erb
new file mode 100644
index 0000000..ae9c0f4
--- /dev/null
+++ b/app/views/votes/_form.html.erb
@@ -0,0 +1,13 @@
+
+<%= form_tag votes_path, method: upvote_form_method(votable), class: "vote-form", "data-remote" => true do %>
+
+
+
+<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index 2cf2759..acfe861 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -11,6 +11,12 @@
resources :at_who
resources :indications, only: :create
+ resources :votes, only: [:create] do
+ collection do
+ delete :destroy
+ end
+ end
+
resources :people do
collection do
post :send_message
diff --git a/db/migrate/20171014222022_create_votes.rb b/db/migrate/20171014222022_create_votes.rb
new file mode 100644
index 0000000..6b3d470
--- /dev/null
+++ b/db/migrate/20171014222022_create_votes.rb
@@ -0,0 +1,17 @@
+class CreateVotes < ActiveRecord::Migration
+ def change
+ create_table :votes do |t|
+ t.references :votable, polymorphic: true
+ t.references :voter, polymorphic: true
+
+ t.boolean :vote_flag
+ t.string :vote_scope
+ t.integer :vote_weight
+
+ t.timestamps
+ end
+
+ add_index :votes, [:voter_id, :voter_type, :vote_scope]
+ add_index :votes, [:votable_id, :votable_type, :vote_scope]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f83b938..48652ca 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,6 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
+ActiveRecord::Schema.define(version: 20171014222022) do
ActiveRecord::Schema.define(version: 20171009191656) do
create_table "events", force: true do |t|
@@ -174,4 +175,19 @@
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
add_index "users", ["uid"], name: "index_users_on_uid"
+ create_table "votes", force: true do |t|
+ t.integer "votable_id"
+ t.string "votable_type"
+ t.integer "voter_id"
+ t.string "voter_type"
+ t.boolean "vote_flag"
+ t.string "vote_scope"
+ t.integer "vote_weight"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "votes", ["votable_id", "votable_type", "vote_scope"], name: "index_votes_on_votable_id_and_votable_type_and_vote_scope"
+ add_index "votes", ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope"
+
end
diff --git a/spec/controllers/votes_controller_spec.rb b/spec/controllers/votes_controller_spec.rb
new file mode 100644
index 0000000..209c2da
--- /dev/null
+++ b/spec/controllers/votes_controller_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe VotesController, type: :controller do
+ render_views
+ let(:votable_owner) { FactoryGirl.create(:user) }
+ let(:votable) { FactoryGirl.create(:event, user: votable_owner) }
+
+ context "authenticated" do
+ let(:user) { FactoryGirl.create(:user) }
+ let(:my_votable) { FactoryGirl.create(:event, user: user) }
+
+ before(:each) do
+ request.env["devise.mapping"] = Devise.mappings[:user]
+ sign_in user
+ end
+
+ describe "#create" do
+ it "upvotes item the current user cannot edit" do
+ expect(user.voted_up_on?(votable)).to eq(false)
+
+ post :create, votable_type: votable.class.name, votable_id: votable.id
+
+ expect(response).to have_http_status(:ok)
+ expect(user.voted_up_on?(votable)).to eq(true)
+ end
+
+ it "does not upvote item the current user can edit" do
+ expect(user.voted_up_on?(my_votable)).to eq(false)
+
+ post :create, votable_type: my_votable.class.name, votable_id: my_votable.id
+
+ expect(response).to have_http_status(:forbidden)
+ expect(user.voted_up_on?(my_votable)).to eq(false)
+ end
+
+ it "404s when invalid votable type is given" do
+ post :create, votable_type: user.class.name, votable_id: user.id
+
+ expect(response).to have_http_status(:not_found)
+ end
+
+ it "404s when invalid votable ID is given" do
+ post :create, votable_type: votable.class.name, votable_id: votable.id + 100
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
+ describe "#destroy" do
+ it "removes an upvote" do
+ votable.liked_by user
+ expect(user.voted_for?(votable)).to eq(true)
+
+ delete :destroy, votable_type: votable.class.name, votable_id: votable.id
+
+ expect(response).to have_http_status(:ok)
+ expect(user.voted_for?(votable)).to eq(false)
+ end
+ end
+ end
+
+ context "unauthenticated" do
+ describe "#create" do
+ it "redirects" do
+ post :create, votable_type: votable.class.name, votable_id: votable.id
+
+ expect(response).to have_http_status(:redirect)
+ end
+ end
+
+ describe "#destroy" do
+ it "redirects" do
+ delete :destroy, votable_type: votable.class.name, votable_id: votable.id
+
+ expect(response).to have_http_status(:redirect)
+ end
+ end
+ end
+end
diff --git a/spec/models/indication_spec.rb b/spec/models/indication_spec.rb
index 063d9a9..84e4013 100644
--- a/spec/models/indication_spec.rb
+++ b/spec/models/indication_spec.rb
@@ -1,4 +1,4 @@
-require 'rails_helper'
+require 'spec_helper'
RSpec.describe Indication, :type => :model do
pending "add some examples to (or delete) #{__FILE__}"