nfo-resque-mongo 1.15.1 → 1.17.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
1
- require 'net/https'
2
- require 'builder'
3
- require 'uri'
1
+ begin
2
+ require 'hoptoad_notifier'
3
+ rescue LoadError
4
+ raise "Can't find 'hoptoad_notifier' gem. Please add it to your Gemfile or install it."
5
+ end
4
6
 
5
7
  module Resque
6
8
  module Failure
@@ -10,27 +12,20 @@ module Resque
10
12
  #
11
13
  # require 'resque/failure/hoptoad'
12
14
  #
13
- # Resque::Failure::Hoptoad.configure do |config|
14
- # config.api_key = 'blah'
15
- # config.secure = true
15
+ # Resque::Failure::Multiple.classes = [Resque::Failure::Redis, Resque::Failure::Hoptoad]
16
+ # Resque::Failure.backend = Resque::Failure::Multiple
16
17
  #
17
- # # optional proxy support
18
- # config.proxy_host = 'x.y.z.t'
19
- # config.proxy_port = 8080
18
+ # Once you've configured resque to use the Hoptoad failure backend,
19
+ # you'll want to setup an initializer to configure the Hoptoad.
20
20
  #
21
- # # server env support, defaults to RAILS_ENV or RACK_ENV
22
- # config.server_environment = "test"
23
- # end
21
+ # HoptoadNotifier.configure do |config|
22
+ # config.api_key = 'your_key_here'
23
+ # end
24
+ # For more information see https://github.com/thoughtbot/hoptoad_notifier
24
25
  class Hoptoad < Base
