Commit 258cf879520c2496acf8123c1c795920275bd7b6

Authored by Nathan B
2 parents ab826443 b9791f86
Exists in master and in 1 other branch production

Merge pull request #113 from martinciu/performance_tweaks

Performance tweaks
Showing 43 changed files with 616 additions and 75 deletions   Show diff stats
@@ -2,10 +2,12 @@ @@ -2,10 +2,12 @@
2 db/*.sqlite3 2 db/*.sqlite3
3 log/*.log 3 log/*.log
4 tmp/**/* 4 tmp/**/*
  5 +tmp/*
5 config/config.yml 6 config/config.yml
6 config/deploy.rb 7 config/deploy.rb
7 config/deploy 8 config/deploy
8 config/mongoid.yml 9 config/mongoid.yml
  10 +config/newrelic.yml
9 .rvmrc 11 .rvmrc
10 *~ 12 *~
11 *.rbc 13 *.rbc
@@ -4,7 +4,6 @@ gem 'rails', '3.0.10' @@ -4,7 +4,6 @@ gem 'rails', '3.0.10'
4 gem 'nokogiri' 4 gem 'nokogiri'
5 gem 'mongoid', '2.1.2' 5 gem 'mongoid', '2.1.2'
6 gem 'haml' 6 gem 'haml'
7 -gem 'will_paginate', '>=3'  
8 gem 'htmlentities', "~> 4.3.0" 7 gem 'htmlentities', "~> 4.3.0"
9 gem 'devise', '~> 1.4.0' 8 gem 'devise', '~> 1.4.0'
10 gem 'lighthouse-api' 9 gem 'lighthouse-api'
@@ -18,6 +17,7 @@ gem 'inherited_resources' @@ -18,6 +17,7 @@ gem 'inherited_resources'
18 gem 'SystemTimer', :platform => :ruby_18 17 gem 'SystemTimer', :platform => :ruby_18
19 gem 'hoptoad_notifier', "~> 2.4" 18 gem 'hoptoad_notifier', "~> 2.4"
20 gem 'actionmailer_inline_css', "~> 1.3.0" 19 gem 'actionmailer_inline_css', "~> 1.3.0"
  20 +gem 'kaminari'
21 21
22 platform :ruby do 22 platform :ruby do
23 gem 'bson_ext', '~> 1.4.0' 23 gem 'bson_ext', '~> 1.4.0'
@@ -34,6 +34,7 @@ group :development, :test do @@ -34,6 +34,7 @@ group :development, :test do
34 gem 'ruby-debug', :platform => :mri_18 34 gem 'ruby-debug', :platform => :mri_18
35 gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug' 35 gem 'ruby-debug19', :platform => :mri_19, :require => 'ruby-debug'
36 end 36 end
  37 + gem 'rpm_contrib', :git => "git://github.com/bensymonds/rpm_contrib.git", :branch => "mongo-1.4.0_update"
37 end 38 end
38 39
39 group :test do 40 group :test do
  1 +GIT
  2 + remote: git://github.com/bensymonds/rpm_contrib.git
  3 + revision: 84ee78da6d0f154468d47c9e1e6597997e157291
  4 + branch: mongo-1.4.0_update
  5 + specs:
  6 + rpm_contrib (2.1.6.beta1)
  7 + newrelic_rpm (>= 3.1.1)
  8 + newrelic_rpm (>= 3.1.1)
  9 +
1 GEM 10 GEM
2 remote: http://rubygems.org/ 11 remote: http://rubygems.org/
3 specs: 12 specs:
@@ -80,6 +89,8 @@ GEM @@ -80,6 +89,8 @@ GEM
80 inherited_resources (1.3.0) 89 inherited_resources (1.3.0)
81 has_scope (~> 0.5.0) 90 has_scope (~> 0.5.0)
82 responders (~> 0.6.0) 91 responders (~> 0.6.0)
  92 + kaminari (0.12.4)
  93 + rails (>= 3.0.0)
83 libxml-ruby (2.2.2) 94 libxml-ruby (2.2.2)
84 lighthouse-api (2.0) 95 lighthouse-api (2.0)
85 activeresource (>= 3.0.0) 96 activeresource (>= 3.0.0)
@@ -107,6 +118,7 @@ GEM @@ -107,6 +118,7 @@ GEM
107 railties (>= 3.0.0) 118 railties (>= 3.0.0)
108 multi_json (1.0.3) 119 multi_json (1.0.3)
109 multipart-post (1.1.3) 120 multipart-post (1.1.3)
  121 + newrelic_rpm (3.2.0)
110 nokogiri (1.5.0) 122 nokogiri (1.5.0)
111 octokit (0.6.4) 123 octokit (0.6.4)
112 addressable (~> 2.2.6) 124 addressable (~> 2.2.6)
@@ -205,7 +217,6 @@ GEM @@ -205,7 +217,6 @@ GEM
205 webmock (1.7.6) 217 webmock (1.7.6)
206 addressable (~> 2.2, > 2.2.5) 218 addressable (~> 2.2, > 2.2.5)
207 crack (>= 0.1.7) 219 crack (>= 0.1.7)
208 - will_paginate (3.0.2)  
209 220
210 PLATFORMS 221 PLATFORMS
211 ruby 222 ruby
@@ -223,6 +234,7 @@ DEPENDENCIES @@ -223,6 +234,7 @@ DEPENDENCIES
223 hoptoad_notifier (~> 2.4) 234 hoptoad_notifier (~> 2.4)
224 htmlentities (~> 4.3.0) 235 htmlentities (~> 4.3.0)
225 inherited_resources 236 inherited_resources
  237 + kaminari
226 lighthouse-api 238 lighthouse-api
227 mongoid (= 2.1.2) 239 mongoid (= 2.1.2)
228 mongoid_rails_migrations 240 mongoid_rails_migrations
@@ -232,6 +244,7 @@ DEPENDENCIES @@ -232,6 +244,7 @@ DEPENDENCIES
232 pivotal-tracker 244 pivotal-tracker
233 rails (= 3.0.10) 245 rails (= 3.0.10)
234 ri_cal 246 ri_cal
  247 + rpm_contrib!
235 rspec (~> 2.6) 248 rspec (~> 2.6)
236 rspec-rails (~> 2.6) 249 rspec-rails (~> 2.6)
237 ruby-debug 250 ruby-debug
@@ -240,4 +253,3 @@ DEPENDENCIES @@ -240,4 +253,3 @@ DEPENDENCIES
240 thin 253 thin
241 useragent (~> 0.3.1) 254 useragent (~> 0.3.1)
242 webmock 255 webmock
243 - will_paginate (>= 3)  
app/controllers/apps_controller.rb
@@ -15,7 +15,7 @@ class AppsController < InheritedResources::Base @@ -15,7 +15,7 @@ class AppsController < InheritedResources::Base
15 15
16 @problems = resource.problems 16 @problems = resource.problems
17 @problems = @problems.unresolved unless @all_errs 17 @problems = @problems.unresolved unless @all_errs
18 - @problems = @problems.in_env(params[:environment]).ordered_by(@sort, @order).paginate(:page => params[:page], :per_page => current_user.per_page) 18 + @problems = @problems.in_env(params[:environment]).ordered_by(@sort, @order).page(params[:page]).per(current_user.per_page)
19 19
20 @selected_problems = params[:problems] || [] 20 @selected_problems = params[:problems] || []
21 @deploys = @app.deploys.order_by(:created_at.desc).limit(5) 21 @deploys = @app.deploys.order_by(:created_at.desc).limit(5)
app/controllers/deploys_controller.rb
@@ -16,8 +16,8 @@ class DeploysController < ApplicationController @@ -16,8 +16,8 @@ class DeploysController < ApplicationController
16 app = App.find(params[:app_id]) 16 app = App.find(params[:app_id])
17 raise Mongoid::Errors::DocumentNotFound.new(App, app.id) unless current_user.admin? || current_user.watching?(app) 17 raise Mongoid::Errors::DocumentNotFound.new(App, app.id) unless current_user.admin? || current_user.watching?(app)
18 18
19 - @deploys = app.deploys.order_by(:created_at.desc).  
20 - paginate(:page => params[:page], :per_page => 10) 19 + @deploys = Kaminari.paginate_array(app.deploys.order_by(:created_at.desc)).
  20 + page(params[:page]).per(10)
21 @app = app 21 @app = app
22 end 22 end
23 23
app/controllers/errs_controller.rb
@@ -16,7 +16,7 @@ class ErrsController < ApplicationController @@ -16,7 +16,7 @@ class ErrsController < ApplicationController
16 @selected_problems = params[:problems] || [] 16 @selected_problems = params[:problems] || []
17 respond_to do |format| 17 respond_to do |format|
18 format.html do 18 format.html do
19 - @problems = @problems.paginate(:page => params[:page], :per_page => current_user.per_page) 19 + @problems = @problems.page(params[:page]).per(current_user.per_page)
20 end 20 end
21 format.atom 21 format.atom
22 end 22 end
@@ -24,14 +24,14 @@ class ErrsController < ApplicationController @@ -24,14 +24,14 @@ class ErrsController < ApplicationController
24 24
25 def all 25 def all
26 app_scope = current_user.admin? ? App.all : current_user.apps 26 app_scope = current_user.admin? ? App.all : current_user.apps
27 - @problems = Problem.for_apps(app_scope).ordered.paginate(:page => params[:page], :per_page => current_user.per_page) 27 + @problems = Problem.for_apps(app_scope).ordered.page(params[:page]).per(current_user.per_page)
28 @selected_problems = params[:problems] || [] 28 @selected_problems = params[:problems] || []
29 end 29 end
30 30
31 def show 31 def show
32 page = (params[:notice] || @problem.notices_count) 32 page = (params[:notice] || @problem.notices_count)
33 page = 1 if page.to_i.zero? 33 page = 1 if page.to_i.zero?
34 - @notices = @problem.notices.paginate(:page => page, :per_page => 1) 34 + @notices = @problem.notices.page(page.to_i).per(1)
35 @notice = @notices.first 35 @notice = @notices.first
36 @comment = Comment.new 36 @comment = Comment.new
37 if request.headers['X-PJAX'] 37 if request.headers['X-PJAX']
app/controllers/users_controller.rb
@@ -6,7 +6,7 @@ class UsersController < ApplicationController @@ -6,7 +6,7 @@ class UsersController < ApplicationController
6 before_filter :require_user_edit_priviledges, :only => [:edit, :update] 6 before_filter :require_user_edit_priviledges, :only => [:edit, :update]
7 7
8 def index 8 def index
9 - @users = User.all.paginate(:page => params[:page], :per_page => current_user.per_page) 9 + @users = User.all.page(params[:page]).per(current_user.per_page)
10 end 10 end
11 11
12 def show 12 def show
app/helpers/application_helper.rb
1 module ApplicationHelper 1 module ApplicationHelper
2 def message_graph(problem) 2 def message_graph(problem)
3 - create_percentage_table_for(problem) {|notice| notice.message} 3 + create_percentage_table_for(problem.messages)
4 end 4 end
5 5
6 def generate_problem_ical(notices) 6 def generate_problem_ical(notices)
@@ -35,35 +35,19 @@ module ApplicationHelper @@ -35,35 +35,19 @@ module ApplicationHelper
35 end 35 end
36 36
37 def user_agent_graph(problem) 37 def user_agent_graph(problem)
38 - create_percentage_table_for(problem) {|notice| pretty_user_agent(notice.user_agent)}  
39 - end  
40 -  
41 - def pretty_user_agent(user_agent)  
42 - (user_agent.nil? || user_agent.none?) ? "N/A" : "#{user_agent.browser} #{user_agent.version}" 38 + create_percentage_table_for(problem.user_agents)
43 end 39 end
44 40
45 def tenant_graph(problem) 41 def tenant_graph(problem)
46 - create_percentage_table_for(problem) {|notice| get_host(notice.request['url'])}  
47 - end  
48 -  
49 - def get_host(url)  
50 - begin  
51 - uri = url && URI.parse(url)  
52 - uri.blank? ? "N/A" : uri.host  
53 - rescue URI::InvalidURIError  
54 - "N/A"  
55 - end 42 + create_percentage_table_for(problem.hosts)
56 end 43 end
57 44
58 -  
59 - def create_percentage_table_for(problem, &block)  
60 - tallies = tally(problem.notices, &block)  
61 - create_percentage_table_from_tallies(tallies, :total => problem.notices.count) 45 + def create_percentage_table_for(collection)
  46 + create_percentage_table_from_tallies(tally(collection))
62 end 47 end
63 48
64 - def tally(collection, &block)  
65 - collection.inject({}) do |tallies, item|  
66 - value = yield item 49 + def tally(collection)
  50 + collection.inject({}) do |tallies, value|
67 tallies[value] = (tallies[value] || 0) + 1 51 tallies[value] = (tallies[value] || 0) + 1
68 tallies 52 tallies
69 end 53 end
app/models/comment.rb
@@ -2,12 +2,25 @@ class Comment @@ -2,12 +2,25 @@ class Comment
2 include Mongoid::Document 2 include Mongoid::Document
3 include Mongoid::Timestamps 3 include Mongoid::Timestamps
4 4
  5 + after_create :increase_counter_cache
  6 + before_destroy :decrease_counter_cache
  7 +
5 field :body, :type => String 8 field :body, :type => String
6 index :user_id 9 index :user_id
7 10
8 - belongs_to :err 11 + belongs_to :err, :class_name => "Problem"
9 belongs_to :user 12 belongs_to :user
10 13
11 validates_presence_of :body 14 validates_presence_of :body
  15 +
  16 + protected
  17 + def increase_counter_cache
  18 + err.inc(:comments_count, 1)
  19 + end
  20 +
  21 + def decrease_counter_cache
  22 + err.inc(:comments_count, -1) if err
  23 + end
  24 +
12 end 25 end
13 26
app/models/notice.rb
@@ -19,7 +19,7 @@ class Notice @@ -19,7 +19,7 @@ class Notice
19 after_create :increase_counter_cache, :cache_attributes_on_problem, :unresolve_problem 19 after_create :increase_counter_cache, :cache_attributes_on_problem, :unresolve_problem
20 after_create :deliver_notification, :if => :should_notify? 20 after_create :deliver_notification, :if => :should_notify?
21 before_save :sanitize 21 before_save :sanitize
22 - before_destroy :decrease_counter_cache 22 + before_destroy :decrease_counter_cache, :remove_cached_attributes_from_problem
23 23
24 validates_presence_of :backtrace, :server_environment, :notifier 24 validates_presence_of :backtrace, :server_environment, :notifier
25 25
@@ -33,6 +33,10 @@ class Notice @@ -33,6 +33,10 @@ class Notice
33 agent_string.blank? ? nil : UserAgent.parse(agent_string) 33 agent_string.blank? ? nil : UserAgent.parse(agent_string)
34 end 34 end
35 35
  36 + def user_agent_string
  37 + (user_agent.nil? || user_agent.none?) ? "N/A" : "#{user_agent.browser} #{user_agent.version}"
  38 + end
  39 +
36 def environment_name 40 def environment_name
37 server_environment['server-environment'] || server_environment['environment-name'] 41 server_environment['server-environment'] || server_environment['environment-name']
38 end 42 end
@@ -59,6 +63,17 @@ class Notice @@ -59,6 +63,17 @@ class Notice
59 read_attribute(:request) || {} 63 read_attribute(:request) || {}
60 end 64 end
61 65
  66 + def url
  67 + request['url']
  68 + end
  69 +
  70 + def host
  71 + uri = url && URI.parse(url)
  72 + uri.blank? ? "N/A" : uri.host
  73 + rescue URI::InvalidURIError
  74 + "N/A"
  75 + end
  76 +
62 def env_vars 77 def env_vars
63 request['cgi-data'] || {} 78 request['cgi-data'] || {}
64 end 79 end
@@ -94,11 +109,14 @@ class Notice @@ -94,11 +109,14 @@ class Notice
94 problem.inc(:notices_count, -1) if err 109 problem.inc(:notices_count, -1) if err
95 end 110 end
96 111
  112 + def remove_cached_attributes_from_problem
  113 + problem.remove_cached_notice_attribures(self) if err
  114 + end
  115 +
97 def unresolve_problem 116 def unresolve_problem
98 problem.update_attribute(:resolved, false) if problem.resolved? 117 problem.update_attribute(:resolved, false) if problem.resolved?
99 end 118 end
100 119
101 -  
102 def cache_attributes_on_problem 120 def cache_attributes_on_problem
103 problem.cache_notice_attributes(self) 121 problem.cache_notice_attributes(self)
104 end 122 end
app/models/problem.rb
@@ -18,6 +18,10 @@ class Problem @@ -18,6 +18,10 @@ class Problem
18 field :environment 18 field :environment
19 field :klass 19 field :klass
20 field :where 20 field :where
  21 + field :user_agents, :type => Array, :default => []
  22 + field :messages, :type => Array, :default => []
  23 + field :hosts, :type => Array, :default => []
  24 + field :comments_count, :type => Integer, :default => 0
21 25
22 index :app_id 26 index :app_id
23 index :app_name 27 index :app_name
@@ -123,8 +127,20 @@ class Problem @@ -123,8 +127,20 @@ class Problem
123 :message => notice.message, 127 :message => notice.message,
124 :environment => notice.environment_name, 128 :environment => notice.environment_name,
125 :klass => notice.klass, 129 :klass => notice.klass,
126 - :where => notice.where) if notice 130 + :where => notice.where,
  131 + :messages => messages.push(notice.message),
  132 + :hosts => hosts.push(notice.host),
  133 + :user_agents => user_agents.push(notice.user_agent_string)
  134 + ) if notice
127 update_attributes!(attrs) 135 update_attributes!(attrs)
128 end 136 end
  137 +
  138 + def remove_cached_notice_attribures(notice)
  139 + messages.delete_at(messages.index(notice.message))
  140 + hosts.delete_at(hosts.index(notice.host))
  141 + user_agents.delete_at(user_agents.index(notice.user_agent_string))
  142 + save!
  143 + end
  144 +
129 end 145 end
130 146
app/views/deploys/index.html.haml
@@ -3,5 +3,5 @@ @@ -3,5 +3,5 @@
3 - if current_user.authentication_token 3 - if current_user.authentication_token
4 %span= link_to 'iCal', app_deploys_path(@app, :format => "ics", :auth_token => current_user.authentication_token), :class => "calendar_link" 4 %span= link_to 'iCal', app_deploys_path(@app, :format => "ics", :auth_token => current_user.authentication_token), :class => "calendar_link"
5 = render 'table', :deploys => @deploys 5 = render 'table', :deploys => @deploys
6 -= will_paginate @deploys, :previous_label => '« Previous', :next_label => 'Next »' 6 += paginate @deploys
7 7
app/views/errs/_table.html.haml
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 %td.message 26 %td.message
27 = link_to trucated_err_message(problem), app_err_path(problem.app, problem) 27 = link_to trucated_err_message(problem), app_err_path(problem.app, problem)
28 %em= problem.where 28 %em= problem.where
29 - - if problem.comments.any? 29 + - if problem.comments_count > 0
30 - comment = problem.comments.last 30 - comment = problem.comments.last
31 %br 31 %br
32 .inline_comment 32 .inline_comment
@@ -34,7 +34,7 @@ @@ -34,7 +34,7 @@
34 %em= truncate(comment.body, :length => 100, :separator => ' ') 34 %em= truncate(comment.body, :length => 100, :separator => ' ')
35 %td.latest #{time_ago_in_words(last_notice_at problem)} ago 35 %td.latest #{time_ago_in_words(last_notice_at problem)} ago
36 %td.deploy= problem.last_deploy_at ? problem.last_deploy_at.to_s(:micro) : 'n/a' 36 %td.deploy= problem.last_deploy_at ? problem.last_deploy_at.to_s(:micro) : 'n/a'
37 - %td.count= link_to problem.notices.count, app_err_path(problem.app, problem) 37 + %td.count= link_to problem.notices_count, app_err_path(problem.app, problem)
38 - if any_issue_links 38 - if any_issue_links
39 %td.issue_link 39 %td.issue_link
40 - if problem.issue_link.present? 40 - if problem.issue_link.present?
@@ -44,7 +44,7 @@ @@ -44,7 +44,7 @@
44 %tr 44 %tr
45 %td{:colspan => (@app ? 5 : 6)} 45 %td{:colspan => (@app ? 5 : 6)}
46 %em No errs here 46 %em No errs here
47 - = will_paginate @problems, :previous_label => '« Previous', :next_label => 'Next »' 47 + = paginate errs
48 .tab-bar 48 .tab-bar
49 %ul 49 %ul
50 %li= submit_tag 'Merge', :id => 'merge_errs', :class => 'button', 'data-action' => merge_several_errs_path 50 %li= submit_tag 'Merge', :id => 'merge_errs', :class => 'button', 'data-action' => merge_several_errs_path
app/views/errs/show.html.haml
@@ -43,9 +43,7 @@ @@ -43,9 +43,7 @@
43 43
44 %h4= @notice.try(:message) 44 %h4= @notice.try(:message)
45 45
46 -= will_paginate @notices, :param_name => :notice, :page_links => false, :class => 'notice-pagination'  
47 -viewing occurrence #{@notices.current_page} of #{@notices.total_pages}  
48 -.notice-pagination-loader= image_tag 'loader.gif' 46 += paginate @notices, :param_name => :notice, :theme => :notices
49 47
50 .tab-bar 48 .tab-bar
51 %ul 49 %ul
@@ -58,7 +56,7 @@ viewing occurrence #{@notices.current_page} of #{@notices.total_pages} @@ -58,7 +56,7 @@ viewing occurrence #{@notices.current_page} of #{@notices.total_pages}
58 - if @notice 56 - if @notice
59 #summary 57 #summary
60 %h3 Summary 58 %h3 Summary
61 - = render 'notices/summary', :notice => @notice 59 + = render 'notices/summary', :notice => @notice, :problem => @problem
62 60
63 #backtrace 61 #backtrace
64 %h3 Backtrace 62 %h3 Backtrace
app/views/issue_trackers/pivotal_body.txt.erb
@@ -3,7 +3,7 @@ See this exception on Errbit: <%= app_err_url problem.app, problem %> @@ -3,7 +3,7 @@ See this exception on Errbit: <%= app_err_url problem.app, problem %>
3 <% if notice.request['url'].present? %>URL: <%= notice.request['url'] %><% end %> 3 <% if notice.request['url'].present? %>URL: <%= notice.request['url'] %><% end %>
4 Where: <%= notice.where %> 4 Where: <%= notice.where %>
5 Occurred: <%= notice.created_at.to_s :micro %> 5 Occurred: <%= notice.created_at.to_s :micro %>
6 - Similar: <%= (notice.problem.notices.count - 1).to_s %> 6 + Similar: <%= (notice.problem.notices_count - 1).to_s %>
7 7
8 Params: 8 Params:
9 <%= pretty_hash notice.params %> 9 <%= pretty_hash notice.params %>
app/views/kaminari/_first_page.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +-# Link to the "First" page
  2 +-# available local variables
  3 +-# url: url to the first page
  4 +-# current_page: a page object for the currently displayed page
  5 +-# num_pages: total number of pages
  6 +-# per_page: number of items to fetch per page
  7 +-# remote: data-remote
  8 +%span.first
  9 + = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote
app/views/kaminari/_gap.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +-# Non-link tag that stands for skipped pages...
  2 +-# available local variables
  3 +-# current_page: a page object for the currently displayed page
  4 +-# num_pages: total number of pages
  5 +-# per_page: number of items to fetch per page
  6 +-# remote: data-remote
  7 +%span.page.gap
  8 + = raw(t 'views.pagination.truncate')
app/views/kaminari/_last_page.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +-# Link to the "Last" page
  2 +-# available local variables
  3 +-# url: url to the last page
  4 +-# current_page: a page object for the currently displayed page
  5 +-# num_pages: total number of pages
  6 +-# per_page: number of items to fetch per page
  7 +-# remote: data-remote
  8 +%span.last
  9 + = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote}
app/views/kaminari/_next_page.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +-# Link to the "Next" page
  2 +-# available local variables
  3 +-# url: url to the next page
  4 +-# current_page: a page object for the currently displayed page
  5 +-# num_pages: total number of pages
  6 +-# per_page: number of items to fetch per page
  7 +-# remote: data-remote
  8 +%span.next
  9 + = link_to_unless current_page.last?, raw('Next &raquo;'), url, :rel => 'next'
app/views/kaminari/_page.html.haml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +-# Link showing page number
  2 +-# available local variables
  3 +-# page: a page object for "this" page
  4 +-# url: url to this page
  5 +-# current_page: a page object for the currently displayed page
  6 +-# num_pages: total number of pages
  7 +-# per_page: number of items to fetch per page
  8 +-# remote: data-remote
  9 +%span{:class => "page#{' current' if page.current?}"}
  10 + = link_to_unless page.current?, page, url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil}
app/views/kaminari/_paginator.html.haml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +-# The container tag
  2 +-# available local variables
  3 +-# current_page: a page object for the currently displayed page
  4 +-# num_pages: total number of pages
  5 +-# per_page: number of items to fetch per page
  6 +-# remote: data-remote
  7 +-# paginator: the paginator that renders the pagination tags inside
  8 += paginator.render do
  9 + %nav.pagination
  10 + = prev_page_tag
  11 + - each_page do |page|
  12 + - if page.left_outer? || page.right_outer? || page.inside_window?
  13 + = page_tag page
  14 + - elsif !page.was_truncated?
  15 + = gap_tag
  16 + = next_page_tag
app/views/kaminari/_prev_page.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +-# Link to the "Previous" page
  2 +-# available local variables
  3 +-# url: url to the previous page
  4 +-# current_page: a page object for the currently displayed page
  5 +-# num_pages: total number of pages
  6 +-# per_page: number of items to fetch per page
  7 +-# remote: data-remote
  8 +%span.prev
  9 + = link_to_unless current_page.first?, raw('&laquo; Previous'), url, :rel => 'prev'
app/views/kaminari/notices/_first_page.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +-# Link to the "First" page
  2 +-# available local variables
  3 +-# url: url to the first page
  4 +-# current_page: a page object for the currently displayed page
  5 +-# num_pages: total number of pages
  6 +-# per_page: number of items to fetch per page
  7 +-# remote: data-remote
  8 +%span.first
  9 + = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote
app/views/kaminari/notices/_gap.html.haml 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +-# Non-link tag that stands for skipped pages...
  2 +-# available local variables
  3 +-# current_page: a page object for the currently displayed page
  4 +-# num_pages: total number of pages
  5 +-# per_page: number of items to fetch per page
  6 +-# remote: data-remote
  7 +%span.page.gap
  8 + = raw(t 'views.pagination.truncate')
app/views/kaminari/notices/_last_page.html.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +-# Link to the "Last" page
  2 +-# available local variables
  3 +-# url: url to the last page
  4 +-# current_page: a page object for the currently displayed page
  5 +-# num_pages: total number of pages
  6 +-# per_page: number of items to fetch per page
  7 +-# remote: data-remote
  8 +%span.last
  9 + = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote}
app/views/kaminari/notices/_next_page.html.haml 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +~ link_to_unless current_page.last?, raw('Next →'), url, :class => "next_page", :rel => "next", :remote => remote do |name|
  2 + ~ content_tag :span, name, :class => 'next_page disabled'
app/views/kaminari/notices/_page.html.haml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +-# Link showing page number
  2 +-# available local variables
  3 +-# page: a page object for "this" page
  4 +-# url: url to this page
  5 +-# current_page: a page object for the currently displayed page
  6 +-# num_pages: total number of pages
  7 +-# per_page: number of items to fetch per page
  8 +-# remote: data-remote
  9 +%span{:class => "page#{' current' if page.current?}"}
  10 + = link_to_unless page.current?, page, url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil}
app/views/kaminari/notices/_paginator.html.haml 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +-# The container tag
  2 +-# available local variables
  3 +-# current_page: a page object for the currently displayed page
  4 +-# num_pages: total number of pages
  5 +-# per_page: number of items to fetch per page
  6 +-# remote: data-remote
  7 +-# paginator: the paginator that renders the pagination tags inside
  8 += paginator.render do
  9 + .notice-pagination<
  10 + = prev_page_tag
  11 + = next_page_tag
  12 +.notice-pagination-loader= image_tag 'loader.gif'
  13 +viewing occurrence #{current_page} of #{num_pages}
app/views/kaminari/notices/_prev_page.html.haml 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +~ link_to_unless current_page.first?, raw('← Previous'), url, :class => "previous_page", :rel => "prev", :remote => remote do |name|
  2 + ~ content_tag :span, name, :class => 'previous_page disabled'
app/views/notices/_summary.html.haml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 %table.summary 2 %table.summary
3 %tr 3 %tr
4 %th Message 4 %th Message
5 - %td.main.nowrap= message_graph(notice.problem) 5 + %td.main.nowrap= message_graph(problem)
6 - if notice.request['url'].present? 6 - if notice.request['url'].present?
7 %tr 7 %tr
8 %th URL 8 %th URL
@@ -15,13 +15,13 @@ @@ -15,13 +15,13 @@
15 %td= notice.created_at.to_s(:micro) 15 %td= notice.created_at.to_s(:micro)
16 %tr 16 %tr
17 %th Similar 17 %th Similar
18 - %td= notice.problem.notices.count - 1 18 + %td= problem.notices_count - 1
19 %tr 19 %tr
20 %th Browser 20 %th Browser
21 - %td= user_agent_graph(notice.problem) 21 + %td= user_agent_graph(problem)
22 %tr 22 %tr
23 %th Tenant 23 %th Tenant
24 - %td= tenant_graph(notice.problem) 24 + %td= tenant_graph(problem)
25 %tr 25 %tr
26 %th App Server 26 %th App Server
27 %td= notice.server_environment && notice.server_environment["hostname"] 27 %td= notice.server_environment && notice.server_environment["hostname"]
app/views/users/index.html.haml
@@ -18,5 +18,5 @@ @@ -18,5 +18,5 @@
18 %td= user.username 18 %td= user.username
19 %td= user.email 19 %td= user.email
20 %td= user.admin? ? 'Y' : 'N' 20 %td= user.admin? ? 'Y' : 'N'
21 -= will_paginate @users, :previous_label => '&laquo; Previous', :next_label => 'Next &raquo;' 21 += paginate @users
22 22
config/application.rb
@@ -7,7 +7,6 @@ require &quot;action_controller/railtie&quot; @@ -7,7 +7,6 @@ require &quot;action_controller/railtie&quot;
7 require "action_mailer/railtie" 7 require "action_mailer/railtie"
8 # require "active_resource/railtie" 8 # require "active_resource/railtie"
9 require 'mongoid/railtie' 9 require 'mongoid/railtie'
10 -require 'will_paginate/array' # make paginate available on Array  
11 10
12 11
13 # If you have a Gemfile, require the gems listed there, including any gems 12 # If you have a Gemfile, require the gems listed there, including any gems
config/newrelic.example.yml 0 → 100644
@@ -0,0 +1,226 @@ @@ -0,0 +1,226 @@
  1 +#
  2 +# This file configures the New Relic Agent. New Relic monitors
  3 +# Ruby, Java, .NET, PHP, and Python applications with deep visibility and low overhead.
  4 +# For more information, visit www.newrelic.com.
  5 +#
  6 +# Generated October 18, 2011
  7 +#
  8 +# This configuration file is custom generated for errbit
  9 +
  10 +# Here are the settings that are common to all environments:
  11 +common: &default_settings
  12 + # ============================== LICENSE KEY ===============================
  13 +
  14 + # You must specify the license key associated with your New Relic
  15 + # account. This key binds your Agent's data to your account in the
  16 + # New Relic service.
  17 + license_key: <%= ENV["NEW_RELIC_LICENSE_KEY"] %>
  18 +
  19 + # Agent Enabled (Ruby/Rails Only)
  20 + # Use this setting to force the agent to run or not run.
  21 + # Default is 'auto' which means the agent will install and run only
  22 + # if a valid dispatcher such as Mongrel is running. This prevents
  23 + # it from running with Rake or the console. Set to false to
  24 + # completely turn the agent off regardless of the other settings.
  25 + # Valid values are true, false and auto.
  26 + # agent_enabled: auto
  27 +
  28 + # Application Name
  29 + # Set this to be the name of your application as you'd like it show
  30 + # up in New Relic. New Relic will then auto-map instances of your application
  31 + # into a New Relic "application" on your home dashboard page. If you want
  32 + # to map this instance into multiple apps, like "AJAX Requests" and
  33 + # "All UI" then specify a semicolon-separated list of up to three
  34 + # distinct names. If you comment this out, it defaults to the
  35 + # capitalized RAILS_ENV (i.e., Production, Staging, etc)
  36 + app_name: <%= ENV["NEW_RELIC_APP_NAME"] %>
  37 +
  38 + # When "true", the agent collects performance data about your
  39 + # application and reports this data to the New Relic service at
  40 + # newrelic.com. This global switch is normally overridden for each
  41 + # environment below. (formerly called 'enabled')
  42 + monitor_mode: true
  43 +
  44 + # Developer mode should be off in every environment but
  45 + # development as it has very high overhead in memory.
  46 + developer_mode: false
  47 +
  48 + # The newrelic agent generates its own log file to keep its logging
  49 + # information separate from that of your application. Specify its
  50 + # log level here.
  51 + log_level: info
  52 +
  53 + # The newrelic agent communicates with the New Relic service via http by
  54 + # default. If you want to communicate via https to increase
  55 + # security, then turn on SSL by setting this value to true. Note,
  56 + # this will result in increased CPU overhead to perform the
  57 + # encryption involved in SSL communication, but this work is done
  58 + # asynchronously to the threads that process your application code,
  59 + # so it should not impact response times.
  60 + ssl: false
  61 +
  62 + # EXPERIMENTAL: enable verification of the SSL certificate sent by
  63 + # the server. This setting has no effect unless SSL is enabled
  64 + # above. This may block your application. Only enable it if the data
  65 + # you send us needs end-to-end verified certificates.
  66 + #
  67 + # This means we cannot cache the DNS lookup, so each request to the
  68 + # New Relic service will perform a lookup. It also means that we cannot
  69 + # use a non-blocking lookup, so in a worst case, if you have DNS
  70 + # problems, your app may block indefinitely.
  71 + # verify_certificate: true
  72 +
  73 + # Set your application's Apdex threshold value with the 'apdex_t'
  74 + # setting, in seconds. The apdex_t value determines the buckets used
  75 + # to compute your overall Apdex score.
  76 + # Requests that take less than apdex_t seconds to process will be
  77 + # classified as Satisfying transactions; more than apdex_t seconds
  78 + # as Tolerating transactions; and more than four times the apdex_t
  79 + # value as Frustrating transactions.
  80 + # For more about the Apdex standard, see
  81 + # http://support.newrelic.com/faqs/general/apdex
  82 + apdex_t: 0.5
  83 +
  84 + # Proxy settings for connecting to the New Relic server.
  85 + #
  86 + # If a proxy is used, the host setting is required. Other settings
  87 + # are optional. Default port is 8080.
  88 + #
  89 + # proxy_host: hostname
  90 + # proxy_port: 8080
  91 + # proxy_user:
  92 + # proxy_pass:
  93 +
  94 + # Tells transaction tracer and error collector (when enabled)
  95 + # whether or not to capture HTTP params. When true, frameworks can
  96 + # exclude HTTP parameters from being captured.
  97 + # Rails: the RoR filter_parameter_logging excludes parameters
  98 + # Java: create a config setting called "ignored_params" and set it to
  99 + # a comma separated list of HTTP parameter names.
  100 + # ex: ignored_params: credit_card, ssn, password
  101 + capture_params: false
  102 +
  103 + # Transaction tracer captures deep information about slow
  104 + # transactions and sends this to the New Relic service once a
  105 + # minute. Included in the transaction is the exact call sequence of
  106 + # the transactions including any SQL statements issued.
  107 + transaction_tracer:
  108 +
  109 + # Transaction tracer is enabled by default. Set this to false to
  110 + # turn it off. This feature is only available at the Professional
  111 + # product level.
  112 + enabled: true
  113 +
  114 + # Threshold in seconds for when to collect a transaction
  115 + # trace. When the response time of a controller action exceeds
  116 + # this threshold, a transaction trace will be recorded and sent to
  117 + # New Relic. Valid values are any float value, or (default) "apdex_f",
  118 + # which will use the threshold for an dissatisfying Apdex
  119 + # controller action - four times the Apdex T value.
  120 + transaction_threshold: apdex_f
  121 +
  122 + # When transaction tracer is on, SQL statements can optionally be
  123 + # recorded. The recorder has three modes, "off" which sends no
  124 + # SQL, "raw" which sends the SQL statement in its original form,
  125 + # and "obfuscated", which strips out numeric and string literals.
  126 + record_sql: obfuscated
  127 +
  128 + # Threshold in seconds for when to collect stack trace for a SQL
  129 + # call. In other words, when SQL statements exceed this threshold,
  130 + # then capture and send to New Relic the current stack trace. This is
  131 + # helpful for pinpointing where long SQL calls originate from.
  132 + stack_trace_threshold: 0.500
  133 +
  134 + # Determines whether the agent will capture query plans for slow
  135 + # SQL queries. Only supported in mysql and postgres. Should be
  136 + # set to false when using other adapters.
  137 + # explain_enabled: true
  138 +
  139 + # Threshold for query execution time below which query plans will not
  140 + # not be captured. Relevant only when `explain_enabled` is true.
  141 + # explain_threshold: 0.5
  142 +
  143 + # Error collector captures information about uncaught exceptions and
  144 + # sends them to New Relic for viewing
  145 + error_collector:
  146 +
  147 + # Error collector is enabled by default. Set this to false to turn
  148 + # it off. This feature is only available at the Professional
  149 + # product level.
  150 + enabled: true
  151 +
  152 + # Rails Only - tells error collector whether or not to capture a
  153 + # source snippet around the place of the error when errors are View
  154 + # related.
  155 + capture_source: true
  156 +
  157 + # To stop specific errors from reporting to New Relic, set this property
  158 + # to comma-separated values. Default is to ignore routing errors,
  159 + # which are how 404's get triggered.
  160 + ignore_errors: ActionController::RoutingError
  161 +
  162 + # (Advanced) Uncomment this to ensure the CPU and memory samplers
  163 + # won't run. Useful when you are using the agent to monitor an
  164 + # external resource
  165 + # disable_samplers: true
  166 +
  167 + # If you aren't interested in visibility in these areas, you can
  168 + # disable the instrumentation to reduce overhead.
  169 + #
  170 + # disable_view_instrumentation: true
  171 + # disable_activerecord_instrumentation: true
  172 + # disable_memcache_instrumentation: true
  173 + # disable_dj: true
  174 +
  175 + # Certain types of instrumentation such as GC stats will not work if
  176 + # you are running multi-threaded. Please let us know.
  177 + # multi_threaded = false
  178 +
  179 +# Application Environments
  180 +# ------------------------------------------
  181 +# Environment-specific settings are in this section.
  182 +# For Rails applications, RAILS_ENV is used to determine the environment.
  183 +# For Java applications, pass -Dnewrelic.environment <environment> to set
  184 +# the environment.
  185 +
  186 +# NOTE if your application has other named environments, you should
  187 +# provide newrelic configuration settings for these environments here.
  188 +
  189 +development:
  190 + <<: *default_settings
  191 + # Turn off communication to New Relic service in development mode (also
  192 + # 'enabled').
  193 + # NOTE: for initial evaluation purposes, you may want to temporarily
  194 + # turn agent communication on in development mode.
  195 + monitor_mode: false
  196 +
  197 + # Rails Only - when running in Developer Mode, the New Relic Agent will
  198 + # present performance information on the last 100 transactions you have
  199 + # executed since starting the app server.
  200 + # NOTE: There is substantial overhead when running in developer mode.
  201 + # Do not use for production or load testing.
  202 + developer_mode: true
  203 +
  204 + # Enable textmate links
  205 + # textmate: true
  206 +
  207 +test:
  208 + <<: *default_settings
  209 + # It almost never makes sense to turn on the agent when running
  210 + # unit, functional or integration tests or the like.
  211 + monitor_mode: false
  212 +
  213 +# Turn on the agent in production for 24x7 monitoring. New Relic
  214 +# testing shows an average performance impact of < 5 ms per
  215 +# transaction, so you can leave this on all the time without
  216 +# incurring any user-visible performance degradation.
  217 +production:
  218 + <<: *default_settings
  219 + monitor_mode: true
  220 +
  221 +# Many applications have a staging environment which behaves
  222 +# identically to production. Support for that environment is provided
  223 +# here. By default, the staging environment has the agent turned on.
  224 +staging:
  225 + <<: *default_settings
  226 + monitor_mode: true
db/migrate/20111019064503_cache_problem_statistics.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +class CacheProblemStatistics < Mongoid::Migration
  2 + def self.up
  3 + Problem.all.each do |problem|
  4 + problem.notices.each do |notice|
  5 + problem.messages << notice.message
  6 + problem.hosts << notice.host
  7 + problem.user_agents << notice.user_agent_string
  8 + end
  9 + problem.save!
  10 + end
  11 + end
  12 +
  13 + def self.down
  14 + Problem.all.each do |problem|
  15 + problem.update_attributes(:messages => [], :hosts => [], :user_agents => [])
  16 + end
  17 + end
  18 +end
0 \ No newline at end of file 19 \ No newline at end of file
db/migrate/20111019163257_add_problem_comments_count.rb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +class AddProblemCommentsCount < Mongoid::Migration
  2 + def self.up
  3 + Problem.all.each do |problem|
  4 + problem.update_attributes(:comments_count => problem.comments.count)
  5 + end
  6 + end
  7 +
  8 + def self.down
  9 + end
  10 +end
0 \ No newline at end of file 11 \ No newline at end of file
spec/controllers/apps_controller_spec.rb
@@ -65,13 +65,13 @@ describe AppsController do @@ -65,13 +65,13 @@ describe AppsController do
65 65
66 it "should have default per_page value for user" do 66 it "should have default per_page value for user" do
67 get :show, :id => @app.id 67 get :show, :id => @app.id
68 - assigns(:problems).size.should == User::PER_PAGE 68 + assigns(:problems).to_a.size.should == User::PER_PAGE
69 end 69 end
70 70
71 it "should be able to override default per_page value" do 71 it "should be able to override default per_page value" do
72 @user.update_attribute :per_page, 10 72 @user.update_attribute :per_page, 10
73 get :show, :id => @app.id 73 get :show, :id => @app.id
74 - assigns(:problems).size.should == 10 74 + assigns(:problems).to_a.size.should == 10
75 end 75 end
76 end 76 end
77 77
spec/controllers/errs_controller_spec.rb
@@ -39,13 +39,13 @@ describe ErrsController do @@ -39,13 +39,13 @@ describe ErrsController do
39 39
40 it "should have default per_page value for user" do 40 it "should have default per_page value for user" do
41 get :index 41 get :index
42 - assigns(:problems).size.should == User::PER_PAGE 42 + assigns(:problems).to_a.size.should == User::PER_PAGE
43 end 43 end
44 44
45 it "should be able to override default per_page value" do 45 it "should be able to override default per_page value" do
46 @user.update_attribute :per_page, 10 46 @user.update_attribute :per_page, 10
47 get :index 47 get :index
48 - assigns(:problems).size.should == 10 48 + assigns(:problems).to_a.size.should == 10
49 end 49 end
50 end 50 end
51 51
@@ -111,11 +111,11 @@ describe ErrsController do @@ -111,11 +111,11 @@ describe ErrsController do
111 context 'when logged in as an admin' do 111 context 'when logged in as an admin' do
112 it "gets a paginated list of all errs" do 112 it "gets a paginated list of all errs" do
113 sign_in Factory(:admin) 113 sign_in Factory(:admin)
114 - errs = WillPaginate::Collection.new(1,30) 114 + errs = Kaminari.paginate_array((1..30).to_a)
115 3.times { errs << Factory(:err).problem } 115 3.times { errs << Factory(:err).problem }
116 3.times { errs << Factory(:err, :problem => Factory(:problem, :resolved => true)).problem } 116 3.times { errs << Factory(:err, :problem => Factory(:problem, :resolved => true)).problem }
117 Problem.should_receive(:ordered).and_return( 117 Problem.should_receive(:ordered).and_return(
118 - mock('proxy', :paginate => errs) 118 + mock('proxy', :page => mock('other_proxy', :per => errs))
119 ) 119 )
120 get :all 120 get :all
121 assigns(:problems).should == errs 121 assigns(:problems).should == errs
spec/controllers/users_controller_spec.rb
@@ -100,7 +100,7 @@ describe UsersController do @@ -100,7 +100,7 @@ describe UsersController do
100 @user.update_attribute :per_page, 2 100 @user.update_attribute :per_page, 2
101 users = 3.times { Factory(:user) } 101 users = 3.times { Factory(:user) }
102 get :index 102 get :index
103 - assigns(:users).size.should == 2 103 + assigns(:users).to_a.size.should == 2
104 end 104 end
105 end 105 end
106 106
spec/factories/comment_factories.rb
1 Factory.define :comment do |c| 1 Factory.define :comment do |c|
2 c.user {|u| u.association :user} 2 c.user {|u| u.association :user}
3 c.body 'Test comment' 3 c.body 'Test comment'
  4 + c.err {|e| e.association :problem}
4 end 5 end
5 6
spec/helpers/application_helper_spec.rb
@@ -10,18 +10,3 @@ require &#39;spec_helper&#39; @@ -10,18 +10,3 @@ require &#39;spec_helper&#39;
10 # end 10 # end
11 # end 11 # end
12 # end 12 # end
13 -describe ApplicationHelper do  
14 - describe "get_host" do  
15 - it "returns host if url is valid" do  
16 - helper.get_host("http://example.com/resource/12").should == 'example.com'  
17 - end  
18 -  
19 - it "returns 'N/A' when url is not valid" do  
20 - helper.get_host("some string").should == 'N/A'  
21 - end  
22 -  
23 - it "returns 'N/A' when url is empty" do  
24 - helper.get_host({}).should == 'N/A'  
25 - end  
26 - end  
27 -end  
spec/models/notice_spec.rb
@@ -82,6 +82,36 @@ describe Notice do @@ -82,6 +82,36 @@ describe Notice do
82 end 82 end
83 end 83 end
84 84
  85 + describe "user agent string" do
  86 + it "should be parsed and human-readable" do
  87 + notice = Factory.build(:notice, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}})
  88 + notice.user_agent_string.should == 'Chrome 10.0.648.204'
  89 + end
  90 +
  91 + it "should be nil if HTTP_USER_AGENT is blank" do
  92 + notice = Factory.build(:notice)
  93 + notice.user_agent_string.should == "N/A"
  94 + end
  95 + end
  96 +
  97 + describe "host" do
  98 + it "returns host if url is valid" do
  99 + notice = Factory.build(:notice, :request => {'url' => "http://example.com/resource/12"})
  100 + notice.host.should == 'example.com'
  101 + end
  102 +
  103 + it "returns 'N/A' when url is not valid" do
  104 + notice = Factory.build(:notice, :request => {'url' => "some string"})
  105 + notice.host.should == 'N/A'
  106 + end
  107 +
  108 + it "returns 'N/A' when url is empty" do
  109 + notice = Factory.build(:notice, :request => {})
  110 + notice.host.should == 'N/A'
  111 + end
  112 +
  113 + end
  114 +
85 115
86 describe "email notifications (configured individually for each app)" do 116 describe "email notifications (configured individually for each app)" do
87 custom_thresholds = [2, 4, 8, 16, 32, 64] 117 custom_thresholds = [2, 4, 8, 16, 32, 64]
spec/models/problem_spec.rb
@@ -197,5 +197,110 @@ describe Problem do @@ -197,5 +197,110 @@ describe Problem do
197 }.should change(problem, :last_deploy_at).from(@last_deploy).to(next_deploy) 197 }.should change(problem, :last_deploy_at).from(@last_deploy).to(next_deploy)
198 end 198 end
199 end 199 end
  200 +
  201 + context "notice messages cache" do
  202 + before do
  203 + @app = Factory(:app)
  204 + @problem = Factory(:problem, :app => @app)
  205 + @err = Factory(:err, :problem => @problem)
  206 + end
  207 +
  208 + it "#messages returns [] by default" do
  209 + @problem.messages.should == []
  210 + end
  211 +
  212 + it "adding a notice adds a string to #messages" do
  213 + lambda {
  214 + Factory(:notice, :err => @err, :message => 'ERR 1')
  215 + }.should change(@problem, :messages).from([]).to(['ERR 1'])
  216 + end
  217 +
  218 + it "removing a notice removes string from #messages" do
  219 + notice1 = Factory(:notice, :err => @err, :message => 'ERR 1')
  220 + lambda {
  221 + @err.notices.first.destroy
  222 + @problem.reload
  223 + }.should change(@problem, :messages).from(['ERR 1']).to([])
  224 + end
  225 + end
  226 +
  227 + context "notice hosts cache" do
  228 + before do
  229 + @app = Factory(:app)
  230 + @problem = Factory(:problem, :app => @app)
  231 + @err = Factory(:err, :problem => @problem)
  232 + end
  233 +
  234 + it "#hosts returns [] by default" do
  235 + @problem.hosts.should == []
  236 + end
  237 +
  238 + it "adding a notice adds a string to #hosts" do
  239 + lambda {
  240 + Factory(:notice, :err => @err, :request => {'url' => "http://example.com/resource/12"})
  241 + }.should change(@problem, :hosts).from([]).to(['example.com'])
  242 + end
  243 +
  244 + it "removing a notice removes string from #hosts" do
  245 + notice1 = Factory(:notice, :err => @err, :request => {'url' => "http://example.com/resource/12"})
  246 + lambda {
  247 + @err.notices.first.destroy
  248 + @problem.reload
  249 + }.should change(@problem, :hosts).from(['example.com']).to([])
  250 + end
  251 + end
  252 +
  253 + context "notice user_agents cache" do
  254 + before do
  255 + @app = Factory(:app)
  256 + @problem = Factory(:problem, :app => @app)
  257 + @err = Factory(:err, :problem => @problem)
  258 + end
  259 +
  260 + it "#user_agents returns [] by default" do
  261 + @problem.user_agents.should == []
  262 + end
  263 +
  264 + it "adding a notice adds a string to #user_agents" do
  265 + lambda {
  266 + Factory(:notice, :err => @err, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}})
  267 + }.should change(@problem, :user_agents).from([]).to(['Chrome 10.0.648.204'])
  268 + end
  269 +
  270 + it "removing a notice removes string from #user_agents" do
  271 + notice1 = Factory(:notice, :err => @err, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}})
  272 + lambda {
  273 + @err.notices.first.destroy
  274 + @problem.reload
  275 + }.should change(@problem, :user_agents).from(['Chrome 10.0.648.204']).to([])
  276 + end
  277 + end
  278 +
  279 + context "comment counter cache" do
  280 + before do
  281 + @app = Factory(:app)
  282 + @problem = Factory(:problem, :app => @app)
  283 + end
  284 +
  285 + it "#comments_count returns 0 by default" do
  286 + @problem.comments_count.should == 0
  287 + end
  288 +
  289 + it "adding a comment increases #comments_count by 1" do
  290 + lambda {
  291 + Factory(:comment, :err => @problem)
  292 + }.should change(@problem, :comments_count).from(0).to(1)
  293 + end
  294 +
  295 + it "removing a comment decreases #comments_count by 1" do
  296 + comment1 = Factory(:comment, :err => @problem)
  297 + lambda {
  298 + @problem.reload.comments.first.destroy
  299 + @problem.reload
  300 + }.should change(@problem, :comments_count).from(1).to(0)
  301 + end
  302 + end
  303 +
  304 +
200 end 305 end
201 306
spec/views/errs/show.html.haml_spec.rb
@@ -8,7 +8,7 @@ describe &quot;errs/show.html.haml&quot; do @@ -8,7 +8,7 @@ describe &quot;errs/show.html.haml&quot; do
8 assign :problem, problem 8 assign :problem, problem
9 assign :comment, comment 9 assign :comment, comment
10 assign :app, problem.app 10 assign :app, problem.app
11 - assign :notices, err.notices.paginate(:page => 1, :per_page => 1) 11 + assign :notices, err.notices.page(1).per(1)
12 assign :notice, err.notices.first 12 assign :notice, err.notices.first
13 controller.stub(:current_user) { Factory(:user) } 13 controller.stub(:current_user) { Factory(:user) }
14 end 14 end