Skip to content

Commit

Permalink
[fixes #5, #2] add Comment CRUD
Browse files Browse the repository at this point in the history
now user has ability to create blog and comments
  • Loading branch information
kuldeepaggarwal committed Nov 28, 2016
1 parent 0535f0a commit 14c9e8c
Show file tree
Hide file tree
Showing 17 changed files with 369 additions and 2 deletions.
2 changes: 2 additions & 0 deletions app/abilities/blogger_ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ class BloggerAbility < Ability
def initialize(user)
can [:show, :destroy, :update], User, { id: user.id }
can [:create, :destroy, :update], Blog, { user_id: user.id }
can [:index, :create], Comment
can [:update, :destroy], Comment, { creator_id: user.id }
super
end
end
9 changes: 9 additions & 0 deletions app/controllers/api/v1/comments_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Api::V1::CommentsController < Api::V1::ResourceController
load_and_authorize_resource :blog
load_and_authorize_resource :comment, through: :blog

private
def resource_params
params.require(:comment).permit(:text).merge(creator_id: current_user.id)
end
end
1 change: 1 addition & 0 deletions app/models/blog.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class Blog < ApplicationRecord
# Associations
belongs_to :author, foreign_key: :user_id, class_name: :User
has_many :comments, dependent: :destroy

# Validations
validates :title, :description, presence: true
Expand Down
8 changes: 8 additions & 0 deletions app/models/comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Comment < ApplicationRecord
# Validations
belongs_to :blog
belongs_to :creator, class_name: :User

# Validations
validates :text, presence: true
end
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class User < ApplicationRecord

# Associations
has_many :blogs, dependent: :destroy
has_many :comments, dependent: :destroy, foreign_key: :creator_id

# Validations
validates :email, :first_name, presence: true
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/api/v1/blog_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Api::V1::BlogSerializer < ActiveModel::Serializer
attributes :id, :title, :description

has_one :author, serializer: UserSerializer
has_one :author, serializer: Api::V1::UserSerializer
end
3 changes: 3 additions & 0 deletions app/serializers/api/v1/comment_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Api::V1::CommentSerializer < ActiveModel::Serializer
attributes :id, :text, :blog_id, :creator_id
end
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
resources :users, only: [:index, :create, :show, :update, :destroy] do
resources :blogs, only: [:create, :show, :update, :destroy], shallow: true
end
resources :blogs, only: [] do
resources :comments, only: [:create, :update, :destroy, :index]
end
resources :blogs, only: [:index]
end
end
Expand Down
11 changes: 11 additions & 0 deletions db/migrate/20160129160122_create_comments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateComments < ActiveRecord::Migration[5.0]
def change
create_table :comments do |t|
t.references :blog, index: true, foreign_key: true
t.references :creator, index: true, foreign_key: true
t.text :text

t.timestamps
end
end
end
12 changes: 11 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160128192002) do
ActiveRecord::Schema.define(version: 20160129160122) do

create_table "blogs", force: :cascade do |t|
t.integer "user_id"
Expand All @@ -22,6 +22,16 @@
t.index ["user_id"], name: "index_blogs_on_user_id"
end

create_table "comments", force: :cascade do |t|
t.integer "blog_id"
t.integer "creator_id"
t.text "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["blog_id"], name: "index_comments_on_blog_id"
t.index ["creator_id"], name: "index_comments_on_creator_id"
end

create_table "users", force: :cascade do |t|
t.string "email"
t.string "password_digest"
Expand Down
276 changes: 276 additions & 0 deletions spec/controllers/api/v1/comments_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
require 'rails_helper'

RSpec.describe Api::V1::BlogsController, type: :api do
let(:admin) { FactoryGirl.create(:user, :admin) }
let(:blogger) { FactoryGirl.create(:user, :blogger) }
let(:blog) { FactoryGirl.create(:blog) }

describe '#index' do
context 'when guest' do
before { get api_v1_blog_comments_path(blog) }

it 'returns unauthorized error' do
expect(last_response.status).to eq(401)
end
end

shared_examples_for 'logged in user who sees list of comments' do
let!(:comments) { FactoryGirl.create_list(:comment, 2, blog: blog) }
let(:response) do
ActiveModel::ArraySerializer.new(
comments,
each_serializer: Api::V1::CommentSerializer,
root: 'comments',
meta: meta_attributes(comments)
).to_json
end

before { get api_v1_blog_comments_path(blog) }

it 'returns comments for a blog' do
expect(last_response.body).to eq(response)
end
end

context 'when admin' do
sign_in(:admin)

it_behaves_like('logged in user who sees list of comments')
end

context 'when blogger' do
sign_in(:blogger)

it_behaves_like('logged in user who sees list of comments')
end
end

describe '#create' do
context 'when guest' do
before { post api_v1_blog_comments_path(blog) }

