whoops 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/Gemfile +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +25 -0
  5. data/app/controllers/event_groups_controller.rb +34 -0
  6. data/app/controllers/events_controller.rb +24 -0
  7. data/app/helpers/event_groups_helper.rb +32 -0
  8. data/app/helpers/events_helper.rb +28 -0
  9. data/app/helpers/notifications_helper.rb +2 -0
  10. data/app/lib/field_names.rb +9 -0
  11. data/app/models/whoops/event.rb +27 -0
  12. data/app/models/whoops/event_group.rb +32 -0
  13. data/app/models/whoops/filter.rb +15 -0
  14. data/app/views/event_groups/_list.html.haml +19 -0
  15. data/app/views/event_groups/index.html.haml +14 -0
  16. data/app/views/events/_detail.html.haml +5 -0
  17. data/app/views/events/_details.html.haml +8 -0
  18. data/app/views/events/index.html.haml +25 -0
  19. data/app/views/layouts/whoops.html.haml +39 -0
  20. data/config/initializers/date_formats.rb +3 -0
  21. data/config/routes.rb +7 -0
  22. data/lib/generators/whoops/assets_generator.rb +16 -0
  23. data/lib/generators/whoops/templates/assets/images/_ui/breadcrumb_divider.png +0 -0
  24. data/lib/generators/whoops/templates/assets/images/_ui/btn_submit.png +0 -0
  25. data/lib/generators/whoops/templates/assets/images/_ui/btn_submit_2.png +0 -0
  26. data/lib/generators/whoops/templates/assets/images/_ui/btn_view_site.png +0 -0
  27. data/lib/generators/whoops/templates/assets/images/_ui/header_bg.png +0 -0
  28. data/lib/generators/whoops/templates/assets/images/_ui/header_shadow.png +0 -0
  29. data/lib/generators/whoops/templates/assets/images/_ui/icn_add_user.png +0 -0
  30. data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_error.png +0 -0
  31. data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_info.png +0 -0
  32. data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_success.png +0 -0
  33. data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_warning.png +0 -0
  34. data/lib/generators/whoops/templates/assets/images/_ui/icn_audio.png +0 -0
  35. data/lib/generators/whoops/templates/assets/images/_ui/icn_categories.png +0 -0
  36. data/lib/generators/whoops/templates/assets/images/_ui/icn_edit.png +0 -0
  37. data/lib/generators/whoops/templates/assets/images/_ui/icn_edit_article.png +0 -0
  38. data/lib/generators/whoops/templates/assets/images/_ui/icn_folder.png +0 -0
  39. data/lib/generators/whoops/templates/assets/images/_ui/icn_jump_back.png +0 -0
  40. data/lib/generators/whoops/templates/assets/images/_ui/icn_logout.png +0 -0
  41. data/lib/generators/whoops/templates/assets/images/_ui/icn_new_article.png +0 -0
  42. data/lib/generators/whoops/templates/assets/images/_ui/icn_photo.png +0 -0
  43. data/lib/generators/whoops/templates/assets/images/_ui/icn_profile.png +0 -0
  44. data/lib/generators/whoops/templates/assets/images/_ui/icn_search.png +0 -0
  45. data/lib/generators/whoops/templates/assets/images/_ui/icn_security.png +0 -0
  46. data/lib/generators/whoops/templates/assets/images/_ui/icn_settings.png +0 -0
  47. data/lib/generators/whoops/templates/assets/images/_ui/icn_tags.png +0 -0
  48. data/lib/generators/whoops/templates/assets/images/_ui/icn_trash.png +0 -0
  49. data/lib/generators/whoops/templates/assets/images/_ui/icn_user.png +0 -0
  50. data/lib/generators/whoops/templates/assets/images/_ui/icn_video.png +0 -0
  51. data/lib/generators/whoops/templates/assets/images/_ui/icn_view_users.png +0 -0
  52. data/lib/generators/whoops/templates/assets/images/_ui/module_footer_bg.png +0 -0
  53. data/lib/generators/whoops/templates/assets/images/_ui/post_message.png +0 -0
  54. data/lib/generators/whoops/templates/assets/images/_ui/secondary_bar.png +0 -0
  55. data/lib/generators/whoops/templates/assets/images/_ui/secondary_bar_shadow.png +0 -0
  56. data/lib/generators/whoops/templates/assets/images/_ui/sidebar.png +0 -0
  57. data/lib/generators/whoops/templates/assets/images/_ui/sidebar_divider.png +0 -0
  58. data/lib/generators/whoops/templates/assets/images/_ui/sidebar_shadow.png +0 -0
  59. data/lib/generators/whoops/templates/assets/images/_ui/table_sorter_header.png +0 -0
  60. data/lib/generators/whoops/templates/assets/javascripts/application.js +35 -0
  61. data/lib/generators/whoops/templates/assets/javascripts/hideshow.js +39 -0
  62. data/lib/generators/whoops/templates/assets/javascripts/jquery-1.5.2.min.js +16 -0
  63. data/lib/generators/whoops/templates/assets/javascripts/jquery.equalHeight.js +20 -0
  64. data/lib/generators/whoops/templates/assets/javascripts/jquery.form.js +825 -0
  65. data/lib/generators/whoops/templates/assets/javascripts/jquery.tablesorter.min.js +4 -0
  66. data/lib/generators/whoops/templates/assets/javascripts/ui.js +28 -0
  67. data/lib/generators/whoops/templates/assets/stylesheets/ie.css +49 -0
  68. data/lib/generators/whoops/templates/assets/stylesheets/sass/_base.scss +18 -0
  69. data/lib/generators/whoops/templates/assets/stylesheets/sass/ie.scss +43 -0
  70. data/lib/generators/whoops/templates/assets/stylesheets/sass/screen.scss +945 -0
  71. data/lib/generators/whoops/templates/assets/stylesheets/screen.css +1082 -0
  72. data/lib/whoops.rb +5 -0
  73. data/lib/whoops/engine.rb +4 -0
  74. metadata +356 -0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ = Whoops
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rake/rdoctask'
11
+
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task :default => :spec
18
+
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'Whoops'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
@@ -0,0 +1,34 @@
1
+ class EventGroupsController < ApplicationController
2
+ layout 'whoops'
3
+ before_filter :update_event_group_filter
4
+ helper_method :event_group_filter
5
+
6
+ def index
7
+ @event_groups = Whoops::EventGroup.where(event_group_filter.to_query_document).desc(:last_recorded_at).paginate(
8
+ :page => params[:page],
9
+ :per_page => 20
10
+ )
11
+
12
+ respond_to do |format|
13
+ format.html
14
+ format.js { render :partial => 'list' }
15
+ end
16
+ end
17
+
18
+ def show
19
+ @event_group = Whoops::EventGroup.find(params[:id])
20
+ end
21
+
22
+ def update_event_group_filter
23
+ self.event_group_filter = params[:whoops_filter] if params[:whoops_filter]
24
+ end
25
+
26
+ def event_group_filter
27
+ session[:event_group_filter] ||= Whoops::Filter.new
28
+ end
29
+
30
+ def event_group_filter=(filter)
31
+ session[:event_group_filter] = Whoops::Filter.new(filter)
32
+ end
33
+
34
+ end
@@ -0,0 +1,24 @@
1
+ class EventsController < ApplicationController
2
+ layout 'whoops'
3
+
4
+ def index
5
+ @event_group = Whoops::EventGroup.find(params[:whoops_event_group_id])
6
+ @events = @event_group.events.desc(:event_time).paginate(
7
+ :page => params[:page],
8
+ :per_page => 20
9
+ )
10
+ end
11
+
12
+ def show
13
+ @event = Whoops::Event.find(params[:id])
14
+ respond_to do |format|
15
+ format.js { render :partial => 'details', :object => @event, :as => :event}
16
+ end
17
+ end
18
+
19
+ # TODO break this out into a more metal-y controller
20
+ def create
21
+ Whoops::Event.record(params[:event])
22
+ render :status => 200, :nothing => true
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ module EventGroupsHelper
2
+ def event_group_scoped_link(event_group, scope)
3
+ new_filter = {:whoops_filter => event_group_filter.to_query_document.merge(scope => event_group.send(scope))}
4
+ link_to(event_group.send(scope), whoops_event_groups_path(new_filter))
5
+ end
6
+
7
+ # meant for consumption by options_from_collection_for_select
8
+ def filter_options
9
+ all_event_groups = Whoops::EventGroup.all
10
+ return @filter_options if @filter_options
11
+
12
+ @filter_options = Hash.new{|h, k| h[k] = []}
13
+
14
+ @filter_options["service"] = Whoops::EventGroup.services.collect{|s| [s, s]}
15
+
16
+ all_event_groups.each do |event_group|
17
+ mundane_fields = (Whoops::Filter.field_names & Whoops::EventGroup.field_names) - ["service", "message"]
18
+ mundane_fields.each do |field_name|
19
+ @filter_options[field_name] << [event_group.send(field_name), event_group.send(field_name)]
20
+ end
21
+ end
22
+
23
+ # add the field name as an empty option
24
+ @filter_options.keys.each do |field_name|
25
+ @filter_options[field_name].compact!
26
+ @filter_options[field_name].sort!{|a, b| a.first <=> b.first}.uniq! if @filter_options[field_name]
27
+ @filter_options[field_name].unshift(["", "--" + field_name.humanize.downcase + " filter--"])
28
+ end
29
+
30
+ @filter_options
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ module EventsHelper
2
+ def format_detail(detail)
3
+ case detail
4
+ when String, Numeric: detail
5
+ when Array: simple_format(detail.join("\n"), :class => "simple")
6
+ when Hash: detail_table(detail)
7
+ else
8
+ detail.to_s
9
+ end
10
+ end
11
+
12
+ def detail_table(detail)
13
+ render :partial => 'events/detail', :object => detail
14
+ end
15
+
16
+ def breadcrumbs(*groups)
17
+ breadcrumbs = ""
18
+ groups.each do |group|
19
+ breadcrumbs << "<div class='breadcrumb_divider'></div>"
20
+ if groups.last == group
21
+ breadcrumbs << "<a class='current'>#{group.first}</a>"
22
+ else
23
+ breadcrumbs << link_to(group.first, group.last)
24
+ end
25
+ end
26
+ breadcrumbs.html_safe
27
+ end
28
+ end
@@ -0,0 +1,2 @@
1
+ module NotificationsHelper
2
+ end
@@ -0,0 +1,9 @@
1
+ module FieldNames
2
+ extend ActiveSupport::Concern
3
+
4
+ module ClassMethods
5
+ def field_names
6
+ self.fields.keys - ["_id", "_type"]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ class Whoops::Event
2
+ include Mongoid::Document
3
+ include FieldNames
4
+
5
+ belongs_to :event_group, :class_name => "Whoops::EventGroup"
6
+
7
+ field :details
8
+ field :message, :type => String
9
+ field :event_time, :type => DateTime
10
+
11
+ validates_presence_of :message
12
+
13
+ def self.record(params)
14
+ params = params.with_indifferent_access
15
+
16
+ event_group_params = params.slice(*Whoops::EventGroup.field_names)
17
+ event_group_params[:identifier] = params[:event_group_identifier]
18
+ event_group_params[:last_recorded_at] = params[:event_time]
19
+
20
+ event_group = Whoops::EventGroup.first(:conditions => event_group_params.slice(*Whoops::EventGroup.identifying_fields))
21
+ event_group ||= Whoops::EventGroup.create(event_group_params)
22
+
23
+ event_params = params.slice(*Whoops::Event.field_names)
24
+ event_group.events.create(event_params)
25
+ end
26
+
27
+ end
@@ -0,0 +1,32 @@
1
+ class Whoops::EventGroup
2
+ # notifier responsible for creating identifier from notice details
3
+ include Mongoid::Document
4
+ include FieldNames
5
+
6
+ [:service, :environment, :event_type, :message, :identifier].each do |string_field|
7
+ field string_field, :type => String
8
+ end
9
+ field :last_recorded_at, :type => DateTime
10
+
11
+ has_many :events, :class_name => "Whoops::Event"
12
+
13
+ validates_presence_of :identifier, :event_type, :service, :message
14
+
15
+ def self.identifying_fields
16
+ field_names - ["message", "last_recorded_at"]
17
+ end
18
+
19
+ # @return sorted set of all applicable namespaces
20
+ def self.services
21
+ services = SortedSet.new
22
+ previous_service = []
23
+ all(:sort => [[:service, :asc]]).each do |group|
24
+ services << group.service
25
+ split = group.service.split(".")
26
+ common = (previous_service & split)
27
+ services << common.join(".") unless common.blank?
28
+ previous_service = split
29
+ end
30
+ services
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ class Whoops::Filter
2
+ include Mongoid::Document
3
+ include FieldNames
4
+
5
+ [:service, :environment, :event_type, :message, :details].each do |document_field|
6
+ field document_field
7
+ end
8
+
9
+ def to_query_document
10
+ doc = attributes.except(:_id).delete_if{|k, v| v.blank?}
11
+ # match all services under namespace. ie, if "app" given, match "app.web", "app.backend" etc
12
+ doc[:service] = /#{doc[:service]}(\..*)?/ if doc[:service]
13
+ doc
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ = will_paginate(@event_groups)
2
+ %table.event-group-list
3
+ %thead
4
+ %tr
5
+ %th.header Service
6
+ %th.header Message
7
+ %th.header Type
8
+ %th.header Env
9
+ %th.header Last Occurrence
10
+ %th.header
11
+ - @event_groups.each do |event_group|
12
+ %tr
13
+ %td= event_group_scoped_link(event_group, :service)
14
+ %td= link_to event_group.message, whoops_event_group_events_path(event_group)
15
+ %td= event_group_scoped_link(event_group, :event_type)
16
+ %td= event_group_scoped_link(event_group, :environment)
17
+ %td= event_group.last_recorded_at.to_s(:whoops_default)
18
+ %td= event_group.events.count
19
+ = will_paginate(@event_groups)
@@ -0,0 +1,14 @@
1
+ - content_for :sidebar do
2
+ .space
3
+ %h3 Filters
4
+ - form_for event_group_filter, :url => whoops_event_groups_path, :html => {:method => "get"} do |f|
5
+ %ul.filter_selects
6
+ - filter_options.each do |field_name, options|
7
+ %li= f.select field_name, options_from_collection_for_select(options, :first, :last, event_group_filter.send(field_name))
8
+ %li
9
+ %button#reset reset
10
+
11
+ %article.module.width_full
12
+ %header
13
+ %h3 Events
14
+ = render :partial => 'list'
@@ -0,0 +1,5 @@
1
+ %ul.detail
2
+ - detail.to_a.sort{|a,b| a[0] <=> b[0]}.each do |sub_detail|
3
+ %li
4
+ %span.label= sub_detail.first + ":"
5
+ = format_detail(sub_detail.last)
@@ -0,0 +1,8 @@
1
+ %table.details
2
+ %tr
3
+ %td.label Message
4
+ %td.value= event.message
5
+ - event.details.to_a.sort{|a,b| a[0] <=> b[0]}.each do |detail|
6
+ %tr
7
+ %td.label= detail.first
8
+ %td.value= format_detail(detail.last)
@@ -0,0 +1,25 @@
1
+ - content_for :breadcrumbs do
2
+ = breadcrumbs(["Event Details", whoops_event_group_path(@event_group)])
3
+
4
+ - content_for :sidebar do
5
+ %h3 Instances
6
+ = will_paginate @event, :previous_label => "", :next_label => ""
7
+ %ul#instances
8
+ - @events.each_with_index do |event, index|
9
+ %li{:class => ("selected" if index == 0)}= link_to event.event_time.to_s(:whoops_default), event
10
+
11
+ %article.module.width_full
12
+ %header
13
+ %h3= @event_group.message
14
+ %table
15
+ - [:environment, :event_type, :last_recorded_at].each do |field|
16
+ %tr
17
+ %td.label= field.to_s.humanize
18
+ %td= @event_group.send(field)
19
+
20
+ %article.module.width_full
21
+ %header
22
+ %h3 Details
23
+ #event-details
24
+ = render :partial => "events/details", :object => @events.first, :as => :event
25
+
@@ -0,0 +1,39 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title Whoops
5
+ = stylesheet_link_tag 'screen.css', :media => 'screen, projection'
6
+ /[if IE]
7
+ = stylesheet_link_tag 'ie.css', :media => 'screen, projection'
8
+
9
+ = javascript_include_tag 'jquery-1.5.2.min', 'jquery.equalHeight', 'jquery.tablesorter.min', 'application.js'
10
+ = yield :extra_javascripts
11
+ = csrf_meta_tag
12
+ %body
13
+ %header#header
14
+ %hgroup
15
+ %h1.site_title
16
+ = link_to "Whoops", whoops_event_groups_path
17
+ %h2.section_title
18
+
19
+ / end of header bar
20
+ %section#secondary_bar
21
+ .user
22
+ .breadcrumbs_container
23
+ %article.breadcrumbs
24
+ = link_to "All Events", whoops_event_groups_path, :class => ("current" if controller_name == 'event_groups')
25
+ = yield :breadcrumbs
26
+ / end of secondary bar
27
+ %aside#sidebar.column
28
+ = yield :sidebar
29
+ %footer
30
+ %hr/
31
+ %p
32
+ %strong Whoops!
33
+
34
+ / end of sidebar
35
+ %section#main.column
36
+ = yield
37
+ / end of styles article
38
+ .spacer
39
+
@@ -0,0 +1,3 @@
1
+ DateTime::DATE_FORMATS[:whoops_default]="%Y-%m-%d %H:%M"
2
+ Time::DATE_FORMATS[:whoops_default] = "%Y-%m-%d %H:%M"
3
+ Date::DATE_FORMATS[:whoops_default] = "%Y-%m-%d"
@@ -0,0 +1,7 @@
1
+ Rails.application.routes.draw do
2
+ resources :event_groups, :as => "whoops_event_groups" do
3
+ resources :events
4
+ end
5
+
6
+ resources :events, :as => "whoops_events"
7
+ end
@@ -0,0 +1,16 @@
1
+ require 'rails/generators'
2
+
3
+ module Whoops
4
+ class AssetsGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ desc 'Installs assets into your public directory.'
7
+
8
+ def install_assets
9
+ directory 'assets', 'public/'
10
+ end
11
+
12
+ def self.source_root
13
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
14
+ end
15
+ end
16
+ end