Merge branch 'atomaka/feature/comments' into 'master'
Add comments to creddit Initial pass at commenting with some things left to consider. Primarily, double nesting of resources is advised as poor practice. Investigate alternatives. Testing on comments isn't entirely complete (delete missing and some other things). Comment replies need to be moved to inline. See merge request !15
This commit is contained in:
commit
4636bc837d
36 changed files with 687 additions and 62 deletions
1
Gemfile
1
Gemfile
|
@ -21,6 +21,7 @@ gem 'bootstrap-sass'
|
|||
gem 'simple_form'
|
||||
|
||||
gem 'friendly_id', '~> 5.1.0'
|
||||
gem 'ancestry'
|
||||
|
||||
gem 'bcrypt'
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ GEM
|
|||
ice_nine (~> 0.11.0)
|
||||
memoizable (~> 0.4.0)
|
||||
addressable (2.3.8)
|
||||
ancestry (2.1.0)
|
||||
activerecord (>= 3.0.0)
|
||||
arel (6.0.0)
|
||||
arrayfields (4.9.2)
|
||||
ast (2.0.0)
|
||||
|
@ -365,6 +367,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
ancestry
|
||||
bcrypt
|
||||
better_errors
|
||||
binding_of_caller
|
||||
|
|
3
app/assets/javascripts/comments.coffee
Normal file
3
app/assets/javascripts/comments.coffee
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/stylesheets/comments.scss
Normal file
3
app/assets/stylesheets/comments.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Place all the styles related to the Comments controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -42,6 +42,10 @@ $navbar-default-link-hover-color: #369;
|
|||
}
|
||||
}
|
||||
|
||||
.links {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
.logo {
|
||||
margin-top: 3px;
|
||||
|
@ -49,32 +53,7 @@ $navbar-default-link-hover-color: #369;
|
|||
}
|
||||
}
|
||||
|
||||
.posts {
|
||||
.post {
|
||||
p {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.links {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.content {
|
||||
border: 1px solid #369;
|
||||
background-color: #fafafa;
|
||||
border-radius: 7px; // need to work all browsers
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.contents {
|
||||
ul {
|
||||
list-style: none;
|
||||
list-style-type: none;
|
||||
|
@ -90,6 +69,38 @@ $navbar-default-link-hover-color: #369;
|
|||
li:first-child {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: .8em;
|
||||
}
|
||||
}
|
||||
|
||||
.posts {
|
||||
.post {
|
||||
margin: 0 0 15px 0;
|
||||
|
||||
.content {
|
||||
margin: 4px 0 4px 0;
|
||||
border: 1px solid #369;
|
||||
background-color: #fafafa;
|
||||
border-radius: 7px; // need to work all browsers
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comments {
|
||||
.comment {
|
||||
margin: 15px 0 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-custom {
|
||||
|
@ -99,3 +110,15 @@ $navbar-default-link-hover-color: #369;
|
|||
.main {
|
||||
margin: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.nested_comments {
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.in-page {
|
||||
padding: 5px;
|
||||
}
|
||||
|
|
62
app/controllers/comments_controller.rb
Normal file
62
app/controllers/comments_controller.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
class CommentsController < ApplicationController
|
||||
before_filter :set_comment, only: [:show, :edit, :update, :destroy]
|
||||
before_filter :set_post
|
||||
before_filter :set_subcreddit
|
||||
|
||||
def show
|
||||
@comments = @comment.subtree.arrange(order: :created_at)
|
||||
end
|
||||
|
||||
def new
|
||||
@comment = Comment.new(params[:parent_id])
|
||||
end
|
||||
|
||||
def create
|
||||
@comment = @post.comments.build comment_params
|
||||
@comment.user = current_user
|
||||
|
||||
if @comment.save
|
||||
flash[:notice] = 'Comment saved'
|
||||
else
|
||||
flash[:alert] = 'Comment could not be saved'
|
||||
end
|
||||
|
||||
redirect_to subcreddit_post_path(@subcreddit, @post)
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
if @comment.update comment_params
|
||||
redirect_to subcreddit_post_path(@subcreddit, @post),
|
||||
notice: 'Comment updated'
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@comment.destroy
|
||||
redirect_to subcreddit_post_path(@subcreddit, @post),
|
||||
notice: 'Comment deleted'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_subcreddit
|
||||
@subcreddit = Subcreddit.friendly.find(params[:subcreddit_id])
|
||||
end
|
||||
|
||||
def set_post
|
||||
@post = Post.find(params[:post_id])
|
||||
end
|
||||
|
||||
def set_comment
|
||||
@comment = Comment.find(params[:id])
|
||||
end
|
||||
|
||||
def comment_params
|
||||
params.require(:comment).permit(:parent_id, :content)
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@ class PostsController < ApplicationController
|
|||
before_filter :set_subcreddit
|
||||
|
||||
def show
|
||||
@comments = @post.comments.arrange(order: :created_at)
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
10
app/helpers/comments_helper.rb
Normal file
10
app/helpers/comments_helper.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
module CommentsHelper
|
||||
def nested_comments(comments)
|
||||
comments.map do |comment, sub_comments|
|
||||
render(comment, post: comment.post, subcreddit: comment.post.subcreddit) +
|
||||
content_tag(:div,
|
||||
nested_comments(sub_comments),
|
||||
class: 'nested_comments')
|
||||
end.join.html_safe
|
||||
end
|
||||
end
|
22
app/models/comment.rb
Normal file
22
app/models/comment.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
class Comment < ActiveRecord::Base
|
||||
has_ancestry
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :post, counter_cache: true
|
||||
|
||||
delegate :username, to: :user, prefix: true
|
||||
|
||||
validates :content, presence: true
|
||||
|
||||
def content
|
||||
destroyed? ? '[deleted]' : read_attribute(:content)
|
||||
end
|
||||
|
||||
def destroy
|
||||
update_attribute(:deleted_at, Time.now)
|
||||
end
|
||||
|
||||
def destroyed?
|
||||
self.deleted_at != nil
|
||||
end
|
||||
end
|
|
@ -2,6 +2,8 @@ class Post < ActiveRecord::Base
|
|||
belongs_to :user
|
||||
belongs_to :subcreddit
|
||||
|
||||
has_many :comments
|
||||
|
||||
delegate :username, to: :user, prefix: true
|
||||
|
||||
validates :title,
|
||||
|
@ -12,6 +14,16 @@ class Post < ActiveRecord::Base
|
|||
length: { maximum: 15000 }
|
||||
|
||||
def to_param
|
||||
# This "just works" because of the way Rails IDs work. .to_i must be run on
|
||||
# any incoming ID. "1-title-parameterized" will automatically be converted
|
||||
# to 1 and "2-title-paramerterized-with-number-2" will automatically be
|
||||
# converted to 2. This gives us desired functionality without adding code
|
||||
# to properly handle retrieving based on slug. Hopefully, this does not
|
||||
# cause issues later on.
|
||||
"#{self.id}-#{self.title.parameterize}"
|
||||
end
|
||||
|
||||
def comments?
|
||||
self.comments_count != 0
|
||||
end
|
||||
end
|
||||
|
|
13
app/views/comments/_comment.html.slim
Normal file
13
app/views/comments/_comment.html.slim
Normal file
|
@ -0,0 +1,13 @@
|
|||
.comment
|
||||
p.details= "#{comment.user_username} X points #{distance_of_time_in_words comment.created_at, Time.now} ago"
|
||||
p.content= comment.content
|
||||
ul.links.list-inline
|
||||
li= link_to 'permalink', subcreddit_post_comment_path(subcreddit, post, comment)
|
||||
li= link_to 'save', ''
|
||||
- if comment.parent
|
||||
li= link_to 'parent', subcreddit_post_comment_path(subcreddit, post, comment.parent)
|
||||
li= link_to 'edit', edit_subcreddit_post_comment_path(subcreddit, post, comment)
|
||||
li= link_to 'delete', subcreddit_post_comment_path(subcreddit, post, comment), method: :delete
|
||||
li= link_to 'spam', ''
|
||||
li= link_to 'remove', ''
|
||||
li= link_to 'give gold', ''
|
7
app/views/comments/_form.html.slim
Normal file
7
app/views/comments/_form.html.slim
Normal file
|
@ -0,0 +1,7 @@
|
|||
= simple_form_for [subcreddit, post, comment] do |f|
|
||||
- if local_assigns.has_key?(:parent) && parent
|
||||
= f.hidden_field :parent_id, value: parent.id
|
||||
.form-inputs
|
||||
= f.input :content, label: false
|
||||
.form-actions
|
||||
= f.button :submit
|
2
app/views/comments/edit.html.slim
Normal file
2
app/views/comments/edit.html.slim
Normal file
|
@ -0,0 +1,2 @@
|
|||
h1 Edit comment
|
||||
== render 'form', subcreddit: @subcreddit, post: @post, comment: @comment
|
0
app/views/comments/new.html.slim
Normal file
0
app/views/comments/new.html.slim
Normal file
8
app/views/comments/show.html.slim
Normal file
8
app/views/comments/show.html.slim
Normal file
|
@ -0,0 +1,8 @@
|
|||
== render 'posts/post', post: @post
|
||||
.alert.alert-info.in-page
|
||||
p you are viewing a single comment's thread.
|
||||
p #{link_to 'view the rest of the comments', subcreddit_post_path(@subcreddit, @post)} →
|
||||
= "Commenting as: #{current_user.username}"
|
||||
== render 'comments/form', subcreddit: @subcreddit, post: @post, comment: @post.comments.build, parent: @comment
|
||||
.comments.contents
|
||||
== nested_comments(@comments)
|
|
@ -7,7 +7,7 @@ html
|
|||
= csrf_meta_tags
|
||||
body
|
||||
== render 'navbar'
|
||||
.container
|
||||
.container-fluid
|
||||
== render 'flash_messages'
|
||||
.main.container-fluid
|
||||
.col-md-3.pull-right
|
||||
|
|
14
app/views/posts/_post.html.slim
Normal file
14
app/views/posts/_post.html.slim
Normal file
|
@ -0,0 +1,14 @@
|
|||
.posts.contents
|
||||
.post.show
|
||||
p.title= link_to post.title, [post.subcreddit, post]
|
||||
p.details= "submitted #{distance_of_time_in_words post.created_at, Time.now} ago by #{post.user_username}"
|
||||
p.content= post.content
|
||||
ul.links.list-inline
|
||||
li= link_to "#{post.comments_count} comments", subcreddit_post_path(post.subcreddit, post)
|
||||
li= link_to 'share', ''
|
||||
li= link_to 'edit', edit_subcreddit_post_path(post.subcreddit, post)
|
||||
li= link_to 'save', ''
|
||||
li= link_to 'hide', ''
|
||||
li= link_to 'remove', ''
|
||||
li= link_to 'approve', ''
|
||||
li= link_to 'nsfw', ''
|
|
@ -1,18 +1,9 @@
|
|||
.posts
|
||||
.post.show
|
||||
p.title= link_to @post.title, [@subcreddit, @post]
|
||||
p.details= "submitted #{distance_of_time_in_words @post.created_at, Time.now} ago by #{@post.user_username}"
|
||||
ul.links.list-inline
|
||||
li= link_to 'XXX comments', ''
|
||||
li= link_to 'source', ''
|
||||
li= link_to 'share', ''
|
||||
li= link_to 'save', ''
|
||||
li= link_to 'hide', ''
|
||||
li= link_to 'give gold', ''
|
||||
li= link_to 'spam', ''
|
||||
li= link_to 'remove', ''
|
||||
li= link_to 'approve', ''
|
||||
li= link_to 'report', ''
|
||||
li= link_to 'nsfw', ''
|
||||
li= link_to 'hide all child comments', ''
|
||||
p.content= @post.content
|
||||
== render 'post', post: @post
|
||||
- if @post.comments?
|
||||
.title= "all #{@post.comments_count} comments"
|
||||
- else
|
||||
.title= "no comments (yet)"
|
||||
= "Commenting as: #{current_user.username}"
|
||||
== render 'comments/form', subcreddit: @subcreddit, post: @post, comment: @post.comments.build, parent: nil
|
||||
.comments.contents
|
||||
== nested_comments(@comments)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
- if @subcreddit.closed?
|
||||
= "Board has been closed"
|
||||
- else
|
||||
.posts
|
||||
.posts.contents
|
||||
ul
|
||||
- @subcreddit.posts.order('created_at DESC').each_with_index do |post, rank|
|
||||
li
|
||||
.post
|
||||
p.title= link_to post.title, [@subcreddit, post]
|
||||
p.title= link_to post.title, subcreddit_post_path(@subcreddit, post)
|
||||
p.details= "submitted #{distance_of_time_in_words post.created_at, Time.now} ago by #{post.user_username}"
|
||||
ul.links.list-inline
|
||||
li= link_to 'XXX comments', ''
|
||||
li= link_to "#{post.comments_count} comments", subcreddit_post_path(@subcreddit, post)
|
||||
li= link_to 'share', ''
|
||||
|
|
|
@ -38,4 +38,11 @@ Rails.application.configure do
|
|||
|
||||
# Raises error for missing translations
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
||||
# Bullet
|
||||
config.after_initialize do
|
||||
Bullet.enable = true
|
||||
Bullet.bullet_logger = true
|
||||
Bullet.add_footer = true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,9 @@ Rails.application.routes.draw do
|
|||
get 'signout', to: 'user_sessions#destroy', as: :signout
|
||||
|
||||
resources :subcreddits, path: 'c', except: [:destroy] do
|
||||
resources :posts, except: [:index]
|
||||
resources :posts, path: '', constraints: { id: /\d+\-.+/ }, except: [:index] do
|
||||
resources :comments, path: '', constraints: { id: /\d+/ }, except: [:index]
|
||||
end
|
||||
end
|
||||
|
||||
resources :user_sessions, only: [:new, :create, :destroy]
|
||||
|
|
14
db/migrate/20150716155403_create_comments.rb
Normal file
14
db/migrate/20150716155403_create_comments.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
class CreateComments < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :comments do |t|
|
||||
t.references :user, index: true, foreign_key: true
|
||||
t.references :post, index: true, foreign_key: true
|
||||
t.string :ancestry
|
||||
t.text :content
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
add_index :comments, :ancestry
|
||||
end
|
||||
end
|
10
db/migrate/20150804000313_add_comments_count_to_post.rb
Normal file
10
db/migrate/20150804000313_add_comments_count_to_post.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
class AddCommentsCountToPost < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :posts, :comments_count, :integer, default: 0
|
||||
|
||||
Post.reset_column_information
|
||||
Post.all.each do |p|
|
||||
p.update_attribute :comments_count, p.comments.length
|
||||
end
|
||||
end
|
||||
end
|
5
db/migrate/20150804145405_add_deleted_at_to_comments.rb
Normal file
5
db/migrate/20150804145405_add_deleted_at_to_comments.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class AddDeletedAtToComments < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :comments, :deleted_at, :datetime
|
||||
end
|
||||
end
|
17
db/schema.rb
17
db/schema.rb
|
@ -11,7 +11,21 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20150716050055) do
|
||||
ActiveRecord::Schema.define(version: 20150804145405) do
|
||||
|
||||
create_table "comments", force: :cascade do |t|
|
||||
t.integer "user_id"
|
||||
t.integer "post_id"
|
||||
t.string "ancestry"
|
||||
t.text "content"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.datetime "deleted_at"
|
||||
end
|
||||
|
||||
add_index "comments", ["ancestry"], name: "index_comments_on_ancestry"
|
||||
add_index "comments", ["post_id"], name: "index_comments_on_post_id"
|
||||
add_index "comments", ["user_id"], name: "index_comments_on_user_id"
|
||||
|
||||
create_table "friendly_id_slugs", force: :cascade do |t|
|
||||
t.string "slug", null: false
|
||||
|
@ -34,6 +48,7 @@ ActiveRecord::Schema.define(version: 20150716050055) do
|
|||
t.text "content"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "comments_count", default: 0
|
||||
end
|
||||
|
||||
add_index "posts", ["subcreddit_id"], name: "index_posts_on_subcreddit_id"
|
||||
|
|
201
spec/controllers/comments_controller_spec.rb
Normal file
201
spec/controllers/comments_controller_spec.rb
Normal file
|
@ -0,0 +1,201 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe CommentsController, type: :controller do
|
||||
let!(:user) { build(:user) }
|
||||
let!(:subcreddit) { create(:subcreddit) }
|
||||
let!(:tpost) { create(:post, subcreddit: subcreddit) }
|
||||
let(:data) { { content: 'Here is some updated content for a comment' } }
|
||||
|
||||
before(:each) do
|
||||
allow_any_instance_of(ApplicationController)
|
||||
.to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
describe '#show' do
|
||||
let!(:comment) { create(:comment) }
|
||||
before(:each) do
|
||||
get :show,
|
||||
subcreddit_id: comment.post.subcreddit,
|
||||
post_id: comment.post,
|
||||
id: comment
|
||||
end
|
||||
|
||||
it 'should render :show' do
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
|
||||
it 'should assign correct Comment to @comment' do
|
||||
expect(assigns(:comment)).to eq(comment)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new' do
|
||||
let(:comment) { build(:comment) }
|
||||
before(:each) do
|
||||
get :new,
|
||||
subcreddit_id: comment.post.subcreddit,
|
||||
post_id: comment.post
|
||||
end
|
||||
|
||||
it 'should render :new' do
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
|
||||
it 'should assign new Comment to @comment' do
|
||||
expect(assigns(:comment)).to be_a_new(Comment)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
context 'with valid data' do
|
||||
it 'should create a comment' do
|
||||
expect do
|
||||
post :create,
|
||||
subcreddit_id: subcreddit,
|
||||
post_id: tpost,
|
||||
comment: data
|
||||
end.to change(Comment, :count).by(1)
|
||||
end
|
||||
|
||||
it 'should redirect to the parent post' do
|
||||
expect(post :create,
|
||||
subcreddit_id: subcreddit,
|
||||
post_id: tpost,
|
||||
comment: data
|
||||
).to redirect_to(subcreddit_post_path(assigns(:post).subcreddit,
|
||||
assigns(:post)))
|
||||
end
|
||||
|
||||
it 'should send a notice flash message' do
|
||||
expect(post :create,
|
||||
subcreddit_id: subcreddit,
|
||||
post_id: tpost,
|
||||
comment: data)
|
||||
|
||||
expect(flash[:notice]).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
before(:each) { data['content'] = '' }
|
||||
|
||||
it 'should not create a new comment' do
|
||||
expect do
|
||||
post :create,
|
||||
subcreddit_id: subcreddit,
|
||||
post_id: tpost,
|
||||
comment: data
|
||||
end.to change(Comment, :count).by(0)
|
||||
end
|
||||
|
||||
it 'should render :new' do
|
||||
expect(post :create,
|
||||
subcreddit_id: subcreddit,
|
||||
post_id: tpost,
|
||||
comment: data
|
||||
).to redirect_to(subcreddit_post_path(subcreddit, tpost))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#edit' do
|
||||
let!(:comment) { create(:comment) }
|
||||
before(:each) do
|
||||
get :edit,
|
||||
id: comment,
|
||||
post_id: comment.post,
|
||||
subcreddit_id: comment.post.subcreddit
|
||||
end
|
||||
|
||||
context 'with valid comment' do
|
||||
it 'should render :edit' do
|
||||
expect(response).to render_template(:edit)
|
||||
end
|
||||
|
||||
it 'should assign correct Comment to @comment' do
|
||||
expect(assigns(:comment)).to eq(comment)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#update' do
|
||||
let!(:comment) { create(:comment) }
|
||||
let(:data) { { content: 'Some edited comment content goes here' } }
|
||||
|
||||
context 'with valid data' do
|
||||
before(:each) do
|
||||
put :update,
|
||||
id: comment,
|
||||
post_id: comment.post,
|
||||
subcreddit_id: comment.post.subcreddit,
|
||||
comment: data
|
||||
end
|
||||
|
||||
it 'should assign correct Comment to @comment' do
|
||||
expect(assigns(:comment)).to eq(comment)
|
||||
end
|
||||
|
||||
it 'should update the comment' do
|
||||
comment.reload
|
||||
|
||||
expect(comment.content).to eq(data[:content])
|
||||
end
|
||||
|
||||
it 'should redirect to the post' do
|
||||
expect(response)
|
||||
.to redirect_to(subcreddit_post_path(assigns(:post).subcreddit,
|
||||
assigns(:post)))
|
||||
end
|
||||
|
||||
it 'should display a notice flash message' do
|
||||
expect(flash[:notice]).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
before(:each) { data[:content] = '' }
|
||||
|
||||
it 'should render :edit' do
|
||||
put :update,
|
||||
id: comment,
|
||||
post_id: comment.post,
|
||||
subcreddit_id: comment.post.subcreddit,
|
||||
comment: data
|
||||
|
||||
expect(response).to render_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#destroy' do
|
||||
let!(:comment) { create(:comment) }
|
||||
|
||||
it 'should delete the post' do
|
||||
delete :destroy,
|
||||
id: comment,
|
||||
post_id: comment.post,
|
||||
subcreddit_id: comment.post.subcreddit
|
||||
|
||||
comment.reload
|
||||
expect(comment.destroyed?).to be(true)
|
||||
end
|
||||
|
||||
it 'should redirect to the post' do
|
||||
delete :destroy,
|
||||
id: comment,
|
||||
post_id: comment.post,
|
||||
subcreddit_id: comment.post.subcreddit
|
||||
|
||||
expect(response).to redirect_to(subcreddit_post_path(assigns(:subcreddit),
|
||||
assigns(:post)))
|
||||
end
|
||||
|
||||
it 'should send a notice flash message' do
|
||||
delete :destroy,
|
||||
id: comment,
|
||||
post_id: comment.post,
|
||||
subcreddit_id: comment.post.subcreddit
|
||||
expect(flash[:notice]).to be_present
|
||||
end
|
||||
end
|
||||
end
|
7
spec/factories/comment_factory.rb
Normal file
7
spec/factories/comment_factory.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
FactoryGirl.define do
|
||||
factory :comment do
|
||||
user
|
||||
post
|
||||
content { Faker::Lorem.paragraph }
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
FactoryGirl.define do
|
||||
factory :user do
|
||||
username { Faker::Internet.user_name }
|
||||
sequence(:username) { |n| Faker::Internet.user_name + "#{n}" }
|
||||
password { Faker::Internet.password(8, 50) }
|
||||
email { Faker::Internet.email }
|
||||
end
|
||||
|
|
30
spec/features/comments/edit_comment_spec.rb
Normal file
30
spec/features/comments/edit_comment_spec.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe 'Edit Comment', type: :feature do
|
||||
let!(:user) { create(:user) }
|
||||
let!(:post) { create(:post) }
|
||||
let!(:comment) { create(:comment, post: post, user: user) }
|
||||
|
||||
context 'when signed in' do
|
||||
let(:content) { 'Some different data' }
|
||||
before(:each) { signin(user: user) }
|
||||
|
||||
context 'with valid data' do
|
||||
before(:each) do
|
||||
visit edit_subcreddit_post_comment_path(post.subcreddit, post, comment)
|
||||
|
||||
fill_in :comment_content, with: content
|
||||
|
||||
click_button 'Update Comment'
|
||||
end
|
||||
|
||||
it 'should notify that the comment was edited' do
|
||||
expect(page).to have_content('updated')
|
||||
end
|
||||
|
||||
it 'should update the comment' do
|
||||
expect(page).to have_content(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
57
spec/features/comments/new_comment_spec.rb
Normal file
57
spec/features/comments/new_comment_spec.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe 'New Comment', type: :feature do
|
||||
let!(:post) { create(:post) }
|
||||
let(:comment) { build(:comment) }
|
||||
|
||||
context 'when signed in' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before(:each) { signin(user: user) }
|
||||
|
||||
context 'with valid data' do
|
||||
before(:each) do
|
||||
visit subcreddit_post_path(post.subcreddit, post)
|
||||
|
||||
fill_in :comment_content, with: comment.content
|
||||
|
||||
click_button 'Create Comment'
|
||||
end
|
||||
|
||||
it 'should notify that a new content was created' do
|
||||
expect(page).to have_content('saved')
|
||||
end
|
||||
|
||||
it 'should display the new comment' do
|
||||
expect(page).to have_content(comment.content)
|
||||
end
|
||||
|
||||
context 'when nesting comment' do
|
||||
let!(:comment) { create(:comment, post: post) }
|
||||
|
||||
it 'should display a nested comment' do
|
||||
visit subcreddit_post_comment_path(post.subcreddit, post, comment)
|
||||
|
||||
fill_in :comment_content, with: comment.content
|
||||
click_button 'Create Comment'
|
||||
|
||||
expect(page).to have_css('div.nested_comments')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
before(:each) do
|
||||
visit subcreddit_post_path(post.subcreddit, post)
|
||||
|
||||
fill_in :comment_content, with: ''
|
||||
|
||||
click_button 'Create Comment'
|
||||
end
|
||||
|
||||
it 'should display errors' do
|
||||
expect(page).to have_content('could not')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -24,8 +24,9 @@ describe 'Edit Post', type: :feature do
|
|||
expect(page).to have_content('updated')
|
||||
end
|
||||
|
||||
it 'should show the post' do
|
||||
it 'should show the updated post' do
|
||||
expect(page).to have_content(new_post.title)
|
||||
expect(page).to have_content(new_post.content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ describe 'List Posts', type: :feature do
|
|||
|
||||
posts.each do |post|
|
||||
expect(page)
|
||||
.to have_link(post.title, subcreddit_post_path(post, post.subcreddit))
|
||||
.to have_link(post.title, subcreddit_post_path(post.subcreddit, post))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
7
spec/helpers/comments_helper_spec.rb
Normal file
7
spec/helpers/comments_helper_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe CommentsHelper do
|
||||
describe '#nested_comments' do
|
||||
it 'renders the comment partial'
|
||||
end
|
||||
end
|
63
spec/models/comment_spec.rb
Normal file
63
spec/models/comment_spec.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Comment, type: :model do
|
||||
let(:comment) { build(:comment) }
|
||||
|
||||
it { should belong_to(:user) }
|
||||
it { should belong_to(:post).counter_cache(true) }
|
||||
|
||||
it { should delegate_method(:username).to(:user).with_prefix }
|
||||
|
||||
context 'with valid data' do
|
||||
it 'should be valid' do
|
||||
expect(comment).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid data' do
|
||||
it 'should not be valid with blank content' do
|
||||
comment.content = ''
|
||||
|
||||
expect(comment).to be_invalid
|
||||
end
|
||||
end
|
||||
|
||||
context 'when comment is deleted' do
|
||||
before(:each) { comment.deleted_at = Time.now }
|
||||
|
||||
context '#destroyed?' do
|
||||
it 'should respond with true' do
|
||||
expect(comment.destroyed?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context '#content' do
|
||||
it 'should return [deleted]' do
|
||||
expect(comment.content).to eq('[deleted]')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when comment is not deleted' do
|
||||
context '#destroyed?' do
|
||||
it 'should respond with false' do
|
||||
expect(comment.destroyed?).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context '#content' do
|
||||
it 'should return comment content' do
|
||||
expect(comment.content).to eq(comment.content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context '#destroy' do
|
||||
it 'should set the deleted_at time appropriately' do
|
||||
Timecop.freeze do
|
||||
comment.destroy
|
||||
expect(comment.deleted_at).to eq(Time.now)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,9 +5,20 @@ describe Post, type: :model do
|
|||
|
||||
it { should belong_to(:user) }
|
||||
it { should belong_to(:subcreddit) }
|
||||
it { should have_many(:comments) }
|
||||
|
||||
it { should delegate_method(:username).to(:user).with_prefix }
|
||||
|
||||
context 'when adding a comment' do
|
||||
let(:post) { create(:post) }
|
||||
|
||||
it 'should update the cache_counter for comments' do
|
||||
expect do
|
||||
create(:comment, post: post)
|
||||
end.to change { post.comments_count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid data' do
|
||||
it 'should be valid' do
|
||||
expect(post).to be_valid
|
||||
|
@ -53,4 +64,24 @@ describe Post, type: :model do
|
|||
expect(post.to_param).to eq("#{post.id}-#{post.title.parameterize}")
|
||||
end
|
||||
end
|
||||
|
||||
context '#comments?' do
|
||||
let(:post) { create(:post) }
|
||||
|
||||
context 'with comments' do
|
||||
before(:each) do
|
||||
create(:comment, post: post)
|
||||
end
|
||||
|
||||
it 'should respond with true' do
|
||||
expect(post.comments?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without comments' do
|
||||
it 'should respond with false' do
|
||||
expect(post.comments?).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue