From 02fd84a46387c50cb3c3907aebf5fd77e40aaa79 Mon Sep 17 00:00:00 2001 From: Masatoshi Iwasaki Date: Tue, 20 May 2014 18:07:21 +0900 Subject: [PATCH] Add a feature to send likes for ideas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit コメントへのlike機能を追加しようと思ったんですが、間に合わなそうなのでidea用だけ。。 --- app/assets/stylesheets/welcome.css.scss | 11 ++++ app/controllers/ideas_controller.rb | 20 +++++- app/models/comment.rb | 1 + app/models/idea.rb | 1 + app/models/like.rb | 6 ++ app/models/user.rb | 14 +++++ app/views/welcome/index.html.haml | 13 +++- config/routes.rb | 4 ++ db/migrate/20140520051103_create_likes.rb | 14 +++++ db/schema.rb | 14 ++++- spec/factories/likes.rb | 9 +++ spec/features/like_idea_spec.rb | 49 +++++++++++++++ spec/models/like_spec.rb | 6 ++ spec/models/user_spec.rb | 75 +++++++++++++++++++++++ 14 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 app/models/like.rb create mode 100644 db/migrate/20140520051103_create_likes.rb create mode 100644 spec/factories/likes.rb create mode 100644 spec/features/like_idea_spec.rb create mode 100644 spec/models/like_spec.rb diff --git a/app/assets/stylesheets/welcome.css.scss b/app/assets/stylesheets/welcome.css.scss index 77ce11a..4d36335 100644 --- a/app/assets/stylesheets/welcome.css.scss +++ b/app/assets/stylesheets/welcome.css.scss @@ -1,3 +1,14 @@ // Place all the styles related to the welcome controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ +.likeLink { + float: right; +} + +.ideaLink { + float: left; +} + +.list-group-item { + clear: both; +} diff --git a/app/controllers/ideas_controller.rb b/app/controllers/ideas_controller.rb index e7d4cdb..fca6125 100644 --- a/app/controllers/ideas_controller.rb +++ b/app/controllers/ideas_controller.rb @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- class IdeasController < ApplicationController before_action :authenticate, except: :show + before_action :find_idea, only: [:show, :like, :dislike] def show - @idea = Idea.find(params[:id]) @comments = @idea.comments @comment = Comment.new end @@ -18,4 +18,22 @@ def create render 'welcome/index' end end + + # PUT /like/1 + def like + current_user.like @idea + + redirect_to root_path, notice: 'いいね!しました。' + end + + def dislike + current_user.dislike @idea + + redirect_to root_path + end + + private + def find_idea + @idea = Idea.find(params[:id]) + end end diff --git a/app/models/comment.rb b/app/models/comment.rb index a3d82d4..a05d836 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,6 +1,7 @@ class Comment < ActiveRecord::Base belongs_to :idea belongs_to :user + has_many :likes, as: :likeable validates :body, length: { maximum: 1000 }, presence: true end diff --git a/app/models/idea.rb b/app/models/idea.rb index bfdf8f2..78e0c47 100644 --- a/app/models/idea.rb +++ b/app/models/idea.rb @@ -1,6 +1,7 @@ class Idea < ActiveRecord::Base has_many :comments belongs_to :user + has_many :likes, as: :likeable validates :title, length: { maximum: 100 }, presence: true end diff --git a/app/models/like.rb b/app/models/like.rb new file mode 100644 index 0000000..f8c4fc5 --- /dev/null +++ b/app/models/like.rb @@ -0,0 +1,6 @@ +class Like < ActiveRecord::Base + belongs_to :user + belongs_to :likeable, polymorphic: true + + validates :user_id, uniqueness: {scope: [:likeable_id, :likeable_type]} +end diff --git a/app/models/user.rb b/app/models/user.rb index 8a46437..92db1c0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -12,4 +12,18 @@ def self.find_or_create_from_auth_hash(auth_hash) user.image_url = image_url end end + + def like(idea) + idea.likes.create!(user_id: id) + end + + def dislike(idea) + like = idea.likes.where(user_id: id).first + raise ActiveRecord::RecordNotFound if like.nil? + like.destroy + end + + def liked?(idea) + idea.likes.where(user_id: id).count > 0 + end end diff --git a/app/views/welcome/index.html.haml b/app/views/welcome/index.html.haml index 2d0fc84..0a2c174 100644 --- a/app/views/welcome/index.html.haml +++ b/app/views/welcome/index.html.haml @@ -23,9 +23,16 @@ .list-group - @ideas.each do |idea| .list-group-item + .ideaLink = link_to idea do - if idea.user.try(:image_url) = image_tag(idea.user.image_url, width: 30, height: 30, alt: idea.user.nickname, title: idea.user.nickname).html_safe - = idea.title - - else - = idea.title + = idea.title + = "(#{idea.likes.count} いいね!)" + + .likeLink + - if current_user + - if current_user.liked?(idea) + = link_to 'いいね!を取り消す', dislike_idea_path(idea), class: 'dislike', method: :put + - else + = link_to 'いいね!', like_idea_path(idea), class: 'like', method: :put diff --git a/config/routes.rb b/config/routes.rb index 8c94f3a..654c654 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,6 +5,10 @@ get '/logout' => 'sessions#destroy', as: :logout resources :ideas, only: [:show, :create] do + member do + put :like + put :dislike + end resources :comments, only: :create end diff --git a/db/migrate/20140520051103_create_likes.rb b/db/migrate/20140520051103_create_likes.rb new file mode 100644 index 0000000..0bc94a3 --- /dev/null +++ b/db/migrate/20140520051103_create_likes.rb @@ -0,0 +1,14 @@ +class CreateLikes < ActiveRecord::Migration + def change + create_table :likes do |t| + t.integer :likeable_id + t.string :likeable_type + t.belongs_to :user, index: true + + t.timestamps + end + + add_index :likes, [:likeable_id, :likeable_type] + add_index :likes, [:likeable_id, :likeable_type, :user_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 58b5f42..c224884 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140427143623) do +ActiveRecord::Schema.define(version: 20140520051103) do create_table "comments", force: true do |t| t.integer "idea_id" @@ -33,6 +33,18 @@ add_index "ideas", ["user_id"], name: "index_ideas_on_user_id" + create_table "likes", force: true do |t| + t.integer "likeable_id" + t.string "likeable_type" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "likes", ["likeable_id", "likeable_type", "user_id"], name: "index_likes_on_likeable_id_and_likeable_type_and_user_id", unique: true + add_index "likes", ["likeable_id", "likeable_type"], name: "index_likes_on_likeable_id_and_likeable_type" + add_index "likes", ["user_id"], name: "index_likes_on_user_id" + create_table "users", force: true do |t| t.string "provider", null: false t.string "uid", null: false diff --git a/spec/factories/likes.rb b/spec/factories/likes.rb new file mode 100644 index 0000000..0e3f0ef --- /dev/null +++ b/spec/factories/likes.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :like do + likeable_id 1 + likeable_type "MyString" + user nil + end +end diff --git a/spec/features/like_idea_spec.rb b/spec/features/like_idea_spec.rb new file mode 100644 index 0000000..438c6a5 --- /dev/null +++ b/spec/features/like_idea_spec.rb @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +require 'spec_helper' + +describe 'アイデアに「いいね!」を付けられるようにする' do + let(:idea) { create :idea } + + context '未ログイン状態でトップページにアクセスしたとき' do + before { visit root_path(idea) } + + it 'いいね!用のリンクが表示されていないこと' do + expect(page).to have_no_css 'a.like' + end + end + + context 'ログイン状態でトップページにアクセスしたとき' do + before do + idea + login + visit root_path + end + + context 'いいね!を付ける前のとき' do + it 'いいね!用のリンクが表示されていること' do + expect(page).to have_css 'a.like' + end + end + + context 'いいね!ボタンを押したとき' do + before do + click_link 'いいね!' + end + + it '"いいね!を取り消す"と表示されていること' do + expect(page).to have_css 'a.dislike' + end + + it '"いいね!"した人の数が表示されていること' do + expect(page).to have_content "(1 いいね!)" + end + + it '"いいね!"を取り消すことができること' do + click_link 'いいね!を取り消す' + expect(page).to have_content "(0 いいね!)" + expect(page).to have_css 'a.like' + end + end + + end +end diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb new file mode 100644 index 0000000..aeda4d7 --- /dev/null +++ b/spec/models/like_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' + +describe Like do + it { should belong_to(:user) } + it { should belong_to(:likeable) } +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 156641f..3cbd384 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -46,4 +46,79 @@ end end end + + describe '#like' do + let!(:user) { create :user, provider: 'twitter', uid: 'uid' } + let!(:comment) { create :comment } + let(:idea) { comment.idea } + + context "アイデアに対して" do + it 'いいね!をするとlikeが増える' do + expect { + user.like(idea) + }.to change(idea.likes, :count).by(1) + end + end + + context "コメントに対して" do + it 'いいね!をするとlikeが増える' do + expect { + user.like(comment) + }.to change(comment.likes, :count).by(1) + end + end + end + + describe "#dislike" do + let!(:user) { create :user, provider: 'twitter', uid: 'uid' } + let!(:comment) { create :comment } + let(:idea) { comment.idea } + + context "アイデアに対して" do + context "すでにいいね!している場合" do + before do + user.like(idea) + end + + it 'いいね!を取り消すとlikeが減る' do + expect { + user.dislike(idea) + }.to change(idea.likes, :count).by(-1) + end + end + + context "いいね!していない場合" do + it 'RecordNotFoundが返る' do + expect { + user.dislike(idea) + }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + + context "コメントに対して" do + context "すでにいいね!している場合" do + before do + user.like(comment) + end + + it 'いいね!を取り消すとlikeが減る' do + expect { + user.dislike(comment) + }.to change(comment.likes, :count).by(-1) + end + end + + context "いいね!していない場合" do + it 'RecordNotFoundが返る' do + expect { + user.dislike(comment) + }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + end + + + end end