qmin 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +50 -0
- data/Rakefile +11 -0
- data/lib/qmin/core_ext/class.rb +24 -0
- data/lib/qmin/core_ext/string.rb +34 -0
- data/lib/qmin/qmin.rb +38 -0
- data/lib/qmin/resque/background_call_job.rb +22 -0
- data/lib/qmin/resque/base_job.rb +53 -0
- data/lib/qmin/strategy/inline.rb +15 -0
- data/lib/qmin/strategy/noop.rb +11 -0
- data/lib/qmin/strategy/resque.rb +48 -0
- data/lib/qmin/version.rb +3 -0
- data/lib/qmin.rb +25 -0
- data/qmin.gemspec +29 -0
- data/spec/qmin/core_ext/class_spec.rb +70 -0
- data/spec/qmin/core_ext/string_spec.rb +18 -0
- data/spec/qmin/qmin_spec.rb +60 -0
- data/spec/qmin/resque/background_call_job_spec.rb +28 -0
- data/spec/qmin/resque/base_job_spec.rb +31 -0
- data/spec/qmin/strategy/inline_spec.rb +14 -0
- data/spec/qmin/strategy/noop_spec.rb +15 -0
- data/spec/qmin/strategy/resque_spec.rb +21 -0
- data/spec/spec_helper.rb +98 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5c2e896aee9f13d55b90c872e085fc5299d557d7
|
4
|
+
data.tar.gz: e5b1d269a78af22367a6e88d937554dfea633d9b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6eb6b7b525f97a928e87861df48c8d9687216f5f72441b4e4afd4642ca1ec2be447d9828d6ecfcba18ec68d46dc888c304c1a9521eeb65b4d5cab6e52c75b43d
|
7
|
+
data.tar.gz: 5bdd3ac3c1f885257ad8c243b10462ac07c931f6952eb29e0ba2c37f3bc6635090794a846e36c55a5dc6ce7d76bb69f1c56c84703b71475024f79881cbb9e524
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
|
19
|
+
.rvmrc
|
20
|
+
.ruby-version
|
21
|
+
.ruby-gemset
|
22
|
+
/bin
|
23
|
+
.idea
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 the-architect
|
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,50 @@
|
|
1
|
+
# Qmin
|
2
|
+
|
3
|
+
Enables your application to handle method calls in the background without the need to write a worker for every task.
|
4
|
+
|
5
|
+
|
6
|
+
## Assumptions
|
7
|
+
|
8
|
+
You can transparently call methods on instances that adhere to some assumptions Qmin makes about your code:
|
9
|
+
|
10
|
+
* Classes (i.e. ActiveRecord Models) have a #find class method that takes an ID as an argument and instantiates an instance of that class.
|
11
|
+
* Instances of that class have a public #id method that returns a value which can be used by the #find class method to instantiate that instance.
|
12
|
+
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'qmin'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install qmin
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
### Qmin::Strategy
|
31
|
+
|
32
|
+
Currently these strategies are implemented:
|
33
|
+
|
34
|
+
* Noop (does nothing)
|
35
|
+
* Inline (calls the method inline, helpful for testing or development)
|
36
|
+
* Resque (moves the work off to the resque queue)
|
37
|
+
|
38
|
+
## Todo
|
39
|
+
|
40
|
+
* better documentation :)
|
41
|
+
* Qmin::Strategy::DelayedJob
|
42
|
+
* Qmin::Strategy::Sidekiq
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
1. Fork it
|
47
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
48
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
49
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Extends class with #background_method(method_name)
|
2
|
+
# The annotated method will be handled by Qmin and it's current strategy
|
3
|
+
class Class
|
4
|
+
# @param method_name
|
5
|
+
# Decorates a method to be handled by Qmin's #background_call method
|
6
|
+
# Calls to the method will then be handled by the configured Qmin::Strategy#background_call method
|
7
|
+
# The original method is still available through "<method_name>_without_qmin"
|
8
|
+
def background_method(method_name)
|
9
|
+
return if respond_to?("#{method_name}_without_qmin") || !!method_name.to_s.match(/_without_qmin$/)
|
10
|
+
|
11
|
+
alias_method "#{method_name}_without_qmin", method_name
|
12
|
+
|
13
|
+
define_method method_name do
|
14
|
+
::Qmin::Qmin.background_call(self, method_name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param *method_names
|
19
|
+
# annotates all methods as background_methods
|
20
|
+
# see #background_method for more information
|
21
|
+
def background_methods(*method_names)
|
22
|
+
method_names.map(&:to_s).each{|method_name| background_method method_name }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
unless String.instance_methods.include?(:snake_case)
|
2
|
+
|
3
|
+
class String
|
4
|
+
def snake_case
|
5
|
+
self.to_s.gsub(/::/, '/').
|
6
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
7
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
8
|
+
tr('-', '_').
|
9
|
+
downcase
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
unless String.instance_methods.include?(:constantize)
|
16
|
+
|
17
|
+
class String
|
18
|
+
def constantize
|
19
|
+
Kernel.const_get(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
unless String.instance_methods.include?(:to_queue_name)
|
27
|
+
|
28
|
+
class String
|
29
|
+
def to_queue_name
|
30
|
+
gsub('::','').snake_case
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/lib/qmin/qmin.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Qmin
|
2
|
+
# Handles configuration and dispatches method calls to configured strategy
|
3
|
+
class Qmin
|
4
|
+
# configure default strategy
|
5
|
+
def self.default_strategy=(strategy)
|
6
|
+
@@default_strategy = strategy
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.current
|
10
|
+
@@current ||= new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.enqueue(worker_class, *args)
|
14
|
+
current.enqueue worker_class, *args
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.background_call(instance, method_name)
|
18
|
+
current.background_call(instance, method_name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(strategy = nil)
|
22
|
+
begin
|
23
|
+
@strategy = (strategy || @@default_strategy).new
|
24
|
+
rescue NameError
|
25
|
+
raise MustDefineStrategyError.new
|
26
|
+
end
|
27
|
+
@@current = self
|
28
|
+
end
|
29
|
+
|
30
|
+
def enqueue(worker_class, *args)
|
31
|
+
@strategy.enqueue worker_class, *args
|
32
|
+
end
|
33
|
+
|
34
|
+
def background_call(instance, method_name)
|
35
|
+
@strategy.background_call(instance, method_name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Qmin
|
2
|
+
module Resque
|
3
|
+
class BackgroundCallJob
|
4
|
+
|
5
|
+
def self.perform(*args)
|
6
|
+
new(*args).perform
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(klass, method_name, id)
|
10
|
+
@klass = klass.is_a?(Class) ? klass : klass.constantize
|
11
|
+
@method_name = method_name
|
12
|
+
@id = id
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform
|
16
|
+
instance = @klass.find(@id)
|
17
|
+
instance.send(::Qmin.method_name_for_instance(instance, @method_name))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Qmin
|
2
|
+
module Resque
|
3
|
+
# Defines a handy base class for Resque workers for the following pattern:
|
4
|
+
# * Find an instance of a model by id
|
5
|
+
# * call a method on the instance
|
6
|
+
#
|
7
|
+
class BaseJob
|
8
|
+
MustRespondToFindMethodError = Class.new(StandardError) do
|
9
|
+
@message = 'Model must respond to #find method!'
|
10
|
+
end
|
11
|
+
|
12
|
+
ImplementationMissing = Class.new(StandardError)
|
13
|
+
|
14
|
+
# delegate perform to job instance
|
15
|
+
def self.perform(*args)
|
16
|
+
new(*args).perform
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.model(model)
|
20
|
+
if model.respond_to? :find
|
21
|
+
@@model = model
|
22
|
+
else
|
23
|
+
raise MustRespondToFindMethodError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(id, *args)
|
28
|
+
@id = id
|
29
|
+
end
|
30
|
+
|
31
|
+
def perform
|
32
|
+
raise ImplementationMissing.new("Please implement #perform instance method for job: #{self.class.name}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def instance
|
36
|
+
@instance ||= find_instance(@id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_instance(id)
|
40
|
+
model.find(id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def model
|
44
|
+
@@model
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.queue
|
48
|
+
# automatic queue naming
|
49
|
+
@queue ||= self.name.to_queue_name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Qmin
|
2
|
+
module Strategy
|
3
|
+
class Inline
|
4
|
+
# handle job in process
|
5
|
+
def enqueue(worker_class, *args)
|
6
|
+
worker_class.send :perform, *args
|
7
|
+
end
|
8
|
+
|
9
|
+
# call method directly
|
10
|
+
def background_call(instance, method_name)
|
11
|
+
instance.send(::Qmin.method_name_for_instance(instance, method_name))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Qmin
|
2
|
+
module Strategy
|
3
|
+
# Strategy to handle background calls and queuing of worker jobs with Resque
|
4
|
+
# This will only be available if the Resque constant is defined when you require 'qmin'
|
5
|
+
# If somehow this is not working for you, add:
|
6
|
+
# require 'qmin/resque/background_call_job'
|
7
|
+
# require 'qmin/resque/base_job'
|
8
|
+
# require 'qmin/strategy/resque'
|
9
|
+
# where needed
|
10
|
+
#
|
11
|
+
# Methods that are annotated with "background_method" will be handled by
|
12
|
+
# the generic Qmin::Resque::BackgroundCallJob worker.
|
13
|
+
# It will automatically create a queue name based on the class and method it is working on.
|
14
|
+
#
|
15
|
+
# So for example:
|
16
|
+
#
|
17
|
+
# * Jobs::Profile#update_facebook_friends is annotated to be a background_method
|
18
|
+
# * 'qmin_resque_background_call_job_jobs_profile_update_facebook_friends' will be the queue name
|
19
|
+
#
|
20
|
+
class Resque
|
21
|
+
# Just passes
|
22
|
+
def enqueue(worker_class, *args)
|
23
|
+
::Resque.enqueue worker_class, *args
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param klass
|
27
|
+
# Queue name is based on worker class and method name
|
28
|
+
# By default Qmin::Resque::BackgroundCallJob will handle jobs
|
29
|
+
def background_call(instance, method_name)
|
30
|
+
::Resque.enqueue_to(queue_name(instance.class.name, method_name), self.class.job_class, instance.class.name, method_name, instance.id)
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def queue_name(klass_name, method_name)
|
36
|
+
[
|
37
|
+
self.class.job_class.name.to_queue_name,
|
38
|
+
klass_name.to_queue_name,
|
39
|
+
method_name
|
40
|
+
].join('_')
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.job_class
|
44
|
+
::Qmin::Resque::BackgroundCallJob
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/qmin/version.rb
ADDED
data/lib/qmin.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'qmin/qmin'
|
2
|
+
require 'qmin/core_ext/class'
|
3
|
+
require 'qmin/core_ext/string'
|
4
|
+
require 'qmin/strategy/inline'
|
5
|
+
require 'qmin/strategy/noop'
|
6
|
+
|
7
|
+
if defined? Resque
|
8
|
+
require 'qmin/resque/background_call_job'
|
9
|
+
require 'qmin/resque/base_job'
|
10
|
+
require 'qmin/strategy/resque'
|
11
|
+
end
|
12
|
+
|
13
|
+
module Qmin
|
14
|
+
MustDefineStrategyError = Class.new(StandardError)
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def self.method_name_for_instance(instance, method_name)
|
19
|
+
if instance.respond_to? :"#{method_name}_without_qmin"
|
20
|
+
:"#{method_name}_without_qmin"
|
21
|
+
else
|
22
|
+
method_name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/qmin.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'qmin/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'qmin'
|
8
|
+
spec.version = Qmin::VERSION
|
9
|
+
spec.authors = ['the-architect']
|
10
|
+
spec.email = ['marcel.scherf@epicteams.com']
|
11
|
+
spec.description = %q{Simple framwork to coordinate background tasks}
|
12
|
+
spec.summary = %q{Simple framwork to coordinate background tasks}
|
13
|
+
spec.homepage = 'https://github.com/the-architect/qmin'
|
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 'debugger'
|
24
|
+
spec.add_development_dependency 'rspec'
|
25
|
+
spec.add_development_dependency 'mocha'
|
26
|
+
spec.add_development_dependency 'simplecov'
|
27
|
+
spec.add_development_dependency 'simplecov-html'
|
28
|
+
spec.add_development_dependency 'metric_fu'
|
29
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Class do
|
4
|
+
it 'responds to background_method' do
|
5
|
+
Class.new.should respond_to(:background_method)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'annotates methods' do
|
9
|
+
klass = Class.new do
|
10
|
+
def foo; end
|
11
|
+
background_method :foo
|
12
|
+
|
13
|
+
def bar; end
|
14
|
+
background_method :bar
|
15
|
+
end
|
16
|
+
|
17
|
+
subject = klass.new
|
18
|
+
|
19
|
+
subject.should.respond_to? :foo
|
20
|
+
subject.should.respond_to? :foo_without_qmin
|
21
|
+
|
22
|
+
subject.should.respond_to? :bar
|
23
|
+
subject.should.respond_to? :bar_without_qmin
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
it 'annotates many methods' do
|
28
|
+
klass = Class.new do
|
29
|
+
def foo; end
|
30
|
+
def bar; end
|
31
|
+
|
32
|
+
background_methods :foo, :bar
|
33
|
+
end
|
34
|
+
|
35
|
+
subject = klass.new
|
36
|
+
|
37
|
+
subject.should.respond_to? :foo
|
38
|
+
subject.should.respond_to? :foo_without_qmin
|
39
|
+
|
40
|
+
subject.should.respond_to? :bar
|
41
|
+
subject.should.respond_to? :bar_without_qmin
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'does not annotate method twice' do
|
45
|
+
klass = Class.new do
|
46
|
+
def foo; end
|
47
|
+
background_method :foo
|
48
|
+
background_method :foo
|
49
|
+
end
|
50
|
+
|
51
|
+
subject = klass.new
|
52
|
+
|
53
|
+
subject.should.respond_to? :foo
|
54
|
+
subject.should.respond_to? :foo_without_qmin
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not annotate original method' do
|
58
|
+
klass = Class.new do
|
59
|
+
def foo; end
|
60
|
+
background_method :foo
|
61
|
+
background_method :foo_without_qmin
|
62
|
+
end
|
63
|
+
|
64
|
+
subject = klass.new
|
65
|
+
|
66
|
+
subject.should.respond_to? :foo
|
67
|
+
subject.should.respond_to? :foo_without_qmin
|
68
|
+
subject.should_not.respond_to? :foo_without_qmin_without_qmin
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
it '.snake_case' do
|
5
|
+
'MyClassName'.snake_case.should eql 'my_class_name'
|
6
|
+
'Module::MyClassName'.snake_case.should eql 'module/my_class_name'
|
7
|
+
end
|
8
|
+
|
9
|
+
it '.constantize' do
|
10
|
+
'TestClass'.constantize.should eql TestClass
|
11
|
+
end
|
12
|
+
|
13
|
+
it '.to_queue_name' do
|
14
|
+
'MyClassName'.to_queue_name.should eql 'my_class_name'
|
15
|
+
'Module::MyClassName'.to_queue_name.should eql 'module_my_class_name'
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path '../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Qmin do
|
4
|
+
|
5
|
+
it 'loads strategies' do
|
6
|
+
defined?(Qmin::Strategy::Inline).should eql('constant')
|
7
|
+
defined?(Qmin::Strategy::Noop).should eql('constant')
|
8
|
+
defined?(Qmin::Strategy::Resque).should eql('constant')
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'initialize' do
|
12
|
+
it 'raises error if no strategy defined' do
|
13
|
+
Qmin::Qmin.default_strategy = nil
|
14
|
+
|
15
|
+
-> {
|
16
|
+
Qmin::Qmin.new
|
17
|
+
}.should raise_error(Qmin::MustDefineStrategyError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises error for current if no strategy defined' do
|
21
|
+
Qmin::Qmin.default_strategy = nil
|
22
|
+
Qmin::Qmin.class_variable_set(:@@current, nil)
|
23
|
+
|
24
|
+
-> {
|
25
|
+
Qmin::Qmin.current
|
26
|
+
}.should raise_error(Qmin::MustDefineStrategyError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'background_method' do
|
31
|
+
it 'aliases background methods'do
|
32
|
+
subject = BackgroundTestClass.new
|
33
|
+
|
34
|
+
subject.should respond_to(:action)
|
35
|
+
subject.should respond_to(:action_without_qmin)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'calls aliased method' do
|
39
|
+
subject = BackgroundTestClass.new
|
40
|
+
|
41
|
+
Qmin::Qmin.expects(:background_call).with(subject, :action)
|
42
|
+
subject.action
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'calls original method with inline strategy' do
|
46
|
+
subject = BackgroundTestClass.new
|
47
|
+
subject.action
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'enqueue' do
|
52
|
+
it 'enqueues job' do
|
53
|
+
Qmin::Qmin.new(Qmin::Strategy::Inline)
|
54
|
+
|
55
|
+
TestJob.expects(:perform).with(1,2,3)
|
56
|
+
Qmin::Qmin.enqueue TestJob, 1,2,3
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Qmin::Resque::BackgroundCallJob do
|
4
|
+
let(:id){ 123 }
|
5
|
+
|
6
|
+
before do
|
7
|
+
Qmin::Qmin.new(Qmin::Strategy::Resque)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'calls method on instance' do
|
11
|
+
::Qmin::Resque::BackgroundCallJob.new(TestClass, :action, id).perform.should eql id
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'calls method on instance wrapped in background_call_job' do
|
15
|
+
Qmin::Qmin.new(Qmin::Strategy::Resque)
|
16
|
+
|
17
|
+
BackgroundTestClass.new(id).action
|
18
|
+
::Resque.queue['qmin_resque_background_call_job_background_test_class_action'][0].should eql ({:class => Qmin::Resque::BackgroundCallJob, :args => ['BackgroundTestClass', :action, 123]})
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'delegates perform to instance' do
|
22
|
+
Qmin::Qmin.new(Qmin::Strategy::Resque)
|
23
|
+
|
24
|
+
Qmin::Resque::BackgroundCallJob.any_instance.expects(:perform)
|
25
|
+
Qmin::Resque::BackgroundCallJob.perform(BackgroundTestClass, 'action', 123)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Qmin::Resque::BaseJob do
|
4
|
+
|
5
|
+
it 'performs test job' do
|
6
|
+
Qmin::Qmin.new(Qmin::Strategy::Resque)
|
7
|
+
TestJob.perform(10).should eql 10
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'demands find method on model class' do
|
11
|
+
Qmin::Qmin.new(Qmin::Strategy::Resque)
|
12
|
+
-> {
|
13
|
+
Class.new(Qmin::Resque::BaseJob) do
|
14
|
+
model Class
|
15
|
+
end
|
16
|
+
}.should raise_error(Qmin::Resque::BaseJob::MustRespondToFindMethodError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'demands implementation of perform method' do
|
20
|
+
instance = Class.new(Qmin::Resque::BaseJob).new(TestClass, :action, 123)
|
21
|
+
-> {
|
22
|
+
instance.perform
|
23
|
+
}.should raise_error(Qmin::Resque::BaseJob::ImplementationMissing)
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'supplies instance' do
|
28
|
+
TestJob.new(10).instance.should be_a_kind_of(TestClass)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Qmin::Strategy::Inline do
|
4
|
+
subject{ Qmin::Strategy::Inline.new }
|
5
|
+
|
6
|
+
it 'enqueues job' do
|
7
|
+
WorkerClass.expects(:perform).with(1,2,3)
|
8
|
+
subject.enqueue(WorkerClass, 1,2,3)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'calls background call method' do
|
12
|
+
subject.background_call(BackgroundTestClass.new, :action).should eql 123
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Qmin::Strategy::Noop do
|
4
|
+
subject{ Qmin::Strategy::Noop.new }
|
5
|
+
|
6
|
+
it 'enqueues job' do
|
7
|
+
WorkerClass.expects(:perform).never
|
8
|
+
subject.enqueue(WorkerClass, 1,2,3)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'calls background call method' do
|
12
|
+
BackgroundTestClass.expects(:action).never
|
13
|
+
subject.background_call(BackgroundTestClass, :action, 123).should
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path '../../spec_helper', File.dirname(__FILE__)
|
2
|
+
|
3
|
+
describe Qmin::Strategy::Resque do
|
4
|
+
subject{ Qmin::Strategy::Resque.new }
|
5
|
+
|
6
|
+
it 'creates background_call_job' do
|
7
|
+
::Resque.expects(:enqueue_to).with('qmin_resque_background_call_job_test_class_action', Qmin::Resque::BackgroundCallJob, 'TestClass', :action, 555)
|
8
|
+
subject.background_call(TestClass.new(555), :action)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'enqueues job' do
|
12
|
+
::Resque.expects(:enqueue).with(TestClass, 1, 2, 3)
|
13
|
+
subject.enqueue(TestClass, 1, 2, 3)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'pushes to specified queue' do
|
17
|
+
subject.enqueue(TestJob, 1, 2, 3)
|
18
|
+
::Resque.queue['test_job'].should eql [{:class=>TestJob, :args=>[1, 2, 3]}]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter '/spec'
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'rspec/autorun'
|
7
|
+
|
8
|
+
# mock Resque behavior
|
9
|
+
class Resque
|
10
|
+
def self.enqueue_to(queue_name, worker, *args)
|
11
|
+
queue[queue_name].push({ :class => worker, :args => args })
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.enqueue(worker_class, *args)
|
15
|
+
queue[queue_for_class(worker_class)].push({ :class => worker_class, :args => args })
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.queue_for_class(klass)
|
19
|
+
klass.instance_variable_get(:@queue) || (klass.respond_to?(:queue) and klass.queue)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.queue
|
23
|
+
@@queue ||= Hash.new{|h,k| h[k] = Array.new }
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.reset_queue!
|
27
|
+
@@queue = Hash.new{|h,k| h[k] = Array.new }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require File.expand_path '../lib/qmin', File.dirname(__FILE__)
|
32
|
+
|
33
|
+
RSpec.configure do |config|
|
34
|
+
config.mock_with :mocha
|
35
|
+
config.order = 'random'
|
36
|
+
|
37
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
38
|
+
config.filter_run focus: true
|
39
|
+
config.filter_run_excluding :wip => true
|
40
|
+
config.run_all_when_everything_filtered = true
|
41
|
+
|
42
|
+
config.before(:each) do
|
43
|
+
Resque.reset_queue!
|
44
|
+
Qmin::Qmin.new(Qmin::Strategy::Inline)
|
45
|
+
end
|
46
|
+
|
47
|
+
config.after(:each) do
|
48
|
+
Qmin::Qmin.new(Qmin::Strategy::Inline)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class WorkerClass
|
53
|
+
def initialize(*args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.perform(*args)
|
57
|
+
new(*args).perform
|
58
|
+
end
|
59
|
+
|
60
|
+
def perform
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class TestClass
|
66
|
+
def action
|
67
|
+
@id
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.name
|
71
|
+
'TestClass'
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.find(id)
|
75
|
+
new(id)
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize(id = 123)
|
79
|
+
@id = id
|
80
|
+
end
|
81
|
+
attr_reader :id
|
82
|
+
end
|
83
|
+
|
84
|
+
class BackgroundTestClass < TestClass
|
85
|
+
def self.name
|
86
|
+
'BackgroundTestClass'
|
87
|
+
end
|
88
|
+
|
89
|
+
background_method :action
|
90
|
+
end
|
91
|
+
|
92
|
+
class TestJob < Qmin::Resque::BaseJob
|
93
|
+
model TestClass
|
94
|
+
|
95
|
+
def perform
|
96
|
+
instance.id
|
97
|
+
end
|
98
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qmin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- the-architect
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-06-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: debugger
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov-html
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: metric_fu
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Simple framwork to coordinate background tasks
|
126
|
+
email:
|
127
|
+
- marcel.scherf@epicteams.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- .gitignore
|
133
|
+
- .rspec
|
134
|
+
- Gemfile
|
135
|
+
- LICENSE.txt
|
136
|
+
- README.md
|
137
|
+
- Rakefile
|
138
|
+
- lib/qmin.rb
|
139
|
+
- lib/qmin/core_ext/class.rb
|
140
|
+
- lib/qmin/core_ext/string.rb
|
141
|
+
- lib/qmin/qmin.rb
|
142
|
+
- lib/qmin/resque/background_call_job.rb
|
143
|
+
- lib/qmin/resque/base_job.rb
|
144
|
+
- lib/qmin/strategy/inline.rb
|
145
|
+
- lib/qmin/strategy/noop.rb
|
146
|
+
- lib/qmin/strategy/resque.rb
|
147
|
+
- lib/qmin/version.rb
|
148
|
+
- qmin.gemspec
|
149
|
+
- spec/qmin/core_ext/class_spec.rb
|
150
|
+
- spec/qmin/core_ext/string_spec.rb
|
151
|
+
- spec/qmin/qmin_spec.rb
|
152
|
+
- spec/qmin/resque/background_call_job_spec.rb
|
153
|
+
- spec/qmin/resque/base_job_spec.rb
|
154
|
+
- spec/qmin/strategy/inline_spec.rb
|
155
|
+
- spec/qmin/strategy/noop_spec.rb
|
156
|
+
- spec/qmin/strategy/resque_spec.rb
|
157
|
+
- spec/spec_helper.rb
|
158
|
+
homepage: https://github.com/the-architect/qmin
|
159
|
+
licenses:
|
160
|
+
- MIT
|
161
|
+
metadata: {}
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - '>='
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - '>='
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
requirements: []
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 2.0.2
|
179
|
+
signing_key:
|
180
|
+
specification_version: 4
|
181
|
+
summary: Simple framwork to coordinate background tasks
|
182
|
+
test_files:
|
183
|
+
- spec/qmin/core_ext/class_spec.rb
|
184
|
+
- spec/qmin/core_ext/string_spec.rb
|
185
|
+
- spec/qmin/qmin_spec.rb
|
186
|
+
- spec/qmin/resque/background_call_job_spec.rb
|
187
|
+
- spec/qmin/resque/base_job_spec.rb
|
188
|
+
- spec/qmin/strategy/inline_spec.rb
|
189
|
+
- spec/qmin/strategy/noop_spec.rb
|
190
|
+
- spec/qmin/strategy/resque_spec.rb
|
191
|
+
- spec/spec_helper.rb
|