Skip to content
This repository has been archived by the owner on Feb 8, 2020. It is now read-only.

Commit

Permalink
Implement commenting on articles
Browse files Browse the repository at this point in the history
  • Loading branch information
avdgaag committed Mar 24, 2015
1 parent 42ded30 commit e1bce61
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 7 deletions.
1 change: 1 addition & 0 deletions apps/web/config/routes.rb
Expand Up @@ -7,3 +7,4 @@
# Please, uncomment the following line to have a working example.
get '/', to: 'home#index', as: 'root'
get '/articles/:id', to: 'articles#show', as: 'article'
post '/articles/:article_id/comments', to: 'comments#create', as: 'article_comments'
9 changes: 8 additions & 1 deletion apps/web/controllers/articles/show.rb
Expand Up @@ -3,9 +3,16 @@ class Show
include Web::Action

expose :article
expose :comments

def initialize(article_repository: Demo::ArticleRepository, comment_repository: Demo::CommentRepository)
@article_repository = article_repository
@comment_repository = comment_repository
end

def call(params)
@article = Demo::ArticleRepository.find(params[:id])
@article = @article_repository.find(params[:id])
@comments = @comment_repository.for_article(@article)
end
end
end
14 changes: 14 additions & 0 deletions apps/web/controllers/comments/create.rb
@@ -0,0 +1,14 @@
module Web::Controllers::Comments
class Create
include Web::Action

def initialize(comment_repository: Demo::CommentRepository)
@comment_repository = comment_repository
end

def call(params)
@comment_repository.create(Demo::Comment.new(params))
redirect_to "/articles/#{params[:article_id]}"
end
end
end
15 changes: 15 additions & 0 deletions apps/web/presenters/comment_presenter.rb
@@ -0,0 +1,15 @@
require 'kramdown'

module Web::Presenters
class CommentPresenter
include Lotus::Presenter

def created_at
super.strftime '%e %b %Y %H:%M'
end

def body
_raw Kramdown::Document.new(super).to_html
end
end
end
33 changes: 33 additions & 0 deletions apps/web/templates/articles/show.html.erb
Expand Up @@ -4,3 +4,36 @@
<%= article.body %>
</div>
</div>

<% if comments.any? %>
<h3 class="comments__heading"><%= comments_header %></h3>
<ol class="comments__list">
<% comments.each do |comment| %>
<li class="comment">
<footer class="comment__meta">
<span class="comment__author"><%= comment.author %></span>
on
<span class="comment__created-at"><%= comment.created_at %></span>:
</footer>
<blockquote class="comment__body">
<%= comment.body %>
</blockquote>
</li>
<% end %>
</ol>
<% end %>

<h3>Leave a comment</h3>
<form action="<%= routes.article_comments_path(article_id: article.id) %>" method="post">
<p>
<label for="comment_author">Name</label>
<input type="text" id="comment_author" name="author" value="">
</p>
<p>
<label for="comment_body">Comment</label>
<textarea id="comment_body" name="body"></textarea>
</p>
<p>
<input type="submit" value="Submit Comment">
</p>
</form>
14 changes: 14 additions & 0 deletions apps/web/views/articles/show.rb
Expand Up @@ -5,5 +5,19 @@ class Show
def article
Web::Presenters::ArticlePresenter.new(locals[:article])
end

def comments
locals[:comments].map do |comment|
Web::Presenters::CommentPresenter.new(comment)
end
end

def comments_header
if comments.count == 1
'1 comment'
else
"#{comments.count} comments"
end
end
end
end
26 changes: 21 additions & 5 deletions spec/web/controllers/articles/show_spec.rb
Expand Up @@ -2,20 +2,36 @@

module Web::Controllers::Articles
describe Show do
let(:action) { Web::Controllers::Articles::Show.new }
let(:article_repository) { Minitest::Mock.new }
let(:comment_repository) { Minitest::Mock.new }
let(:article) { Object.new }
let(:comments) {[] }
let(:action) { Web::Controllers::Articles::Show.new(article_repository: article_repository, comment_repository: comment_repository) }
let(:params) { Hash[id: 1] }

before do
article_repository.expect :find, article, [1]
comment_repository.expect :for_article, comments, [article]
end

