Commit ce442af995f39734dd3d9d827b634b586c476fcd

Authored by Jared Pace
1 parent 7e53d6cc
Exists in master and in 1 other branch production

Add full management support for projects

app/controllers/projects_controller.rb
@@ -9,4 +9,42 @@ class ProjectsController < ApplicationController @@ -9,4 +9,42 @@ class ProjectsController < ApplicationController
9 @errs = @project.errs.paginate 9 @errs = @project.errs.paginate
10 end 10 end
11 11
  12 + def new
  13 + @project = Project.new
  14 + @project.watchers.build
  15 + end
  16 +
  17 + def edit
  18 + @project = Project.find(params[:id])
  19 + @project.watchers.build if @project.watchers.none?
  20 + end
  21 +
  22 + def create
  23 + @project = Project.new(params[:project])
  24 +
  25 + if @project.save
  26 + flash[:success] = 'Great success! Configure your project with the API key below'
  27 + redirect_to project_path(@project)
  28 + else
  29 + render :new
  30 + end
  31 + end
  32 +
  33 + def update
  34 + @project = Project.find(params[:id])
  35 +
  36 + if @project.update_attributes(params[:project])
  37 + flash[:success] = "Good news everyone! '#{@project.name}' was successfully updated."
  38 + redirect_to project_path(@project)
  39 + else
  40 + render :edit
  41 + end
  42 + end
  43 +
  44 + def destroy
  45 + @project = Project.find(params[:id])
  46 + @project.destroy
  47 + flash[:success] = "'#{@project.name}' was successfully destroyed."
  48 + redirect_to projects_path
  49 + end
12 end 50 end
app/models/project.rb
@@ -8,13 +8,16 @@ class Project @@ -8,13 +8,16 @@ class Project
8 8
9 embeds_many :watchers 9 embeds_many :watchers
10 embeds_many :deploys 10 embeds_many :deploys
11 - references_many :errs 11 + references_many :errs, :dependent => :destroy
12 12
13 before_validation :generate_api_key, :on => :create 13 before_validation :generate_api_key, :on => :create
14 14
15 validates_presence_of :name, :api_key 15 validates_presence_of :name, :api_key
16 validates_uniqueness_of :name, :api_key, :allow_blank => true 16 validates_uniqueness_of :name, :api_key, :allow_blank => true
17 17
  18 + accepts_nested_attributes_for :watchers,
  19 + :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }
  20 +
18 def self.find_by_api_key!(key) 21 def self.find_by_api_key!(key)
19 where(:api_key => key).first || raise(Mongoid::Errors::DocumentNotFound.new(self,key)) 22 where(:api_key => key).first || raise(Mongoid::Errors::DocumentNotFound.new(self,key))
20 end 23 end
app/views/errs/_table.html.haml
@@ -20,4 +20,8 @@ @@ -20,4 +20,8 @@
20 %em= err.where 20 %em= err.where
21 %td.latest #{time_ago_in_words(err.last_notice_at)} ago 21 %td.latest #{time_ago_in_words(err.last_notice_at)} ago
22 %td.deploy= err.project.last_deploy_at ? err.project.last_deploy_at.to_date.to_s(:micro) : 'n/a' 22 %td.deploy= err.project.last_deploy_at ? err.project.last_deploy_at.to_date.to_s(:micro) : 'n/a'
23 - %td.count= err.notices.count  
24 \ No newline at end of file 23 \ No newline at end of file
  24 + %td.count= err.notices.count
  25 + - if errs.none?
  26 + %tr
  27 + %td{:colspan => (@project ? 5 : 6)}
  28 + %em No errors have been caught, yet
25 \ No newline at end of file 29 \ No newline at end of file
app/views/layouts/application.html.haml
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 Hypnotoad — 5 Hypnotoad —
6 = yield(:page_title).present? ? yield(:page_title) : yield(:title) 6 = yield(:page_title).present? ? yield(:page_title) : yield(:title)
7 %meta{ :content => "text/html; charset=utf-8", "http-equiv" => "content-type" }/ 7 %meta{ :content => "text/html; charset=utf-8", "http-equiv" => "content-type" }/
  8 + = csrf_meta_tag
