Commit e549876493e453971c28cd048c333f0e56f59cb9
1 parent
03c9a613
Exists in
master
and in
1 other branch
add the ability to sort the errs table by its
headers; - cache sortable attributes on Problem - index sortable attributes for performance
Showing
8 changed files
with
118 additions
and
12 deletions
Show diff stats
app/controllers/apps_controller.rb
... | ... | @@ -8,10 +8,14 @@ class AppsController < InheritedResources::Base |
8 | 8 | respond_to do |format| |
9 | 9 | format.html do |
10 | 10 | @all_errs = !!params[:all_errs] |
11 | + @sort = params[:sort] | |
12 | + @order = params[:order] | |
13 | + @sort = "app" unless %w{message last_notice_at last_deploy_at count}.member?(@sort) | |
14 | + @order = "asc" unless (@order == "desc") | |
11 | 15 | |
12 | 16 | @problems = resource.problems |
13 | 17 | @problems = @problems.unresolved unless @all_errs |
14 | - @problems = @problems.in_env(params[:environment]).ordered.paginate(:page => params[:page], :per_page => current_user.per_page) | |
18 | + @problems = @problems.in_env(params[:environment]).ordered_by(@sort, @order).paginate(:page => params[:page], :per_page => current_user.per_page) | |
15 | 19 | |
16 | 20 | @selected_problems = params[:problems] || [] |
17 | 21 | @deploys = @app.deploys.order_by(:created_at.desc).limit(5) | ... | ... |
app/controllers/errs_controller.rb
... | ... | @@ -9,7 +9,13 @@ class ErrsController < ApplicationController |
9 | 9 | |
10 | 10 | def index |
11 | 11 | app_scope = current_user.admin? ? App.all : current_user.apps |
12 | - @problems = Problem.for_apps(app_scope).in_env(params[:environment]).unresolved.ordered | |
12 | + | |
13 | + @sort = params[:sort] | |
14 | + @order = params[:order] | |
15 | + @sort = "app" unless %w{message last_notice_at last_deploy_at count}.member?(@sort) | |
16 | + @order = "asc" unless (@order == "desc") | |
17 | + | |
18 | + @problems = Problem.for_apps(app_scope).in_env(params[:environment]).unresolved.ordered_by(@sort, @order) | |
13 | 19 | @selected_problems = params[:problems] || [] |
14 | 20 | respond_to do |format| |
15 | 21 | format.html do | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +# encoding: utf-8 | |
2 | +module SortHelper | |
3 | + | |
4 | + def link_for_sort(name, field=nil) | |
5 | + field ||= name.underscore | |
6 | + current = (@sort == field) | |
7 | + order = (current && (@order == "asc")) ? "desc" : "asc" | |
8 | + url = request.path + "?sort=#{field}&order=#{order}" | |
9 | + options = {} | |
10 | + options.merge(:class => "current") if current | |
11 | + link_to(name, url, options) | |
12 | + end | |
13 | + | |
14 | +end | ... | ... |
app/models/app.rb
... | ... | @@ -28,6 +28,7 @@ class App |
28 | 28 | |
29 | 29 | before_validation :generate_api_key, :on => :create |
30 | 30 | before_save :normalize_github_url |
31 | + after_update :store_cached_attributes_on_problems | |
31 | 32 | |
32 | 33 | validates_presence_of :name, :api_key |
33 | 34 | validates_uniqueness_of :name, :allow_blank => true |
... | ... | @@ -148,7 +149,11 @@ class App |
148 | 149 | end |
149 | 150 | |
150 | 151 | protected |
151 | - | |
152 | + | |
153 | + def store_cached_attributes_on_problems | |
154 | + problems.each(&:cache_app_attributes) | |
155 | + end | |
156 | + | |
152 | 157 | def generate_api_key |
153 | 158 | self.api_key ||= ActiveSupport::SecureRandom.hex |
154 | 159 | end | ... | ... |
app/models/deploy.rb
... | ... | @@ -14,6 +14,7 @@ class Deploy |
14 | 14 | |
15 | 15 | after_create :deliver_notification, :if => :should_notify? |
16 | 16 | after_create :resolve_app_errs, :if => :should_resolve_app_errs? |
17 | + after_create :store_cached_attributes_on_problems | |
17 | 18 | |
18 | 19 | validates_presence_of :username, :environment |
19 | 20 | |
... | ... | @@ -39,5 +40,8 @@ class Deploy |
39 | 40 | app.resolve_errs_on_deploy? |
40 | 41 | end |
41 | 42 | |
43 | + def store_cached_attributes_on_problems | |
44 | + Problem.where(:app_id => app.id).each(&:cache_app_attributes) | |
45 | + end | |
46 | + | |
42 | 47 | end |
43 | - | ... | ... |
app/models/problem.rb
... | ... | @@ -7,18 +7,24 @@ class Problem |
7 | 7 | include Mongoid::Timestamps |
8 | 8 | |
9 | 9 | field :last_notice_at, :type => DateTime |
10 | + field :last_deploy_at, :type => Time | |
10 | 11 | field :resolved, :type => Boolean, :default => false |
11 | 12 | field :issue_link, :type => String |
12 | 13 | |
13 | 14 | # Cached fields |
15 | + field :app_name, :type => String | |
14 | 16 | field :notices_count, :type => Integer, :default => 0 |
15 | 17 | field :message |
16 | 18 | field :environment |
17 | 19 | field :klass |
18 | 20 | field :where |
19 | 21 | |
20 | - index :last_notice_at | |
21 | 22 | index :app_id |
23 | + index :app_name | |
24 | + index :message | |
25 | + index :last_notice_at | |
26 | + index :last_deploy_at | |
27 | + index :notices_count | |
22 | 28 | |
23 | 29 | belongs_to :app |
24 | 30 | has_many :errs, :inverse_of => :problem, :dependent => :destroy |
... | ... | @@ -29,6 +35,10 @@ class Problem |
29 | 35 | scope :ordered, order_by(:last_notice_at.desc) |
30 | 36 | scope :for_apps, lambda {|apps| where(:app_id.in => apps.all.map(&:id))} |
31 | 37 | |
38 | + before_create :cache_app_attributes | |
39 | + | |
40 | + | |
41 | + | |
32 | 42 | def self.in_env(env) |
33 | 43 | env.present? ? where(:environment => env) : scoped |
34 | 44 | end |
... | ... | @@ -67,6 +77,18 @@ class Problem |
67 | 77 | merged_problem |
68 | 78 | end |
69 | 79 | |
80 | + def self.ordered_by(sort, order) | |
81 | + case sort | |
82 | + when "app"; order_by(["app_name", order]) | |
83 | + when "message"; order_by(["message", order]) | |
84 | + when "last_notice_at"; order_by(["last_notice_at", order]) | |
85 | + when "last_deploy_at"; order_by(["last_deploy_at", order]) | |
86 | + when "count"; order_by(["notices_count", order]) | |
87 | + else raise("\"#{sort}\" is not a recognized sort") | |
88 | + end | |
89 | + end | |
90 | + | |
91 | + | |
70 | 92 | def merged? |
71 | 93 | errs.length > 1 |
72 | 94 | end |
... | ... | @@ -85,9 +107,18 @@ class Problem |
85 | 107 | |
86 | 108 | def reset_cached_attributes |
87 | 109 | update_attribute(:notices_count, notices.count) |
110 | + cache_app_attributes | |
88 | 111 | cache_notice_attributes |
89 | 112 | end |
90 | 113 | |
114 | + def cache_app_attributes | |
115 | + if app | |
116 | + self.app_name = app.name | |
117 | + self.last_deploy_at = app.last_deploy_at | |
118 | + self.save if persisted? | |
119 | + end | |
120 | + end | |
121 | + | |
91 | 122 | def cache_notice_attributes(notice=nil) |
92 | 123 | notice ||= notices.first |
93 | 124 | attrs = {:last_notice_at => notices.max(:created_at)} | ... | ... |
app/views/errs/_table.html.haml
... | ... | @@ -3,11 +3,11 @@ |
3 | 3 | %thead |
4 | 4 | %tr |
5 | 5 | %th |
6 | - %th App | |
7 | - %th What & Where | |
8 | - %th Latest | |
9 | - %th Deploy | |
10 | - %th Count | |
6 | + %th= link_for_sort "App" | |
7 | + %th= link_for_sort "What & Where".html_safe, "message" | |
8 | + %th= link_for_sort "Latest", "last_notice_at" | |
9 | + %th= link_for_sort "Deploy", "last_deploy_at" | |
10 | + %th= link_for_sort "Count" | |
11 | 11 | %th Resolve |
12 | 12 | %tbody |
13 | 13 | - errs.each do |problem| |
... | ... | @@ -24,7 +24,7 @@ |
24 | 24 | = link_to problem.message, app_err_path(problem.app, problem) |
25 | 25 | %em= problem.where |
26 | 26 | %td.latest #{time_ago_in_words(last_notice_at problem)} ago |
27 | - %td.deploy= problem.app.last_deploy_at ? problem.app.last_deploy_at.to_s(:micro) : 'n/a' | |
27 | + %td.deploy= problem.last_deploy_at ? problem.last_deploy_at.to_s(:micro) : 'n/a' | |
28 | 28 | %td.count= link_to problem.notices.count, app_err_path(problem.app, problem) |
29 | 29 | %td.resolve= link_to image_tag("thumbs-up.png"), resolve_app_err_path(problem.app, problem), :title => "Resolve", :method => :put, :confirm => err_confirm, :class => 'resolve' if problem.unresolved? |
30 | 30 | - if errs.none? | ... | ... |
spec/models/problem_spec.rb
... | ... | @@ -131,7 +131,6 @@ describe Problem do |
131 | 131 | |
132 | 132 | |
133 | 133 | context "notice counter cache" do |
134 | - | |
135 | 134 | before do |
136 | 135 | @app = Factory(:app) |
137 | 136 | @problem = Factory(:problem, :app => @app) |
... | ... | @@ -158,4 +157,47 @@ describe Problem do |
158 | 157 | end |
159 | 158 | |
160 | 159 | |
160 | + context "#app_name" do | |
161 | + before do | |
162 | + @app = Factory(:app) | |
163 | + end | |
164 | + | |
165 | + it "is set when a problem is created" do | |
166 | + problem = Factory(:problem, :app => @app) | |
167 | + assert_equal @app.name, problem.app_name | |
168 | + end | |
169 | + | |
170 | + it "is updated when an app is updated" do | |
171 | + problem = Factory(:problem, :app => @app) | |
172 | + lambda { | |
173 | + @app.update_attributes!(:name => "Bar App") | |
174 | + problem.reload | |
175 | + }.should change(problem, :app_name).to("Bar App") | |
176 | + end | |
177 | + end | |
178 | + | |
179 | + | |
180 | + context "#last_deploy_at" do | |
181 | + before do | |
182 | + @app = Factory(:app) | |
183 | + @last_deploy = 10.days.ago.localtime.round(0) | |
184 | + deploy = Factory(:deploy, :app => @app, :created_at => @last_deploy) | |
185 | + end | |
186 | + | |
187 | + it "is set when a problem is created" do | |
188 | + problem = Factory(:problem, :app => @app) | |
189 | + assert_equal @last_deploy, problem.last_deploy_at | |
190 | + end | |
191 | + | |
192 | + it "is updated when a deploy is created" do | |
193 | + problem = Factory(:problem, :app => @app) | |
194 | + next_deploy = 5.minutes.ago.localtime.round(0) | |
195 | + lambda { | |
196 | + @deploy = Factory(:deploy, :app => @app, :created_at => next_deploy) | |
197 | + problem.reload | |
198 | + }.should change(problem, :last_deploy_at).from(@last_deploy).to(next_deploy) | |
199 | + end | |
200 | + end | |
201 | + | |
202 | + | |
161 | 203 | end | ... | ... |