Commit ce442af995f39734dd3d9d827b634b586c476fcd
1 parent
7e53d6cc
Exists in
master
and in
1 other branch
Add full management support for projects
Showing
12 changed files
with
232 additions
and
3 deletions
Show diff stats
app/controllers/projects_controller.rb
| ... | ... | @@ -9,4 +9,42 @@ class ProjectsController < ApplicationController |
| 9 | 9 | @errs = @project.errs.paginate |
| 10 | 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 | 50 | end | ... | ... |
app/models/project.rb
| ... | ... | @@ -8,13 +8,16 @@ class Project |
| 8 | 8 | |
| 9 | 9 | embeds_many :watchers |
| 10 | 10 | embeds_many :deploys |
| 11 | - references_many :errs | |
| 11 | + references_many :errs, :dependent => :destroy | |
| 12 | 12 | |
| 13 | 13 | before_validation :generate_api_key, :on => :create |
| 14 | 14 | |
| 15 | 15 | validates_presence_of :name, :api_key |
| 16 | 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 | 21 | def self.find_by_api_key!(key) |
| 19 | 22 | where(:api_key => key).first || raise(Mongoid::Errors::DocumentNotFound.new(self,key)) |
| 20 | 23 | end | ... | ... |
app/views/errs/_table.html.haml
| ... | ... | @@ -20,4 +20,8 @@ |
| 20 | 20 | %em= err.where |
| 21 | 21 | %td.latest #{time_ago_in_words(err.last_notice_at)} ago |
| 22 | 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 | 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 | 29 | \ No newline at end of file | ... | ... |
app/views/layouts/application.html.haml
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | Hypnotoad — |
| 6 | 6 | = yield(:page_title).present? ? yield(:page_title) : yield(:title) |
| 7 | 7 | %meta{ :content => "text/html; charset=utf-8", "http-equiv" => "content-type" }/ |
| 8 | + = csrf_meta_tag | |
| 8 | 9 | = javascript_include_tag :defaults |
| 9 | 10 | = stylesheet_link_tag 'reset', 'application' |
| 10 | 11 | = yield :head | ... | ... |
app/views/projects/_configuration_instructions.html.haml
0 → 100644
| ... | ... | @@ -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 | 22 | \ No newline at end of file | ... | ... |
app/views/projects/index.html.haml
app/views/projects/show.html.haml
| ... | ... | @@ -7,7 +7,25 @@ |
| 7 | 7 | - content_for :action_bar do |
| 8 | 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 | 30 | %h3 Errors |
| 13 | 31 | = render 'errs/table', :errs => @errs |
| 14 | 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 | 151 | table tbody tr:nth-child(odd) td { background-color: #F9F9F9; } |
| 152 | 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 | 165 | /* HTML Styling */ |
| 155 | 166 | .html { padding-left: 1em; border-left: 2px solid #C6C6C6;} |
| 156 | 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 | 19 | end |
| 20 | 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 | 128 | end | ... | ... |