Dex_Rack 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Dex_Rack.gemspec +36 -0
- data/Gemfile +6 -0
- data/README.md +36 -0
- data/Rakefile +3 -0
- data/lib/Dex_Rack.rb +194 -0
- data/lib/Dex_Rack/config.ru +4 -0
- data/lib/Dex_Rack/public/favicon.ico +0 -0
- data/lib/Dex_Rack/public/images/texture/9.jpg +0 -0
- data/lib/Dex_Rack/public/javascripts/dex.js +16 -0
- data/lib/Dex_Rack/public/javascripts/tabs.js +29 -0
- data/lib/Dex_Rack/public/robots.txt +5 -0
- data/lib/Dex_Rack/public/stylesheets/dex.css +207 -0
- data/lib/Dex_Rack/public/stylesheets/reset.css +48 -0
- data/lib/Dex_Rack/version.rb +5 -0
- data/lib/Dex_Rack/views/index.rb +68 -0
- data/lib/Dex_Rack/views/layout.rb +36 -0
- data/lib/Dex_Rack/views/record.rb +51 -0
- data/lib/Dex_Rack/views/record_404.rb +3 -0
- data/spec/Dex_App.rb +88 -0
- data/spec/bin.rb +13 -0
- data/spec/lib/main.rb +75 -0
- metadata +244 -0
data/.gitignore
ADDED
data/Dex_Rack.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
require "Dex_Rack/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "Dex_Rack"
|
8
|
+
s.version = Dex_Rack::VERSION
|
9
|
+
s.authors = ["da99"]
|
10
|
+
s.email = ["i-hate-spam-45671204@mailinator.com"]
|
11
|
+
s.homepage = "https://github.com/da99/Dex_Rack"
|
12
|
+
s.summary = %q{A Rack app to display Dex exceptions.}
|
13
|
+
s.description = %q{
|
14
|
+
It is a web-frontend for Dex.
|
15
|
+
Update and view exceptions logged with the Dex gem.
|
16
|
+
}
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_development_dependency 'bacon'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
s.add_development_dependency 'Bacon_Colored'
|
26
|
+
s.add_development_dependency 'pry'
|
27
|
+
s.add_development_dependency 'rack-test'
|
28
|
+
|
29
|
+
# Specify any dependencies here; for example:
|
30
|
+
s.add_runtime_dependency 'sinatra'
|
31
|
+
s.add_runtime_dependency 'markaby'
|
32
|
+
s.add_runtime_dependency 'thin'
|
33
|
+
s.add_runtime_dependency 'sinatra-reloader'
|
34
|
+
s.add_runtime_dependency 'chronic_duration'
|
35
|
+
s.add_runtime_dependency 'Dex'
|
36
|
+
end
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
Dex\_Rack
|
3
|
+
================
|
4
|
+
|
5
|
+
A Ruby gem.
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
gem install Dex_Rack
|
11
|
+
|
12
|
+
Usage
|
13
|
+
------
|
14
|
+
|
15
|
+
Create a `config.ru` file:
|
16
|
+
|
17
|
+
require "Dex_Rack"
|
18
|
+
|
19
|
+
Dex_Rack.set :dex, ( Dex.db("My.SQLITE.file.db") && Dex )
|
20
|
+
run Dex_Rack
|
21
|
+
|
22
|
+
In your shell:
|
23
|
+
|
24
|
+
rackup -p 3000
|
25
|
+
|
26
|
+
Visit: [http://localhost:3000](http://localhost:3000)
|
27
|
+
|
28
|
+
Run Tests
|
29
|
+
---------
|
30
|
+
|
31
|
+
git clone git@github.com:da99/Dex_Rack.git
|
32
|
+
cd Dex_Rack
|
33
|
+
bundle update
|
34
|
+
bundle exec bacon spec/lib/main.rb
|
35
|
+
|
36
|
+
|
data/Rakefile
ADDED
data/lib/Dex_Rack.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
require 'Dex_Rack/version'
|
2
|
+
require 'Dex'
|
3
|
+
require 'markaby'
|
4
|
+
require 'chronic_duration'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
class Dex_Rack
|
8
|
+
|
9
|
+
RACK_DIR = File.join( File.dirname( __FILE__ ) , "/Dex_Rack" )
|
10
|
+
|
11
|
+
set :dex, ( Dex.db("/tmp/dex.rack.sample.db") && Dex )
|
12
|
+
|
13
|
+
use Rack::Lint
|
14
|
+
|
15
|
+
set :public_folder, RACK_DIR + "/public"
|
16
|
+
set :views_folder, RACK_DIR + "/views"
|
17
|
+
|
18
|
+
# use Rack::Static, \
|
19
|
+
# :urls=>%w{ /stylesheets/ /javascripts/ /images/ /index.html }, \
|
20
|
+
# :root=> RACK_DIR
|
21
|
+
|
22
|
+
configure :development do
|
23
|
+
require 'sinatra/reloader'
|
24
|
+
register Sinatra::Reloader
|
25
|
+
use Rack::CommonLogger
|
26
|
+
end
|
27
|
+
|
28
|
+
get "/" do
|
29
|
+
recent
|
30
|
+
end
|
31
|
+
|
32
|
+
get "/recent/:page" do | page |
|
33
|
+
recent page
|
34
|
+
end
|
35
|
+
|
36
|
+
get %r!\A/(\d+)\Z! do | id |
|
37
|
+
r = dex.filter(:id=>id).first
|
38
|
+
|
39
|
+
if r
|
40
|
+
vars= Hash[
|
41
|
+
:title=>"#{r[:exception]}: #{r[:message]}",
|
42
|
+
:record=>r,
|
43
|
+
:table_keys=>(r.keys - [:exception, :message, :backtrace, :status]),
|
44
|
+
:app=>self
|
45
|
+
]
|
46
|
+
|
47
|
+
s = self
|
48
|
+
status = status_to_word(r[:status])
|
49
|
+
vars[:status_class] = status.downcase
|
50
|
+
vars[:status_word] = status
|
51
|
+
r[:human_time] = human_time(r[:created_at])
|
52
|
+
r[:backtrace] = backtrace_to_html(r[:backtrace])
|
53
|
+
vars[:table_keys] << :human_time
|
54
|
+
layout vars, :record
|
55
|
+
else
|
56
|
+
status 404
|
57
|
+
layout Hash[:id=>id, :title=>"Not Found: Record ##{id}"], :record_404
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
get "/:id/toggle" do |id|
|
63
|
+
r = dex.filter(:id=>id).first
|
64
|
+
s = r[:status] == 0 ? 1 : 0
|
65
|
+
dex.filter(:id=>id).update(:status=>s)
|
66
|
+
redirect to("/#{r[:id]}"), 302
|
67
|
+
end
|
68
|
+
|
69
|
+
delete "/:id" do | id |
|
70
|
+
dex.filter(:id=>id).delete
|
71
|
+
redirect to('/'), 303
|
72
|
+
end
|
73
|
+
|
74
|
+
get "/create-sample" do
|
75
|
+
begin
|
76
|
+
raise ArgumentError, request.path_info
|
77
|
+
rescue ArgumentError => e
|
78
|
+
dex.insert e
|
79
|
+
end
|
80
|
+
redirect to("/"), 302
|
81
|
+
end
|
82
|
+
|
83
|
+
%w{ dex dex? }.each { |m|
|
84
|
+
eval %~
|
85
|
+
def #{m} *args
|
86
|
+
settings.#{m}(*args)
|
87
|
+
end
|
88
|
+
~
|
89
|
+
}
|
90
|
+
|
91
|
+
def recent page = :last, limit = 10
|
92
|
+
nav = page_nav( dex.count, limit, page)
|
93
|
+
|
94
|
+
return redirect(to('/'), 302) if !nav
|
95
|
+
|
96
|
+
nav[:prev_url] = "/recent/#{nav[:prev]}" if nav[:prev]
|
97
|
+
nav[:next_url] = "/recent/#{nav[:next]}" if nav[:next]
|
98
|
+
nav[:next_url] = "/" if nav[:next] == nav[:total]
|
99
|
+
|
100
|
+
vars = nav.merge(
|
101
|
+
Hash[
|
102
|
+
:title => "Dex List",
|
103
|
+
:list => dex.limit(nav[:limit], nav[:offset]).to_a.reverse,
|
104
|
+
:app => self
|
105
|
+
])
|
106
|
+
|
107
|
+
layout(vars, :index)
|
108
|
+
end
|
109
|
+
|
110
|
+
def layout vars, file_name
|
111
|
+
Markaby::Builder.set(:indent, 2)
|
112
|
+
mab = Markaby::Builder.new
|
113
|
+
file = File.join(settings.views_folder, "/layout.rb")
|
114
|
+
vars[:view_file] = File.join(settings.views_folder, "#{file_name}.rb" )
|
115
|
+
vars[:app] = self
|
116
|
+
mab.instance_eval {
|
117
|
+
eval File.read(file), nil, file, 1
|
118
|
+
}
|
119
|
+
mab.to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
def status_to_word num
|
123
|
+
case num
|
124
|
+
when 0
|
125
|
+
"Unresolved"
|
126
|
+
when 1
|
127
|
+
"Resolved"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def backtrace_to_html s
|
132
|
+
last_file = nil
|
133
|
+
str = ""
|
134
|
+
s.split("\n").map { |l|
|
135
|
+
file, num, code = l.split(':')
|
136
|
+
str.<< %!
|
137
|
+
<div class="line">
|
138
|
+
<span class="num">#{CGI::escapeHTML num}</span>
|
139
|
+
<span class="code">#{CGI::escapeHTML code}</span>
|
140
|
+
</div>!
|
141
|
+
str.<< %!
|
142
|
+
<div class="file">#{CGI::escapeHTML file}</div>
|
143
|
+
! if file != last_file
|
144
|
+
|
145
|
+
last_file = file
|
146
|
+
str
|
147
|
+
}.join
|
148
|
+
end
|
149
|
+
|
150
|
+
def human_time t
|
151
|
+
target = Time.now.utc.to_i - t.to_i
|
152
|
+
if target < 61
|
153
|
+
return "<1m ago"
|
154
|
+
else
|
155
|
+
ChronicDuration.output(target, :format=>:short).sub(/\d+s\Z/, '') + " ago"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def page_nav count, div, page = :last
|
160
|
+
count = count.to_i
|
161
|
+
|
162
|
+
div = div.to_i
|
163
|
+
total = ( count / Float(div) ).ceil
|
164
|
+
page = total if page == :last
|
165
|
+
page = page.to_i
|
166
|
+
|
167
|
+
if count > 0
|
168
|
+
if total < 1 || page < 1 || page > total || div < 2
|
169
|
+
return nil
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
n = page + 1
|
174
|
+
n = nil if n > total
|
175
|
+
p = page - 1
|
176
|
+
p = nil if p < 1
|
177
|
+
offset = div * (page - 1)
|
178
|
+
offset = 0 if offset < 1
|
179
|
+
|
180
|
+
Hash[
|
181
|
+
|
182
|
+
:total => total,
|
183
|
+
:next => n,
|
184
|
+
:prev => p,
|
185
|
+
:page => page,
|
186
|
+
|
187
|
+
:count => count,
|
188
|
+
:limit => div,
|
189
|
+
:offset => offset
|
190
|
+
|
191
|
+
]
|
192
|
+
end
|
193
|
+
|
194
|
+
end # === class Dex_Rack
|
Binary file
|
Binary file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
$(document).ready(function() {
|
2
|
+
|
3
|
+
$("#show_backtrace").click(function() {
|
4
|
+
$("#show_backtrace").css("visibility", "hidden");
|
5
|
+
$("#hide_backtrace").css("visibility", "visible");
|
6
|
+
$("#backtrace").show();
|
7
|
+
});
|
8
|
+
|
9
|
+
$("#hide_backtrace").click(function() {
|
10
|
+
$("#show_backtrace").css("visibility", "visible");
|
11
|
+
$("#hide_backtrace").css("visibility", "hidden");
|
12
|
+
$("#backtrace").hide();
|
13
|
+
});
|
14
|
+
|
15
|
+
});
|
16
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* Skeleton V1.1
|
4
|
+
* Copyright 2011, Dave Gamache
|
5
|
+
* www.getskeleton.com
|
6
|
+
* Free to use under the MIT license.
|
7
|
+
* http://www.opensource.org/licenses/mit-license.php
|
8
|
+
* 8/17/2011
|
9
|
+
*/
|
10
|
+
|
11
|
+
(function ($) {
|
12
|
+
// hash change handler
|
13
|
+
function hashchange () {
|
14
|
+
var hash = window.location.hash
|
15
|
+
, el = $('ul.tabs [href*="' + hash + '"]')
|
16
|
+
, content = $(hash)
|
17
|
+
|
18
|
+
if (el.length && !el.hasClass('active') && content.length) {
|
19
|
+
el.closest('.tabs').find('.active').removeClass('active');
|
20
|
+
el.addClass('active');
|
21
|
+
content.show().addClass('active').siblings().hide().removeClass('active');
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
// listen on event and fire right away
|
26
|
+
$(window).on('hashchange.skeleton', hashchange);
|
27
|
+
hashchange();
|
28
|
+
$(hashchange);
|
29
|
+
})(jQuery);
|
@@ -0,0 +1,207 @@
|
|
1
|
+
/* From: http://css-tricks.com/perfect-full-page-background-image/ */
|
2
|
+
body {
|
3
|
+
background: url("/images/texture/straw-flowers.gif") repeat-x 0 29px #FCFBF0;
|
4
|
+
background: #FCFBF0;
|
5
|
+
background: #FAFAFA;
|
6
|
+
font-family: 'Oxygen', sans-serif;
|
7
|
+
padding: 0 40px 40px 40px;
|
8
|
+
}
|
9
|
+
|
10
|
+
div.container {
|
11
|
+
background: white;
|
12
|
+
-moz-border-radius: 0 0 10px 10px;
|
13
|
+
border-radius: 0 0 10px 10px;
|
14
|
+
}
|
15
|
+
|
16
|
+
#header, #footer {
|
17
|
+
background: url('/images/texture/9.jpg') grey;
|
18
|
+
padding: 5px 10px 10px 20px ;
|
19
|
+
}
|
20
|
+
|
21
|
+
#header, h1, h2, h3 {
|
22
|
+
font-family: 'Bree Serif', serif;
|
23
|
+
}
|
24
|
+
|
25
|
+
#footer {
|
26
|
+
color: lightgrey;
|
27
|
+
font-size: 80%;
|
28
|
+
-moz-border-radius: 0 0 10px 10px;
|
29
|
+
border-radius: 0 0 10px 10px;
|
30
|
+
min-height: 30px;
|
31
|
+
}
|
32
|
+
|
33
|
+
#header h1 {
|
34
|
+
font-size: 200%;
|
35
|
+
color: white;
|
36
|
+
}
|
37
|
+
|
38
|
+
body.unresolved #header h1 {
|
39
|
+
color: pink;
|
40
|
+
}
|
41
|
+
|
42
|
+
body.unresolved #header h1 span.message {
|
43
|
+
color: white;
|
44
|
+
}
|
45
|
+
|
46
|
+
body.resolved #header h1 {
|
47
|
+
color: white;
|
48
|
+
}
|
49
|
+
|
50
|
+
#nav {
|
51
|
+
padding: 0 20px;
|
52
|
+
font-size: small;
|
53
|
+
background: white;
|
54
|
+
}
|
55
|
+
|
56
|
+
#content {
|
57
|
+
padding: 10px 20px 20px 20px;
|
58
|
+
background: white;
|
59
|
+
}
|
60
|
+
|
61
|
+
a#hide_backtrace {
|
62
|
+
visibility: hidden;
|
63
|
+
}
|
64
|
+
|
65
|
+
div.line:hover, div.file:hover {
|
66
|
+
background: lightyellow;
|
67
|
+
}
|
68
|
+
|
69
|
+
#more_info div.key {
|
70
|
+
font-weight: bold;
|
71
|
+
text-align: right;
|
72
|
+
display: inline-block;
|
73
|
+
min-width: 200px;
|
74
|
+
}
|
75
|
+
|
76
|
+
#more_info div.val {
|
77
|
+
display: inline-block;
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
table {
|
82
|
+
margin: 10px 10px 10px 0;
|
83
|
+
}
|
84
|
+
|
85
|
+
table tr:first-child td:first-child {
|
86
|
+
-moz-border-radius: 5px 0 0 0;
|
87
|
+
border-radius: 5px 0 0 0;
|
88
|
+
}
|
89
|
+
|
90
|
+
table tr:first-child td:last-child {
|
91
|
+
-moz-border-radius: 0 5px 0 0;
|
92
|
+
border-radius: 0 5px 0 0;
|
93
|
+
}
|
94
|
+
|
95
|
+
table tr:last-child td:first-child {
|
96
|
+
-moz-border-radius: 0 0 0 5px;
|
97
|
+
border-radius: 0 0 0 5px;
|
98
|
+
}
|
99
|
+
|
100
|
+
table tr:last-child td:last-child {
|
101
|
+
-moz-border-radius: 0 0 5px 0;
|
102
|
+
border-radius: 0 0 5px 0;
|
103
|
+
}
|
104
|
+
|
105
|
+
td {
|
106
|
+
background: lightgrey;
|
107
|
+
padding: 8px;
|
108
|
+
}
|
109
|
+
|
110
|
+
td.key {
|
111
|
+
text-align: right;
|
112
|
+
font-weight: bold;
|
113
|
+
padding: 8px 10px 8px 10px;
|
114
|
+
}
|
115
|
+
|
116
|
+
td.val {
|
117
|
+
background: #FFFFE6;
|
118
|
+
}
|
119
|
+
|
120
|
+
span.exception {
|
121
|
+
font-weight: bold;
|
122
|
+
}
|
123
|
+
|
124
|
+
|
125
|
+
span.status, a.button, a:link {
|
126
|
+
display: inline-block;
|
127
|
+
padding: 5px 8px;
|
128
|
+
font-size: 8pt;
|
129
|
+
font-weight: bold;
|
130
|
+
text-transform: uppercase;
|
131
|
+
text-align: center;
|
132
|
+
-moz-border-radius: 5px;
|
133
|
+
border-radius: 5px;
|
134
|
+
color: white;
|
135
|
+
}
|
136
|
+
|
137
|
+
a:link {
|
138
|
+
color: #8C728A;
|
139
|
+
text-shadow: none;
|
140
|
+
}
|
141
|
+
|
142
|
+
a:visited {
|
143
|
+
color: #B87FB3;
|
144
|
+
}
|
145
|
+
|
146
|
+
a:hover {
|
147
|
+
color: pink;
|
148
|
+
background: grey;
|
149
|
+
}
|
150
|
+
|
151
|
+
span.status {
|
152
|
+
background: grey;
|
153
|
+
}
|
154
|
+
|
155
|
+
a.button:link {
|
156
|
+
background: lightgrey;
|
157
|
+
color: #B87FB3;
|
158
|
+
}
|
159
|
+
|
160
|
+
a.button:visited {
|
161
|
+
color: grey;
|
162
|
+
}
|
163
|
+
|
164
|
+
a.button:hover, a.button:active {
|
165
|
+
color: pink;
|
166
|
+
background: grey;
|
167
|
+
}
|
168
|
+
|
169
|
+
div.unresolved h1, span.unresolved {
|
170
|
+
color: pink;
|
171
|
+
}
|
172
|
+
|
173
|
+
div.resolved h1, span.resolved {
|
174
|
+
color: white;
|
175
|
+
}
|
176
|
+
|
177
|
+
span.num {
|
178
|
+
font-weight: bold;
|
179
|
+
}
|
180
|
+
|
181
|
+
div.line {
|
182
|
+
padding: 5px;
|
183
|
+
}
|
184
|
+
|
185
|
+
div.file {
|
186
|
+
margin: 0 0 10px 0;
|
187
|
+
font-weight: bold;
|
188
|
+
padding: 5px;
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
div.record_in_list {
|
193
|
+
padding: 2px 0;
|
194
|
+
}
|
195
|
+
|
196
|
+
#toggle_backtrace {
|
197
|
+
display: none;
|
198
|
+
}
|
199
|
+
|
200
|
+
h3 {
|
201
|
+
font-size: 150%;
|
202
|
+
margin-bottom: 10px;
|
203
|
+
}
|
204
|
+
#backtrace {
|
205
|
+
padding-left: 20px;
|
206
|
+
}
|
207
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
/* http://meyerweb.com/eric/tools/css/reset/
|
2
|
+
v2.0 | 20110126
|
3
|
+
License: none (public domain)
|
4
|
+
*/
|
5
|
+
|
6
|
+
html, body, div, span, applet, object, iframe,
|
7
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
8
|
+
a, abbr, acronym, address, big, cite, code,
|
9
|
+
del, dfn, em, img, ins, kbd, q, s, samp,
|
10
|
+
small, strike, strong, sub, sup, tt, var,
|
11
|
+
b, u, i, center,
|
12
|
+
dl, dt, dd, ol, ul, li,
|
13
|
+
fieldset, form, label, legend,
|
14
|
+
table, caption, tbody, tfoot, thead, tr, th, td,
|
15
|
+
article, aside, canvas, details, embed,
|
16
|
+
figure, figcaption, footer, header, hgroup,
|
17
|
+
menu, nav, output, ruby, section, summary,
|
18
|
+
time, mark, audio, video {
|
19
|
+
margin: 0;
|
20
|
+
padding: 0;
|
21
|
+
border: 0;
|
22
|
+
font-size: 100%;
|
23
|
+
font: inherit;
|
24
|
+
vertical-align: baseline;
|
25
|
+
}
|
26
|
+
/* HTML5 display-role reset for older browsers */
|
27
|
+
article, aside, details, figcaption, figure,
|
28
|
+
footer, header, hgroup, menu, nav, section {
|
29
|
+
display: block;
|
30
|
+
}
|
31
|
+
body {
|
32
|
+
line-height: 1;
|
33
|
+
}
|
34
|
+
ol, ul {
|
35
|
+
list-style: none;
|
36
|
+
}
|
37
|
+
blockquote, q {
|
38
|
+
quotes: none;
|
39
|
+
}
|
40
|
+
blockquote:before, blockquote:after,
|
41
|
+
q:before, q:after {
|
42
|
+
content: '';
|
43
|
+
content: none;
|
44
|
+
}
|
45
|
+
table {
|
46
|
+
border-collapse: collapse;
|
47
|
+
border-spacing: 0;
|
48
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
div.header! {
|
2
|
+
h1 {
|
3
|
+
span.exception vars[:title]
|
4
|
+
span.message vars[:message]
|
5
|
+
}
|
6
|
+
}
|
7
|
+
|
8
|
+
if vars[:app].request.path_info != '/'
|
9
|
+
div.nav! {
|
10
|
+
a(:href=>'/') { "Home"}
|
11
|
+
span.towards ">>"
|
12
|
+
span.location vars[:title]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
div.content! {
|
17
|
+
|
18
|
+
if vars[:app].request.path_info == '/'
|
19
|
+
table {
|
20
|
+
tr {
|
21
|
+
td.key "Database:"
|
22
|
+
td.val vars[:app].dex.db_name.to_s
|
23
|
+
}
|
24
|
+
tr {
|
25
|
+
td.key "Table:"
|
26
|
+
td.val vars[:app].dex.table_name.to_s
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
div.recent_nav! {
|
32
|
+
|
33
|
+
if vars[:prev_url]
|
34
|
+
a(:href=>vars[:prev_url]) { "<< Back in Time" }
|
35
|
+
end
|
36
|
+
|
37
|
+
if vars[:next_url]
|
38
|
+
a(:href=>vars[:next_url]) { "Forward In Time >>" }
|
39
|
+
end
|
40
|
+
|
41
|
+
}
|
42
|
+
|
43
|
+
if vars[:list].empty?
|
44
|
+
div.empty {
|
45
|
+
p "No exceptions created."
|
46
|
+
a.button( :href=>"/create-sample" ) {
|
47
|
+
"Create Sample Exception"
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
else
|
52
|
+
|
53
|
+
vars[:list].each { |db|
|
54
|
+
div.record_in_list {
|
55
|
+
p {
|
56
|
+
a.button(:href=>"/#{db[:id]}") {
|
57
|
+
"View"
|
58
|
+
}
|
59
|
+
span.time( vars[:app].human_time(db[:created_at]) )
|
60
|
+
span.status.send(vars[:app].status_to_word(db[:status]).downcase, vars[:app].status_to_word(db[:status]))
|
61
|
+
span.exception "#{db[:exception]}:"
|
62
|
+
span.message db[:message]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
end
|
68
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
xhtml_strict do
|
2
|
+
|
3
|
+
head {
|
4
|
+
|
5
|
+
link( :href=>'http://fonts.googleapis.com/css?family=Bree+Serif|Oxygen', :rel=>'stylesheet', :type=>'text/css' )
|
6
|
+
|
7
|
+
title vars[:title]
|
8
|
+
%w{ reset dex }.each { |name|
|
9
|
+
link(:rel=>"stylesheet", :href=>"/stylesheets/#{name}.css")
|
10
|
+
}
|
11
|
+
script(
|
12
|
+
:type=>"text/javascript",
|
13
|
+
:src=>"http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"
|
14
|
+
) {}
|
15
|
+
script(
|
16
|
+
:type=>"text/javascript",
|
17
|
+
:src=>"/javascripts/dex.js"
|
18
|
+
) {}
|
19
|
+
}
|
20
|
+
|
21
|
+
body(:class=>[vars[:status_class]].compact.join(' ')) {
|
22
|
+
|
23
|
+
eval File.read(vars[:view_file]), nil, vars[:view_file], 1
|
24
|
+
|
25
|
+
|
26
|
+
div.footer! {
|
27
|
+
if vars[:app].request.path_info == '/'
|
28
|
+
%~
|
29
|
+
<span>Background from: </span>
|
30
|
+
<a href="http://www.backgroundlabs.com" title="Background Labs"><img src="http://www.backgroundlabs.com/images/backgroundlabs88x15.gif" border="0" alt="Background Labs"/></a>
|
31
|
+
~
|
32
|
+
end
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
div.header! {
|
5
|
+
h1 {
|
6
|
+
span.exception "#{vars[:record][:exception]}:"
|
7
|
+
span.message vars[:record][:message]
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
if vars[:app].request.path_info != '/'
|
12
|
+
div.nav! {
|
13
|
+
a(:href=>'/') { "Home"}
|
14
|
+
span.towards ">>"
|
15
|
+
span.location vars[:title]
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
div.content! {
|
20
|
+
div {
|
21
|
+
span.status.send vars[:status_class].downcase, vars[:status_word]
|
22
|
+
a.button(:href=>"/#{vars[:record][:id]}/toggle") { "Toggle" }
|
23
|
+
}
|
24
|
+
|
25
|
+
unless vars[:table_keys].empty?
|
26
|
+
table {
|
27
|
+
vars[:table_keys].each { |k|
|
28
|
+
tr {
|
29
|
+
td.key k.inspect
|
30
|
+
td.val vars[:record][k]
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
div.toggle_backtrace! {
|
37
|
+
div {
|
38
|
+
a.button.show_backtrace!(:href=>"#show") { "Show Backtrace" }
|
39
|
+
}
|
40
|
+
div.omega {
|
41
|
+
a.button.hide_backtrace!(:href=>"#hide") { "Hide Backtrace" }
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
h3 "Backtrace:"
|
46
|
+
div.backtrace! {
|
47
|
+
vars[:record][:backtrace]
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
}
|
data/spec/Dex_App.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
describe "get /" do
|
3
|
+
|
4
|
+
behaves_like 'Test DB'
|
5
|
+
|
6
|
+
it "renders message when there are no exceptions" do
|
7
|
+
get "/"
|
8
|
+
should_render %!No exceptions!
|
9
|
+
end
|
10
|
+
|
11
|
+
it "renders a recent list of 10" do
|
12
|
+
5.times { |i| Dex.insert except("Err: #{i}") }
|
13
|
+
|
14
|
+
get "/"
|
15
|
+
5.times { |i|
|
16
|
+
last_response.body.should.match %r!Err: #{i}!
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
end # === get /
|
21
|
+
|
22
|
+
describe "get /recent/:num" do
|
23
|
+
|
24
|
+
behaves_like 'Test DB'
|
25
|
+
|
26
|
+
it "/recent/:num renders a list" do
|
27
|
+
n = rand(10) + 1
|
28
|
+
n.times { |i| Dex.insert except("N: #{i}") }
|
29
|
+
|
30
|
+
get "/recent/#{1}"
|
31
|
+
n.times { |i|
|
32
|
+
last_response.body.should.match %r!N: #{i}!
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
end # === get /recent/num
|
37
|
+
|
38
|
+
describe "get /:id" do
|
39
|
+
|
40
|
+
behaves_like 'Test DB'
|
41
|
+
|
42
|
+
it "displays record" do
|
43
|
+
id = Dex.insert(except "rand err")
|
44
|
+
get "/#{id}"
|
45
|
+
should_render "rand err"
|
46
|
+
end
|
47
|
+
|
48
|
+
end # === get /:id
|
49
|
+
|
50
|
+
describe "get /:id/toggle" do
|
51
|
+
|
52
|
+
behaves_like 'Test DB'
|
53
|
+
|
54
|
+
it "updates status of record" do
|
55
|
+
id = Dex.insert(except "rand err")
|
56
|
+
get "/#{id}/toggle"
|
57
|
+
Dex.filter(:id=>id).first[:status]
|
58
|
+
.should == 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it "redirects to /:id 302" do
|
62
|
+
id = Dex.insert(except "rand err")
|
63
|
+
get "/#{id}/toggle"
|
64
|
+
should_redirect_to "/#{id}", 302
|
65
|
+
end
|
66
|
+
|
67
|
+
end # === put /:id/status
|
68
|
+
|
69
|
+
describe "delete /:id" do
|
70
|
+
|
71
|
+
behaves_like 'Test DB'
|
72
|
+
|
73
|
+
it "deletes record" do
|
74
|
+
Dex.insert(except("Deleted"))
|
75
|
+
r = Dex.recent(1)
|
76
|
+
delete "/#{r[:id]}"
|
77
|
+
Dex.count.should == 0
|
78
|
+
end
|
79
|
+
|
80
|
+
it "redirects to / 303" do
|
81
|
+
Dex.insert(except("Deleted"))
|
82
|
+
r = Dex.recent(1)
|
83
|
+
delete "/#{r[:id]}"
|
84
|
+
should_redirect_to "/"
|
85
|
+
end
|
86
|
+
|
87
|
+
end # === delete /:id
|
88
|
+
|
data/spec/bin.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
bins = Dir.glob("bin/*")
|
3
|
+
|
4
|
+
unless bins.empty?
|
5
|
+
describe "permissions of bin/" do
|
6
|
+
bins.each { |file|
|
7
|
+
it "should chmod 755 for: #{file}" do
|
8
|
+
`stat -c %a #{file}`.strip
|
9
|
+
.should.be == "755"
|
10
|
+
end
|
11
|
+
}
|
12
|
+
end # === permissions of bin/
|
13
|
+
end # === unless bins.empty?
|
data/spec/lib/main.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.print e.message, "\n"
|
7
|
+
$stderr.print "Run `bundle install` to install missing gems\n"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'bacon'
|
11
|
+
|
12
|
+
Gem_Dir = File.expand_path( File.join(File.dirname(__FILE__) + '/../..') )
|
13
|
+
$LOAD_PATH.unshift Gem_Dir
|
14
|
+
$LOAD_PATH.unshift( Gem_Dir + "/lib" )
|
15
|
+
|
16
|
+
Bacon.summary_on_exit
|
17
|
+
|
18
|
+
require 'Bacon_Colored'
|
19
|
+
require 'pry'
|
20
|
+
require 'rack/test'
|
21
|
+
require 'Dex_Rack'
|
22
|
+
|
23
|
+
Dex_Rack.set :environment, :test
|
24
|
+
Dex_Rack.set :dex, ( Dex.db(":memory:") && Dex )
|
25
|
+
|
26
|
+
def except e
|
27
|
+
begin
|
28
|
+
raise e
|
29
|
+
rescue Object => e
|
30
|
+
$!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
shared "Test DB" do
|
35
|
+
before {
|
36
|
+
Dex.table.delete
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
class Bacon::Context
|
41
|
+
include Rack::Test::Methods
|
42
|
+
|
43
|
+
def app
|
44
|
+
Dex_Rack
|
45
|
+
end
|
46
|
+
|
47
|
+
def should_render txt
|
48
|
+
last_response.should.be.ok
|
49
|
+
r = txt.respond_to?(:~) ? txt : %r!#{txt}!
|
50
|
+
last_response.body.should.match r
|
51
|
+
end
|
52
|
+
|
53
|
+
def should_redirect_to url, status = 303
|
54
|
+
last_response.status.should == status
|
55
|
+
last_response['Location'].sub( %r!http://(www.)?example.(com|org)!, '' )
|
56
|
+
.should == url
|
57
|
+
end
|
58
|
+
|
59
|
+
end # === class Bacon::Context
|
60
|
+
|
61
|
+
|
62
|
+
# ======== Include the tests.
|
63
|
+
target_files = ARGV[1, ARGV.size - 1].select { |a| File.file?(a) }
|
64
|
+
|
65
|
+
if target_files.empty?
|
66
|
+
|
67
|
+
# include all files
|
68
|
+
Dir.glob('./spec/*.rb').each { |file|
|
69
|
+
require file.sub('.rb', '') if File.file?(file)
|
70
|
+
}
|
71
|
+
|
72
|
+
else
|
73
|
+
# Do nothing. Bacon grabs the file.
|
74
|
+
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Dex_Rack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- da99
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bacon
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: Bacon_Colored
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rack-test
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: sinatra
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: markaby
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: thin
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: sinatra-reloader
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :runtime
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: chronic_duration
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: Dex
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
type: :runtime
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
description: ! "\nIt is a web-frontend for Dex. \nUpdate and view exceptions logged
|
191
|
+
with the Dex gem.\n "
|
192
|
+
email:
|
193
|
+
- i-hate-spam-45671204@mailinator.com
|
194
|
+
executables: []
|
195
|
+
extensions: []
|
196
|
+
extra_rdoc_files: []
|
197
|
+
files:
|
198
|
+
- .gitignore
|
199
|
+
- Dex_Rack.gemspec
|
200
|
+
- Gemfile
|
201
|
+
- README.md
|
202
|
+
- Rakefile
|
203
|
+
- lib/Dex_Rack.rb
|
204
|
+
- lib/Dex_Rack/config.ru
|
205
|
+
- lib/Dex_Rack/public/favicon.ico
|
206
|
+
- lib/Dex_Rack/public/images/texture/9.jpg
|
207
|
+
- lib/Dex_Rack/public/javascripts/dex.js
|
208
|
+
- lib/Dex_Rack/public/javascripts/tabs.js
|
209
|
+
- lib/Dex_Rack/public/robots.txt
|
210
|
+
- lib/Dex_Rack/public/stylesheets/dex.css
|
211
|
+
- lib/Dex_Rack/public/stylesheets/reset.css
|
212
|
+
- lib/Dex_Rack/version.rb
|
213
|
+
- lib/Dex_Rack/views/index.rb
|
214
|
+
- lib/Dex_Rack/views/layout.rb
|
215
|
+
- lib/Dex_Rack/views/record.rb
|
216
|
+
- lib/Dex_Rack/views/record_404.rb
|
217
|
+
- spec/Dex_App.rb
|
218
|
+
- spec/bin.rb
|
219
|
+
- spec/lib/main.rb
|
220
|
+
homepage: https://github.com/da99/Dex_Rack
|
221
|
+
licenses: []
|
222
|
+
post_install_message:
|
223
|
+
rdoc_options: []
|
224
|
+
require_paths:
|
225
|
+
- lib
|
226
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
227
|
+
none: false
|
228
|
+
requirements:
|
229
|
+
- - ! '>='
|
230
|
+
- !ruby/object:Gem::Version
|
231
|
+
version: '0'
|
232
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ! '>='
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0'
|
238
|
+
requirements: []
|
239
|
+
rubyforge_project:
|
240
|
+
rubygems_version: 1.8.23
|
241
|
+
signing_key:
|
242
|
+
specification_version: 3
|
243
|
+
summary: A Rack app to display Dex exceptions.
|
244
|
+
test_files: []
|