resque 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of resque might be problematic. Click here for more details.
- data/HISTORY.md +8 -1
- data/README.markdown +66 -1
- data/examples/demo/app.rb +1 -1
- data/examples/god/resque.god +52 -0
- data/examples/god/stale.god +26 -0
- data/lib/resque/failure.rb +6 -0
- data/lib/resque/failure/base.rb +4 -0
- data/lib/resque/failure/redis.rb +6 -1
- data/lib/resque/server.rb +26 -6
- data/lib/resque/server/public/idle.png +0 -0
- data/lib/resque/server/public/poll.png +0 -0
- data/lib/resque/server/public/ranger.js +14 -0
- data/lib/resque/server/public/reset.css +0 -3
- data/lib/resque/server/public/style.css +72 -64
- data/lib/resque/server/public/working.png +0 -0
- data/lib/resque/server/views/error.erb +1 -1
- data/lib/resque/server/views/failed.erb +35 -29
- data/lib/resque/server/views/layout.erb +15 -17
- data/lib/resque/server/views/next_more.erb +10 -12
- data/lib/resque/server/views/overview.erb +2 -0
- data/lib/resque/server/views/queues.erb +21 -15
- data/lib/resque/server/views/stats.erb +11 -11
- data/lib/resque/server/views/workers.erb +21 -15
- data/lib/resque/server/views/working.erb +10 -9
- data/lib/resque/version.rb +1 -1
- data/test/worker_test.rb +8 -0
- metadata +5 -9
- data/TODO.md +0 -60
- data/examples/existing_classes_as_jobs.rb +0 -3
- data/lib/resque/server/public/nav-bg.png +0 -0
- data/lib/resque/server/public/tab_b.gif +0 -0
- data/lib/resque/server/public/tab_r.gif +0 -0
- data/lib/resque/server/public/tabs.css +0 -189
data/HISTORY.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
-
##
|
1
|
+
## 1.1.0 (2009-11-04)
|
2
|
+
|
3
|
+
* Bugfix: Broken ERB tag in failure UI
|
4
|
+
* Bugfix: Save the worker's ID, not the worker itself, in the failure module
|
5
|
+
* Redesigned the sinatra web interface
|
6
|
+
* Added option to clear failed jobs
|
7
|
+
|
8
|
+
## 1.0.0 (2009-11-03)
|
2
9
|
|
3
10
|
* First release.
|
data/README.markdown
CHANGED
@@ -365,7 +365,7 @@ The Front End
|
|
365
365
|
Resque comes with a Sinatra-based front end for seeing what's up with
|
366
366
|
your queue.
|
367
367
|
|
368
|
-
![The Front End](http://img.skitch.com/
|
368
|
+
![The Front End](http://img.skitch.com/20091104-tqh5pgkwgbskjbk7qbtmpesnyw.jpg)
|
369
369
|
|
370
370
|
## Standalone
|
371
371
|
|
@@ -619,6 +619,69 @@ processed in the background.
|
|
619
619
|
Try it out by looking at the README, found at `examples/demo/README.markdown`.
|
620
620
|
|
621
621
|
|
622
|
+
Monitoring
|
623
|
+
----------
|
624
|
+
|
625
|
+
If you're using god to monitor Resque, we have provided example
|
626
|
+
configs in `examples/god/`. One is for starting / stopping workers,
|
627
|
+
the other is for killing workers that have been running too long.
|
628
|
+
|
629
|
+
|
630
|
+
Development
|
631
|
+
-----------
|
632
|
+
|
633
|
+
Want to hack on Resque?
|
634
|
+
|
635
|
+
First clone the repo and run the tests:
|
636
|
+
|
637
|
+
git clone git://github.com/defunkt/resque.git
|
638
|
+
cd resque
|
639
|
+
rake test
|
640
|
+
|
641
|
+
If the tests do not pass make sure you have Redis installed
|
642
|
+
correctly (though we make an effort to tell you if we feel this is the
|
643
|
+
case). The tests attempt to start an isolated instance of Redis to
|
644
|
+
run against.
|
645
|
+
|
646
|
+
Also make sure you've installed all the depenedencies correctly. For
|
647
|
+
example, try loading the `redis-namespace` gem after you've installed
|
648
|
+
it:
|
649
|
+
|
650
|
+
$ irb
|
651
|
+
>> require 'rubygems'
|
652
|
+
=> true
|
653
|
+
>> require 'redis/namespace'
|
654
|
+
=> true
|
655
|
+
|
656
|
+
If you get an error requiring any of the dependencies, you may have
|
657
|
+
failed to install them or be seeing load path issues.
|
658
|
+
|
659
|
+
Feel free to ping the mailing list with your problem and we'll try to
|
660
|
+
sort it out.
|
661
|
+
|
662
|
+
|
663
|
+
Contributing
|
664
|
+
------------
|
665
|
+
|
666
|
+
Once you've made your great commits:
|
667
|
+
|
668
|
+
1. [Fork](fk) Resque
|
669
|
+
2. Create a topic branch - `git checkout -b my_branch`
|
670
|
+
3. Push to your branch - `git push origin my_branch`
|
671
|
+
4. Create an [Issue](is) with a link to your branch
|
672
|
+
5. That's it!
|
673
|
+
|
674
|
+
|
675
|
+
Mailing List
|
676
|
+
------------
|
677
|
+
|
678
|
+
To join the list simply send an email to <resque@librelist.com>. This
|
679
|
+
will subscribe you and send you information about your subscription,
|
680
|
+
include unsubscribe information.
|
681
|
+
|
682
|
+
The archive can be found at <http://librelist.com/browser/>.
|
683
|
+
|
684
|
+
|
622
685
|
Meta
|
623
686
|
----
|
624
687
|
|
@@ -636,3 +699,5 @@ Author
|
|
636
699
|
Chris Wanstrath :: chris@ozmm.org :: @defunkt
|
637
700
|
|
638
701
|
[0]: http://github.com/blog/542-introducing-resque
|
702
|
+
[fk]: http://help.github.com/forking/
|
703
|
+
[is]: http://github.com/defunkt/resque/issues
|
data/examples/demo/app.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
rails_env = ENV['RAILS_ENV'] || "production"
|
2
|
+
rails_root = ENV['RAILS_ROOT'] || "/data/github/current"
|
3
|
+
num_workers = rails_env == 'production' ? 5 : 2
|
4
|
+
|
5
|
+
num_workers.times do |num|
|
6
|
+
God.watch do |w|
|
7
|
+
w.name = "resque-#{num}"
|
8
|
+
w.group = 'resque'
|
9
|
+
w.interval = 30.seconds
|
10
|
+
w.start = "env QUEUE=critical,high,low /usr/bin/rake -f #{rails_root}/Rakefile #{rails_env} resque:work"
|
11
|
+
|
12
|
+
w.uid = 'git'
|
13
|
+
w.gid = 'git'
|
14
|
+
|
15
|
+
# retart if memory gets too high
|
16
|
+
w.transition(:up, :restart) do |on|
|
17
|
+
on.condition(:memory_usage) do |c|
|
18
|
+
c.above = 350.megabytes
|
19
|
+
c.times = 2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# determine the state on startup
|
24
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
25
|
+
on.condition(:process_running) do |c|
|
26
|
+
c.running = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# determine when process has finished starting
|
31
|
+
w.transition([:start, :restart], :up) do |on|
|
32
|
+
on.condition(:process_running) do |c|
|
33
|
+
c.running = true
|
34
|
+
c.interval = 5.seconds
|
35
|
+
end
|
36
|
+
|
37
|
+
# failsafe
|
38
|
+
on.condition(:tries) do |c|
|
39
|
+
c.times = 5
|
40
|
+
c.transition = :start
|
41
|
+
c.interval = 5.seconds
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# start if process is not running
|
46
|
+
w.transition(:up, :start) do |on|
|
47
|
+
on.condition(:process_running) do |c|
|
48
|
+
c.running = false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# This will ride alongside god and kill any rogue stale worker
|
2
|
+
# processes. Their sacrifice is for the greater good.
|
3
|
+
|
4
|
+
WORKER_TIMEOUT = 60 * 10 # 10 minutes
|
5
|
+
|
6
|
+
Thread.new do
|
7
|
+
loop do
|
8
|
+
begin
|
9
|
+
`ps -e -o pid,command | grep [r]esque`.split("\n").each do |line|
|
10
|
+
parts = line.split(' ')
|
11
|
+
next if parts[-2] != "at"
|
12
|
+
started = parts[-1].to_i
|
13
|
+
elapsed = Time.now - Time.at(started)
|
14
|
+
|
15
|
+
if elapsed >= WORKER_TIMEOUT
|
16
|
+
::Process.kill('USR1', parts[0].to_i)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
rescue
|
20
|
+
# don't die because of stupid exceptions
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
sleep 30
|
25
|
+
end
|
26
|
+
end
|
data/lib/resque/failure.rb
CHANGED
data/lib/resque/failure/base.rb
CHANGED
data/lib/resque/failure/redis.rb
CHANGED
@@ -9,7 +9,7 @@ module Resque
|
|
9
9
|
:payload => payload,
|
10
10
|
:error => exception.to_s,
|
11
11
|
:backtrace => exception.backtrace,
|
12
|
-
:worker => worker,
|
12
|
+
:worker => worker.to_s,
|
13
13
|
:queue => queue
|
14
14
|
}
|
15
15
|
data = Resque.encode(data)
|
@@ -23,6 +23,11 @@ module Resque
|
|
23
23
|
def self.all(start = 0, count = 1)
|
24
24
|
Resque.list_range(:failed, start, count)
|
25
25
|
end
|
26
|
+
|
27
|
+
def self.clear
|
28
|
+
Resque.redis.delete('resque:failed')
|
29
|
+
end
|
30
|
+
|
26
31
|
end
|
27
32
|
end
|
28
33
|
end
|
data/lib/resque/server.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'erb'
|
3
3
|
require 'resque'
|
4
|
+
require 'resque/version'
|
4
5
|
|
5
6
|
module Resque
|
6
7
|
class Server < Sinatra::Base
|
@@ -80,13 +81,23 @@ module Resque
|
|
80
81
|
ensure
|
81
82
|
@partial = false
|
82
83
|
end
|
84
|
+
|
85
|
+
def poll
|
86
|
+
if @polling
|
87
|
+
text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
|
88
|
+
else
|
89
|
+
text = "<a href='#{url(request.path_info)}.poll' rel='poll'>Live Poll</a>"
|
90
|
+
end
|
91
|
+
"<p class='poll'>#{text}</p>"
|
92
|
+
end
|
93
|
+
|
83
94
|
end
|
84
95
|
|
85
|
-
def show(page)
|
96
|
+
def show(page, layout = true)
|
86
97
|
begin
|
87
|
-
erb page.to_sym, {}, :resque => Resque
|
98
|
+
erb page.to_sym, {:layout => layout}, :resque => Resque
|
88
99
|
rescue Errno::ECONNREFUSED
|
89
|
-
erb :error, {}, :error => "Can't connect to Redis! (#{Resque.redis.server})"
|
100
|
+
erb :error, {:layout => false}, :error => "Can't connect to Redis! (#{Resque.redis.server})"
|
90
101
|
end
|
91
102
|
end
|
92
103
|
|
@@ -104,6 +115,14 @@ module Resque
|
|
104
115
|
show page
|
105
116
|
end
|
106
117
|
end
|
118
|
+
|
119
|
+
%w( overview workers ).each do |page|
|
120
|
+
get "/#{page}.poll" do
|
121
|
+
content_type "text/plain"
|
122
|
+
@polling = true
|
123
|
+
show(page.to_sym, false).gsub(/\s{1,}/, ' ')
|
124
|
+
end
|
125
|
+
end
|
107
126
|
|
108
127
|
get "/failed" do
|
109
128
|
if Resque::Failure.url
|
@@ -112,9 +131,10 @@ module Resque
|
|
112
131
|
show :failed
|
113
132
|
end
|
114
133
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
134
|
+
|
135
|
+
post "/failed/clear" do
|
136
|
+
Resque::Failure.clear
|
137
|
+
redirect u('failed')
|
118
138
|
end
|
119
139
|
|
120
140
|
get "/stats" do
|
Binary file
|
Binary file
|
@@ -4,4 +4,18 @@ $(function() {
|
|
4
4
|
$(this).next().toggle()
|
5
5
|
return false
|
6
6
|
})
|
7
|
+
|
8
|
+
$('a[rel=poll]').click(function() {
|
9
|
+
var href = $(this).attr('href')
|
10
|
+
$(this).parent().text('Starting...')
|
11
|
+
$("#main").addClass('polling')
|
12
|
+
setInterval(function() {
|
13
|
+
$.ajax({dataType:'text', type:'get', url:href, success:function(data) {
|
14
|
+
$('#main').html(data)
|
15
|
+
$('#main .time').relatizeDate()
|
16
|
+
}})
|
17
|
+
}, 2 * 1000)
|
18
|
+
return false
|
19
|
+
})
|
20
|
+
|
7
21
|
})
|
@@ -14,7 +14,6 @@ table, caption, tbody, tfoot, thead, tr, th, td {
|
|
14
14
|
font-style: normal;
|
15
15
|
font-size: 100%;
|
16
16
|
font-family: inherit;
|
17
|
-
vertical-align: baseline;
|
18
17
|
}
|
19
18
|
|
20
19
|
:focus {
|
@@ -23,8 +22,6 @@ table, caption, tbody, tfoot, thead, tr, th, td {
|
|
23
22
|
|
24
23
|
body {
|
25
24
|
line-height: 1;
|
26
|
-
color: black;
|
27
|
-
background: white;
|
28
25
|
}
|
29
26
|
|
30
27
|
ul {
|
@@ -1,67 +1,75 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
/* font: normal 12px/1.5em "Helvetica Neue", Helvetica, Arial, sans-serif; */
|
4
|
-
}
|
5
|
-
h1, h2, h3, h4, h5 {
|
6
|
-
color: #333;
|
7
|
-
font: normal 12px/1.5em "Helvetica Neue", Helvetica, Arial, sans-serif;
|
8
|
-
text-shadow: 1px 1px #ddd;
|
9
|
-
}
|
10
|
-
h1 {
|
11
|
-
font-size: 1.5em;
|
12
|
-
}
|
13
|
-
h2 {
|
14
|
-
font-size: 1.0em;
|
15
|
-
}
|
16
|
-
h3 {
|
17
|
-
font-size: 0.7em;
|
18
|
-
}
|
19
|
-
table {
|
20
|
-
margin-bottom: 50px;
|
21
|
-
border-collapse: collapse;
|
22
|
-
}
|
23
|
-
th, td {
|
24
|
-
padding: 4px;
|
25
|
-
border-bottom: 1px solid #eee;
|
1
|
+
html { background:#efefef; font-family:Arial, Verdana, sans-serif; font-size:13px; }
|
2
|
+
body { padding:0; margin:0; }
|
26
3
|
|
27
|
-
}
|
28
|
-
|
29
|
-
|
30
|
-
}
|
31
|
-
|
32
|
-
|
33
|
-
font-family: Helvetica, Arial, sans-serif;
|
34
|
-
font-weight: bold;
|
35
|
-
text-align: left;
|
36
|
-
font-size: .9em;
|
37
|
-
padding: 0 15px 5px 15px;
|
38
|
-
}
|
39
|
-
a {
|
40
|
-
color: #59768A;
|
41
|
-
text-decoration: none;
|
42
|
-
font-size: .9em;
|
43
|
-
}
|
44
|
-
a:hover {
|
45
|
-
color: #000;
|
46
|
-
background-color: #e0f3ff;
|
47
|
-
}
|
48
|
-
a.queue {
|
49
|
-
text-transform: uppercase;
|
50
|
-
}
|
51
|
-
code {
|
52
|
-
font-size: 1.2em;
|
53
|
-
}
|
4
|
+
.header { background:#000; padding:8px 5% 0 5%; border-bottom:1px solid #444;border-bottom:5px solid #ce1212;}
|
5
|
+
.header h1 { color:#333; font-size:90%; font-weight:bold; margin-bottom:6px;}
|
6
|
+
.header ul li { display:inline;}
|
7
|
+
.header ul li a { color:#fff; text-decoration:none; margin-right:10px; display:inline-block; padding:8px; -webkit-border-top-right-radius:6px; -webkit-border-top-left-radius:6px; }
|
8
|
+
.header ul li a:hover { background:#333;}
|
9
|
+
.header ul li.current a { background:#ce1212; font-weight:bold; color:#fff;}
|
54
10
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
padding-right: 20px;
|
61
|
-
letter-spacing: 3px;
|
62
|
-
color: #333;
|
63
|
-
}
|
11
|
+
.subnav { padding:2px 5% 7px 5%; background:#ce1212; font-size:90%;}
|
12
|
+
.subnav li { display:inline;}
|
13
|
+
.subnav li a { color:#fff; text-decoration:none; margin-right:10px; display:inline-block; background:#dd5b5b; padding:5px; -webkit-border-radius:3px; -moz-border-radius:3px;}
|
14
|
+
.subnav li.current a { background:#fff; font-weight:bold; color:#ce1212;}
|
15
|
+
.subnav li a:active { background:#b00909;}
|
64
16
|
|
65
|
-
#main {
|
66
|
-
|
67
|
-
}
|
17
|
+
#main { padding:10px 5%; background:#fff; overflow:hidden; }
|
18
|
+
#main .logo { float:right; margin:10px;}
|
19
|
+
#main span.hl { background:#efefef; padding:2px;}
|
20
|
+
#main h1 { margin:10px 0; font-size:190%; font-weight:bold; color:#ce1212;}
|
21
|
+
#main h2 { margin:10px 0; font-size:130%;}
|
22
|
+
#main table { width:100%; margin:10px 0;}
|
23
|
+
#main table tr td, #main table tr th { border:1px solid #ccc; padding:6px;}
|
24
|
+
#main table tr th { background:#efefef; color:#888; font-size:80%; font-weight:bold;}
|
25
|
+
#main table tr td.no-data { text-align:center; padding:40px 0; color:#999; font-style:italic; font-size:130%;}
|
26
|
+
#main a { color:#111;}
|
27
|
+
#main p { margin:5px 0;}
|
28
|
+
#main p.intro { margin-bottom:15px; font-size:85%; color:#999; margin-top:0; line-height:1.3;}
|
29
|
+
#main h1.wi { margin-bottom:5px;}
|
30
|
+
#main p.sub { font-size:95%; color:#999;}
|
31
|
+
|
32
|
+
#main table.queues { width:40%;}
|
33
|
+
#main table.queues td.queue { font-weight:bold; width:50%;}
|
34
|
+
#main table.queues tr.failed td { background:#ffecec; border-top:2px solid #d37474; font-size:90%; color:#d37474;}
|
35
|
+
#main table.queues tr.failed td a{ color:#d37474;}
|
36
|
+
|
37
|
+
#main table.jobs td.class { font-family:Monaco, "Courier New", monospace; font-size:90%; width:50%;}
|
38
|
+
#main table.jobs td.args{ width:50%;}
|
39
|
+
|
40
|
+
#main table.workers td.icon {width:1%; background:#efefef;text-align:center;}
|
41
|
+
#main table.workers td.where { width:25%;}
|
42
|
+
#main table.workers td.queues { width:35%;}
|
43
|
+
#main .queue-tag { background:#b1d2e9; padding:2px; margin:0 3px; font-size:80%; text-decoration:none; text-transform:uppercase; font-weight:bold; color:#3274a2; -webkit-border-radius:4px; -moz-border-radius:4px;}
|
44
|
+
#main table.workers td.queues.queue { width:10%;}
|
45
|
+
#main table.workers td.process { width:35%;}
|
46
|
+
#main table.workers td.process span.waiting { color:#999; font-size:90%;}
|
47
|
+
#main table.workers td.process small { font-size:80%; margin-left:5px;}
|
48
|
+
#main table.workers td.process code { font-family:Monaco, "Courier New", monospace; font-size:90%;}
|
49
|
+
#main table.workers td.process small a { color:#999;}
|
50
|
+
#main.polling table.workers tr.working td { background:#f4ffe4; color:#7ac312;}
|
51
|
+
#main.polling table.workers tr.working td.where a { color:#7ac312;}
|
52
|
+
#main.polling table.workers tr.working td.process code { font-weight:bold;}
|
53
|
+
|
54
|
+
|
55
|
+
#main table.stats th { font-size:100%; width:40%; color:#000;}
|
56
|
+
#main hr { border:0; border-top:5px solid #efefef; margin:15px 0;}
|
57
|
+
|
58
|
+
#footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
|
59
|
+
#footer p a { color:#999;}
|
60
|
+
|
61
|
+
#main p.poll { background:url(poll.png) no-repeat 0 2px; padding:3px 0; padding-left:23px; float:right; font-size:85%; }
|
62
|
+
|
63
|
+
#main ul.failed {}
|
64
|
+
#main ul.failed li {background:-webkit-gradient(linear, left top, left bottom, from(#efefef), to(#fff)) #efefef; margin-top:10px; padding:10px; overflow:hidden; -webkit-border-radius:5px; border:1px solid #ccc; }
|
65
|
+
#main ul.failed li dl dt {font-size:80%; color:#999; width:60px; float:left; padding-top:1px; text-align:right;}
|
66
|
+
#main ul.failed li dl dd {margin-bottom:10px; margin-left:70px;}
|
67
|
+
#main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%;}
|
68
|
+
#main ul.failed li dl dd.error a {font-family:Monaco, "Courier New", monospace; font-size:90%; }
|
69
|
+
#main ul.failed li dl dd.error pre { margin-top:3px; line-height:1.3;}
|
70
|
+
|
71
|
+
#main p.pagination { background:#efefef; padding:10px; overflow:hidden;}
|
72
|
+
#main p.pagination a.less { float:left;}
|
73
|
+
#main p.pagination a.more { float:right;}
|
74
|
+
|
75
|
+
#main form.clear-failed {float:right; margin-top:-10px;}
|
Binary file
|
@@ -1 +1 @@
|
|
1
|
-
<h1><%= error %></h1>
|
1
|
+
<h1 style="font-size:110%;font-family:Arial, sans-serif;"><%= error %></h1>
|
@@ -1,29 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
<
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
<%start = params[:start].to_i %>
|
2
|
+
<%failed = Resque::Failure.all(start, 20)%>
|
3
|
+
|
4
|
+
<h1>Failed Jobs</h1>
|
5
|
+
<%unless failed.empty?%>
|
6
|
+
<form method="POST" action="<%=u 'failed/clear'%>" class='clear-failed'>
|
7
|
+
<input type='submit' name='' value='Clear Failed Jobs' />
|
8
|
+
</form>
|
9
|
+
<%end%>
|
10
|
+
|
11
|
+
<p class='sub'>Showing <%=start%> to <%= start + 20 %> of <b><%= size = Resque::Failure.count %></b> jobs</p>
|
12
|
+
|
13
|
+
<ul class='failed'>
|
14
|
+
<%for job in failed%>
|
15
|
+
<li>
|
16
|
+
<dl>
|
17
|
+
<dt>Worker</dt>
|
18
|
+
<dd><a href="<%= url(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= job['failed_at'] %></span></b></dd>
|
19
|
+
<dt>Class</dt>
|
20
|
+
<dd><code><%= job['payload']['class'] %></code></dd>
|
21
|
+
<dt>Arguments</dt>
|
22
|
+
<dd><pre><%=h show_args(job['payload']['args']) %></pre></dd>
|
23
|
+
<dt>Error</dt>
|
24
|
+
<dd class='error'>
|
25
|
+
<a href="#" class="backtrace"><%= h(job['error']) %></a>
|
26
|
+
<pre style='display:none'><%=h job['backtrace'].join("\n") %></pre>
|
27
|
+
</dd>
|
28
|
+
</dl>
|
29
|
+
<div class='r'>
|
30
|
+
</div>
|
31
|
+
</li>
|
32
|
+
<%end%>
|
33
|
+
</ul>
|
34
|
+
|
35
|
+
<%= partial :next_more, :start => start, :size => size %>
|
@@ -2,20 +2,15 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<title>Resque.</title>
|
5
|
+
<link href="<%=u 'reset.css' %>" media="screen" rel="stylesheet" type="text/css">
|
5
6
|
<link href="<%=u 'style.css' %>" media="screen" rel="stylesheet" type="text/css">
|
6
|
-
<link href="<%=u 'tabs.css' %>" media="screen" rel="stylesheet" type="text/css">
|
7
7
|
<script src="<%=u 'jquery-1.3.2.min.js' %>" type="text/javascript"</script>
|
8
8
|
<script src="<%=u 'jquery.relatize_date.js' %>" type="text/javascript"</script>
|
9
9
|
<script src="<%=u 'ranger.js' %>" type="text/javascript"></script>
|
10
10
|
</head>
|
11
11
|
<body>
|
12
|
-
|
13
|
-
<
|
14
|
-
<div class="tabs">
|
15
|
-
<div id="logo">
|
16
|
-
Resque
|
17
|
-
</div>
|
18
|
-
<ul>
|
12
|
+
<div class="header">
|
13
|
+
<ul class='nav'>
|
19
14
|
<%= tab "Overview" %>
|
20
15
|
<%= tab "Working" %>
|
21
16
|
<%= tab "Failed" %>
|
@@ -24,20 +19,23 @@
|
|
24
19
|
<%= tab "Stats" %>
|
25
20
|
</ul>
|
26
21
|
</div>
|
22
|
+
|
27
23
|
<% if @subtabs %>
|
28
|
-
<
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
</ul>
|
34
|
-
</div>
|
24
|
+
<ul class='subnav'>
|
25
|
+
<% for subtab in @subtabs %>
|
26
|
+
<li <%= class_if_current "#{current_section}/#{subtab}" %>><a href="<%= current_section %>/<%= subtab %>"><span><%= subtab %></span></a></li>
|
27
|
+
<% end %>
|
28
|
+
</ul>
|
35
29
|
<% end %>
|
36
|
-
|
37
|
-
|
30
|
+
|
38
31
|
<div id="main">
|
39
32
|
<%= yield %>
|
40
33
|
</div>
|
41
34
|
|
35
|
+
<div id="footer">
|
36
|
+
<p>Powered by <a href="http://github.com/defunkt/resque">Resque</a> v<%=Resque::Version%></p>
|
37
|
+
<p>Connected to Redis on <%=Resque.redis.server%></p>
|
38
|
+
</div>
|
39
|
+
|
42
40
|
</body>
|
43
41
|
</html>
|
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
</td>
|
12
|
-
</tr>
|
1
|
+
<%if start - 20 >= 0 || start + 20 <= size%>
|
2
|
+
<p class='pagination'>
|
3
|
+
<% if start - 20 >= 0 %>
|
4
|
+
<a href="<%= current_page %>?start=<%= start - 20 %>" class='less'>« less</a>
|
5
|
+
<% end %>
|
6
|
+
<% if start + 20 <= size %>
|
7
|
+
<a href="<%= current_page %>?start=<%= start + 20 %>" class='more'>more »</a>
|
8
|
+
<% end %>
|
9
|
+
</p>
|
10
|
+
<%end%>
|
@@ -2,38 +2,44 @@
|
|
2
2
|
|
3
3
|
<% if queue = params[:id] %>
|
4
4
|
|
5
|
-
<h1
|
6
|
-
<
|
7
|
-
<table>
|
5
|
+
<h1>Pending jobs on <span class='hl'><%= queue %></span></h1>
|
6
|
+
<p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue)%></b> jobs</p>
|
7
|
+
<table class='jobs'>
|
8
8
|
<tr>
|
9
9
|
<th>Class</th>
|
10
10
|
<th>Args</th>
|
11
11
|
</tr>
|
12
|
-
<% for job in resque.peek(queue, start, 20) %>
|
12
|
+
<% for job in (jobs = resque.peek(queue, start, 20)) %>
|
13
13
|
<tr>
|
14
|
-
<td><%= job['class'] %></td>
|
15
|
-
<td><%=h job['args'].inspect %></td>
|
14
|
+
<td class='class'><%= job['class'] %></td>
|
15
|
+
<td class='args'><%=h job['args'].inspect %></td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
<% if jobs.empty? %>
|
19
|
+
<tr>
|
20
|
+
<td class='no-data' colspan='2'>There are no pending jobs in this queue</td>
|
16
21
|
</tr>
|
17
22
|
<% end %>
|
18
|
-
<%= partial :next_more, :start => start, :size => size %>
|
19
23
|
</table>
|
24
|
+
<%= partial :next_more, :start => start, :size => size %>
|
20
25
|
<% else %>
|
21
26
|
|
22
|
-
<h1>Queues</h1>
|
23
|
-
<
|
27
|
+
<h1 class='wi'>Queues</h1>
|
28
|
+
<p class='intro'>The list below contains all the registered queues with the number of jobs currently in the queue. Select a queue from above to view all jobs currently pending on the queue.</p>
|
29
|
+
<table class='queues'>
|
24
30
|
<tr>
|
25
31
|
<th>Name</th>
|
26
|
-
<th>
|
32
|
+
<th>Jobs</th>
|
27
33
|
</tr>
|
28
34
|
<% for queue in resque.queues.sort_by { |q| q.to_s } %>
|
29
35
|
<tr>
|
30
|
-
<td><a class="queue" href="<%= url "queues/#{queue}" %>"><%= queue %></a></td>
|
31
|
-
<td><%= resque.size queue %></td>
|
36
|
+
<td class='queue'><a class="queue" href="<%= url "queues/#{queue}" %>"><%= queue %></a></td>
|
37
|
+
<td class='size'><%= resque.size queue %></td>
|
32
38
|
</tr>
|
33
39
|
<% end %>
|
34
|
-
<tr>
|
35
|
-
<td><a class="queue" href="<%= url :failed %>">failed</a></td>
|
36
|
-
<td><%= Resque::Failure.count %></td>
|
40
|
+
<tr class='failed'>
|
41
|
+
<td class='queue failed'><a class="queue" href="<%= url :failed %>">failed</a></td>
|
42
|
+
<td class='size'><%= Resque::Failure.count %></td>
|
37
43
|
</tr>
|
38
44
|
</table>
|
39
45
|
|
@@ -7,12 +7,12 @@
|
|
7
7
|
<% elsif params[:id] == "resque" %>
|
8
8
|
|
9
9
|
<h1><%= resque %></h1>
|
10
|
-
<table>
|
10
|
+
<table class='stats'>
|
11
11
|
<% for key, value in resque.info.to_a.sort_by { |i| i[0].to_s } %>
|
12
12
|
<tr>
|
13
|
-
<
|
13
|
+
<th>
|
14
14
|
<%= key %>
|
15
|
-
</
|
15
|
+
</th>
|
16
16
|
<td>
|
17
17
|
<%= value %>
|
18
18
|
</td>
|
@@ -22,13 +22,13 @@
|
|
22
22
|
|
23
23
|
<% elsif params[:id] == 'redis' %>
|
24
24
|
|
25
|
-
<h1><%= resque.redis %></h1>
|
26
|
-
<table>
|
25
|
+
<h1><%= resque.redis.server %></h1>
|
26
|
+
<table class='stats'>
|
27
27
|
<% for key, value in resque.redis.info.to_a.sort_by { |i| i[0].to_s } %>
|
28
28
|
<tr>
|
29
|
-
<
|
29
|
+
<th>
|
30
30
|
<%= key %>
|
31
|
-
</
|
31
|
+
</th>
|
32
32
|
<td>
|
33
33
|
<%= value %>
|
34
34
|
</td>
|
@@ -39,8 +39,8 @@
|
|
39
39
|
<% elsif params[:id] == 'keys' %>
|
40
40
|
|
41
41
|
<h1>Keys owned by <%= resque %></h1>
|
42
|
-
<
|
43
|
-
<table>
|
42
|
+
<p class='sub'>(All keys are actually prefixed with "resque:")</p>
|
43
|
+
<table class='stats'>
|
44
44
|
<tr>
|
45
45
|
<th>key</th>
|
46
46
|
<th>type</th>
|
@@ -48,9 +48,9 @@
|
|
48
48
|
</tr>
|
49
49
|
<% for key in resque.keys.sort %>
|
50
50
|
<tr>
|
51
|
-
<
|
51
|
+
<th>
|
52
52
|
<a href="/stats/keys/<%= key %>"><%= key %></a>
|
53
|
-
</
|
53
|
+
</th>
|
54
54
|
<td><%= resque.redis.type key %></td>
|
55
55
|
<td><%= redis_get_size key %></td>
|
56
56
|
</tr>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<% if params[:id] && worker = Resque::Worker.find(params[:id]) %>
|
2
2
|
|
3
3
|
<h1>Worker <%= worker %></h1>
|
4
|
-
<table>
|
4
|
+
<table class='workers'>
|
5
5
|
<tr>
|
6
6
|
<th> </th>
|
7
7
|
<th>Host</th>
|
@@ -13,22 +13,22 @@
|
|
13
13
|
<th>Processing</th>
|
14
14
|
</tr>
|
15
15
|
<tr>
|
16
|
-
<td><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
16
|
+
<td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
17
17
|
|
18
18
|
<% host, pid, queues = worker.to_s.split(':') %>
|
19
19
|
<td><%= host %></td>
|
20
20
|
<td><%= pid %></td>
|
21
21
|
<td><span class="time"><%= worker.started %></a></td>
|
22
|
-
<td><%= queues.split(',').map { |q| '<a class="queue" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('
|
22
|
+
<td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
|
23
23
|
<td><%= worker.processed %></td>
|
24
24
|
<td><%= worker.failed %></td>
|
25
|
-
<td>
|
25
|
+
<td class='process'>
|
26
26
|
<% data = worker.processing || {} %>
|
27
27
|
<% if data['queue'] %>
|
28
28
|
<code><%= data['payload']['class'] %></code>
|
29
29
|
<small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= data['run_at'] %></a></small>
|
30
30
|
<% else %>
|
31
|
-
Waiting for
|
31
|
+
<span class='waiting'>Waiting for a job...</span>
|
32
32
|
<% end %>
|
33
33
|
</td>
|
34
34
|
</tr>
|
@@ -40,33 +40,39 @@
|
|
40
40
|
|
41
41
|
<% else %>
|
42
42
|
|
43
|
-
<h1><%= resque.workers.size %> Workers</h1>
|
44
|
-
<
|
43
|
+
<h1 class='wi'><%= resque.workers.size %> Workers</h1>
|
44
|
+
<p class='intro'>The workers listed below are all registered as active on your system.</p>
|
45
|
+
<table class='workers'>
|
45
46
|
<tr>
|
46
47
|
<th> </th>
|
47
48
|
<th>Where</th>
|
48
49
|
<th>Queues</th>
|
49
50
|
<th>Processing</th>
|
50
51
|
</tr>
|
51
|
-
<% for worker in resque.workers.sort_by { |w| w.to_s } %>
|
52
|
-
<tr>
|
53
|
-
<td><img src="<%=u state
|
52
|
+
<% for worker in (workers = resque.workers.sort_by { |w| w.to_s }) %>
|
53
|
+
<tr class="<%=state = worker.state%>">
|
54
|
+
<td class='icon'><img src="<%=u state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
54
55
|
|
55
56
|
<% host, pid, queues = worker.to_s.split(':') %>
|
56
|
-
<td><a href="
|
57
|
-
<td><%= queues.split(',').map { |q| '<a class="queue" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('
|
57
|
+
<td class='where'><a href="<%=u "workers/#{worker}"%>"><%= host %>:<%= pid %></a></td>
|
58
|
+
<td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
|
58
59
|
|
59
|
-
<td>
|
60
|
+
<td class='process'>
|
60
61
|
<% data = worker.processing || {} %>
|
61
62
|
<% if data['queue'] %>
|
62
63
|
<code><%= data['payload']['class'] %></code>
|
63
64
|
<small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= data['run_at'] %></a></small>
|
64
65
|
<% else %>
|
65
|
-
Waiting for
|
66
|
+
<span class='waiting'>Waiting for a job...</span>
|
66
67
|
<% end %>
|
67
68
|
</td>
|
68
69
|
</tr>
|
69
70
|
<% end %>
|
71
|
+
<% if workers.empty? %>
|
72
|
+
<tr>
|
73
|
+
<td colspan='4' class='no-data'>There are no registered workers</td>
|
74
|
+
</tr>
|
75
|
+
<% end %>
|
70
76
|
</table>
|
71
|
-
|
77
|
+
<%=poll%>
|
72
78
|
<% end %>
|
@@ -28,8 +28,9 @@
|
|
28
28
|
<% else %>
|
29
29
|
|
30
30
|
<% workers = resque.working %>
|
31
|
-
<h1><%= workers.size %> of <%= resque.workers.size %> Workers Working</h1>
|
32
|
-
<
|
31
|
+
<h1 class='wi'><%= workers.size %> of <%= resque.workers.size %> Workers Working</h1>
|
32
|
+
<p class='intro'>The list below contains all workers which are currently running a job.</p>
|
33
|
+
<table class='workers'>
|
33
34
|
<tr>
|
34
35
|
<th> </th>
|
35
36
|
<th>Where</th>
|
@@ -38,25 +39,25 @@
|
|
38
39
|
</tr>
|
39
40
|
<% if workers.empty? %>
|
40
41
|
<tr>
|
41
|
-
<td colspan="4">
|
42
|
+
<td colspan="4" class='no-data'>Nothing is happening right now...</td>
|
42
43
|
</tr>
|
43
44
|
<% end %>
|
44
45
|
|
45
46
|
<% for worker in workers.sort_by { |w| w.job['run_at'] ? w.job['run_at'] : '' } %>
|
46
47
|
<% job = worker.job %>
|
47
48
|
<tr>
|
48
|
-
<td><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
49
|
+
<td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
49
50
|
<% host, pid, queues = worker.to_s.split(':') %>
|
50
|
-
<td><a href="<%=u "/workers/#{worker}" %>"><%= host %>:<%= pid %></a></td>
|
51
|
-
<td>
|
52
|
-
<a class="queue" href="<%=u "/queues/#{job['queue']}" %>"><%= job['queue'] %></a>
|
51
|
+
<td class='where'><a href="<%=u "/workers/#{worker}" %>"><%= host %>:<%= pid %></a></td>
|
52
|
+
<td class='queues queue'>
|
53
|
+
<a class="queue-tag" href="<%=u "/queues/#{job['queue']}" %>"><%= job['queue'] %></a>
|
53
54
|
</td>
|
54
|
-
<td>
|
55
|
+
<td class='process'>
|
55
56
|
<% if job['queue'] %>
|
56
57
|
<code><%= job['payload']['class'] %></code>
|
57
58
|
<small><a class="queue time" href="<%=u "/working/#{worker}" %>"><%= job['run_at'] %></a></small>
|
58
59
|
<% else %>
|
59
|
-
Waiting for
|
60
|
+
<span class='waiting'>Waiting for a job...</span>
|
60
61
|
<% end %>
|
61
62
|
</td>
|
62
63
|
</tr>
|
data/lib/resque/version.rb
CHANGED
data/test/worker_test.rb
CHANGED
@@ -22,6 +22,14 @@ context "Resque::Worker" do
|
|
22
22
|
assert_equal 10, Resque::Failure.all(0, 20).size
|
23
23
|
end
|
24
24
|
|
25
|
+
test "can clear failed jobs" do
|
26
|
+
Resque::Job.create(:jobs, BadJob)
|
27
|
+
@worker.work(0)
|
28
|
+
assert_equal 1, Resque::Failure.count
|
29
|
+
Resque::Failure.clear
|
30
|
+
assert_equal 0, Resque::Failure.count
|
31
|
+
end
|
32
|
+
|
25
33
|
test "catches exceptional jobs" do
|
26
34
|
Resque::Job.create(:jobs, BadJob)
|
27
35
|
Resque::Job.create(:jobs, BadJob)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Wanstrath
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-04 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -29,7 +29,6 @@ files:
|
|
29
29
|
- LICENSE
|
30
30
|
- README.markdown
|
31
31
|
- Rakefile
|
32
|
-
- TODO.md
|
33
32
|
- bin/resque
|
34
33
|
- bin/resque-web
|
35
34
|
- config.ru
|
@@ -40,7 +39,8 @@ files:
|
|
40
39
|
- examples/demo/app.rb
|
41
40
|
- examples/demo/config.ru
|
42
41
|
- examples/demo/job.rb
|
43
|
-
- examples/
|
42
|
+
- examples/god/resque.god
|
43
|
+
- examples/god/stale.god
|
44
44
|
- examples/instance.rb
|
45
45
|
- examples/simple.rb
|
46
46
|
- init.rb
|
@@ -56,13 +56,10 @@ files:
|
|
56
56
|
- lib/resque/server/public/idle.png
|
57
57
|
- lib/resque/server/public/jquery-1.3.2.min.js
|
58
58
|
- lib/resque/server/public/jquery.relatize_date.js
|
59
|
-
- lib/resque/server/public/
|
59
|
+
- lib/resque/server/public/poll.png
|
60
60
|
- lib/resque/server/public/ranger.js
|
61
61
|
- lib/resque/server/public/reset.css
|
62
62
|
- lib/resque/server/public/style.css
|
63
|
-
- lib/resque/server/public/tab_b.gif
|
64
|
-
- lib/resque/server/public/tab_r.gif
|
65
|
-
- lib/resque/server/public/tabs.css
|
66
63
|
- lib/resque/server/public/working.png
|
67
64
|
- lib/resque/server/views/error.erb
|
68
65
|
- lib/resque/server/views/failed.erb
|
@@ -119,6 +116,5 @@ test_files:
|
|
119
116
|
- examples/async_helper.rb
|
120
117
|
- examples/demo/app.rb
|
121
118
|
- examples/demo/job.rb
|
122
|
-
- examples/existing_classes_as_jobs.rb
|
123
119
|
- examples/instance.rb
|
124
120
|
- examples/simple.rb
|
data/TODO.md
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
Little Stuff
|
2
|
-
-----------
|
3
|
-
|
4
|
-
[ ] Show stale workers in red with a warning icon in the Sinatra app
|
5
|
-
|
6
|
-
|
7
|
-
Big Stuff
|
8
|
-
---------
|
9
|
-
|
10
|
-
### async
|
11
|
-
|
12
|
-
I want the `async` helper to be first class. Something like this:
|
13
|
-
|
14
|
-
class Repository < ActiveRecord::Base
|
15
|
-
include Resque::AsyncHelper
|
16
|
-
end
|
17
|
-
|
18
|
-
This adds an `async` instance method and a `perform` class method.
|
19
|
-
|
20
|
-
The `async` method looks like this:
|
21
|
-
|
22
|
-
def async(method, *args)
|
23
|
-
Resque.enqueue(self.class, id, method, *args)
|
24
|
-
end
|
25
|
-
|
26
|
-
The `perform` method looks like this:
|
27
|
-
|
28
|
-
def self.perform(id, method, *args)
|
29
|
-
find(id).send(method, *args)
|
30
|
-
end
|
31
|
-
|
32
|
-
Of course, you can define your own `self.perform` and have it still
|
33
|
-
work beautifully. We might override ours in GitHub to look like this:
|
34
|
-
|
35
|
-
def self.perform(id, method, *args)
|
36
|
-
return unless repo = cached_by_id(id)
|
37
|
-
repo.send(method, *args)
|
38
|
-
end
|
39
|
-
|
40
|
-
Then: `@repo.async(:update_disk_usage)` issues a job equivalent to:
|
41
|
-
|
42
|
-
Resque.enqueue(Repository, 44, :update_disk_usage)
|
43
|
-
|
44
|
-
Booyah.
|
45
|
-
|
46
|
-
|
47
|
-
### gem install
|
48
|
-
|
49
|
-
`gem install resque` should pull in yajl, redis, sinatra, rake, and rack
|
50
|
-
|
51
|
-
Do it like Unicorn does it.
|
52
|
-
|
53
|
-
### Parent / Child => Master / Workers
|
54
|
-
|
55
|
-
On an ideal setup (REE + Linux) you'll have 2N Resque processes
|
56
|
-
running at any time: N parents and N children.
|
57
|
-
|
58
|
-
We can cut this number down to N+1 by moving from a parent / child
|
59
|
-
architecture to a master / workers architecture.
|
60
|
-
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,189 +0,0 @@
|
|
1
|
-
/* tabs styles, based on http://www.alistapart.com/articles/slidingdoors */
|
2
|
-
|
3
|
-
DIV.tabs
|
4
|
-
{
|
5
|
-
float : left;
|
6
|
-
width : 100%;
|
7
|
-
background : url("tab_b.gif") repeat-x bottom;
|
8
|
-
margin-bottom : 4px;
|
9
|
-
}
|
10
|
-
|
11
|
-
DIV.tabs UL
|
12
|
-
{
|
13
|
-
margin : 0px;
|
14
|
-
padding-left : 10px;
|
15
|
-
list-style : none;
|
16
|
-
}
|
17
|
-
|
18
|
-
DIV.tabs LI, DIV.tabs FORM
|
19
|
-
{
|
20
|
-
display : inline;
|
21
|
-
margin : 0px;
|
22
|
-
padding : 0px;
|
23
|
-
}
|
24
|
-
|
25
|
-
DIV.tabs FORM
|
26
|
-
{
|
27
|
-
float : right;
|
28
|
-
}
|
29
|
-
|
30
|
-
DIV.tabs A
|
31
|
-
{
|
32
|
-
float : left;
|
33
|
-
background : url("tab_r.gif") no-repeat right top;
|
34
|
-
border-bottom : 1px solid #84B0C7;
|
35
|
-
font-size : 80%;
|
36
|
-
font-weight : bold;
|
37
|
-
text-decoration : none;
|
38
|
-
}
|
39
|
-
|
40
|
-
DIV.tabs A:hover
|
41
|
-
{
|
42
|
-
background-position: 100% -150px;
|
43
|
-
}
|
44
|
-
|
45
|
-
DIV.tabs A:link, DIV.tabs A:visited,
|
46
|
-
DIV.tabs A:active, DIV.tabs A:hover
|
47
|
-
{
|
48
|
-
color: #1A419D;
|
49
|
-
}
|
50
|
-
|
51
|
-
DIV.tabs SPAN
|
52
|
-
{
|
53
|
-
float : left;
|
54
|
-
display : block;
|
55
|
-
background : url("tab_l.gif") no-repeat left top;
|
56
|
-
padding : 5px 9px;
|
57
|
-
white-space : nowrap;
|
58
|
-
}
|
59
|
-
|
60
|
-
DIV.tabs INPUT
|
61
|
-
{
|
62
|
-
float : right;
|
63
|
-
display : inline;
|
64
|
-
font-size : 1em;
|
65
|
-
}
|
66
|
-
|
67
|
-
DIV.tabs TD
|
68
|
-
{
|
69
|
-
font-size : 80%;
|
70
|
-
font-weight : bold;
|
71
|
-
text-decoration : none;
|
72
|
-
}
|
73
|
-
|
74
|
-
/* Commented Backslash Hack hides rule from IE5-Mac \*/
|
75
|
-
DIV.tabs SPAN {float : none;}
|
76
|
-
/* End IE5-Mac hack */
|
77
|
-
|
78
|
-
DIV.tabs A:hover SPAN
|
79
|
-
{
|
80
|
-
background-position: 0% -150px;
|
81
|
-
}
|
82
|
-
|
83
|
-
DIV.tabs LI.current A
|
84
|
-
{
|
85
|
-
background-position: 100% -150px;
|
86
|
-
border-width : 0px;
|
87
|
-
}
|
88
|
-
|
89
|
-
DIV.tabs LI.current SPAN
|
90
|
-
{
|
91
|
-
background-position: 0% -150px;
|
92
|
-
padding-bottom : 6px;
|
93
|
-
}
|
94
|
-
|
95
|
-
DIV.navpath
|
96
|
-
{
|
97
|
-
background : none;
|
98
|
-
border : none;
|
99
|
-
border-bottom : 1px solid #84B0C7;
|
100
|
-
text-align : center;
|
101
|
-
margin : 2px;
|
102
|
-
padding : 2px;
|
103
|
-
}
|
104
|
-
div.tabs {
|
105
|
-
background: url(nav-bg.png) repeat-x !important;
|
106
|
-
margin: 0 !important;
|
107
|
-
margin-bottom: 0 !important;
|
108
|
-
padding: 8px 0;
|
109
|
-
float: none;
|
110
|
-
border-bottom: 1px solid #A4B0BC;
|
111
|
-
font-size: 100%;
|
112
|
-
}
|
113
|
-
|
114
|
-
div.tabs li {
|
115
|
-
line-height: 100%;
|
116
|
-
display: block;
|
117
|
-
float: left;
|
118
|
-
margin-right: 10px;
|
119
|
-
}
|
120
|
-
|
121
|
-
div.tabs a {
|
122
|
-
background: none;
|
123
|
-
font-size: 100%;
|
124
|
-
padding: 5px 8px;
|
125
|
-
text-shadow: 1px 1px #eff4f7;
|
126
|
-
}
|
127
|
-
|
128
|
-
div.tabs span {
|
129
|
-
background: none;
|
130
|
-
padding: 0;
|
131
|
-
}
|
132
|
-
|
133
|
-
div.tabs li.current a {
|
134
|
-
background: #bdcad4;
|
135
|
-
-webkit-border-radius: 4px;
|
136
|
-
-moz-border-radius: 4px;
|
137
|
-
-o-border-radius: 4px;
|
138
|
-
border-radius: 4px;
|
139
|
-
text-shadow: none;
|
140
|
-
}
|
141
|
-
|
142
|
-
div.tabs li.current span {
|
143
|
-
padding: 0;
|
144
|
-
}
|
145
|
-
|
146
|
-
div.tabs a:link, div.tabs a:visited, div.tabs a:active, div.tabs a:hover
|
147
|
-
{
|
148
|
-
color: #546e80;
|
149
|
-
border: none;
|
150
|
-
}
|
151
|
-
|
152
|
-
div.tabs ul:after {
|
153
|
-
content: ".";
|
154
|
-
display: block;
|
155
|
-
clear: both;
|
156
|
-
height: 0;
|
157
|
-
visibility: hidden;
|
158
|
-
}
|
159
|
-
|
160
|
-
div.tabs + div.tabs {
|
161
|
-
background: #f2f6f9 !important;
|
162
|
-
border-bottom: 1px solid #e2ecf3 !important;
|
163
|
-
}
|
164
|
-
|
165
|
-
div.navpath {
|
166
|
-
line-height: 100%;
|
167
|
-
text-align: left;
|
168
|
-
background: #f2f6f9 !important;
|
169
|
-
border-bottom: 1px solid #e2ecf3 !important;
|
170
|
-
padding: 0;
|
171
|
-
margin: 0;
|
172
|
-
}
|
173
|
-
|
174
|
-
div.navpath:after {
|
175
|
-
content: ".";
|
176
|
-
display: block;
|
177
|
-
visibility: hidden;
|
178
|
-
clear: both;
|
179
|
-
padding: 0;
|
180
|
-
margin: 0;
|
181
|
-
height: 0;
|
182
|
-
}
|
183
|
-
|
184
|
-
div.navpath a {
|
185
|
-
display: block;
|
186
|
-
float: left;
|
187
|
-
padding: 8px 20px;
|
188
|
-
color: #8c99a3;
|
189
|
-
}
|