Commit c4549e910b79338c5e2df88e959fdd0127b35d45

Authored by Nathan Broadbent
1 parent d0e170ee
Exists in master and in 1 other branch production

Added simple comments form to errors. (including tests)

app/controllers/errs_controller.rb
... ... @@ -27,6 +27,7 @@ class ErrsController < ApplicationController
27 27 page = 1 if page.to_i.zero?
28 28 @notices = @err.notices.ordered.paginate(:page => page, :per_page => 1)
29 29 @notice = @notices.first
  30 + @comment = Comment.new
30 31 end
31 32  
32 33 def create_issue
... ... @@ -62,6 +63,30 @@ class ErrsController < ApplicationController
62 63 redirect_to app_path(@app)
63 64 end
64 65  
  66 +
  67 + def create_comment
  68 + @comment = Comment.new(params[:comment].merge(:user_id => current_user.id))
  69 + if @comment.valid?
  70 + @err.comments << @comment
  71 + @err.save
  72 + flash[:success] = "Comment saved!"
  73 + else
  74 + flash[:error] = "I'm sorry, your comment was blank! Try again?"
  75 + end
  76 + redirect_to app_err_path(@app, @err)
  77 + end
  78 +
  79 + def destroy_comment
  80 + @comment = Comment.find(params[:comment_id])
  81 + if @comment.destroy
  82 + flash[:success] = "Comment deleted!"
  83 + else
  84 + flash[:error] = "Sorry, I couldn't delete your comment for some reason. I hope you don't have any sensitive information in there!"
  85 + end
  86 + redirect_to app_err_path(@app, @err)
  87 + end
  88 +
  89 +
65 90 protected
66 91  
67 92 def find_app
... ... @@ -83,3 +108,4 @@ class ErrsController &lt; ApplicationController
83 108 end
84 109  
85 110 end
  111 +
... ...
app/models/comment.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class Comment
  2 + include Mongoid::Document
  3 + include Mongoid::Timestamps
  4 +
  5 + field :body, :type => String
  6 + index :user_id
  7 +
  8 + belongs_to :err
  9 + belongs_to :user
  10 +
  11 + validates_presence_of :body
  12 +end
  13 +
... ...
app/models/err.rb
... ... @@ -18,6 +18,7 @@ class Err
18 18  
19 19 belongs_to :app
20 20 has_many :notices
  21 + has_many :comments, :inverse_of => :err, :dependent => :destroy
21 22  
22 23 validates_presence_of :klass, :environment
23 24  
... ... @@ -51,3 +52,4 @@ class Err
51 52 end
52 53  
53 54 end
  55 +
... ...
app/views/errs/show.html.haml
... ... @@ -19,6 +19,23 @@
19 19 = link_to 'unlink issue', unlink_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue"
20 20 - if @err.unresolved?
21 21 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve'
  22 +- content_for :comments do
  23 + %h3 Comments on this Err
  24 + - @err.comments.each do |comment|
  25 + .window
  26 + %table.comment
  27 + %tr
  28 + %th
  29 + %span= link_to '&#10008;'.html_safe, destroy_comment_app_err_path(@app, @err) << "?comment_id=#{comment.id}", :method => :delete, :confirm => "Are sure you don't need this comment?", :class => "destroy-comment"
  30 + = time_ago_in_words(comment.created_at, true) << " ago by "
  31 + = link_to comment.user.email, user_path(comment.user)
  32 + %tr
  33 + %td= comment.body.gsub("\n", "<br>").html_safe
  34 +
  35 + = form_for @comment, :url => create_comment_app_err_path(@app, @err) do |comment_form|
  36 + %p Add a comment
  37 + = comment_form.text_area :body, :style => "width: 420px; height: 80px;"
  38 + = comment_form.submit "Save Comment"
22 39  
23 40 %h4= @notice.try(:message)
24 41  
... ... @@ -53,3 +70,4 @@ viewing occurrence #{@notices.current_page} of #{@notices.total_pages}
53 70 #session
54 71 %h3 Session
55 72 = render 'notices/session', :notice => @notice
  73 +
... ...
app/views/layouts/application.html.haml
... ... @@ -28,5 +28,9 @@
28 28 #content
29 29 = render :partial => 'shared/flash_messages'
30 30 = yield
  31 + - if content_for?(:comments)
  32 + #content-comments
  33 + = yield :comments
