resque_jobs_tree 0.0.2
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +17 -0
- data/lib/resque_jobs_tree.rb +12 -0
- data/lib/resque_jobs_tree/factory.rb +22 -0
- data/lib/resque_jobs_tree/job.rb +32 -0
- data/lib/resque_jobs_tree/node.rb +63 -0
- data/lib/resque_jobs_tree/resources_serializer.rb +25 -0
- data/lib/resque_jobs_tree/storage.rb +62 -0
- data/lib/resque_jobs_tree/tree.rb +37 -0
- data/lib/resque_jobs_tree/version.rb +3 -0
- data/resque_jobs_tree.gemspec +26 -0
- data/test/factory_test.rb +36 -0
- data/test/job_test.rb +33 -0
- data/test/node_test.rb +68 -0
- data/test/resources_serializer_test.rb +16 -0
- data/test/storage_test.rb +60 -0
- data/test/test_helper.rb +33 -0
- data/test/tree_test.rb +54 -0
- metadata +156 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 FinalCAD
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# ResqueJobsTree -- Resque jobs as a tree
|
2
|
+
|
3
|
+
To manage complexe background job processes, this gem simplify the task of creating
|
4
|
+
sequences of [Resque](https://github.com/resque/resque) jobs by putting them into a tree.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'resque'
|
11
|
+
gem 'resque_jobs_tree'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Organise each sequences of jobs into a single file
|
20
|
+
|
21
|
+
``` ruby
|
22
|
+
my_tree = ResqueJobsTree::Factory.create :my_complex_process do
|
23
|
+
root :send_my_email do
|
24
|
+
perform do |*args|
|
25
|
+
# your code goes here...
|
26
|
+
end
|
27
|
+
childs do |resources|
|
28
|
+
user = resources.first
|
29
|
+
[].tap do |jobs|
|
30
|
+
jobs << [:my_fetch_on_an_outside_slowish_api, user.company, user.group]
|
31
|
+
user.comments.each do |comment|
|
32
|
+
jobs << [:my_precomputation_of_data, comment]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
node :my_fetch_on_an_outside_slowish_api do
|
37
|
+
perform do |*args|
|
38
|
+
# your code goes here...
|
39
|
+
end
|
40
|
+
end
|
41
|
+
node :my_precomputation_of_data do
|
42
|
+
perform do |*args|
|
43
|
+
# your code goes here...
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
my_tree.launch User.find(1)
|
50
|
+
```
|
51
|
+
|
52
|
+
This code is defining the tree, then when it launches the sequence of jobs, it:
|
53
|
+
* stocks in Redis all the Resque jobs which needs to be done including the needed parameters to run them.
|
54
|
+
* stokcs in Redis the childhood relationsips between them.
|
55
|
+
* enqueues in Resque the jobs which are the leaves of the tree
|
56
|
+
|
57
|
+
The rule: the name of a tree of jobs should be uniq, and the name of a node should be uniq in a scope of a tree.
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
1. Fork it
|
62
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
63
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
64
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
65
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# Bundler tasks
|
3
|
+
#
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
|
6
|
+
#
|
7
|
+
# Tests
|
8
|
+
#
|
9
|
+
require 'rake/testtask'
|
10
|
+
|
11
|
+
Rake::TestTask.new do |test|
|
12
|
+
test.verbose = true
|
13
|
+
test.libs << "test"
|
14
|
+
test.libs << "lib"
|
15
|
+
test.test_files = FileList['test/**/*_test.rb']
|
16
|
+
end
|
17
|
+
task :default => :test
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "resque_jobs_tree/version"
|
2
|
+
require 'resque'
|
3
|
+
|
4
|
+
require 'resque_jobs_tree/factory'
|
5
|
+
require 'resque_jobs_tree/tree'
|
6
|
+
require 'resque_jobs_tree/node'
|
7
|
+
require 'resque_jobs_tree/job'
|
8
|
+
require 'resque_jobs_tree/resources_serializer'
|
9
|
+
require 'resque_jobs_tree/storage'
|
10
|
+
|
11
|
+
module ResqueJobsTree
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ResqueJobsTree::Factory
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def create name, &block
|
6
|
+
@trees ||= []
|
7
|
+
@trees.delete_if{|tree| tree.name == name.to_s}
|
8
|
+
ResqueJobsTree::Tree.new(name).tap do |tree|
|
9
|
+
@trees << tree
|
10
|
+
tree.instance_eval &block
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def trees
|
15
|
+
@trees
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_tree_by_name name
|
19
|
+
@trees.detect{ |tree| tree.name == name.to_s }
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class ResqueJobsTree::Job
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def perform *args
|
6
|
+
tree, node, resources = tree_node_and_resources(*args)
|
7
|
+
node.perform.call resources
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def after_perform_enqueue_parent *args
|
13
|
+
tree, node, resources = tree_node_and_resources(*args)
|
14
|
+
unless node.root?
|
15
|
+
parent_job_args = ResqueJobsTree::Storage.parent_job_args node, resources
|
16
|
+
ResqueJobsTree::Storage.remove(node, resources) do
|
17
|
+
Resque.enqueue_to tree.name, ResqueJobsTree::Job, *parent_job_args
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def tree_node_and_resources *args
|
23
|
+
tree_name , job_name = args.shift(2)
|
24
|
+
tree = ResqueJobsTree::Factory.find_tree_by_name tree_name
|
25
|
+
node = tree.find_node_by_name job_name
|
26
|
+
resources = ResqueJobsTree::ResourcesSerializer.to_resources args
|
27
|
+
[tree, node, resources]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class ResqueJobsTree::Node
|
2
|
+
|
3
|
+
attr_accessor :tree, :parent, :name, :node_childs
|
4
|
+
|
5
|
+
def initialize name, tree, parent=nil
|
6
|
+
@tree = tree
|
7
|
+
@name = name.to_s
|
8
|
+
@parent = parent
|
9
|
+
@node_childs = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def resources &block
|
13
|
+
@resources_block ||= block
|
14
|
+
end
|
15
|
+
|
16
|
+
def perform &block
|
17
|
+
@perform_block ||= block
|
18
|
+
end
|
19
|
+
|
20
|
+
def childs &block
|
21
|
+
@childs ||= block
|
22
|
+
end
|
23
|
+
|
24
|
+
# Defines a child node.
|
25
|
+
def node name, &block
|
26
|
+
ResqueJobsTree::Node.new(name, tree, self).tap do |node|
|
27
|
+
@node_childs << node
|
28
|
+
node.instance_eval(&block) if block_given?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def leaf? resources
|
33
|
+
childs.kind_of?(Proc) ? childs.call(resources).empty? : true
|
34
|
+
end
|
35
|
+
|
36
|
+
def root?
|
37
|
+
parent == nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def siblings
|
41
|
+
return [] unless parent
|
42
|
+
parent.node_childs - [self]
|
43
|
+
end
|
44
|
+
|
45
|
+
def launch resources, parent_resources=nil
|
46
|
+
unless root?
|
47
|
+
ResqueJobsTree::Storage.store self, resources, parent, parent_resources
|
48
|
+
end
|
49
|
+
if node_childs.empty?
|
50
|
+
@tree.enqueue name, *resources
|
51
|
+
else
|
52
|
+
childs.call(resources).each do |name, *child_resources|
|
53
|
+
find_node_by_name(name).launch child_resources, resources
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_node_by_name _name
|
59
|
+
return self if name == _name.to_s
|
60
|
+
node_childs.detect{ |node| node.find_node_by_name _name }
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ResqueJobsTree::ResourcesSerializer
|
2
|
+
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# in: [<Localisation id=1>, :pdf]
|
6
|
+
# out: [[Localisation, 1], :pdf]
|
7
|
+
def to_args resources
|
8
|
+
resources.to_a.map do |resource|
|
9
|
+
resource.respond_to?(:id) ? [resource.class.name, resource.id] : resource
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# in: [['Localisation', 1], :pdf]
|
14
|
+
# out: [<Localisation id=1>, :pdf]
|
15
|
+
def to_resources args
|
16
|
+
args.to_a.map do |arg|
|
17
|
+
if arg.kind_of? Array
|
18
|
+
eval(arg[0]).find(arg[1]) rescue arg
|
19
|
+
else
|
20
|
+
arg
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# put expire on every key
|
2
|
+
module ResqueJobsTree::Storage
|
3
|
+
extend self
|
4
|
+
|
5
|
+
PARENTS_KEY = "JobsTree:Node:Parents"
|
6
|
+
|
7
|
+
def store node, resources, parent, parent_resources
|
8
|
+
node_key = key node, resources
|
9
|
+
parent_key = key parent, parent_resources
|
10
|
+
Resque.redis.hset PARENTS_KEY, node_key, parent_key
|
11
|
+
childs_key = childs_key parent, parent_resources
|
12
|
+
Resque.redis.sadd childs_key, node_key
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove node, resources
|
16
|
+
lock_with parent_lock_key(node, resources) do
|
17
|
+
siblings_key = siblings_key node, resources
|
18
|
+
Resque.redis.srem siblings_key, key(node, resources)
|
19
|
+
yield if Resque.redis.scard(siblings_key) == 0 && block_given?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def parent_job_args node, resources
|
24
|
+
JSON.load parent_key(node, resources).gsub(/JobsTree:Node:/, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def key node, resources
|
30
|
+
job_args = ResqueJobsTree::ResourcesSerializer.to_args(resources)
|
31
|
+
job_args.unshift node.name
|
32
|
+
job_args.unshift node.tree.name
|
33
|
+
"JobsTree:Node:#{job_args.to_json}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def childs_key node, resources
|
37
|
+
"#{key node, resources}:childs"
|
38
|
+
end
|
39
|
+
|
40
|
+
def parent_key node, resources
|
41
|
+
node_key = key node, resources
|
42
|
+
Resque.redis.hget PARENTS_KEY, node_key
|
43
|
+
end
|
44
|
+
|
45
|
+
def siblings_key node, resources
|
46
|
+
"#{parent_key node, resources}:childs"
|
47
|
+
end
|
48
|
+
|
49
|
+
def parent_lock_key node, resources
|
50
|
+
"#{parent_key node, resources}:lock"
|
51
|
+
end
|
52
|
+
|
53
|
+
def lock_with key
|
54
|
+
while !Resque.redis.setnx(key, 'locked')
|
55
|
+
sleep 0.05 # 50 ms
|
56
|
+
end
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
Resque.redis.del key
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class ResqueJobsTree::Tree
|
2
|
+
|
3
|
+
attr_accessor :name
|
4
|
+
attr_reader :jobs
|
5
|
+
|
6
|
+
def initialize name
|
7
|
+
@name = name.to_s
|
8
|
+
@jobs = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def root name=nil, &block
|
12
|
+
@root ||= ResqueJobsTree::Node.new(name, self).tap do |root|
|
13
|
+
root.instance_eval &block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def launch *resources
|
18
|
+
@root.launch resources
|
19
|
+
enqueue_leaves_jobs
|
20
|
+
end
|
21
|
+
|
22
|
+
def enqueue *job_args
|
23
|
+
job_args.unshift name
|
24
|
+
@jobs << job_args
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_node_by_name name
|
28
|
+
root.find_node_by_name name.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def enqueue_leaves_jobs
|
34
|
+
@jobs.each{ |job_args| Resque.enqueue_to name, ResqueJobsTree::Job, *job_args }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'resque_jobs_tree/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "resque_jobs_tree"
|
8
|
+
spec.version = ResqueJobsTree::VERSION
|
9
|
+
spec.authors = ["Antoine Qu'hen"]
|
10
|
+
spec.email = ["antoinequhen@gmail.com"]
|
11
|
+
spec.description = %q{To manage complexe background job processes, this gem simplify the task of creating sequences of Resque jobs by putting them into a tree.}
|
12
|
+
spec.summary = %q{Organise Resque jobs as a tree.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency 'minitest'
|
24
|
+
spec.add_development_dependency 'mock_redis'
|
25
|
+
spec.add_dependency 'resque', '~> 1.24'
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FactoryTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@tree = ResqueJobsTree::Factory.create :tree1 do
|
7
|
+
root :job1 do
|
8
|
+
perform do |*args|
|
9
|
+
puts 'FactoryTest job1'
|
10
|
+
end
|
11
|
+
childs do |resources|
|
12
|
+
[].tap do |childs|
|
13
|
+
3.times do
|
14
|
+
childs << [:job2, resources.last]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
node :job2 do
|
19
|
+
perform do |*args|
|
20
|
+
puts 'FactoryTest job2'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_tree_creation
|
28
|
+
assert_equal ResqueJobsTree::Factory.trees.first.object_id, @tree.object_id
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_find_by_name
|
32
|
+
assert_equal ResqueJobsTree::Factory.find_tree_by_name(@tree.name).object_id,
|
33
|
+
@tree.object_id
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/test/job_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class JobTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@tree = ResqueJobsTree::Factory.create :tree1 do
|
7
|
+
root :job1 do
|
8
|
+
perform do |*args|
|
9
|
+
puts 'FactoryTest job1'
|
10
|
+
end
|
11
|
+
childs do |resources|
|
12
|
+
[].tap do |childs|
|
13
|
+
3.times do
|
14
|
+
childs << [:job2, resources.last]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
node :job2 do
|
19
|
+
perform do |*args|
|
20
|
+
puts 'FactoryTest job2'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@args = [@tree.name, @tree.find_node_by_name('job1').name, 1, 2, 3]
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_tree_node_and_resources
|
29
|
+
result = [@tree, @tree.find_node_by_name('job1'), [1, 2, 3]]
|
30
|
+
assert_equal result, ResqueJobsTree::Job.send(:tree_node_and_resources, *@args)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/test/node_test.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class NodeTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@tree = ResqueJobsTree::Tree.new :tree1
|
7
|
+
@root = ResqueJobsTree::Node.new :node1, @tree
|
8
|
+
@leaf = ResqueJobsTree::Node.new :node2, @tree, @root
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_ressources
|
12
|
+
variable = 1
|
13
|
+
@root.resources do |n|
|
14
|
+
variable = n
|
15
|
+
end
|
16
|
+
@root.resources.call 2
|
17
|
+
assert_equal variable, 2
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_perform
|
21
|
+
variable = 1
|
22
|
+
@root.perform do |n|
|
23
|
+
variable = n
|
24
|
+
end
|
25
|
+
@root.perform.call 2
|
26
|
+
assert_equal variable, 2
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_childs
|
30
|
+
variable = 1
|
31
|
+
@leaf.childs do |n|
|
32
|
+
variable = n
|
33
|
+
end
|
34
|
+
@leaf.childs.call 2
|
35
|
+
assert_equal variable, 2
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_node
|
39
|
+
node3 = @root.node :node3
|
40
|
+
assert_equal @root.find_node_by_name('node3').object_id, node3.object_id
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_leaf
|
44
|
+
resources = []
|
45
|
+
assert @leaf.leaf?(resources)
|
46
|
+
@root.childs do |resources|
|
47
|
+
[:node2, resources]
|
48
|
+
end
|
49
|
+
assert !@root.leaf?(resources)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_root
|
53
|
+
assert @root.root?
|
54
|
+
assert !@leaf.root?
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_siblings
|
58
|
+
node3 = ResqueJobsTree::Node.new :node3, @tree, @root
|
59
|
+
assert @leaf.siblings, [node3]
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_launch
|
63
|
+
resources = [1, 2, 3]
|
64
|
+
@leaf.launch resources, resources
|
65
|
+
assert_equal @tree.jobs.first, ['tree1', 'node2', 1, 2, 3]
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ResourcesSerializerTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def test_serialization_deserialization
|
6
|
+
model = Model.new
|
7
|
+
input = [model, :pdf, [1,2]]
|
8
|
+
serialized_input = ResqueJobsTree::ResourcesSerializer.to_args(input)
|
9
|
+
result = [['Model', model.id], :pdf, [1,2]]
|
10
|
+
assert_equal serialized_input, result
|
11
|
+
deserialized = ResqueJobsTree::ResourcesSerializer.
|
12
|
+
to_resources(serialized_input)
|
13
|
+
assert_equal deserialized, ['stubed_instance', :pdf, [1,2]]
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class StorageTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@tree = ResqueJobsTree::Factory.create :tree1 do
|
7
|
+
root :job1 do
|
8
|
+
perform do |*args|
|
9
|
+
puts 'FactoryTest job1'
|
10
|
+
end
|
11
|
+
childs do |resources|
|
12
|
+
[].tap do |childs|
|
13
|
+
3.times do
|
14
|
+
childs << [:job2, resources.last]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
node :job2 do
|
19
|
+
perform do |*args|
|
20
|
+
puts 'FactoryTest job2'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@resources = [1, 2, 3]
|
26
|
+
@root = @tree.find_node_by_name(:job1)
|
27
|
+
@leaf = @tree.find_node_by_name(:job2)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_store
|
31
|
+
parents_key = ResqueJobsTree::Storage::PARENTS_KEY
|
32
|
+
Resque.redis.del parents_key
|
33
|
+
store
|
34
|
+
assert_equal 1, Resque.redis.hlen(parents_key)
|
35
|
+
childs_key = ResqueJobsTree::Storage.send :childs_key, @root, @resources
|
36
|
+
assert_equal 1, Resque.redis.scard(childs_key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_parent_job_args
|
40
|
+
store
|
41
|
+
assert_equal ['tree1', 'job1', 1, 2, 3],
|
42
|
+
ResqueJobsTree::Storage.parent_job_args(@leaf, @resources)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_remove
|
46
|
+
store
|
47
|
+
variable = 1
|
48
|
+
ResqueJobsTree::Storage.remove @leaf, @resources do
|
49
|
+
variable = 2
|
50
|
+
end
|
51
|
+
assert_equal 2, variable
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def store
|
57
|
+
ResqueJobsTree::Storage.store @leaf, @resources, @leaf.parent, @resources
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'minitest' # ensures you're using the gem, and not the built in MT
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'minitest/unit'
|
6
|
+
|
7
|
+
$dir = File.dirname(File.expand_path(__FILE__))
|
8
|
+
$LOAD_PATH.unshift $dir + '/../lib'
|
9
|
+
require 'resque_jobs_tree'
|
10
|
+
$TESTING = true
|
11
|
+
|
12
|
+
Resque.inline = true
|
13
|
+
|
14
|
+
class Model
|
15
|
+
def id
|
16
|
+
@id ||= rand 1000
|
17
|
+
end
|
18
|
+
def self.find id
|
19
|
+
'stubed_instance'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Run resque callbacks in inline mode
|
24
|
+
class ResqueJobsTree::Job
|
25
|
+
class << self
|
26
|
+
def perform_with_hook *args
|
27
|
+
perform_without_hook *args
|
28
|
+
after_perform_enqueue_parent *args
|
29
|
+
end
|
30
|
+
alias_method :perform_without_hook, :perform
|
31
|
+
alias_method :perform, :perform_with_hook
|
32
|
+
end
|
33
|
+
end
|
data/test/tree_test.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TreeTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@tree = ResqueJobsTree::Factory.create :tree1 do
|
7
|
+
root :job1 do
|
8
|
+
perform do |*args|
|
9
|
+
# puts 'TreeTest job1'
|
10
|
+
end
|
11
|
+
childs do |resources|
|
12
|
+
[].tap do |childs|
|
13
|
+
3.times do
|
14
|
+
childs << [:job2, resources.last]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
node :job2 do
|
19
|
+
perform do |*args|
|
20
|
+
# puts 'TreeTest job2'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_name
|
28
|
+
assert_equal @tree.name, 'tree1'
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_jobs
|
32
|
+
assert_equal @tree.jobs, []
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_root
|
36
|
+
assert @tree.root.kind_of? ResqueJobsTree::Node
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_enqueue
|
40
|
+
job_args = [:job4, ['Model', 1], 123]
|
41
|
+
@tree.enqueue *job_args
|
42
|
+
assert_equal @tree.jobs.first, ['tree1', :job4, ['Model', 1], 123]
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_find_node_by_name
|
46
|
+
assert_equal @tree.find_node_by_name('job2').name, 'job2'
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_launch
|
50
|
+
resources = [1, 2, 3]
|
51
|
+
@tree.launch *resources
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque_jobs_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Antoine Qu'hen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mock_redis
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: resque
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.24'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.24'
|
94
|
+
description: To manage complexe background job processes, this gem simplify the task
|
95
|
+
of creating sequences of Resque jobs by putting them into a tree.
|
96
|
+
email:
|
97
|
+
- antoinequhen@gmail.com
|
98
|
+
executables: []
|
99
|
+
extensions: []
|
100
|
+
extra_rdoc_files: []
|
101
|
+
files:
|
102
|
+
- .gitignore
|
103
|
+
- Gemfile
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- lib/resque_jobs_tree.rb
|
108
|
+
- lib/resque_jobs_tree/factory.rb
|
109
|
+
- lib/resque_jobs_tree/job.rb
|
110
|
+
- lib/resque_jobs_tree/node.rb
|
111
|
+
- lib/resque_jobs_tree/resources_serializer.rb
|
112
|
+
- lib/resque_jobs_tree/storage.rb
|
113
|
+
- lib/resque_jobs_tree/tree.rb
|
114
|
+
- lib/resque_jobs_tree/version.rb
|
115
|
+
- resque_jobs_tree.gemspec
|
116
|
+
- test/factory_test.rb
|
117
|
+
- test/job_test.rb
|
118
|
+
- test/node_test.rb
|
119
|
+
- test/resources_serializer_test.rb
|
120
|
+
- test/storage_test.rb
|
121
|
+
- test/test_helper.rb
|
122
|
+
- test/tree_test.rb
|
123
|
+
homepage: ''
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 1.8.25
|
145
|
+
signing_key:
|
146
|
+
specification_version: 3
|
147
|
+
summary: Organise Resque jobs as a tree.
|
148
|
+
test_files:
|
149
|
+
- test/factory_test.rb
|
150
|
+
- test/job_test.rb
|
151
|
+
- test/node_test.rb
|
152
|
+
- test/resources_serializer_test.rb
|
153
|
+
- test/storage_test.rb
|
154
|
+
- test/test_helper.rb
|
155
|
+
- test/tree_test.rb
|
156
|
+
has_rdoc:
|