whoops 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +25 -0
- data/app/controllers/event_groups_controller.rb +34 -0
- data/app/controllers/events_controller.rb +24 -0
- data/app/helpers/event_groups_helper.rb +32 -0
- data/app/helpers/events_helper.rb +28 -0
- data/app/helpers/notifications_helper.rb +2 -0
- data/app/lib/field_names.rb +9 -0
- data/app/models/whoops/event.rb +27 -0
- data/app/models/whoops/event_group.rb +32 -0
- data/app/models/whoops/filter.rb +15 -0
- data/app/views/event_groups/_list.html.haml +19 -0
- data/app/views/event_groups/index.html.haml +14 -0
- data/app/views/events/_detail.html.haml +5 -0
- data/app/views/events/_details.html.haml +8 -0
- data/app/views/events/index.html.haml +25 -0
- data/app/views/layouts/whoops.html.haml +39 -0
- data/config/initializers/date_formats.rb +3 -0
- data/config/routes.rb +7 -0
- data/lib/generators/whoops/assets_generator.rb +16 -0
- data/lib/generators/whoops/templates/assets/images/_ui/breadcrumb_divider.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/btn_submit.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/btn_submit_2.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/btn_view_site.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/header_bg.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/header_shadow.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_add_user.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_error.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_info.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_success.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_alert_warning.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_audio.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_categories.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_edit.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_edit_article.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_folder.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_jump_back.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_logout.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_new_article.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_photo.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_profile.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_search.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_security.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_settings.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_tags.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_trash.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_user.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_video.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/icn_view_users.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/module_footer_bg.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/post_message.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/secondary_bar.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/secondary_bar_shadow.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/sidebar.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/sidebar_divider.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/sidebar_shadow.png +0 -0
- data/lib/generators/whoops/templates/assets/images/_ui/table_sorter_header.png +0 -0
- data/lib/generators/whoops/templates/assets/javascripts/application.js +35 -0
- data/lib/generators/whoops/templates/assets/javascripts/hideshow.js +39 -0
- data/lib/generators/whoops/templates/assets/javascripts/jquery-1.5.2.min.js +16 -0
- data/lib/generators/whoops/templates/assets/javascripts/jquery.equalHeight.js +20 -0
- data/lib/generators/whoops/templates/assets/javascripts/jquery.form.js +825 -0
- data/lib/generators/whoops/templates/assets/javascripts/jquery.tablesorter.min.js +4 -0
- data/lib/generators/whoops/templates/assets/javascripts/ui.js +28 -0
- data/lib/generators/whoops/templates/assets/stylesheets/ie.css +49 -0
- data/lib/generators/whoops/templates/assets/stylesheets/sass/_base.scss +18 -0
- data/lib/generators/whoops/templates/assets/stylesheets/sass/ie.scss +43 -0
- data/lib/generators/whoops/templates/assets/stylesheets/sass/screen.scss +945 -0
- data/lib/generators/whoops/templates/assets/stylesheets/screen.css +1082 -0
- data/lib/whoops.rb +5 -0
- data/lib/whoops/engine.rb +4 -0
- metadata +356 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -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.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -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,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,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
|
+
|
data/config/routes.rb
ADDED
@@ -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
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|