8 = javascript_include_tag :defaults 9 = javascript_include_tag :defaults
9 = stylesheet_link_tag 'reset', 'application' 10 = stylesheet_link_tag 'reset', 'application'
10 = yield :head 11 = yield :head
app/views/projects/_configuration_instructions.html.haml 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +%pre
  2 + %code
  3 + :preserve
  4 +
  5 + # Require the hoptoad_notifier gem in you App.
  6 + #
  7 + # Rails 3 - In your Gemfile
  8 + # gem 'hoptoad_notifier'
  9 + #
  10 + # Rails 2 - In environment.rb
  11 + # config.gem 'hoptoad_notifier'
  12 + #
  13 + # Then add the following to config/initializers/hoptoad.rb
  14 + HoptoadNotifier.configure do |config|
  15 + config.api_key = '#{project.api_key}'
  16 + config.host = '#{request.host}'
  17 + config.port = #{request.port}
  18 + # Note: Deployment notifications only work on port 80
  19 + end
  20 +
  21 +
0 \ No newline at end of file 22 \ No newline at end of file
app/views/projects/_fields.html.haml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +%div.required
  2 + = f.label :name
  3 + = f.text_field :name
  4 +
  5 +%fieldset
  6 + %legend Watchers
  7 + - f.fields_for :watchers do |w|
  8 + %div
  9 + = w.label :email
  10 + = w.text_field :email
0 \ No newline at end of file 11 \ No newline at end of file
app/views/projects/edit.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +- content_for :title, 'Edit Project'
  2 +- content_for :action_bar, link_to('cancel', project_path(@project))
  3 +
  4 += form_for @project do |f|
  5 +
  6 + = render 'fields', :f => f
  7 +
  8 + %div= f.submit 'Update'
0 \ No newline at end of file 9 \ No newline at end of file
app/views/projects/index.html.haml
1 - content_for :title, 'Projects' 1 - content_for :title, 'Projects'
  2 +- content_for :action_bar, link_to('new project', new_project_path)
2 3
3 %table.projects 4 %table.projects
4 %thead 5 %thead
app/views/projects/new.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +- content_for :title, 'Add Project'
  2 +- content_for :action_bar, link_to('cancel', projects_path)
  3 +
  4 += form_for @project do |f|
  5 +
  6 + = render 'fields', :f => f
  7 +
  8 + %div= f.submit 'Add'
0 \ No newline at end of file 9 \ No newline at end of file
app/views/projects/show.html.haml
@@ -7,7 +7,25 @@ @@ -7,7 +7,25 @@
7 - content_for :action_bar do 7 - content_for :action_bar do
8 = link_to 'edit', edit_project_path(@project) 8 = link_to 'edit', edit_project_path(@project)
9 | 9 |
10 - = link_to 'destroy', project_path(@project), :method => :destroy, :confirm => 'Seriously?' 10 + = link_to 'destroy', project_path(@project), :method => :delete, :confirm => 'Seriously?'
  11 +
  12 +- if @project.errs.none?
  13 + %h3 Setup your app
  14 + = render 'configuration_instructions', :project => @project
  15 +
  16 +%h3 Watchers
  17 +%table.watchers
  18 + %thead
  19 + %tr
  20 + %th Email
  21 + %tbody
  22 + - @project.watchers.each do |watcher|
  23 + %tr
  24 + %td= watcher.email
  25 + - if @project.watchers.none?
  26 + %tr
  27 + %td
  28 + %em Sadly, no one is watching this project
