nfo-resque-mongo 1.15.1 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 %>