31 34 #footer= "Powered by #{link_to 'Errbit', 'http://github.com/jdpace/errbit', :target => '_blank'}: the open source error catcher.".html_safe
32 35 = yield :scripts
  36 +
... ...
config/routes.rb
... ... @@ -22,6 +22,8 @@ Errbit::Application.routes.draw do
22 22 put :resolve
23 23 post :create_issue
24 24 delete :unlink_issue
  25 + post :create_comment
  26 + delete :destroy_comment
25 27 end
26 28 end
27 29  
... ... @@ -33,3 +35,4 @@ Errbit::Application.routes.draw do
33 35 root :to => 'apps#index'
34 36  
35 37 end
  38 +
... ...
public/stylesheets/application.css
... ... @@ -139,14 +139,17 @@ a.action { float: right; font-size: 0.9em;}
139 139 border: 1px solid #C6C6C6;
140 140 }
141 141  
142   -/* Content Title */
143   -#content-title {
  142 +/* Content Title and Comments */
  143 +#content-title, #content-comments {
144 144 padding: 30px 20px;
145 145 border-top: 1px solid #FFF;
146 146 border-bottom: 1px solid #FFF;
147 147 background-color: #e2e2e2;
148 148 }
149   -#content-title h1 {
  149 +#content-comments {
  150 + background-color: #ffffff;
  151 +}
  152 +#content-title h1, #content-comments h3 {
150 153 padding: 0; margin: 0;
151 154 width: 85%;
152 155 border: none;
... ... @@ -154,6 +157,11 @@ a.action { float: right; font-size: 0.9em;}
154 157 font-size: 2em; line-height: 1em; font-weight: bold; font-family: arial, sans-serif;
155 158 word-wrap: break-word;
156 159 }
  160 +#content-comments h3 {
  161 + font-size: 1.5em;
  162 + margin-bottom: 14px;
  163 +}
  164 +
157 165 #content-title .meta { font-size: 0.9em; color: #787878; }
158 166  
159 167 /* Action Bar */
... ... @@ -611,6 +619,7 @@ table.tally th.value {
611 619 text-transform:none;
612 620 }
613 621  
  622 +
614 623 /* Resolve Errs */
615 624 #action-bar a.resolve {
616 625 background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat;
... ... @@ -695,3 +704,28 @@ span.click_span {
695 704 display: none;
696 705 }
697 706  
  707 +/* Comments */
  708 +#content-comments form p {
  709 + margin: 30px 0 0 0;
  710 + text-transform: uppercase;
  711 +}
  712 +table.comment tbody th {
  713 + text-transform: none;
  714 + font-weight: normal;
  715 + height: 20px;
  716 + line-height: 0.5em;
  717 +}
  718 +table.comment tbody td {
  719 + background-color: #F9F9F9;
  720 +}
  721 +#content-comments a.destroy-comment {
  722 + color: #EE0000;
  723 + margin-right: 5px;
  724 +}
  725 +#content-comments a.destroy-comment:hover {
  726 + text-decoration: none;
  727 +}
  728 +#content-comments #comment_submit {
  729 + margin-top: 15px;
  730 +}
  731 +
... ...
spec/controllers/errs_controller_spec.rb
... ... @@ -437,4 +437,60 @@ describe ErrsController do
437 437 end
438 438 end
439 439 end
  440 +
  441 +
  442 + describe "POST /apps/:app_id/errs/:id/create_comment" do
  443 + render_views
  444 +
  445 + before(:each) do
  446 + sign_in Factory(:admin)
  447 + end
  448 +
  449 + context "successful comment creation" do
  450 + let(:err) { Factory(:err) }
  451 + let(:user) { Factory(:user) }
  452 +
  453 + before(:each) do
  454 + post :create_comment, :app_id => err.app.id, :id => err.id,
  455 + :comment => { :body => "One test comment", :user_id => user.id }
  456 + err.reload
  457 + end
  458 +
  459 + it "should create the comment" do
  460 + err.comments.size.should == 1
  461 + end
  462 +
  463 + it "should redirect to err page" do
  464 + response.should redirect_to( app_err_path(err.app, err) )
  465 + end
  466 + end
  467 + end
  468 +
  469 + describe "DELETE /apps/:app_id/errs/:id/destroy_comment" do
  470 + render_views
  471 +
  472 + before(:each) do
  473 + sign_in Factory(:admin)
  474 + end
  475 +
  476 + context "successful comment deletion" do
  477 + let(:err) { Factory :err_with_comments }
  478 + let(:comment) { err.comments.first }
  479 +
  480 + before(:each) do
  481 + delete :destroy_comment, :app_id => err.app.id, :id => err.id, :comment_id => comment.id
  482 + err.reload
  483 + end
  484 +
  485 + it "should delete the comment" do
  486 + err.comments.detect{|c| c.id.to_s == comment.id }.should == nil
  487 + end
  488 +
  489 + it "should redirect to err page" do
  490 + response.should redirect_to( app_err_path(err.app, err) )
  491 + end
  492 + end
  493 + end
  494 +
