resque-meta 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Lee Marlow
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ Software), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ Resque Meta
2
+ ===========
3
+
4
+ A [Resque][rq] plugin. Requires Resque 1.8.
5
+
6
+ If you want to be able to add metadata for a job
7
+ to track anything you want, extend it with this module.
8
+
9
+ For example:
10
+
11
+ class MyJob
12
+ extend Resque::Jobs::Meta
13
+
14
+ def self.perform(meta_id, *args)
15
+ heavy_lifting
16
+ end
17
+ end
18
+
19
+ meta0 = MyJob.enqueue('stuff')
20
+ meta0.enqueued_at # => 'Wed May 19 13:42:41 -0600 2010'
21
+ meta0.meta_id # => '03c9e1a045ad012dd20500264a19273c'
22
+ meta0['foo'] = 'bar' # => 'bar'
23
+
24
+ # later
25
+ meta1 = MyJob.get_meta('03c9e1a045ad012dd20500264a19273c')
26
+ meta1.job_class # => MyJob
27
+ meta1.enqueued_at # => 'Wed May 19 13:42:41 -0600 2010'
28
+ meta1['foo'] # => 'bar'
29
+
30
+ [rq]: http://github.com/defunkt/resque
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+
4
+ def command?(command)
5
+ system("type #{command} > /dev/null")
6
+ end
7
+
8
+ #
9
+ # Tests
10
+ #
11
+
12
+ task :default => :test
13
+
14
+ if command? :turn
15
+ desc "Run tests"
16
+ task :test do
17
+ suffix = "-n #{ENV['TEST']}" if ENV['TEST']
18
+ sh "turn test/*_test.rb #{suffix}"
19
+ end
20
+ else
21
+ Rake::TestTask.new do |t|
22
+ t.libs << 'lib'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+ end
27
+
28
+ #
29
+ # Gems
30
+ #
31
+
32
+ begin
33
+ require 'mg'
34
+ MG.new("resque-meta.gemspec")
35
+ rescue LoadError
36
+ warn "mg not available."
37
+ warn "Install it with: gem i mg"
38
+ end
@@ -0,0 +1,101 @@
1
+ require 'digest/sha1'
2
+ require 'resque'
3
+ require 'resque/plugins/meta/version'
4
+ require 'resque/plugins/meta/metadata'
5
+
6
+ module Resque
7
+ module Plugins
8
+ # If you want to be able to add metadata for a job
9
+ # to track anything you want, extend it with this module.
10
+ #
11
+ # For example:
12
+ #
13
+ # class MyJob
14
+ # extend Resque::Jobs::Meta
15
+ #
16
+ # def self.perform(meta_id, *args)
17
+ # heavy_lifting
18
+ # end
19
+ # end
20
+ #
21
+ # meta0 = MyJob.enqueue('stuff')
22
+ # meta0.enqueued_at # => 'Wed May 19 13:42:41 -0600 2010'
23
+ # meta0.meta_id # => '03c9e1a045ad012dd20500264a19273c'
24
+ # meta0['foo'] = 'bar' # => 'bar'
25
+ #
26
+ # # later
27
+ # meta1 = MyJob.get_meta('03c9e1a045ad012dd20500264a19273c')
28
+ # meta1.job_class # => MyJob
29
+ # meta1.enqueued_at # => 'Wed May 19 13:42:41 -0600 2010'
30
+ # meta1['foo'] # => 'bar'
31
+ module Meta
32
+
33
+ # Override in your job to control the metadata id. It is
34
+ # passed the same arguments as `perform`, that is, your job's
35
+ # payload.
36
+ def meta_id(*args)
37
+ Digest::SHA1.hexdigest([ Time.now.to_i, self, args ].join)
38
+ end
39
+
40
+ # Override in your job to control the how many seconds a job's
41
+ # metadata will live after it finishes. Defaults to 24 hours.
42
+ # Return nil or 0 to set them to never expire.
43
+ def expire_meta_in
44
+ 24 * 60 * 60
45
+ end
46
+
47
+ # Enqueues a job in Resque and return the association metadata.
48
+ # The meta_id in the returned object can be used to fetch the
49
+ # metadata again in the future.
50
+ def enqueue(*args)
51
+ meta = Metadata.new({'meta_id' => meta_id(args), 'job_class' => self.to_s})
52
+ meta.save
53
+ Resque.enqueue(self, meta.meta_id, *args)
54
+ meta
55
+ end
56
+
57
+ def store_meta(meta)
58
+ key = "meta:#{meta.meta_id}"
59
+ json = Resque.encode(meta.data)
60
+ Resque.redis.set(key, json)
61
+ Resque.redis.expireat("resque:#{key}", meta.expire_at) if meta.expire_at > 0
62
+ meta
63
+ end
64
+
65
+ # Retrieve the metadata for a given job. If you call this
66
+ # from a class that extends Meta, then the metadata will
67
+ # only be returned if the metadata for that id is for the
68
+ # same class. Explicitly, calling Meta.get_meta(some_id)
69
+ # will return the metadata for a job of any type.
70
+ def get_meta(meta_id)
71
+ key = "meta:#{meta_id}"
72
+ if json = Resque.redis.get(key)
73
+ data = Resque.decode(json)
74
+ if self == Meta || self.to_s == data['job_class']
75
+ Metadata.new(data)
76
+ end
77
+ end
78
+ end
79
+ module_function :get_meta
80
+ public :get_meta
81
+
82
+ def before_perform_meta(meta_id, *args)
83
+ if meta = get_meta(meta_id)
84
+ meta.start!
85
+ end
86
+ end
87
+
88
+ def after_perform_meta(meta_id, *args)
89
+ if meta = get_meta(meta_id)
90
+ meta.finish!
91
+ end
92
+ end
93
+
94
+ def on_failure_meta(e, meta_id, *args)
95
+ if meta = get_meta(meta_id)
96
+ meta.fail!
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,123 @@
1
+ require 'time'
2
+
3
+ module Resque
4
+ module Plugins
5
+ module Meta
6
+ class Metadata
7
+ attr_reader :job_class, :meta_id, :data, :enqueued_at, :expire_in
8
+
9
+ def initialize(data_hash)
10
+ data_hash['enqueued_at'] ||= to_time_format_str(Time.now)
11
+ @data = data_hash
12
+ @meta_id = data_hash['meta_id'].dup
13
+ @enqueued_at = from_time_format_str('enqueued_at')
14
+ @job_class = data_hash['job_class']
15
+ if @job_class.is_a?(String)
16
+ @job_class = Resque.constantize(data_hash['job_class'])
17
+ else
18
+ data_hash['job_class'] = @job_class.to_s
19
+ end
20
+ @expire_in = @job_class.expire_meta_in || 0
21
+ end
22
+
23
+ def reload!
24
+ if new_meta = job_class.get_meta(meta_id)
25
+ @data = new_meta.data
26
+ end
27
+ self
28
+ end
29
+
30
+ def save
31
+ job_class.store_meta(self)
32
+ self
33
+ end
34
+
35
+ def [](key)
36
+ data[key]
37
+ end
38
+
39
+ def []=(key, val)
40
+ data[key.to_s] = val
41
+ end
42
+
43
+ def start!
44
+ self['started_at'] = to_time_format_str(Time.now)
45
+ save
46
+ end
47
+
48
+ def started_at
49
+ from_time_format_str('started_at')
50
+ end
51
+
52
+ def finish!
53
+ data['succeeded'] = true unless data.has_key?('succeeded')
54
+ self['finished_at'] = to_time_format_str(Time.now)
55
+ save
56
+ end
57
+
58
+ def finished_at
59
+ from_time_format_str('finished_at')
60
+ end
61
+
62
+ def expire_at
63
+ if finished? && expire_in > 0
64
+ finished_at.to_i + expire_in
65
+ else
66
+ 0
67
+ end
68
+ end
69
+
70
+ def enqueued?
71
+ started_at ? false : true
72
+ end
73
+
74
+ def working?
75
+ started_at && !finished_at ? true : false
76
+ end
77
+
78
+ def started?
79
+ started_at ? true :false
80
+ end
81
+
82
+ def finished?
83
+ finished_at ? true : false
84
+ end
85
+
86
+ def fail!
87
+ self['succeeded'] = false
88
+ finish!
89
+ end
90
+
91
+ def succeeded?
92
+ finished? ? self['succeeded'] : nil
93
+ end
94
+
95
+ def failed?
96
+ finished? ? !self['succeeded'] : nil
97
+ end
98
+
99
+ def seconds_enqueued
100
+ (started? ? started_at : Time.now.to_i) - enqueued_at.to_i
101
+ end
102
+
103
+ def seconds_processing
104
+ if started?
105
+ (finished? ? finished_at : Time.now.to_i) - started_at.to_i
106
+ else
107
+ 0
108
+ end
109
+ end
110
+
111
+ protected
112
+
113
+ def from_time_format_str(key)
114
+ (t = self[key]) && Time.parse(t)
115
+ end
116
+
117
+ def to_time_format_str(time)
118
+ time.utc.iso8601(6)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,7 @@
1
+ module Resque
2
+ module Plugins
3
+ module Meta
4
+ Version = '1.0.0'
5
+ end
6
+ end
7
+ end
data/test/meta_test.rb ADDED
@@ -0,0 +1,155 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require 'resque/plugins/meta'
3
+
4
+ class MetaJob
5
+ extend Resque::Plugins::Meta
6
+ @queue = :test
7
+
8
+ def self.expire_meta_in
9
+ 1
10
+ end
11
+
12
+ def self.perform(meta_id, key, val)
13
+ meta = get_meta(meta_id)
14
+ meta[key] = val
15
+ meta.save
16
+ end
17
+ end
18
+
19
+ class AnotherJob
20
+ extend Resque::Plugins::Meta
21
+ @queue = :test
22
+
23
+ def self.perform(meta_id)
24
+ end
25
+ end
26
+
27
+ class SlowJob
28
+ extend Resque::Plugins::Meta
29
+ @queue = :test
30
+
31
+ def self.expire_meta_in
32
+ 1
33
+ end
34
+
35
+ def self.perform(meta_id, key, val)
36
+ meta = get_meta(meta_id)
37
+ meta[key] = val
38
+ meta.save
39
+ sleep 1
40
+ end
41
+ end
42
+
43
+ class FailingJob
44
+ extend Resque::Plugins::Meta
45
+ @queue = :test
46
+
47
+ def self.perform(*args)
48
+ raise 'boom'
49
+ end
50
+ end
51
+
52
+ class MetaTest < Test::Unit::TestCase
53
+ def setup
54
+ Resque.redis.flushall
55
+ end
56
+
57
+ def test_meta_version
58
+ assert_equal '1.0.0', Resque::Plugins::Meta::Version
59
+ end
60
+
61
+ def test_lint
62
+ assert_nothing_raised do
63
+ Resque::Plugin.lint(Resque::Plugins::Meta)
64
+ end
65
+ end
66
+
67
+ def test_resque_version
68
+ major, minor, patch = Resque::Version.split('.')
69
+ assert_equal 1, major.to_i
70
+ assert minor.to_i >= 8
71
+ end
72
+
73
+ def test_enqueued_metadata
74
+ now = Time.now
75
+ meta = MetaJob.enqueue('foo', 'bar')
76
+ assert_not_nil meta
77
+ assert_not_nil meta.meta_id
78
+ assert meta.enqueued_at.to_f > now.to_f, "#{meta.enqueued_at} should be after #{now}"
79
+ assert meta.enqueued?
80
+ assert !meta.started?
81
+ assert !meta.finished?
82
+ assert_nil meta['foo']
83
+ assert_equal Resque::Plugins::Meta::Metadata, meta.class
84
+ assert_equal MetaJob, meta.job_class
85
+ end
86
+
87
+ def test_processed_job
88
+ meta = MetaJob.enqueue('foo', 'bar')
89
+ assert_nil meta['foo']
90
+ worker = Resque::Worker.new(:test)
91
+ worker.work(0)
92
+
93
+ meta = MetaJob.get_meta(meta.meta_id)
94
+ assert_equal MetaJob, meta.job_class
95
+ assert meta.started?
96
+ assert meta.finished?, 'Job should be finished'
97
+ assert meta.succeeded?, 'Job should have succeeded'
98
+ assert !meta.enqueued?
99
+ assert_equal 'bar', meta['foo'], "'foo' not found in #{meta.inspect}"
100
+ end
101
+
102
+ def test_wrong_id_for_class
103
+ meta = MetaJob.enqueue('foo', 'bar')
104
+
105
+ assert_nil AnotherJob.get_meta(meta.meta_id)
106
+ assert_not_nil Resque::Plugins::Meta.get_meta(meta.meta_id)
107
+ end
108
+
109
+ def test_expired_metadata
110
+ meta = MetaJob.enqueue('foo', 'bar')
111
+ worker = Resque::Worker.new(:test)
112
+ worker.work(0)
113
+
114
+ sleep 2
115
+ meta = MetaJob.get_meta(meta.meta_id)
116
+ assert_nil meta
117
+ end
118
+
119
+ def test_slow_job
120
+ meta = SlowJob.enqueue('foo', 'bar')
121
+ worker = Resque::Worker.new(:test)
122
+ thread = Thread.new { worker.work(0) }
123
+
124
+ sleep 0.1
125
+ meta = SlowJob.get_meta(meta.meta_id)
126
+ assert !meta.enqueued?
127
+ assert meta.started?
128
+ assert meta.working?
129
+ assert !meta.finished?
130
+
131
+ thread.join # job should be done
132
+ meta.reload!
133
+ assert !meta.enqueued?
134
+ assert meta.started?
135
+ assert !meta.working?
136
+ assert meta.finished?
137
+ assert meta.succeeded?
138
+ assert !meta.failed?
139
+
140
+ sleep 2
141
+ assert_nil Resque::Plugins::Meta.get_meta(meta.meta_id)
142
+ end
143
+
144
+ def test_failing_job
145
+ meta = FailingJob.enqueue()
146
+ assert_nil meta.failed?
147
+ worker = Resque::Worker.new(:test)
148
+ worker.work(0)
149
+
150
+ meta.reload!
151
+ assert meta.finished?
152
+ assert meta.failed?
153
+ assert !meta.succeeded?
154
+ end
155
+ end
@@ -0,0 +1,132 @@
1
+ # Redis configuration file example
2
+
3
+ # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
+ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
+ daemonize yes
6
+
7
+ # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
+ # You can specify a custom pid file location here.
9
+ pidfile ./test/redis-test.pid
10
+
11
+ # Accept connections on the specified port, default is 6379
12
+ port 9736
13
+
14
+ # If you want you can bind a single interface, if the bind option is not
15
+ # specified all the interfaces will listen for connections.
16
+ #
17
+ # bind 127.0.0.1
18
+
19
+ # Close the connection after a client is idle for N seconds (0 to disable)
20
+ timeout 300
21
+
22
+ # Save the DB on disk:
23
+ #
24
+ # save <seconds> <changes>
25
+ #
26
+ # Will save the DB if both the given number of seconds and the given
27
+ # number of write operations against the DB occurred.
28
+ #
29
+ # In the example below the behaviour will be to save:
30
+ # after 900 sec (15 min) if at least 1 key changed
31
+ # after 300 sec (5 min) if at least 10 keys changed
32
+ # after 60 sec if at least 10000 keys changed
33
+ save 900 1
34
+ save 300 10
35
+ save 60 10000
36
+
37
+ # The filename where to dump the DB
38
+ dbfilename dump.rdb
39
+
40
+ # For default save/load DB in/from the working directory
41
+ # Note that you must specify a directory not a file name.
42
+ dir ./test/
43
+
44
+ # Set server verbosity to 'debug'
45
+ # it can be one of:
46
+ # debug (a lot of information, useful for development/testing)
47
+ # notice (moderately verbose, what you want in production probably)
48
+ # warning (only very important / critical messages are logged)
49
+ loglevel debug
50
+
51
+ # Specify the log file name. Also 'stdout' can be used to force
52
+ # the demon to log on the standard output. Note that if you use standard
53
+ # output for logging but daemonize, logs will be sent to /dev/null
54
+ logfile stdout
55
+
56
+ # Set the number of databases. The default database is DB 0, you can select
57
+ # a different one on a per-connection basis using SELECT <dbid> where
58
+ # dbid is a number between 0 and 'databases'-1
59
+ databases 16
60
+
61
+ ################################# REPLICATION #################################
62
+
63
+ # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
+ # another Redis server. Note that the configuration is local to the slave
65
+ # so for example it is possible to configure the slave to save the DB with a
66
+ # different interval, or to listen to another port, and so on.
67
+
68
+ # slaveof <masterip> <masterport>
69
+
70
+ ################################## SECURITY ###################################
71
+
72
+ # Require clients to issue AUTH <PASSWORD> before processing any other
73
+ # commands. This might be useful in environments in which you do not trust
74
+ # others with access to the host running redis-server.
75
+ #
76
+ # This should stay commented out for backward compatibility and because most
77
+ # people do not need auth (e.g. they run their own servers).
78
+
79
+ # requirepass foobared
80
+
81
+ ################################### LIMITS ####################################
82
+
83
+ # Set the max number of connected clients at the same time. By default there
84
+ # is no limit, and it's up to the number of file descriptors the Redis process
85
+ # is able to open. The special value '0' means no limts.
86
+ # Once the limit is reached Redis will close all the new connections sending
87
+ # an error 'max number of clients reached'.
88
+
89
+ # maxclients 128
90
+
91
+ # Don't use more memory than the specified amount of bytes.
92
+ # When the memory limit is reached Redis will try to remove keys with an
93
+ # EXPIRE set. It will try to start freeing keys that are going to expire
94
+ # in little time and preserve keys with a longer time to live.
95
+ # Redis will also try to remove objects from free lists if possible.
96
+ #
97
+ # If all this fails, Redis will start to reply with errors to commands
98
+ # that will use more memory, like SET, LPUSH, and so on, and will continue
99
+ # to reply to most read-only commands like GET.
100
+ #
101
+ # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
+ # 'state' server or cache, not as a real DB. When Redis is used as a real
103
+ # database the memory usage will grow over the weeks, it will be obvious if
104
+ # it is going to use too much memory in the long run, and you'll have the time
105
+ # to upgrade. With maxmemory after the limit is reached you'll start to get
106
+ # errors for write operations, and this may even lead to DB inconsistency.
107
+
108
+ # maxmemory <bytes>
109
+
110
+ ############################### ADVANCED CONFIG ###############################
111
+
112
+ # Glue small output buffers together in order to send small replies in a
113
+ # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
+ # in terms of number of queries per second. Use 'yes' if unsure.
115
+ glueoutputbuf yes
116
+
117
+ # Use object sharing. Can save a lot of memory if you have many common
118
+ # string in your dataset, but performs lookups against the shared objects
119
+ # pool so it uses more CPU and can be a bit slower. Usually it's a good
120
+ # idea.
121
+ #
122
+ # When object sharing is enabled (shareobjects yes) you can use
123
+ # shareobjectspoolsize to control the size of the pool used in order to try
124
+ # object sharing. A bigger pool size will lead to better sharing capabilities.
125
+ # In general you want this value to be at least the double of the number of
126
+ # very common strings you have in your dataset.
127
+ #
128
+ # WARNING: object sharing is experimental, don't enable this feature
129
+ # in production before of Redis 1.0-stable. Still please try this feature in
130
+ # your development environment so that we can test it better.
131
+ shareobjects no
132
+ shareobjectspoolsize 1024
@@ -0,0 +1,34 @@
1
+ dir = File.dirname(File.expand_path(__FILE__))
2
+ $LOAD_PATH.unshift dir + '/../lib'
3
+ require 'test/unit'
4
+ require 'rubygems'
5
+ require 'resque'
6
+
7
+ #
8
+ # make sure we can run redis
9
+ #
10
+
11
+ if !system("which redis-server")
12
+ puts '', "** can't find `redis-server` in your path"
13
+ puts "** try running `sudo rake install`"
14
+ abort ''
15
+ end
16
+
17
+
18
+ #
19
+ # start our own redis when the tests start,
20
+ # kill it when they end
21
+ #
22
+
23
+ at_exit do
24
+ next if $!
25
+
26
+ pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0]
27
+ puts "Killing test redis server..."
28
+ `rm -f #{dir}/dump.rdb`
29
+ Process.kill("KILL", pid.to_i)
30
+ end
31
+
32
+ puts "Starting redis for testing at localhost:9736..."
33
+ `redis-server #{dir}/redis-test.conf`
34
+ Resque.redis = 'localhost:9736'
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-meta
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Lee Marlow
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-04 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: resque
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 55
30
+ segments:
31
+ - 1
32
+ - 8
33
+ - 0
34
+ version: 1.8.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: |
38
+ A Resque plugin. If you want to be able to add metadata for a job
39
+ to track anything you want, extend it with this module.
40
+
41
+ For example:
42
+
43
+ class MyJob
44
+ extend Resque::Jobs::Meta
45
+
46
+ def self.perform(meta_id, *args)
47
+ heavy_lifting
48
+ end
49
+ end
50
+
51
+ meta0 = MyJob.enqueue('stuff')
52
+ meta0.enqueued_at # => 'Wed May 19 13:42:41 -0600 2010'
53
+ meta0.meta_id # => '03c9e1a045ad012dd20500264a19273c'
54
+ meta0['foo'] = 'bar' # => 'bar'
55
+
56
+ # later
57
+ meta1 = MyJob.get_meta('03c9e1a045ad012dd20500264a19273c')
58
+ meta1.job_class # => MyJob
59
+ meta1.enqueued_at # => 'Wed May 19 13:42:41 -0600 2010'
60
+ meta1['foo'] # => 'bar'
61
+
62
+ email: lee.marlow@gmail.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files: []
68
+
69
+ files:
70
+ - README.md
71
+ - Rakefile
72
+ - LICENSE
73
+ - lib/resque/plugins/meta/metadata.rb
74
+ - lib/resque/plugins/meta/version.rb
75
+ - lib/resque/plugins/meta.rb
76
+ - test/meta_test.rb
77
+ - test/redis-test.conf
78
+ - test/test_helper.rb
79
+ has_rdoc: true
80
+ homepage: http://github.com/lmarlow/resque-meta
81
+ licenses: []
82
+
83
+ post_install_message:
84
+ rdoc_options: []
85
+
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ requirements: []
107
+
108
+ rubyforge_project:
109
+ rubygems_version: 1.3.7
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: A Resque plugin for storing job metadata.
113
+ test_files: []
114
+