sidetiq 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ lib/*.bundle
2
+ coverage
3
+ rdoc
4
+ doc
5
+ .yardoc
6
+ .bundle
7
+ Gemfile.lock
8
+ pkg
9
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ before_script:
6
+ - bundle exec rake compile
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development do
4
+ gem 'rake'
5
+ gem 'rake-compiler'
6
+ gem 'sinatra', require: false
7
+ gem 'slim', require: false
8
+ end
9
+
10
+ group :test do
11
+ gem 'simplecov'
12
+ gem 'mocha'
13
+ gem 'rack-test'
14
+ end
15
+
16
+ gemspec
17
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2012 by Tobias Svensson <tobias@musicglue.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+ Sidetiq
2
+ =======
3
+
4
+ [![Build Status](https://travis-ci.org/tobiassvn/sidetiq.png)](https://travis-ci.org/tobiassvn/sidetiq)
5
+
6
+ Recurring jobs for [Sidekiq](http://mperham.github.com/sidekiq/).
7
+
8
+ ## DESCRIPTION
9
+
10
+ Sidetiq provides a simple API for defining recurring workers for Sidekiq.
11
+
12
+ - Flexible DSL based on [ice_cube](http://seejohnrun.github.com/ice_cube/)
13
+
14
+ - High-resolution timer using `clock_gettime(3)` (or `mach_absolute_time()` on
15
+ Apple Mac OS X), allowing for accurate sub-second clock ticks.
16
+
17
+ - Sidetiq uses a locking mechanism (based on `setnx` and `pexpire`) internally
18
+ so Sidetiq clocks can run in each Sidekiq process without interfering with
19
+ each other (tested with sub-second polling of scheduled jobs by Sidekiq and
20
+ Sidetiq clock rates above 100hz).
21
+
22
+ ## DEPENDENCIES
23
+
24
+ - [Sidekiq](http://mperham.github.com/sidekiq/)
25
+ - [ice_cube](http://seejohnrun.github.com/ice_cube/)
26
+
27
+ ## INSTALLATION
28
+
29
+ The best way to install Sidetiq is with RubyGems:
30
+
31
+ $ [sudo] gem install sidetiq
32
+
33
+ If you're installing from source, you can use [Bundler](http://gembundler.com/)
34
+ to pick up all the gems ([more info](http://gembundler.com/bundle_install.html)):
35
+
36
+ $ bundle install
37
+
38
+ ## GETTING STARTED
39
+
40
+ Defining recurring jobs is simple:
41
+
42
+ ```ruby
43
+ class MyWorker
44
+ include Sidekiq::Worker
45
+ include Sidetiq::Schedulable
46
+
47
+ # Daily at midnight
48
+ tiq { daily }
49
+ end
50
+ ```
51
+
52
+ It also is possible to define multiple scheduling rules for a worker:
53
+
54
+ ```ruby
55
+ class MyWorker
56
+ include Sidekiq::Worker
57
+ include Sidetiq::Schedulable
58
+
59
+ tiq do
60
+ # Every third year in March
61
+ yearly(3).month_of_year(:march)
62
+
63
+ # Every second year in February
64
+ yearly(2).month_of_year(:february)
65
+ end
66
+ end
67
+ ```
68
+
69
+ Or complex schedules:
70
+
71
+ ```ruby
72
+ class MyWorker
73
+ include Sidekiq::Worker
74
+ include Sidetiq::Schedulable
75
+
76
+ # Every other month on the first monday and last tuesday at 12 o'clock.
77
+ tiq { monthly(2).day_of_week(1 => [1], 2 => [-1]).hour_of_day(12) }
78
+ end
79
+ ```
80
+
81
+ To start Sidetiq, simply call `Sidetiq::Clock.start!` in a server specific
82
+ configuration block:
83
+
84
+ ```ruby
85
+ Sidekiq.configure_server do |config|
86
+ Sidetiq::Clock.start!
87
+ end
88
+ ```
89
+
90
+ Additionally, Sidetiq includes a middleware that will check if the clock
91
+ thread is still alive and restart it if necessary.
92
+
93
+ ## CONFIGURATION
94
+
95
+ ```ruby
96
+ Sidetiq.configure do |config|
97
+ # Thread priority of the clock thread (default: Thread.main.priority as
98
+ # defined when Sidetiq is loaded).
99
+ config.priority = 2
100
+
101
+ # Clock tick resolution in seconds (default: 1).
102
+ config.resolution = 0.5
103
+
104
+ # Clock locking key expiration in ms (default: 1000).
105
+ config.lock_expire = 100
106
+
107
+ # When `true` uses UTC instead of local times (default: false)
108
+ config.utc = false
109
+ end
110
+ ```
111
+
112
+ ## WEB EXTENSION
113
+
114
+ Sidetiq includes an extension for Sidekiq's web interface. It will not be
115
+ loaded by default, so it will have to be required manually:
116
+
117
+ ```ruby
118
+ require 'sidetiq/web'
119
+ ```
120
+
121
+ ### SCREENSHOT
122
+
123
+ ![Screenshot](http://f.cl.ly/items/1P2u1v091F3V1n381g2I/Screen%20Shot%202013-02-01%20at%2012.16.17.png)
124
+
125
+ ## CONTRIBUTE
126
+
127
+ If you'd like to contribute to Sidetiq, start by forking my repo on GitHub:
128
+
129
+ [http://github.com/tobiassvn/sidetiq](http://github.com/tobiassvn/sidetiq)
130
+
131
+ To get all of the dependencies, install the gem first. The best way to get
132
+ your changes merged back into core is as follows:
133
+
134
+ 1. Clone down your fork
135
+ 1. Create a thoughtfully named topic branch to contain your change
136
+ 1. Write some code
137
+ 1. Add tests and make sure everything still passes by running `rake`
138
+ 1. If you are adding new functionality, document it in the README
139
+ 1. Do not change the version number, I will do that on my end
140
+ 1. If necessary, rebase your commits into logical chunks, without errors
141
+ 1. Push the branch up to GitHub
142
+ 1. Send a pull request to the tobiassvn/sidetiq project.
143
+
144
+ ## LICENSE
145
+
146
+ Sidetiq is released under the MIT License. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+ require 'rake/extensiontask'
4
+
5
+ Rake::ExtensionTask.new('sidetiq_ext')
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.pattern = 'test/**/test_*.rb'
9
+ end
10
+
11
+ task default: :test
@@ -0,0 +1,18 @@
1
+ # Run with `sidekiq -r /path/to/simple.rb`
2
+
3
+ require 'sidekiq'
4
+ require 'sidetiq'
5
+
6
+ Sidekiq.options[:poll_interval] = 1
7
+
8
+ class MyWorker
9
+ include Sidekiq::Worker
10
+ include Sidetiq::Schedulable
11
+
12
+ tiq { secondly }
13
+
14
+ def perform(*args)
15
+ Sidekiq.logger.info "#perform"
16
+ end
17
+ end
18
+
@@ -0,0 +1,4 @@
1
+ require 'rbconfig'
2
+ require 'mkmf'
3
+ create_makefile('sidetiq_ext')
4
+
@@ -0,0 +1,97 @@
1
+ #include <ruby.h>
2
+ #include <assert.h>
3
+ #include "sidetiq_ext.h"
4
+
5
+ #ifdef __APPLE__
6
+ #include <sys/time.h>
7
+ #include <sys/resource.h>
8
+ #include <mach/mach.h>
9
+ #include <mach/clock.h>
10
+ #include <mach/mach_time.h>
11
+ #include <errno.h>
12
+ #include <unistd.h>
13
+ #include <sched.h>
14
+ #else
15
+ #include <time.h>
16
+ #endif
17
+
18
+ VALUE msidetiq;
19
+ VALUE esidetiq_error;
20
+ VALUE csidetiq_clock;
21
+
22
+ #ifdef __APPLE__
23
+ static mach_timebase_info_data_t clock_gettime_inf;
24
+
25
+ typedef enum {
26
+ CLOCK_REALTIME,
27
+ CLOCK_MONOTONIC,
28
+ CLOCK_PROCESS_CPUTIME_ID,
29
+ CLOCK_THREAD_CPUTIME_ID
30
+ } clockid_t;
31
+
32
+ int clock_gettime(clockid_t clk_id, struct timespec *tp)
33
+ {
34
+ kern_return_t ret;
35
+ clock_serv_t clk;
36
+ clock_id_t clk_serv_id;
37
+ mach_timespec_t tm;
38
+ uint64_t start, end, delta, nano;
39
+ int retval = -1;
40
+
41
+ switch (clk_id) {
42
+ case CLOCK_REALTIME:
43
+ case CLOCK_MONOTONIC:
44
+ clk_serv_id = clk_id == CLOCK_REALTIME ? CALENDAR_CLOCK : SYSTEM_CLOCK;
45
+ if (KERN_SUCCESS == (ret = host_get_clock_service(mach_host_self(), clk_serv_id, &clk))) {
46
+ if (KERN_SUCCESS == (ret = clock_get_time(clk, &tm))) {
47
+ tp->tv_sec = tm.tv_sec;
48
+ tp->tv_nsec = tm.tv_nsec;
49
+ retval = 0;
50
+ }
51
+ }
52
+ if (KERN_SUCCESS != ret) {
53
+ errno = EINVAL;
54
+ retval = -1;
55
+ }
56
+ break;
57
+ case CLOCK_PROCESS_CPUTIME_ID:
58
+ case CLOCK_THREAD_CPUTIME_ID:
59
+ start = mach_absolute_time();
60
+ if (clk_id == CLOCK_PROCESS_CPUTIME_ID) {
61
+ getpid();
62
+ } else {
63
+ sched_yield();
64
+ }
65
+ end = mach_absolute_time();
66
+ delta = end - start;
67
+ if (0 == clock_gettime_inf.denom) {
68
+ mach_timebase_info(&clock_gettime_inf);
69
+ }
70
+ nano = delta * clock_gettime_inf.numer / clock_gettime_inf.denom;
71
+ tp->tv_sec = nano * 1e-9;
72
+ tp->tv_nsec = nano - (tp->tv_sec * 1e9);
73
+ retval = 0;
74
+ break;
75
+ default:
76
+ errno = EINVAL;
77
+ retval = -1;
78
+ }
79
+ return retval;
80
+ }
81
+ #endif
82
+
83
+ static VALUE sidetiq_gettime(VALUE self)
84
+ {
85
+ struct timespec time;
86
+ assert(clock_gettime(CLOCK_REALTIME, &time) == 0);
87
+ return rb_time_nano_new(time.tv_sec, time.tv_nsec);
88
+ }
89
+
90
+ void Init_sidetiq_ext()
91
+ {
92
+ msidetiq = rb_define_module("Sidetiq");
93
+ esidetiq_error = rb_define_class_under(msidetiq, "Error", rb_eStandardError);
94
+ csidetiq_clock = rb_define_class_under(msidetiq, "Clock", rb_cObject);
95
+ rb_define_private_method(csidetiq_clock, "clock_gettime", sidetiq_gettime, 0);
96
+ }
97
+
@@ -0,0 +1,17 @@
1
+ #ifndef __SIDETIQ_EXT_H__
2
+ #define __SIDETIQ_EXT_H__
3
+ #include <ruby.h>
4
+
5
+ void Init_sidetiq_ext();
6
+ static VALUE sidetiq_gettime(VALUE self);
7
+
8
+ /* module Sidetiq */
9
+ extern VALUE msidetiq;
10
+
11
+ /* class Sidetiq::Error < StandardError */
12
+ extern VALUE esidetiq_error;
13
+
14
+ /* class Sidetiq::Clock */
15
+ extern VALUE csidetiq_clock;
16
+
17
+ #endif
@@ -0,0 +1,105 @@
1
+ module Sidetiq
2
+ configure do |config|
3
+ config.priority = Thread.main.priority
4
+ config.resolution = 1
5
+ config.lock_expire = 1000
6
+ config.utc = false
7
+ end
8
+
9
+ class Clock
10
+ include Singleton
11
+ include MonitorMixin
12
+
13
+ START_TIME = Sidetiq.config.utc ? Time.utc(2010, 1, 1) : Time.local(2010, 1, 1)
14
+
15
+ attr_reader :schedules, :thread
16
+
17
+ def self.method_missing(meth, *args, &block)
18
+ instance.__send__(meth, *args, &block)
19
+ end
20
+
21
+ def initialize
22
+ super
23
+ @schedules = {}
24
+ end
25
+
26
+ def schedule_for(worker)
27
+ schedules[worker] ||= Sidetiq::Schedule.new(START_TIME)
28
+ end
29
+
30
+ def tick
31
+ @tick = gettime
32
+ synchronize do
33
+ schedules.each do |worker, schedule|
34
+ if schedule.schedule_next?(@tick)
35
+ enqueue(worker, schedule.next_occurrence(@tick))
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def gettime
42
+ Sidetiq.config.utc ? clock_gettime.utc : clock_gettime
43
+ end
44
+
45
+ def start!
46
+ return if ticking?
47
+
48
+ Sidekiq.logger.info "Sidetiq::Clock start"
49
+ @thread = Thread.start { clock { tick } }
50
+ @thread.abort_on_exception = true
51
+ @thread.priority = Sidetiq.config.resolution
52
+ end
53
+
54
+ def stop!
55
+ if ticking?
56
+ @thread.kill
57
+ Sidekiq.logger.info "Sidetiq::Clock stop"
58
+ end
59
+ end
60
+
61
+ def ticking?
62
+ @thread && @thread.alive?
63
+ end
64
+
65
+ private
66
+
67
+ def enqueue(worker, time)
68
+ key = "sidetiq:#{worker.name}"
69
+
70
+ synchronize_clockworks("#{key}:lock") do |redis|
71
+ status = redis.get(key)
72
+
73
+ if status.nil? || status.to_f < time.to_f
74
+ time_f = time.to_f
75
+ Sidekiq.logger.info "Sidetiq::Clock enqueue #{worker.name} (at: #{time_f})"
76
+ redis.set(key, time_f)
77
+ worker.perform_at(time)
78
+ end
79
+ end
80
+ end
81
+
82
+ def synchronize_clockworks(lock)
83
+ Sidekiq.redis do |redis|
84
+ if redis.setnx(lock, 1)
85
+ Sidekiq.logger.debug "Sidetiq::Clock lock #{lock} #{Thread.current.inspect}"
86
+
87
+ redis.pexpire(lock, Sidetiq.config.lock_expire)
88
+ yield redis
89
+ redis.del(lock)
90
+
91
+ Sidekiq.logger.debug "Sidetiq::Clock unlock #{lock} #{Thread.current.inspect}"
92
+ end
93
+ end
94
+ end
95
+
96
+ def clock
97
+ loop do
98
+ yield
99
+ Thread.pass
100
+ sleep Sidetiq.config.resolution
101
+ end
102
+ end
103
+ end
104
+ end
105
+
@@ -0,0 +1,14 @@
1
+ module Sidetiq
2
+ class << self
3
+ attr_writer :config
4
+
5
+ def configure
6
+ yield config
7
+ end
8
+
9
+ def config
10
+ @config ||= OpenStruct.new
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,20 @@
1
+ module Sidetiq
2
+ class Middleware
3
+ def initialize
4
+ @clock = Sidetiq::Clock.instance
5
+ end
6
+
7
+ def call(*args)
8
+ # Restart the clock if the thread died.
9
+ @clock.start! if !@clock.ticking?
10
+ yield
11
+ end
12
+ end
13
+ end
14
+
15
+ Sidekiq.configure_server do |config|
16
+ config.server_middleware do |chain|
17
+ chain.add Sidetiq::Middleware
18
+ end
19
+ end
20
+
@@ -0,0 +1,17 @@
1
+ module Sidetiq
2
+ module Schedulable
3
+ module ClassMethods
4
+ def tiq(&block)
5
+ clock = Sidetiq::Clock.instance
6
+ clock.synchronize do
7
+ clock.schedule_for(self).instance_eval(&block)
8
+ end
9
+ end
10
+ end
11
+
12
+ def self.included(klass)
13
+ klass.extend(Sidetiq::Schedulable::ClassMethods)
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,22 @@
1
+ module Sidetiq
2
+ class Schedule < IceCube::Schedule
3
+ def method_missing(meth, *args, &block)
4
+ if IceCube::Rule.respond_to?(meth)
5
+ rule = IceCube::Rule.send(meth, *args, &block)
6
+ add_recurrence_rule(rule)
7
+ rule
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ def schedule_next?(time)
14
+ if @last_scheduled != (no = next_occurrence(time))
15
+ @last_scheduled = no
16
+ return true
17
+ end
18
+ false
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,10 @@
1
+ module Sidetiq
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 2
6
+
7
+ STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
+ end
9
+ end
10
+
@@ -0,0 +1,23 @@
1
+ header.row
2
+ .span5
3
+ h3 Recurring Jobs
4
+
5
+ - if @schedules.length > 0
6
+ table class="table table-striped table-bordered table-white" style="width: 100%; margin: 0; table-layout:fixed;"
7
+ thead
8
+ th style="width: 50%" Worker
9
+ th style="width: 20%" Queue
10
+ th style="width: 20%" Next Run
11
+ th style="width: 10%" Actions
12
+ - @schedules.each do |worker, schedule|
13
+ tr
14
+ td
15
+ = worker.name
16
+ td= worker.get_sidekiq_options['queue']
17
+ td
18
+ == relative_time(schedule.next_occurrence(@time))
19
+ td
20
+ a href="#{root_path}sidetiq/#{worker.name}" Details
21
+ - else
22
+ .alert.alert-success No recurring jobs found.
23
+
@@ -0,0 +1,40 @@
1
+ header.row
2
+ .span5
3
+ h3
4
+ ' Recurring Job:
5
+ = @worker.name
6
+
7
+ - if (recurrences = @schedule.recurrence_rules).length > 0
8
+ table class="table table-striped table-bordered table-white" style="width: 100%; margin: 0; table-layout:fixed;"
9
+ thead
10
+ th Recurrences
11
+ - recurrences.each do |rule|
12
+ tr
13
+ td
14
+ = rule.to_s
15
+
16
+ br
17
+
18
+ - if (exceptions = @schedule.exception_rules).length > 0
19
+ table class="table table-striped table-bordered table-white" style="width: 100%; margin: 0; table-layout:fixed;"
20
+ thead
21
+ th Exceptions
22
+ - exceptions.each do |rule|
23
+ tr
24
+ td
25
+ = rule.to_s
26
+
27
+ br
28
+
29
+ table class="table table-striped table-bordered table-white" style="width: 100%; margin: 0; table-layout:fixed;"
30
+ thead
31
+ th style="width: 25%" Next 10 runs
32
+ th style="width: 75%"
33
+ - @schedule.next_occurrences(10, @time).each do |time|
34
+ tr
35
+ td
36
+ time= time.getutc
37
+ td
38
+ == relative_time(time)
39
+
40
+ br
@@ -0,0 +1,47 @@
1
+ require 'sidekiq/web'
2
+
3
+ module Sidetiq
4
+ module Web
5
+
6
+ def self.registered(app)
7
+ app.helpers do
8
+ def find_template(view, *args, &block)
9
+ path = File.expand_path(File.join('..', 'views'), __FILE__)
10
+ super(path, *args, &block)
11
+ super
12
+ end
13
+ end
14
+
15
+ app.get "/sidetiq" do
16
+ clock = Sidetiq::Clock.instance
17
+ @schedules = clock.schedules
18
+ @time = clock.gettime
19
+ slim :sidetiq
20
+ end
21
+
22
+ app.get "/sidetiq/:name" do
23
+ halt 404 unless (name = params[:name])
24
+
25
+ clock = Sidetiq::Clock.instance
26
+ schedules = clock.schedules
27
+
28
+ @time = clock.gettime
29
+
30
+ @worker, @schedule = schedules.select do |worker, schedule|
31
+ worker.name == name
32
+ end.flatten
33
+
34
+ slim :sidetiq_details
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ Sidekiq::Web.register(Sidetiq::Web)
41
+
42
+ if Sidekiq::Web.tabs.is_a?(Array)
43
+ Sidekiq::Web.tabs << "sidetiq"
44
+ else
45
+ Sidekiq::Web.tabs["Sidetiq"] = "sidetiq"
46
+ end
47
+
data/lib/sidetiq.rb ADDED
@@ -0,0 +1,20 @@
1
+ # stdlib
2
+ require 'monitor'
3
+ require 'ostruct'
4
+ require 'singleton'
5
+
6
+ # gems
7
+ require 'ice_cube'
8
+ require 'sidekiq'
9
+
10
+ # c extensions
11
+ require 'sidetiq_ext'
12
+
13
+ # internal
14
+ require 'sidetiq/config'
15
+ require 'sidetiq/clock'
16
+ require 'sidetiq/middleware'
17
+ require 'sidetiq/schedule'
18
+ require 'sidetiq/schedulable'
19
+ require 'sidetiq/version'
20
+
data/sidetiq.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path(File.join('..', 'lib'), __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sidetiq/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "sidetiq"
8
+ gem.version = Sidetiq::VERSION::STRING
9
+ gem.authors = ["Tobias Svensson"]
10
+ gem.email = ["tob@tobiassvensson.co.uk"]
11
+ gem.description = "High-resolution job scheduler for Sidekiq"
12
+ gem.summary = gem.description
13
+ gem.homepage = "http://github.com/tobiassvn/sidetiq"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+ gem.extensions = ['ext/sidetiq_ext/extconf.rb']
21
+
22
+ gem.add_dependency 'sidekiq'
23
+ gem.add_dependency 'ice_cube'
24
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'simplecov'
2
+ SimpleCov.start { add_filter "/test/" }
3
+
4
+ require 'minitest/autorun'
5
+ require 'mocha/setup'
6
+ require 'rack/test'
7
+
8
+ require 'sidekiq'
9
+ require 'sidekiq/testing'
10
+
11
+ require 'sidetiq'
12
+ require 'sidetiq/web'
13
+
14
+ # Keep the test output clean
15
+ Sidekiq.logger = Logger.new(nil)
16
+
17
+ class Sidetiq::TestCase < MiniTest::Unit::TestCase
18
+ def setup
19
+ Sidekiq.redis { |r| r.flushall }
20
+ end
21
+
22
+ def clock
23
+ @clock ||= Sidetiq::Clock.instance
24
+ end
25
+ end
26
+
@@ -0,0 +1,65 @@
1
+ require_relative 'helper'
2
+
3
+ class TestClock < Sidetiq::TestCase
4
+ class FakeWorker;
5
+ end
6
+
7
+ def test_delegates_to_instance
8
+ Sidetiq::Clock.instance.expects(:foo).once
9
+ Sidetiq::Clock.foo
10
+ end
11
+
12
+ def test_start_stop
13
+ refute clock.ticking?
14
+ assert_nil clock.thread
15
+
16
+ clock.start!
17
+ Thread.pass
18
+ sleep 0.01
19
+
20
+ assert clock.ticking?
21
+ assert_kind_of Thread, clock.thread
22
+
23
+ clock.stop!
24
+ Thread.pass
25
+ sleep 0.01
26
+
27
+ refute clock.ticking?
28
+ refute clock.thread.alive?
29
+ end
30
+
31
+ def test_gettime_seconds
32
+ assert_equal clock.gettime.tv_sec, Time.now.tv_sec
33
+ end
34
+
35
+ def test_gettime_nsec
36
+ refute_nil clock.gettime.tv_nsec
37
+ end
38
+
39
+ def test_gettime_utc
40
+ refute clock.gettime.utc?
41
+ Sidetiq.config.utc = true
42
+ assert clock.gettime.utc?
43
+ Sidetiq.config.utc = false
44
+ end
45
+
46
+ def test_enqueues_jobs_by_schedule
47
+ schedule = Sidetiq::Schedule.new(Sidetiq::Clock::START_TIME)
48
+ schedule.daily
49
+
50
+ clock.stubs(:schedules).returns(FakeWorker => schedule)
51
+
52
+ FakeWorker.expects(:perform_at).times(10)
53
+
54
+ 10.times do |i|
55
+ clock.stubs(:gettime).returns(Time.local(2011, 1, i + 1, 1))
56
+ clock.tick
57
+ end
58
+
59
+ clock.stubs(:gettime).returns(Time.local(2011, 1, 10, 2))
60
+ clock.tick
61
+ clock.tick
62
+ clock.tick
63
+ end
64
+ end
65
+
@@ -0,0 +1,21 @@
1
+ require_relative 'helper'
2
+
3
+ class TestConfig < Sidetiq::TestCase
4
+ def setup
5
+ @saved = Sidetiq.config
6
+ Sidetiq.config = OpenStruct.new
7
+ end
8
+
9
+ def teardown
10
+ Sidetiq.config = @saved
11
+ end
12
+
13
+ def test_configure
14
+ Sidetiq.configure do |config|
15
+ config.test = 42
16
+ end
17
+
18
+ assert_equal 42, Sidetiq.config.test
19
+ end
20
+ end
21
+
@@ -0,0 +1,7 @@
1
+ require_relative 'helper'
2
+
3
+ class TestErrors < Sidetiq::TestCase
4
+ def test_error_superclass
5
+ assert_equal StandardError, Sidetiq::Error.superclass
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'helper'
2
+
3
+ class TestMiddleware < Sidetiq::TestCase
4
+ def middleware
5
+ Sidetiq::Middleware.new
6
+ end
7
+
8
+ def test_restarts_clock
9
+ clock.stubs(:ticking?).returns(false)
10
+ clock.expects(:start!).once
11
+ middleware.call {}
12
+
13
+ clock.stubs(:ticking?).returns(true)
14
+ clock.expects(:start!).never
15
+ middleware.call {}
16
+ end
17
+ end
18
+
@@ -0,0 +1,25 @@
1
+ require_relative 'helper'
2
+
3
+ class TestSchedule < Sidetiq::TestCase
4
+ def test_super
5
+ assert_equal IceCube::Schedule, Sidetiq::Schedule.superclass
6
+ end
7
+
8
+ def test_method_missing
9
+ sched = Sidetiq::Schedule.new
10
+ sched.daily
11
+ assert_equal "Daily", sched.to_s
12
+ end
13
+
14
+ def test_schedule_next?
15
+ sched = Sidetiq::Schedule.new
16
+
17
+ sched.daily
18
+
19
+ assert sched.schedule_next?(Time.now + (24 * 60 * 60))
20
+ refute sched.schedule_next?(Time.now + (24 * 60 * 60))
21
+ assert sched.schedule_next?(Time.now + (2 * 24 * 60 * 60))
22
+ refute sched.schedule_next?(Time.now + (2 * 24 * 60 * 60))
23
+ end
24
+ end
25
+
@@ -0,0 +1,21 @@
1
+ require_relative 'helper'
2
+
3
+ class TestVersion < Sidetiq::TestCase
4
+ def test_major
5
+ assert_instance_of Fixnum, Sidetiq::VERSION::MAJOR
6
+ end
7
+
8
+ def test_minor
9
+ assert_instance_of Fixnum, Sidetiq::VERSION::MINOR
10
+ end
11
+
12
+ def test_patch
13
+ assert_instance_of Fixnum, Sidetiq::VERSION::PATCH
14
+ end
15
+
16
+ def test_string
17
+ assert_equal Sidetiq::VERSION::STRING, [Sidetiq::VERSION::MAJOR,
18
+ Sidetiq::VERSION::MINOR, Sidetiq::VERSION::PATCH].compact.join('.')
19
+ end
20
+ end
21
+
data/test/test_web.rb ADDED
@@ -0,0 +1,58 @@
1
+ require_relative 'helper'
2
+
3
+ class TestWeb < Sidetiq::TestCase
4
+ include Rack::Test::Methods
5
+
6
+ class Worker
7
+ include Sidekiq::Worker
8
+ include Sidetiq::Schedulable
9
+
10
+ tiq do
11
+ daily(1)
12
+ yearly(2)
13
+ monthly(3)
14
+
15
+ add_exception_rule yearly.month_of_year(:february)
16
+ end
17
+ end
18
+
19
+ def app
20
+ Sidekiq::Web
21
+ end
22
+
23
+ def test_home_tab
24
+ get '/'
25
+ assert_equal 200, last_response.status
26
+ assert_match last_response.body, /Sidekiq/
27
+ assert_match last_response.body, /Sidetiq/
28
+ end
29
+
30
+ def test_sidetiq_page
31
+ get '/sidetiq'
32
+ assert_equal 200, last_response.status
33
+
34
+ clock.schedules.each do |worker, schedule|
35
+ assert_match last_response.body, /#{worker.name}/
36
+ assert_match last_response.body, /#{worker.get_sidekiq_options['queue']}/
37
+ end
38
+ end
39
+
40
+ def test_details_page
41
+ get "/sidetiq/#{Worker.name}"
42
+ assert_equal 200, last_response.status
43
+ schedule = clock.schedules[Worker]
44
+
45
+ schedule.recurrence_rules.each do |rule|
46
+ assert_match last_response.body, /#{rule.to_s}/
47
+ end
48
+
49
+ schedule.exception_rules.each do |rule|
50
+ assert_match last_response.body, /#{rule.to_s}/
51
+ end
52
+
53
+ schedule.next_occurrences(10).each do |time|
54
+ assert_match last_response.body, /#{time.getutc.to_s}/
55
+ end
56
+ end
57
+ end
58
+
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidetiq
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.2
6
+ platform: ruby
7
+ authors:
8
+ - Tobias Svensson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ none: false
21
+ name: sidekiq
22
+ type: :runtime
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ! '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ name: ice_cube
38
+ type: :runtime
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ description: High-resolution job scheduler for Sidekiq
47
+ email:
48
+ - tob@tobiassvensson.co.uk
49
+ executables: []
50
+ extensions:
51
+ - ext/sidetiq_ext/extconf.rb
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - .travis.yml
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - examples/simple.rb
61
+ - ext/sidetiq_ext/extconf.rb
62
+ - ext/sidetiq_ext/sidetiq_ext.c
63
+ - ext/sidetiq_ext/sidetiq_ext.h
64
+ - lib/sidetiq.rb
65
+ - lib/sidetiq/clock.rb
66
+ - lib/sidetiq/config.rb
67
+ - lib/sidetiq/middleware.rb
68
+ - lib/sidetiq/schedulable.rb
69
+ - lib/sidetiq/schedule.rb
70
+ - lib/sidetiq/version.rb
71
+ - lib/sidetiq/views/sidetiq.slim
72
+ - lib/sidetiq/views/sidetiq_details.slim
73
+ - lib/sidetiq/web.rb
74
+ - sidetiq.gemspec
75
+ - test/helper.rb
76
+ - test/test_clock.rb
77
+ - test/test_config.rb
78
+ - test/test_errors.rb
79
+ - test/test_middleware.rb
80
+ - test/test_schedule.rb
81
+ - test/test_version.rb
82
+ - test/test_web.rb
83
+ homepage: http://github.com/tobiassvn/sidetiq
84
+ licenses:
85
+ - MIT
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ none: false
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ none: false
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.23
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: High-resolution job scheduler for Sidekiq
108
+ test_files:
109
+ - test/helper.rb
110
+ - test/test_clock.rb
111
+ - test/test_config.rb
112
+ - test/test_errors.rb
113
+ - test/test_middleware.rb
114
+ - test/test_schedule.rb
115
+ - test/test_version.rb
116
+ - test/test_web.rb
117
+ has_rdoc: