Class: Resque::Plugins::Status::Hash

Inherits:
Hash
  • Object
show all
Defined in:
lib/resque/plugins/status/hash.rb

Overview

Resque::Plugins::Status::Hash is a Hash object that has helper methods for dealing with the common status attributes. It also has a number of class methods for creating/updating/retrieving status objects from Redis

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Hash

Create a new Resque::Plugins::Status::Hash object. If multiple arguments are passed it is assumed the first argument is the UUID and the rest are status objects. All arguments are subsequentily merged in order. Strings are assumed to be messages.



218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/resque/plugins/status/hash.rb', line 218

def initialize(*args)
  super nil
  base_status = {
    'time' => Time.now.to_i,
    'status' => Resque::Plugins::Status::STATUS_QUEUED
  }
  base_status['uuid'] = args.shift if args.length > 1
  status_hash = args.inject(base_status) do |final, m|
    m = {'message' => m} if m.is_a?(String)
    final.merge(m || {})
  end
  self.replace(status_hash)
end

Class Method Details

.clear(range_start = nil, range_end = nil) ⇒ Object

clear statuses from redis passing an optional range. See ‘statuses` for info about ranges



51
52
53
54
55
# File 'lib/resque/plugins/status/hash.rb', line 51

def self.clear(range_start = nil, range_end = nil)
  status_ids(range_start, range_end).each do |id|
    remove(id)
  end
end

.clear_completed(range_start = nil, range_end = nil) ⇒ Object



57
58
59
60
61
62
63
64
65
66
# File 'lib/resque/plugins/status/hash.rb', line 57

def self.clear_completed(range_start = nil, range_end = nil)
  status_ids(range_start, range_end).select do |id|
    if get(id).completed?
      remove(id)
      true
    else
      false
    end
  end
end

.clear_failed(range_start = nil, range_end = nil) ⇒ Object



68
69
70
71
72
73
74
75
76
77
# File 'lib/resque/plugins/status/hash.rb', line 68

def self.clear_failed(range_start = nil, range_end = nil)
  status_ids(range_start, range_end).select do |id|
    if get(id).failed?
      remove(id)
      true
    else
      false
    end
  end
end

.countObject



84
85
86
# File 'lib/resque/plugins/status/hash.rb', line 84

def self.count
  redis.zcard(set_key)
end

.create(uuid, *messages) ⇒ Object

Create a status, generating a new UUID, passing the message to the status Returns the UUID of the new status.



14
15
16
17
18
19
# File 'lib/resque/plugins/status/hash.rb', line 14

def self.create(uuid, *messages)
  set(uuid, *messages)
  redis.zadd(set_key, Time.now.to_i, uuid)
  redis.zremrangebyscore(set_key, 0, Time.now.to_i - @expire_in) if @expire_in
  uuid
end

.expire_inObject

The time in seconds that jobs and statuses should expire from Redis (after the last time they are touched/updated)



152
153
154
# File 'lib/resque/plugins/status/hash.rb', line 152

def self.expire_in
  @expire_in
end

.expire_in=(seconds) ⇒ Object

Set the expire_in time in seconds



157
158
159
# File 'lib/resque/plugins/status/hash.rb', line 157

def self.expire_in=(seconds)
  @expire_in = seconds.nil? ? nil : seconds.to_i
end

.generate_uuidObject



173
174
175
# File 'lib/resque/plugins/status/hash.rb', line 173

def self.generate_uuid
  SecureRandom.hex.to_s
end

.get(uuid) ⇒ Object

Get a status by UUID. Returns a Resque::Plugins::Status::Hash



22
23
24
25
# File 'lib/resque/plugins/status/hash.rb', line 22

def self.get(uuid)
  val = redis.get(status_key(uuid))
  val ? Resque::Plugins::Status::Hash.new(uuid, decode(val)) : nil
end

.hash_accessor(name, options = {}) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/resque/plugins/status/hash.rb', line 177

def self.hash_accessor(name, options = {})
  options[:default] ||= nil
  coerce = options[:coerce] ? ".#{options[:coerce]}" : ""
  module_eval <<-EOT
  def #{name}
    value = (self['#{name}'] ? self['#{name}']#{coerce} : #{options[:default].inspect})
    yield value if block_given?
    value
  end

  def #{name}=(value)
    self['#{name}'] = value
  end

  def #{name}?
    !!self['#{name}']
  end
  EOT
end

.kill(uuid) ⇒ Object

Kill the job at UUID on its next iteration this works by adding the UUID to a kill list (a.k.a. a list of jobs to be killed. Each iteration the job checks if it should be killed by calling tick or at. If so, it raises a Resque::Plugins::Status::Killed error and sets the status to ‘killed’.



117
118
119
# File 'lib/resque/plugins/status/hash.rb', line 117

def self.kill(uuid)
  redis.sadd(kill_key, uuid)
end

.kill_idsObject

Return the UUIDs of the jobs on the kill list



127
128
129
# File 'lib/resque/plugins/status/hash.rb', line 127

def self.kill_ids
  redis.smembers(kill_key)
end

.kill_keyObject



169
170
171
# File 'lib/resque/plugins/status/hash.rb', line 169

def self.kill_key
  "_kill"
end

.killall(range_start = nil, range_end = nil) ⇒ Object

Kills num jobs within range starting with the most recent first. By default kills all jobs. Note that the same conditions apply as kill, i.e. only jobs that check on each iteration by calling tick or at are eligible to killed.

Examples:

killing the last 20 submitted jobs

Resque::Plugins::Status::Hash.killall(0, 20)

Parameters:

  • range_start (Numeric) (defaults to: nil)

    The optional starting range

  • range_end (Numeric) (defaults to: nil)

    The optional ending range



139
140
141
142
143
# File 'lib/resque/plugins/status/hash.rb', line 139

def self.killall(range_start = nil, range_end = nil)
  status_ids(range_start, range_end).collect do |id|
    kill(id)
  end
end

.killed(uuid) ⇒ Object

Remove the job at UUID from the kill list



122
123
124
# File 'lib/resque/plugins/status/hash.rb', line 122

def self.killed(uuid)
  redis.srem(kill_key, uuid)
end

.mget(uuids) ⇒ Object

Get multiple statuses by UUID. Returns array of Resque::Plugins::Status::Hash



28
29
30
31
32
33
34
35
36
# File 'lib/resque/plugins/status/hash.rb', line 28

def self.mget(uuids)
  return [] if uuids.empty?
  status_keys = uuids.map{|u| status_key(u)}
  vals = redis.mget(*status_keys)

  uuids.zip(vals).map do |uuid, val|
    val ? Resque::Plugins::Status::Hash.new(uuid, decode(val)) : nil
  end
end

.remove(uuid) ⇒ Object



79
80
81
82
# File 'lib/resque/plugins/status/hash.rb', line 79

def self.remove(uuid)
  redis.del(status_key(uuid))
  redis.zrem(set_key, uuid)
end

.set(uuid, *messages) ⇒ Object

set a status by UUID. messages can be any number of strings or hashes that are merged in order to create a single status.



40
41
42
43
44
45
46
47
# File 'lib/resque/plugins/status/hash.rb', line 40

def self.set(uuid, *messages)
  val = Resque::Plugins::Status::Hash.new(uuid, *messages)
  redis.set(status_key(uuid), encode(val))
  if expire_in
    redis.expire(status_key(uuid), expire_in)
  end
  val
end

.set_keyObject



165
166
167
# File 'lib/resque/plugins/status/hash.rb', line 165

def self.set_key
  "_statuses"
end

.should_kill?(uuid) ⇒ Boolean

Check whether a job with UUID is on the kill list

Returns:

  • (Boolean)


146
147
148
# File 'lib/resque/plugins/status/hash.rb', line 146

def self.should_kill?(uuid)
  redis.sismember(kill_key, uuid)
end

.status_ids(range_start = nil, range_end = nil) ⇒ Object

Return the num most recent status/job UUIDs in reverse chronological order.



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/resque/plugins/status/hash.rb', line 100

def self.status_ids(range_start = nil, range_end = nil)
  if range_end && range_start
    # Because we want a reverse chronological order, we need to get a range starting
    # by the higest negative number. The ordering is transparent from the API user's
    # perspective so we need to convert the passed params
    (redis.zrevrange(set_key, (range_start.abs), ((range_end || 1).abs)) || [])
  else
    # Because we want a reverse chronological order, we need to get a range starting
    # by the higest negative number.
    redis.zrevrange(set_key, 0, -1) || []
  end
end

.status_key(uuid) ⇒ Object



161
162
163
# File 'lib/resque/plugins/status/hash.rb', line 161

def self.status_key(uuid)
  "status:#{uuid}"
end

.statuses(range_start = nil, range_end = nil) ⇒ Object

Return num Resque::Plugins::Status::Hash objects in reverse chronological order. By default returns the entire set.

Examples:

retuning the last 20 statuses

Resque::Plugins::Status::Hash.statuses(0, 20)

Parameters:

  • range_start (Numeric) (defaults to: nil)

    The optional starting range

  • range_end (Numeric) (defaults to: nil)

    The optional ending range



94
95
96
97
# File 'lib/resque/plugins/status/hash.rb', line 94

def self.statuses(range_start = nil, range_end = nil)
  ids = status_ids(range_start, range_end)
  mget(ids).compact || []
end

Instance Method Details

#inspectObject



276
277
278
# File 'lib/resque/plugins/status/hash.rb', line 276

def inspect
  "#<Resque::Plugins::Status::Hash #{super}>"
end

#jsonObject

Return a JSON representation of the current object.



270
271
272
273
274
# File 'lib/resque/plugins/status/hash.rb', line 270

def json
  h = self.dup
  h['pct_complete'] = pct_complete
  self.class.encode(h)
end

#killable?Boolean

Can the job be killed? failed, completed, and killed jobs can’t be killed, for obvious reasons

Returns:

  • (Boolean)


259
260
261
# File 'lib/resque/plugins/status/hash.rb', line 259

def killable?
  !failed? && !completed? && !killed?
end

#pct_completeObject

calculate the % completion of the job based on status, num and total



234
235
236
237
238
239
240
241
242
243
# File 'lib/resque/plugins/status/hash.rb', line 234

def pct_complete
  if completed?
    100
  elsif queued?
    0
  else
    t = (total == 0 || total.nil?) ? 1 : total
    (((num || 0).to_f / t.to_f) * 100).to_i
  end
end

#timeObject

Return the time of the status initialization. If set returns a Time object, otherwise returns nil



247
248
249
# File 'lib/resque/plugins/status/hash.rb', line 247

def time
  time? ? Time.at(self['time']) : nil
end

#to_json(*args) ⇒ Object



264
265
266
# File 'lib/resque/plugins/status/hash.rb', line 264

def to_json(*args)
  json
end