resque-mongo-groups 0.5

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.
data/.bundle/config ADDED
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/HISTORY.md ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Gheorghita Catalin Bordianu
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
+ NON-INFRINGEMENT. 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.markdown ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'bundler/setup'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ $LOAD_PATH.unshift 'lib'
5
+
6
+ Dir['tasks/*.rake'].each do |f|
7
+ load File.expand_path(f)
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ desc "Run tests"
13
+ task :test do
14
+ Dir['test/*_test.rb'].each do |f|
15
+ require File.expand_path(f)
16
+ end
17
+ end
@@ -0,0 +1,129 @@
1
+ require 'rubygems'
2
+ gem 'resque-mongo'
3
+ require 'resque'
4
+ require 'resque/server'
5
+
6
+ module Resque
7
+ module Plugins
8
+ end
9
+ end
10
+
11
+ require 'resque_groups/tracked_job'
12
+ require 'resque_groups/server'
13
+
14
+ module Resque::Plugins::Groups
15
+
16
+ def job_groups
17
+ self.mongo ||= ENV['MONGO'] || 'localhost:27017'
18
+ @job_groups ||= @db.collection('job_groups')
19
+ end
20
+
21
+ # Triggered whenever a job belonging to group 'gid' has been enqueued. If there's no entry for that group in the 'job_groups'
22
+ # collection, create one and record a timestamp. Otherwise, just increment a total count of enqueued jobs for that group
23
+ def upsert_group(gid)
24
+ if job_groups.find_one({'_id' => gid}).nil?
25
+ job_groups.update( {'_id' => gid},
26
+ {
27
+ '$inc' => {'total' => 1},
28
+ '$set' => {'started_at' => Time.now}
29
+ }, {:upsert => true})
30
+ else
31
+ job_groups.update( {'_id' => gid},
32
+ {
33
+ '$inc' => {'total' => 1}
34
+ }, {:upsert => true})
35
+ end
36
+ #Resque.print_groups
37
+ job_groups.count
38
+ end
39
+
40
+ # Increments the 'completed' count of the group with id 'gid'.
41
+ # Also merge the increment operation with optional custom
42
+ # atomic operations.
43
+ def record_performed_job(gid, atomic_op = nil)
44
+ op = {'$inc' => {'completed' => 1}}
45
+ if atomic_op
46
+ op['$inc'].merge!(atomic_op.delete('$inc')) if atomic_op['$inc']
47
+ op.merge!(atomic_op)
48
+ end
49
+ job_groups.update( {'_id' => gid}, op , {:upsert => true})
50
+ #Resque.print_groups
51
+ end
52
+
53
+ # Increments the 'failed' count of the group with id 'gid'
54
+ def record_failed_job(gid, exception)
55
+ job_groups.update( {'_id' => gid},
56
+ {
57
+ '$inc' => {'failed' => 1},
58
+ '$push' => {'exceptions' => exception.inspect}
59
+ }, {:upsert => true})
60
+ #Resque.print_groups
61
+ end
62
+
63
+ def record_delayed_job(gid)
64
+ job_groups.update( {'_id' => gid},
65
+ {
66
+ '$inc' => {'delayed' => 1}
67
+ }, {:upsert => true})
68
+ #Resque.print_groups
69
+ end
70
+
71
+ def delete_delayed_job(gid)
72
+ job_groups.update( {'_id' => gid},
73
+ {
74
+ '$inc' => {'delayed' => -1}
75
+ }, {:upsert => true})
76
+ #Resque.print_groups
77
+ end
78
+
79
+ # Clears all the groups that are being stored in the job_groups collection
80
+ def reset_groups_queue
81
+ job_groups.remove
82
+ end
83
+
84
+ # Deletes a group from the job_groups collection
85
+ def remove_group(gid)
86
+ job_groups.remove({'_id' => gid})
87
+ end
88
+
89
+ # Returns the stats for a given job group
90
+ def group_stats(gid)
91
+ job_groups.find_one({'_id' => gid})
92
+ end
93
+
94
+ def groups_stats(start, count)
95
+ job_groups.find({}, :skip => start, :limit => count, :sort => ['_id', 1]).to_a
96
+ end
97
+
98
+ def groups_count
99
+ job_groups.count
100
+ end
101
+
102
+ # Given a set of args, looks through all the args that are Hashes for a :group_id key (the inject line replaces string keys with their symbols)
103
+ def parse_gid(*args)
104
+ gid = nil
105
+ args.flatten.each do |arg|
106
+ next unless arg.kind_of? Hash
107
+ sym_arg = arg.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
108
+ if !sym_arg[:group_id].nil?
109
+ gid = sym_arg[:group_id]
110
+ break
111
+ end
112
+ end
113
+ gid
114
+ end
115
+
116
+ def print_groups
117
+ puts "--------------------"
118
+ job_groups.find().each do |row|
119
+ puts row.inspect
120
+ end
121
+ puts "--------------------"
122
+ end
123
+
124
+ end
125
+
126
+ Resque.extend Resque::Plugins::Groups
127
+ Resque::Server.class_eval do
128
+ include ResqueGroups::Server
129
+ end
@@ -0,0 +1,23 @@
1
+
2
+
3
+ # Extend Resque::Server to add tabs
4
+ module ResqueGroups
5
+
6
+ module Server
7
+
8
+ def self.included(base)
9
+
10
+ base.class_eval do
11
+
12
+ get "/groups" do
13
+ erb File.read(File.join(File.dirname(__FILE__), 'server/views/groups.erb'))
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ Resque::Server.tabs << 'Groups'
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,43 @@
1
+ <%start = params[:start].to_i %>
2
+ <%count = params[:count] ? params[:count].to_i : 100 %>
3
+
4
+ <% groups = [resque.groups_stats(start, start + count)].flatten %>
5
+ <% size = resque.groups_count %>
6
+ <h1>Groups</h1>
7
+
8
+ <p class='intro'>
9
+ This list below contains the groups being processed.
10
+ </p>
11
+
12
+ <p class='sub'>
13
+ Showing <%= start %> to <%= start + groups.size %> of <b><%= size %></b> groups
14
+ </p>
15
+
16
+
17
+ <table>
18
+ <tr>
19
+ <th>Group</th>
20
+ <th>Started at</th>
21
+ <th>Total</th>
22
+ <th>Delayed</th>
23
+ <th>Completed</th>
24
+ <th>Failed</th>
25
+ <th>Exceptions</th>
26
+ <th>Custom field</th>
27
+ </tr>
28
+ <% groups.each do |group| %>
29
+ <tr>
30
+ <td><%= group['_id'] %></td>
31
+ <td><%= group['started_at'] %></td>
32
+ <td><%= group['total'] %></td>
33
+ <td><%= group['delayed'] %></td>
34
+ <td><%= group['completed'] %></td>
35
+ <td><%= group['failed'] %></td>
36
+ <td><%= escape_html(group['exceptions'].inspect) if group['exceptions'] %></td>
37
+ <td><%= escape_html(group['custom'].inspect) if group['custom'] %></td>
38
+ </tr>
39
+ <% end %>
40
+ </table>
41
+
42
+
43
+ <%= partial :next_more, :start => start, :count => count, :size => size %>
@@ -0,0 +1,7 @@
1
+ # require 'resque/tasks'
2
+ # will give you the resque tasks
3
+
4
+ namespace :resque do
5
+ task :setup
6
+
7
+ end
@@ -0,0 +1,58 @@
1
+ module Resque::Plugins::Groups
2
+ module TrackedJob
3
+
4
+ # The MongoDB atomic operation stored in this variable will be executed
5
+ # on the group when and if the job is complete.
6
+ # See http://www.mongodb.org/display/DOCS/Updating for a list of all atomic operations.
7
+ # e.g.:
8
+ # {'$inc' => {'custom.size' => 3}}
9
+ # {'$set' => {'custom.api_problem' => true}}
10
+ # It's highly recommended to use the "custom" field to store your data, even
11
+ # if no check is done by the plugin.
12
+ def atomic_op_on_complete
13
+ @atomic_op_on_complete
14
+ end
15
+ def atomic_op_on_complete=(op)
16
+ @atomic_op_on_complete = op
17
+ end
18
+
19
+ # resque hook, triggered immediately after a job with *args has been enqueued
20
+ def after_enqueue_count_job(*args)
21
+ gid = Resque.parse_gid(*args)
22
+ raise "The 'group_id' parameter is missing!" if gid.nil?
23
+
24
+ # If this was a scheduled/delayed job, decrement the 'delayed' counter
25
+ Resque.delete_delayed_job(gid) if !is_delayed(args).nil?
26
+
27
+ # Update the total count for the group in which the enqueued job belongs
28
+ Resque.upsert_group(gid)
29
+ end
30
+
31
+ # resque hook, triggered immediately after a job has been performed
32
+ def after_perform_update_completed(*args)
33
+ gid = Resque.parse_gid(*args)
34
+ Resque.record_performed_job(gid, atomic_op_on_complete)
35
+ end
36
+
37
+ # resque hook, triggered if a job raises any exception. uncaught exceptions propagate up to Resque::Failure
38
+ def on_failure_update_failed(exception, *args)
39
+ gid = Resque.parse_gid(*args)
40
+ Resque.record_failed_job(gid, exception)
41
+ end
42
+
43
+ # Determines if this was a delayed job or not, based on the :delayed => true arg
44
+ def is_delayed(*args)
45
+ delayed = nil
46
+ args.flatten.each do |arg|
47
+ next unless arg.kind_of? Hash
48
+ sym_arg = arg.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
49
+ if !sym_arg[:delayed].nil?
50
+ delayed = sym_arg[:delayed]
51
+ break
52
+ end
53
+ end
54
+ delayed
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ require 'resque'
2
+
3
+ module Resque::Plugins
4
+ module Groups
5
+ Version = '0.5'
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "resque_groups/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "resque-mongo-groups"
7
+ s.version = Resque::Plugins::Groups::Version
8
+ s.platform = Gem::Platform::RUBY
9
+ s.author = 'Gheorghita Catalin Bordianu'
10
+ s.email = ['catalin@silentale.com']
11
+ s.homepage = "http://github.com/omikronn/resque-mongo-groups"
12
+ s.summary = "Keeps track of groups of jobs and their completion status"
13
+ s.description = %q{Provides a way of keeping track of batches of jobs, completion status, start time etc. using a MongoDb collection}
14
+
15
+ s.required_rubygems_version = ">= 1.3.6"
16
+ s.add_development_dependency "bundler", ">= 1.0.0"
17
+
18
+ s.rubyforge_project = "resque-mongo-groups"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
23
+ s.require_path = 'lib'
24
+
25
+ s.add_runtime_dependency(%q<resque-mongo>, [">= 1.11.0"])
26
+
27
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'resque_groups/tasks'
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-mongo-groups
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: "0.5"
6
+ platform: ruby
7
+ authors:
8
+ - Gheorghita Catalin Bordianu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-24 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: bundler
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.0.0
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: resque-mongo
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.11.0
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ description: Provides a way of keeping track of batches of jobs, completion status, start time etc. using a MongoDb collection
39
+ email:
40
+ - catalin@silentale.com
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files: []
46
+
47
+ files:
48
+ - .bundle/config
49
+ - .gitignore
50
+ - Gemfile
51
+ - HISTORY.md
52
+ - LICENSE
53
+ - README.markdown
54
+ - Rakefile
55
+ - lib/resque_groups.rb
56
+ - lib/resque_groups/server.rb
57
+ - lib/resque_groups/server/views/groups.erb
58
+ - lib/resque_groups/tasks.rb
59
+ - lib/resque_groups/tracked_job.rb
60
+ - lib/resque_groups/version.rb
61
+ - resque-mongo-groups.gemspec
62
+ - tasks/resque_counter.rake
63
+ has_rdoc: true
64
+ homepage: http://github.com/omikronn/resque-mongo-groups
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options: []
69
+
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 1.3.6
84
+ requirements: []
85
+
86
+ rubyforge_project: resque-mongo-groups
87
+ rubygems_version: 1.6.1
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Keeps track of groups of jobs and their completion status
91
+ test_files: []
92
+