11 29
12 %h3 Errors 30 %h3 Errors
13 = render 'errs/table', :errs => @errs 31 = render 'errs/table', :errs => @errs
14 \ No newline at end of file 32 \ No newline at end of file
public/stylesheets/application.css
@@ -151,6 +151,17 @@ table th { background-color: #E2E2E2; font-weight: bold; text-transform: upperca @@ -151,6 +151,17 @@ table th { background-color: #E2E2E2; font-weight: bold; text-transform: upperca
151 table tbody tr:nth-child(odd) td { background-color: #F9F9F9; } 151 table tbody tr:nth-child(odd) td { background-color: #F9F9F9; }
152 table .main { width: 100%; } 152 table .main { width: 100%; }
153 153
  154 +/* Code */
  155 +pre {
  156 + padding: 0.8em;
  157 + margin-bottom: 1em;
  158 + color: #f0f0f0;
  159 + background-color: #222;
  160 + border: 1px solid #444;
  161 + font-family: monaco, courier, monospace;
  162 + font-size: 1.1em;
  163 +}
  164 +
154 /* HTML Styling */ 165 /* HTML Styling */
155 .html { padding-left: 1em; border-left: 2px solid #C6C6C6;} 166 .html { padding-left: 1em; border-left: 2px solid #C6C6C6;}
156 .html h1, .html h2, .html h3, .html h4, .html h5, .html h6 { 167 .html h1, .html h2, .html h3, .html h4, .html h5, .html h6 {
spec/controllers/projects_controller_spec.rb
@@ -19,4 +19,110 @@ describe ProjectsController do @@ -19,4 +19,110 @@ describe ProjectsController do
19 end 19 end
20 end 20 end
21 21
  22 + describe "GET /projects/new" do
  23 + it 'instantiates a new project with a prebuilt watcher' do
  24 + get :new
  25 + assigns(:project).should be_a(Project)
  26 + assigns(:project).should be_new_record
  27 + assigns(:project).watchers.should_not be_empty
  28 + end
  29 + end
  30 +
  31 + describe "GET /projects/:id/edit" do
  32 + it 'finds the correct project' do
  33 + project = Factory(:project)
  34 + get :edit, :id => project.id
  35 + assigns(:project).should == project
  36 + end
  37 + end
  38 +
  39 + describe "POST /projects" do
  40 + before do
  41 + @project = Factory(:project)
  42 + Project.stub(:new).and_return(@project)
  43 + end
  44 +
  45 + context "when the create is successful" do
  46 + before do
  47 + @project.should_receive(:save).and_return(true)
  48 + end
  49 +
  50 + it "should redirect to the project page" do
  51 + post :create, :project => {}
  52 + response.should redirect_to(project_path(@project))
  53 + end
  54 +
  55 + it "should display a message" do
  56 + post :create, :project => {}
  57 + request.flash[:success].should match(/success/)
  58 + end
  59 + end
  60 +
  61 + context "when the create is unsuccessful" do
  62 + it "should render the new page" do
  63 + @project.should_receive(:save).and_return(false)
  64 + post :create, :project => {}
  65 + response.should render_template(:new)
  66 + end
  67 + end
  68 + end
  69 +
  70 + describe "PUT /projects/:id" do
  71 + before do
  72 + @project = Factory(:project)
  73 + Project.stub(:find).with(@project.id).and_return(@project)
  74 + end
  75 +
  76 + context "when the update is successful" do
  77 + before do
  78 + @project.should_receive(:update_attributes).and_return(true)
  79 + end
  80 +
  81 + it "should redirect to the project page" do
  82 + put :update, :id => @project.id, :project => {}
  83 + response.should redirect_to(project_path(@project))
  84 + end
  85 +
  86 + it "should display a message" do
  87 + put :update, :id => @project.id, :project => {}
  88 + request.flash[:success].should match(/success/)
  89 + end
  90 + end
  91 +
  92 + context "when the update is unsuccessful" do
  93 + it "should render the edit page" do
  94 + @project.should_receive(:update_attributes).and_return(false)
  95 + put :update, :id => @project.id, :project => {}
  96 + response.should render_template(:edit)
  97 + end
  98 + end
  99 + end
  100 +
  101 + describe "DELETE /projects/:id" do
  102 + before do
  103 + @project = Factory(:project)
  104 + Project.stub(:find).with(@project.id).and_return(@project)
  105 + end
  106 +
  107 + it "should find the project" do
  108 + delete :destroy, :id => @project.id
  109 + assigns(:project).should == @project
  110 + end
  111 +
  112 + it "should destroy the project" do
  113 + @project.should_receive(:destroy)
  114 + delete :destroy, :id => @project.id
  115 + end
  116 +
  117 + it "should display a message" do
  118 + delete :destroy, :id => @project.id
  119 + request.flash[:success].should match(/success/)
  120 + end
  121 +
  122 + it "should redirect to the projects page" do
  123 + delete :destroy, :id => @project.id
  124 + response.should redirect_to(projects_path)
  125 + end
  126 + end
  127 +
22 end 128 end