after do
article_repository.verify
comment_repository.verify
end

it 'is successful' do
response = action.call(params)
assert_equal 200, response[0], 'expected HTTP response 200'
end

it 'exposes article' do
article = Object.new
Demo::ArticleRepository.stub :find, article do
action.call(params)
end
action.call(params)
assert_same article, action.exposures[:article], 'article object was not exposed'
end

it 'exposes comments' do
action.call(params)
assert_same comments, action.exposures[:comments], 'comments object was not exposed'
end
end
end
25 changes: 25 additions & 0 deletions spec/web/controllers/comments/create_spec.rb
@@ -0,0 +1,25 @@
require_relative '../../../spec_helper'

module Web::Controllers::Comments
describe Create do
let(:comment_repository) { Minitest::Mock.new }
let(:comment) { Demo::Comment.new(params) }
let(:action) { Web::Controllers::Comments::Create.new(comment_repository: comment_repository) }
let(:params) { Hash[article_id: 1, author: 'John', body: 'lorem ipsum'] }

before do
comment_repository.expect :create, true, [comment]
end

it 'is redirects to the article' do
response = action.call(params)
assert_equal 302, response[0]
assert_equal '/articles/1', response[1]['Location']
end

it 'creates a comment in the repository' do
action.call(params)
comment_repository.verify
end
end
end
30 changes: 30 additions & 0 deletions spec/web/features/commenting_spec.rb
@@ -0,0 +1,30 @@
require_relative '../../features_helper'

describe 'Commenting on articles' do
before do
Demo::ArticleRepository.clear
@article = Demo::ArticleRepository.create(
Demo::Article.new(title: 'Hello, world', body: 'Lorem ipsum')
)
end

describe 'when there are no comments' do
it 'hides the comments heading' do
visit '/'
click_link 'Hello, world'
assert page.has_no_css?('.comments__heading'), 'there should not have been a comments heading'
end
end

describe 'when there are comments' do
it 'shows the comment and the total number of comments' do
visit '/'
click_link 'Hello, world'
fill_in 'Name', with: 'John'
fill_in 'Comment', with: 'Nice post!'
click_button 'Submit Comment'
assert page.has_css?('.comments__heading', text: '1 comment'), 'there is no comments heading'
assert page.has_css?('.comment__body', text: 'Nice post!'), 'there is no comment'
end
end
end
20 changes: 20 additions & 0 deletions spec/web/presenters/comment_presenter_spec.rb
@@ -0,0 +1,20 @@
require_relative '../../spec_helper'

module Web::Presenters
describe CommentPresenter do
let(:comment) { Demo::Comment.new(author: 'John', body: 'lorem ipsum', created_at: Time.new(2015, 2, 1, 12, 30)) }
let(:presenter) { CommentPresenter.new(comment) }

it 'is a Lotus::Presenter' do
assert_kind_of Lotus::Presenter, presenter
end

it 'formats the created_at timestamp' do
assert_equal ' 1 Feb 2015 12:30', presenter.created_at, 'created_at formatting is incorrect'
end

it 'formats the body using markdown' do
assert_equal "<p>lorem ipsum</p>\n", presenter.body, "no markdown was applied to the body"
end
end
end
10 changes: 9 additions & 1 deletion spec/web/views/articles/show_spec.rb
Expand Up @@ -2,12 +2,20 @@

module Web::Views::Articles
describe Show do
let(:exposures) { Hash[article: Object.new] }
let(:exposures) { Hash[article: Object.new, comments: [Object.new]] }
let(:template) { Lotus::View::Template.new('apps/web/templates/articles/show.html.erb') }
let(:view) { Web::Views::Articles::Show.new(template, exposures) }

it 'exposes #article wrapped in a presenter' do
assert_kind_of Web::Presenters::ArticlePresenter, view.article, 'article should have been wrapped in a presenter'
end

it 'exposes #comments wrapped in presenters' do
assert_kind_of Web::Presenters::CommentPresenter, view.comments.first, 'comments should have been wrapped in a presenter'
end

it 'provides a comments heading' do
assert_equal '1 comment', view.comments_header
end
end
end

0 comments on commit e1bce61

Please sign in to comment.