25
- # From the hoptoad plugin
26
- INPUT_FORMAT = /^([^:]+):(\d+)(?::in `([^']+)')?$/
27
-
28
- class << self
29
- attr_accessor :secure, :api_key
30
- attr_accessor :proxy_host, :proxy_port, :proxy_user, :proxy_pass
31
- attr_accessor :server_environment
32
- attr_accessor :host, :port
33
- attr_accessor :http_read_timeout, :http_open_timeout
26
+ def self.configure(&block)
27
+ Resque::Failure.backend = self
28
+ HoptoadNotifier.configure(&block)
34
29
  end
35
30
 
36
31
  def self.count
@@ -39,101 +34,15 @@ module Resque
39
34
  Stat[:failed]
40
35
  end
41
36
 
42
- def self.configure
43
- yield self
44
- Resque::Failure.backend = self
45
- end
46
-
47
37
  def save
48
- http = use_ssl? ? :https : :http
49
- host = self.class.host || 'hoptoadapp.com'
50
- port = self.class.port
51
- url = URI.parse("#{http}://#{host}:#{port}/notifier_api/v2/notices/")
52
-
53
- request = Net::HTTP::Proxy self.class.proxy_host, self.class.proxy_port,
54
- self.class.proxy_user, self.class.proxy_pass
55
- http = request.new(url.host, url.port)
56
- headers = {
57
- 'Content-type' => 'text/xml',
58
- 'Accept' => 'text/xml, application/xml'
59
- }
60
-
61
- http.read_timeout = self.class.http_read_timeout || 5 # seconds
62
- http.open_timeout = self.class.http_open_timeout || 2 # seconds
63
-
64
- http.use_ssl = use_ssl?
65
-
66
- begin
67
- response = http.post(url.path, xml, headers)
68
- rescue TimeoutError => e
69
- log "Timeout while contacting the Hoptoad server."
70
- end
71
-
72
- case response
73
- when Net::HTTPSuccess then
74
- log "Hoptoad Success: #{response.class}"
75
- else
76
- body = response.body if response.respond_to? :body
77
- log "Hoptoad Failure: #{response.class}\n#{body}"
78
- end
38
+ HoptoadNotifier.notify_or_ignore(exception,
39
+ :parameters => {
40
+ :payload_class => payload['class'].to_s,
41
+ :payload_args => payload['args'].inspect
42
+ }
43
+ )
79
44
  end
80
45
 
81
- def xml
82
- x = Builder::XmlMarkup.new
83
- x.instruct!
84
- x.notice :version=>"2.0" do
85
- x.tag! "api-key", api_key
86
- x.notifier do
87
- x.name "Resqueue"
88
- x.version "0.1"
89
- x.url "http://github.com/defunkt/resque"
90
- end
91
- x.error do
92
- x.tag! "class", exception.class.name
93
- x.message "#{exception.class.name}: #{exception.message}"
94
- x.backtrace do
95
- fill_in_backtrace_lines(x)
96
- end
97
- end
98
- x.request do
99
- x.url queue.to_s
100
- x.component worker.to_s
101
- x.params do
102
- x.var :key=>"payload_class" do
103
- x.text! payload["class"].to_s
104
- end
105
- x.var :key=>"payload_args" do
106
- x.text! payload["args"].to_s
107
- end
108
- end
109
- end
110
- x.tag!("server-environment") do
111
- x.tag!("environment-name",server_environment)
112
- x.tag!("project-root", "RAILS_ROOT")
113
- end
114
-
115
- end
116
- end
117
-
118
- def fill_in_backtrace_lines(x)
119
- Array(exception.backtrace).each do |unparsed_line|
120
- _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
121
- x.line :file => file,:number => number
122
- end
123
- end
124
-
125
- def use_ssl?
126
- self.class.secure
127
- end
128
-
129
- def api_key
130
- self.class.api_key
131
- end
132
-
133
- def server_environment
134
- return self.class.server_environment if self.class.server_environment
135
- defined?(RAILS_ENV) ? RAILS_ENV : (ENV['RACK_ENV'] || 'development')
136
- end
137
46
  end
138
47
  end
139
48
  end
@@ -11,7 +11,7 @@ module Resque
11
11
  :payload => payload,
12
12
  :exception => exception.class.to_s,
13
13
  :error => exception.to_s,
14
- :backtrace => Array(exception.backtrace),
14
+ :backtrace => filter_backtrace(Array(exception.backtrace)),
15
15
  :worker => worker.to_s,
16
16
  :queue => queue
17
17
  }
@@ -87,6 +87,11 @@ module Resque
87
87
  item = all(index)
88
88
  Resque.mongo_failures.remove(:_id => item['_id'])
89
89
  end
90
+
91
+ def filter_backtrace(backtrace)
92
+ index = backtrace.index { |item| item.include?('/lib/resque/job.rb') }
93
+ backtrace.first(index.to_i)
94
+ end
90
95
  end
91
96
  end
92
97
  end
@@ -55,6 +55,10 @@ module Resque
55
55
  def self.requeue(*args)
56
56
  classes.first.requeue(*args)
57
57
  end
58
+
59
+ def self.remove(index)
60
+ classes.each { |klass| klass.remove(index) }
61
+ end
58
62
  end
59
63
  end
60
- end
64
+ end
@@ -0,0 +1,51 @@
1
+ module Resque
2
+ module Failure
3
+ # A Failure backend that stores exceptions in Redis. Very simple but
4
+ # works out of the box, along with support in the Resque web app.
5
+ class Redis < Base
6
+ def save
7
+ data = {
8
+ :failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S"),
9
+ :payload => payload,
10
+ :exception => exception.class.to_s,
11
+ :error => exception.to_s,
12
+ :backtrace => filter_backtrace(Array(exception.backtrace)),
13
+ :worker => worker.to_s,
14
+ :queue => queue
15
+ }
16
+ data = Resque.encode(data)
17
+ Resque.redis.rpush(:failed, data)
18
+ end
19
+
20
+ def self.count
21
+ Resque.redis.llen(:failed).to_i
22
+ end
23
+
24
+ def self.all(start = 0, count = 1)
25
+ Resque.list_range(:failed, start, count)
26
+ end
27
+
28
+ def self.clear
29
+ Resque.redis.del(:failed)
30
+ end
31
+
32
+ def self.requeue(index)
33
+ item = all(index)
34
+ item['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
35
+ Resque.redis.lset(:failed, index, Resque.encode(item))
36
+ Job.create(item['queue'], item['payload']['class'], *item['payload']['args'])
37
+ end
38
+
39
+ def self.remove(index)
40
+ id = rand(0xffffff)
41
+ Resque.redis.lset(:failed, index, id)
42
+ Resque.redis.lrem(:failed, 1, id)
43
+ end
44
+
45
+ def filter_backtrace(backtrace)
46
+ index = backtrace.index { |item| item.include?('/lib/resque/job.rb') }
47
+ backtrace.first(index.to_i)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,11 @@
1
+ require 'multi_json'
2
+
3
+ # OkJson won't work because it doesn't serialize symbols
4
+ # in the same way yajl and json do.
5
+ if MultiJson.engine.to_s == 'MultiJson::Engines::OkJson'
6
+ raise "Please install the yajl-ruby or json gem"
7
+ end
8
+
1
9
  module Resque
2
10
  # Methods used by various classes in Resque.
3
11
  module Helpers
@@ -23,29 +31,17 @@ module Resque
23
31
  # Given a Ruby object, returns a string suitable for storage in a
24
32
  # queue.
25
33
  def encode(object)
26
- if defined? Yajl
27
- Yajl::Encoder.encode(object)
28
- else
29
- object.to_json
30
- end
34
+ ::MultiJson.encode(object)
31
35
  end
32
36
 
33
37
  # Given a string, returns a Ruby object.
34
38
  def decode(object)
35
39
  return unless object
36
40
 
37
- if defined? Yajl
38
- begin
39
- Yajl::Parser.parse(object, :check_utf8 => false)
40
- rescue Yajl::ParseError => e
41
- raise DecodeException, e
42
- end
43
- else
44
- begin
45
- JSON.parse(object)
46
- rescue JSON::ParserError => e
47
- raise DecodeException, e
48
- end
41
+ begin
42
+ ::MultiJson.decode(object)
43
+ rescue ::MultiJson::DecodeError => e
44
+ raise DecodeException, e
49
45
  end
50
46
  end
51
47
 
data/lib/resque/plugin.rb CHANGED
@@ -47,5 +47,10 @@ module Resque
47
47
  def after_enqueue_hooks(job)
48
48
  job.methods.grep(/^after_enqueue/).sort
49
49
  end
50
+
51
+ # Given an object, returns a list `before_enqueue` hook names.
52
+ def before_enqueue_hooks(job)
53
+ job.methods.grep(/^before_enqueue/).sort
54
+ end
50
55
  end
51
56
  end
data/lib/resque/server.rb CHANGED
@@ -2,6 +2,7 @@ require 'sinatra/base'
2
2
  require 'erb'
3
3
  require 'resque'
4
4
  require 'resque/version'
5
+ require 'time'
5
6
 
6
7
  module Resque
7
8
  class Server < Sinatra::Base
@@ -118,6 +119,7 @@ module Resque
118
119
  end
119
120
 
120
121
  def show(page, layout = true)
122
+ response["Cache-Control"] = "max-age=0, private, must-revalidate"
121
123
  begin
122
124
  erb page.to_sym, {:layout => layout}, :resque => Resque
123
125
  rescue Errno::ECONNREFUSED
@@ -125,11 +127,27 @@ module Resque
125
127
  end
126
128
  end
127
129
 
130
+ def show_for_polling(page)
131
+ content_type "text/html"
132
+ @polling = true
133
+ show(page.to_sym, false).gsub(/\s{1,}/, ' ')
134
+ end
135
+
128
136
  # to make things easier on ourselves
129
137
  get "/?" do
130
138
  redirect url_path(:overview)
131
139
  end
132
140
 
141
+ %w( overview workers ).each do |page|
142
+ get "/#{page}.poll" do
143
+ show_for_polling(page)
144
+ end
145
+
146
+ get "/#{page}/:id.poll" do
147
+ show_for_polling(page)
148
+ end
149
+ end
150
+
133
151
  %w( overview queues working workers key ).each do |page|
134
152
  get "/#{page}" do
135
153
  show page
@@ -145,14 +163,6 @@ module Resque
145
163
  redirect u('queues')
146
164
  end
147
165
 
148
- %w( overview workers ).each do |page|
149
- get "/#{page}.poll" do
150
- content_type "text/html"
151
- @polling = true
152
- show(page.to_sym, false).gsub(/\s{1,}/, ' ')
153
- end
154
- end
155
-
156
166
  get "/failed" do
157
167
  if Resque::Failure.url
158
168
  redirect Resque::Failure.url
@@ -59,7 +59,7 @@ body { padding:0; margin:0; }
59
59
  #main table.stats th { font-size:100%; width:40%; color:#000;}
60
60
  #main hr { border:0; border-top:5px solid #efefef; margin:15px 0;}
61
61
 
62
- #footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
62
+ #footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
63
63
  #footer p a { color:#999;}
64
64
 
65
65
  #main p.poll { background:url(poll.png) no-repeat 0 2px; padding:3px 0; padding-left:23px; float:right; font-size:85%; }
@@ -73,7 +73,7 @@ body { padding:0; margin:0; }
73
73
  #main ul.failed li.hover dl dd .retried .remove { display:block; }
74
74
  #main ul.failed li dl dd .controls { display:none; float:right; }
75
75
  #main ul.failed li.hover dl dd .controls { display:block; }
76
- #main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%;}
76
+ #main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%; white-space: pre-wrap;}
77
77
  #main ul.failed li dl dd.error a {font-family:Monaco, "Courier New", monospace; font-size:90%; }
78
78
  #main ul.failed li dl dd.error pre { margin-top:3px; line-height:1.3;}
79
79
 
@@ -11,6 +11,7 @@
11
11
  <h1>Search Results</h1>
12
12
  <%end%>
13
13
  <% index = 0 %>
14
+ <% date_format = "%Y/%m/%d %T %z" %>
14
15
 
15
16
  <%unless failed.empty?%>
16
17
  <form method="POST" action="<%=u 'failed/clear'%>" class="clear-failed" style="display: none">
@@ -35,10 +36,10 @@
35
36
  <% else %>
36
37
  <dt>Worker</dt>
37
38
  <dd>
38
- <a href="<%= u(: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>
39
+ <a href="<%= u(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= Time.parse(job['failed_at']).strftime(date_format) %></span></b>
39
40
  <% if job['retried_at'] %>
40
41
  <div class='retried'>
41
- Retried <b><span class="time"><%= job['retried_at'] %></span></b>
42
+ Retried <b><span class="time"><%= Time.parse(job['retried_at']).strftime(date_format) %></span></b>
42
43
  <a href="<%= u "failed/remove/#{start + index - 1}" %>" class="remove" rel="remove">Remove</a>
43
44
  </div>
44
45
  <% else %>
@@ -4,7 +4,7 @@
4
4
 
5
5
  <h1>Pending jobs on <span class='hl'><%= queue %></span></h1>
6
6
  <form method="POST" action="<%=u "/queues/#{queue}/remove" %>" class='remove-queue'>
7
- <input type='submit' name='' value='Remove Queue' />
7
+ <input type='submit' name='' value='Remove Queue' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
8
8
  </form>
9
9
  <p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue)%></b> jobs</p>
10
10
  <table class='jobs'>
@@ -2,8 +2,13 @@
2
2
 
3
3
  <% if params[:key] %>
4
4
 
5
+ <h1>Not supported in resque-mongo yet</h1>
6
+ <% if false %><!-- No supported in resque-mongo -->
7
+
5
8
  <%= partial resque.redis.type(params[:key]).eql?("string") ? :key_string : :key_sets %>
6
9
 
10
+ <% end %>
11
+
7
12
  <% elsif params[:id] == "resque" %>
8
13
 
9
14
  <h1><%= resque %></h1>
@@ -22,6 +27,9 @@
22
27
 
23
28
  <% elsif params[:id] == 'redis' %>
24
29
 
30
+ <h1>Not supported in resque-mongo yet</h1>
31
+ <% if false %><!-- No supported in resque-mongo -->
32
+
25
33
  <h1><%= resque.redis_id %></h1>
26
34
  <table class='stats'>
27
35
  <% for key, value in resque.redis.info.to_a.sort_by { |i| i[0].to_s } %>
@@ -36,10 +44,14 @@
36
44
  <% end %>
37
45
  </table>
38
46
 
47
+ <% end %>
48
+
39
49
  <% elsif params[:id] == 'keys' %>
40
50
 
51
+ <h1>Not supported in resque-mongo yet</h1>
52
+ <% if false %><!-- No supported in resque-mongo -->
53
+
41
54
  <h1>Keys owned by <%= resque %></h1>
42
- <p class='sub'>(All keys are actually prefixed with "<%= Resque.redis.namespace %>:")</p>
43
55
  <table class='stats'>
44
56
  <tr>
45
57
  <th>key</th>
@@ -57,6 +69,8 @@
57
69
  <% end %>
58
70
  </table>
59
71
 
72
+ <% end %>
73
+
60
74
  <% else %>
61
75
 
62
76
  <% end %>