440 495 end
  496 +
... ...
spec/factories/comment_factories.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +Factory.define :comment do |c|
  2 + c.user {|u| u.association :user}
  3 + c.body 'Test comment'
  4 +end
  5 +
... ...
spec/factories/err_factories.rb
... ... @@ -4,6 +4,11 @@ Factory.define :err do |e|
4 4 e.component 'foo'
5 5 e.action 'bar'
6 6 e.environment 'production'
  7 + e.comments []
  8 +end
  9 +
  10 +Factory.define(:err_with_comments, :parent => :err) do |ec|
  11 + ec.comments { (1..3).map{Factory(:comment)} }
7 12 end
8 13  
9 14 Factory.define :notice do |n|
... ... @@ -22,4 +27,5 @@ def random_backtrace
22 27 'method' => ActiveSupport.methods.shuffle.first
23 28 }}
24 29 backtrace
25   -end
26 30 \ No newline at end of file
  31 +end
  32 +
... ...
spec/models/comment_spec.rb 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Comment do
  4 + context 'validations' do
  5 + it 'should require a body' do
  6 + comment = Factory.build(:comment, :body => nil)
  7 + comment.should_not be_valid
  8 + comment.errors[:body].should include("can't be blank")
  9 + end
  10 + end
  11 +end
  12 +
... ...
spec/views/errs/show.html.haml_spec.rb
1 1 require 'spec_helper'
2 2  
3   -describe "errs/show.html.erb" do
4   - before do
  3 +describe "errs/show.html.erb" do
  4 + before do
5 5 err = Factory(:err)
  6 + comment = Factory(:comment)
6 7 assign :err, err
  8 + assign :comment, comment
7 9 assign :app, err.app
8 10 assign :notices, err.notices.ordered.paginate(:page => 1, :per_page => 1)
9 11 assign :notice, err.notices.first
... ... @@ -12,7 +14,7 @@ describe &quot;errs/show.html.erb&quot; do
12 14 describe "content_for :action_bar" do
13 15  
14 16 it "should confirm the 'resolve' link by default" do
15   - render
  17 + render
16 18 action_bar = String.new(view.instance_variable_get(:@_content_for)[:action_bar])
17 19 resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0]
18 20 resolve_link.should =~ /data-confirm="Seriously\?"/
... ... @@ -20,7 +22,7 @@ describe &quot;errs/show.html.erb&quot; do
20 22  
21 23 it "should confirm the 'resolve' link if configuration is unset" do
22 24 Errbit::Config.stub(:confirm_resolve_err).and_return(nil)
23   - render
  25 + render
24 26 action_bar = String.new(view.instance_variable_get(:@_content_for)[:action_bar])
25 27 resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0]
26 28 resolve_link.should =~ /data-confirm="Seriously\?"/
... ... @@ -28,7 +30,7 @@ describe &quot;errs/show.html.erb&quot; do
28 30  
29 31 it "should not confirm the 'resolve' link if configured not to" do
30 32 Errbit::Config.stub(:confirm_resolve_err).and_return(false)
31   - render
  33 + render
32 34 action_bar = String.new(view.instance_variable_get(:@_content_for)[:action_bar])
33 35 resolve_link = action_bar.match(/(<a href.*?(class="resolve").*?>)/)[0]
34 36 resolve_link.should_not =~ /data-confirm=/
... ... @@ -37,3 +39,4 @@ describe &quot;errs/show.html.erb&quot; do
37 39 end
38 40  
39 41 end
  42 +
... ...