it 'returns unauthorized error' do
expect(last_response.status).to eq(401)
end
end

shared_examples_for 'logged in user who create a comment' do
let!(:comment_attributes) { { text: "New comment" } }
let(:response) { Api::V1::CommentSerializer.new(comment).to_json }

context 'when valid paramaters' do
let(:comment) { Comment.last }
before do
post api_v1_blog_comments_path(blog, params: { comment: comment_attributes })
end

it 'returns 201 status code' do
expect(last_response.status).to eq(201)
end

it 'returns comment details' do
expect(last_response.body).to eq(response)
end
end

context 'when invalid paramaters' do
before do
post api_v1_blog_comments_path(blog, params: { comment: { text: '' } })
end

it 'returns 422 status code' do
expect(last_response.status).to eq(422)
end

it 'returns error messages' do
errors = ["Text can't be blank"]
expect(last_response.body).to eq(errors.to_json)
end
end
end

context 'when admin' do
sign_in(:admin)

it_behaves_like('logged in user who create a comment')
end

context 'when blogger' do
sign_in(:blogger)

it_behaves_like('logged in user who create a comment')
end
end

describe '#update' do
let(:comment) do
_comment = FactoryGirl.build(:comment).tap do |comment|
comment.creator = user if defined?(user)
end
_comment.save!
_comment
end
let(:comment_attributes) { { text: 'Updated comment' } }
let(:response) { Api::V1::CommentSerializer.new(comment).to_json }

shared_examples_for 'user_updates_comment' do
context 'when valid paramaters' do
before do
put api_v1_blog_comment_path(comment.blog, comment, params: { comment: comment_attributes })
end

it 'returns 200 status code' do
expect(last_response.status).to eq(200)
end

it 'returns blog details' do
comment.reload
expect(comment.text).to eq('Updated comment')
expect(last_response.body).to eq(response)
end
end

context 'when invalid paramaters' do
before do
put api_v1_blog_comment_path(comment.blog, comment, params: { comment: { text: '' } })
end

it 'returns 422 status code' do
expect(last_response.status).to eq(422)
end

it 'returns error messages' do
errors = ["Text can't be blank"]
expect(last_response.body).to eq(errors.to_json)
end
end
end

context 'when guest' do
before do
put api_v1_blog_comment_path(comment.blog, comment, params: { comment: comment_attributes })
end

it 'returns authentication error' do
expect(last_response.status).to eq(401)
end
end

context 'when blogger' do
sign_in(:blogger)

context 'when updating for self' do
let(:user) { blogger }

it_behaves_like 'user_updates_comment'
end

context 'when updating for other' do
let(:user) { admin }

before do
put api_v1_blog_comment_path(comment.blog, comment, params: { comment: comment_attributes })
end

it 'returns unauthorized error' do
expect(last_response.status).to eq(403)
end
end
end

context 'when admins visits' do
sign_in(:admin)

context 'when updating for self' do
let(:user) { admin }

it_behaves_like 'user_updates_comment'
end

context 'when updating for other' do
let(:user) { blogger }

it_behaves_like 'user_updates_comment'
end
end
end

describe '#destroy' do
let(:comment) do
_comment = FactoryGirl.build(:comment).tap do |comment|
comment.creator = user if defined?(user)
end
_comment.save!
_comment
end
let(:response) { { message: 'resource deleted successfully' }.to_json }

shared_examples_for 'user_deletes_comment' do
context 'when successful' do
before do
delete api_v1_blog_comment_path(comment.blog, comment)
end

it 'returns 200 status code' do
expect(last_response.status).to eq(200)
end

it 'returns blog details' do
expect(last_response.body).to eq(response)
end
end

context 'when unsuccessful' do
pending "not possible"
end
end

context 'when guest' do
before do
delete api_v1_blog_comment_path(comment.blog, comment)
end

it 'returns authentication error' do
expect(last_response.status).to eq(401)
end
end

context 'when blogger' do
sign_in(:blogger)

context 'when deleting for self' do
let(:user) { blogger }

it_behaves_like 'user_deletes_comment'
end

context 'when deleting for other' do
let(:user) { admin }

before do
delete api_v1_blog_comment_path(comment.blog, comment)
end

it 'returns unauthorized error' do
expect(last_response.status).to eq(403)
end
end
end

context 'when admins visits' do
sign_in(:admin)

context 'when deleting for self' do
let(:user) { admin }

it_behaves_like 'user_deletes_comment'
end

context 'when deleting for other' do
let(:user) { blogger }

it_behaves_like 'user_deletes_comment'
end
end
end
end
7 changes: 7 additions & 0 deletions spec/factories/comments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryGirl.define do
factory :comment do
text { Faker::Lorem.sentences(3).join("\n") }
blog { FactoryGirl.create(:blog) }
creator { blog.author }
end
end
Loading

0 comments on commit 14c9e8c

Please sign in to comment.