sidecloq 0.1.0 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d0f6f9c7d36841096162c3d1ec7775220e58438
4
- data.tar.gz: 4d11bcc58feabca4f13c0cf05f6a8d34257e5d29
3
+ metadata.gz: c42da59e13d5181bbdaed20b903ea9ec5215d341
4
+ data.tar.gz: 80bcf519574b7937caa39c862305fedcec503587
5
5
  SHA512:
6
- metadata.gz: 328c80251970cb9899a5106394a5a0c206ce5317fb88be4d4f01092e6521d7f8fcd487535403d6bd917636739be852b0a0051f51ea25ab739dfbebef040ce894
7
- data.tar.gz: 3a956934c7415080ca8a68664471cab8e86707eb914b3ae4864f58b2ada039c47e7559c76054982f0e4780f58d87172079e8a4426be107f2d41e579e9c571bbd
6
+ metadata.gz: d285cf602bc72794c0f01854550fd9a6315d2ab740633e67880e3ec2704f327fbbed220c8f46c013112525d406a8e0ff3609ac68bdac5b9b7ea9dfb98088bd9e
7
+ data.tar.gz: 6e6af9ab2fb27b80aa41020bb8d9dbd48b967cde2c9d5e8f943b25be7974d27fdb5c6a30127455c336d96c4a5b330e3b8497e01a903277ee4c8533d0a3a0c0ea
data/.travis.yml CHANGED
@@ -16,3 +16,6 @@ matrix:
16
16
  allow_failures:
17
17
  - rvm: ruby-head
18
18
  - rvm: jruby-head
19
+ addons:
20
+ code_climate:
21
+ repo_token: dee2fdcb3562533038a22e862c0fc91a6f1fa38730cd2b77177655e5b9507c83
data/Gemfile CHANGED
@@ -6,3 +6,5 @@ platforms :mri do
6
6
  gem 'pry'
7
7
  gem 'minitest-utils'
8
8
  end
9
+
10
+ gem 'codeclimate-test-reporter', group: :test, require: false
data/README.md CHANGED
@@ -1,13 +1,41 @@
1
+ ![Sidecloq](clock_a_clock_on_the_side.png)
2
+
1
3
  # Sidecloq
2
4
 
3
5
  [![Build Status](https://travis-ci.org/mattyr/sidecloq.svg)](https://travis-ci.org/mattyr/sidecloq)
6
+ [![Gem Version](https://badge.fury.io/rb/sidecloq.svg)](https://badge.fury.io/rb/sidecloq)
7
+ [![Code Climate](https://codeclimate.com/github/mattyr/sidecloq/badges/gpa.svg)](https://codeclimate.com/github/mattyr/sidecloq)
8
+ [![Test Coverage](https://codeclimate.com/github/mattyr/sidecloq/badges/coverage.svg)](https://codeclimate.com/github/mattyr/sidecloq/coverage)
9
+ [![Dependency Status](https://gemnasium.com/mattyr/sidecloq.svg)](https://gemnasium.com/mattyr/sidecloq)
4
10
 
5
- Another recurring job extension for Sidekiq.
11
+ Recurring / Periodic / Scheduled / Cron job extension for
12
+ [Sidekiq](https://github.com/mperham/sidekiq)
6
13
 
7
14
  ## Why
8
15
 
9
- TODO: design principles, differences, inspiration (sidetiq,
10
- sidekiq-scheduler, resque-scheduler)
16
+ There are several options for running periodic tasks with Sidekiq,
17
+ including [Sidetiq](https://github.com/tobiassvn/sidetiq),
18
+ [sidekiq-scheduler](https://github.com/Moove-it/sidekiq-scheduler),
19
+ [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron), as well as
20
+ [Sidekiq Pro](http://sidekiq.org/products/pro). Each tackles the
21
+ problem slightly differently. Sidecloq is inspired by various facets
22
+ of these projects, as well as
23
+ [resque-scheduler](https://github.com/resque/resque-scheduler). I urge
24
+ you to take a look at all of these options to see what works best for
25
+ you.
26
+
27
+ Sidecloq is:
28
+
29
+ - **Lightweight:** Celluloid is not required. This coincides well with
30
+ Sidekiq 4, which no longer uses Celluloid.
31
+ - **Clean:** Sidecloq leverages only the public API of Sidekiq, and does
32
+ not pollute the Sidekiq namespace.
33
+ - **Easy to deploy:** Sidecloq boots with all Sideqik processes,
34
+ automatically. Leader election ensures only one process enqueues
35
+ jobs, and a new leader is automatically chosen should the current
36
+ leader die.
37
+ - **Easy to configure:** Schedule configuration is done in YAML, using
38
+ the familiar cron syntax. No special DSL or job class mixins required.
11
39
 
12
40
  ## Installation
13
41
 
@@ -17,20 +45,61 @@ Add this line to your application's Gemfile:
17
45
  gem 'sidecloq'
18
46
  ```
19
47
 
20
- Configure Sidecloq alongside your Sidekiq config. If using Rails, and
21
- your schedule is located at config/sidecloq.yml, you don't have to do
22
- anything (ie, omit this whole configuration block).
48
+ ## Configuration
49
+
50
+ ### Quickstart
51
+
52
+ Tell Sidecloq where your schedule file is located:
23
53
 
24
54
  ```ruby
25
55
  Sidcloq.configure do |config|
26
- config[:schedule_file] =
27
- File.join(Rails.root, "config/myschedule.yml")
56
+ config[:schedule_file] = "path/to/myschedule.yml"
28
57
  end
29
58
  ```
59
+ ### Rails
30
60
 
31
- TODO: configuration options
61
+ If using Rails, and your schedule is located at config/sidecloq.yml,
62
+ Sidecloq will find the schedule automatically (ie, you don't have to use
63
+ the above configuration block).
32
64
 
33
- TODO: schedule file format (like resque-scheduler)
65
+ ## Schedule file format
66
+
67
+ ### Example:
68
+
69
+ ```yaml
70
+ my_scheduled_job: # a unique name for this schedule
71
+ class: Jobs::DoWork # the job class
72
+ cron: "* * * * *" # cron formatted schedule
73
+ queue: "queue_name" # Sidekiq queue for job
74
+
75
+ my_other_scheduled_job:
76
+ class: Jobs::AnotherClassName
77
+ cron: "1 1 * * *"
78
+ queue: "a_different_queue"
79
+ ```
80
+
81
+ ### Rails
82
+
83
+ If using Rails, you can nest the schedules under top-level environment
84
+ keys, and Sidecloq will select the correct group based on the Rails
85
+ environment. This is useful for development/staging scenarios. For
86
+ example:
87
+
88
+ ```yaml
89
+ production:
90
+ # these will only run in production
91
+ my_scheduled_job:
92
+ class: Jobs::ClassName
93
+ cron: "* * * * *"
94
+ queue: "queue_name"
95
+
96
+ staging:
97
+ # this will only run in staging
98
+ my_other_scheduled_job:
99
+ class: Jobs::AnotherClassName
100
+ cron: "1 1 * * *"
101
+ queue: "a_different_queue"
102
+ ```
34
103
 
35
104
  ## Web Extension
36
105
 
@@ -41,7 +110,11 @@ require 'sidekiq/web'
41
110
  require 'sidecloq/web'
42
111
  ```
43
112
 
44
- TODO: screenshot/directions
113
+ This will add a "Recurring" tab to the sidekiq web ui, where the loaded
114
+ schedules are displayed. You can enqueue a job immediately by clicking
115
+ it's corresponding "Enqueue now" button.
116
+
117
+ ![Sidecloq web ui extension screenshot](screenshot.png)
45
118
 
46
119
  ## Contributing
47
120
 
@@ -50,5 +123,3 @@ Bug reports and pull requests are welcome.
50
123
  ## License
51
124
 
52
125
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
53
-
54
- TODO: project links
data/Rakefile CHANGED
@@ -1,21 +1,38 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
7
  t.test_files = FileList['test/**/test_*.rb']
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
11
11
 
12
- desc "Run the Sidekiq web interface (w/Sidecloq)"
12
+ desc 'Run the Sidekiq web interface (w/Sidecloq)'
13
13
  task :web do
14
14
  require 'sidekiq'
15
15
  require 'sidecloq'
16
16
 
17
17
  Sidekiq.configure_client do |config|
18
- config.redis = { url: 'redis://localhost:6379/0', size: 1, namespace: 'sidecloq' }
18
+ config.redis = {
19
+ url: 'redis://localhost:6379/0',
20
+ size: 1,
21
+ namespace: 'sidecloq'
22
+ }
23
+ end
24
+
25
+ # throw a fake job in
26
+ Sidecloq.configure do |config|
27
+ sched = Sidecloq::Schedule.from_hash({
28
+ my_scheduled_job: {
29
+ class: 'DoWork',
30
+ cron: '* * * * *',
31
+ queue: 'default'
32
+ }
33
+ })
34
+ sched.save_redis
35
+ config[:schedule] = sched
19
36
  end
20
37
 
21
38
  require 'sidekiq/web'
Binary file
@@ -3,9 +3,9 @@ module Sidecloq
3
3
  class Locker
4
4
  include Utils
5
5
 
6
- DEFAULT_LOCK_KEY = "sidecloq_leader_lock"
6
+ DEFAULT_LOCK_KEY = 'sidecloq_leader_lock'
7
7
 
8
- def initialize(options={})
8
+ def initialize(options = {})
9
9
  # we keep a connection from the pool by default
10
10
  @redis = options[:redis] || Sidekiq.redis_pool.checkout
11
11
  @key = options[:lock_key] || DEFAULT_LOCK_KEY
@@ -24,33 +24,36 @@ module Sidecloq
24
24
  end
25
25
 
26
26
  def stop(timeout = nil)
27
- if @check_task
28
- logger.debug("Stopping locker check task")
29
- @check_task.shutdown
30
- @check_task.wait_for_termination(timeout)
31
- logger.debug("Stopped locker check task")
32
- end
27
+ return unless @check_task
28
+
29
+ logger.debug('Stopping locker check task')
30
+ @check_task.shutdown
31
+ @check_task.wait_for_termination(timeout)
32
+ logger.debug('Stopped locker check task')
33
33
  end
34
34
 
35
- def has_lock?
35
+ def locked?
36
36
  @obtained_lock.set?
37
37
  end
38
38
 
39
39
  private unless $TESTING
40
40
 
41
41
  def start
42
- logger.debug("Starting locker check task")
43
- @check_task = Concurrent::TimerTask.new(execution_interval: @check_interval, run_now: true) do
44
- get_or_refresh_lock
42
+ logger.debug('Starting locker check task')
43
+ @check_task = Concurrent::TimerTask.new(
44
+ execution_interval: @check_interval,
45
+ run_now: true
46
+ ) do
47
+ try_to_get_or_refresh_lock
45
48
  end
46
49
  @check_task.execute
47
50
  end
48
51
 
49
- def get_or_refresh_lock
52
+ def try_to_get_or_refresh_lock
50
53
  # redlock is in ms, not seconds
51
54
  @lock = @lock_manager.lock(@key, @ttl * 1000, extend: @lock)
52
55
  @obtained_lock.set if @lock
53
- logger.debug("Leader lock #{"not " if !@lock}held")
56
+ logger.debug("Leader lock #{'not ' unless @lock}held")
54
57
  @lock
55
58
  end
56
59
  end
@@ -13,24 +13,24 @@ module Sidecloq
13
13
 
14
14
  def run
15
15
  @thread = Thread.new do
16
- logger.info("Runner starting")
16
+ logger.info('Runner starting')
17
17
  @locker.with_lock do
18
18
  # i am the leader
19
- logger.info("Obtained leader lock")
19
+ logger.info('Obtained leader lock')
20
20
  @scheduler.run
21
21
  end
22
- logger.info("Runner ending")
22
+ logger.info('Runner ending')
23
23
  end
24
24
  end
25
25
 
26
26
  def stop(timeout = nil)
27
- logger.debug("Stopping runner")
28
- if @locker.has_lock?
27
+ logger.debug('Stopping runner')
28
+ if @locker.locked?
29
29
  @scheduler.stop(timeout)
30
30
  @locker.stop(timeout)
31
31
  end
32
32
  @thread.join if @thread
33
- logger.debug("Stopped runner")
33
+ logger.debug('Stopped runner')
34
34
  end
35
35
 
36
36
  private unless $TESTING
@@ -32,23 +32,20 @@ module Sidecloq
32
32
  end
33
33
 
34
34
  def self.from_hash(hash)
35
- if defined?(Rails) && hash.key?(Rails.env)
36
- hash = hash[Rails.env]
37
- end
35
+ hash = hash[Rails.env] if defined?(Rails) && hash.key?(Rails.env)
38
36
 
39
- specs = hash.inject({}) do |memo, (name, spec)|
37
+ specs = hash.each_with_object({}) do |(name, spec), memo|
40
38
  memo[name] = spec.dup.tap do |s|
41
39
  s['class'] = name unless spec.key?('class') || spec.key?(:class)
42
40
  s['args'] = s['args'] || s[:args] || []
43
41
  end
44
- memo
45
42
  end
46
43
 
47
44
  new(specs)
48
45
  end
49
46
 
50
47
  def save_yaml(filename)
51
- File.open(filename,'w') do |h|
48
+ File.open(filename, 'w') do |h|
52
49
  h.write @job_specs.to_yaml
53
50
  end
54
51
  end
@@ -10,13 +10,13 @@ module Sidecloq
10
10
 
11
11
  # run queues jobs per their schedules, blocking forever
12
12
  def run
13
- logger.info("Loading schedules into redis")
13
+ logger.info('Loading schedules into redis')
14
14
  sync_with_redis
15
- logger.info("Starting scheduler")
15
+ logger.info('Starting scheduler')
16
16
  load_schedule_into_rufus
17
- logger.debug("Joining rufus thread")
17
+ logger.debug('Joining rufus thread')
18
18
  rufus.join
19
- logger.debug("Scheduler run ended")
19
+ logger.debug('Scheduler run ended')
20
20
  end
21
21
 
22
22
  def stop(timeout = nil)
@@ -33,7 +33,7 @@ module Sidecloq
33
33
  rufus.shutdown(:wait)
34
34
  end
35
35
  rufus.join
36
- logger.info("Stopped scheduler")
36
+ logger.info('Stopped scheduler')
37
37
  end
38
38
 
39
39
  private unless $TESTING
@@ -47,7 +47,7 @@ module Sidecloq
47
47
  end
48
48
 
49
49
  def load_schedule_into_rufus
50
- logger.debug("Scheduling jobs")
50
+ logger.debug('Scheduling jobs')
51
51
  @schedule.job_specs.each do |name, spec|
52
52
  load_into_rufus(name, spec)
53
53
  end
@@ -71,14 +71,14 @@ module Sidecloq
71
71
 
72
72
  # failed enqeueuing should not b0rk stuff
73
73
  begin
74
- enqueue_job!(name, spec)
74
+ enqueue_job!(spec)
75
75
  rescue => e
76
76
  logger.info "error enqueuing #{name} - #{e.class.name}: #{e.message}"
77
77
  end
78
78
  end
79
79
 
80
80
  # can raise exceptions, but shouldn't
81
- def enqueue_job!(name, spec)
81
+ def enqueue_job!(spec)
82
82
  Sidekiq::Client.push(spec)
83
83
  end
84
84
  end
@@ -1,5 +1,8 @@
1
1
  module Sidecloq
2
+ # Useful stuff
2
3
  module Utils
4
+ # Sets the Sidekiq::Logging context automatically with direct calls to
5
+ # *.logger
3
6
  class ContextLogger
4
7
  def initialize(ctx)
5
8
  @context = ctx
@@ -25,9 +28,7 @@ module Sidecloq
25
28
  def will_never_run(cronline)
26
29
  # look for non-existent day of month
27
30
  split = cronline.split(/\s+/)
28
- if split.length > 3 &&
29
- split[2] =~ /\d+/
30
- split[3] =~ /\d+/
31
+ if split.length > 3 && split[2] =~ /\d+/ && split[3] =~ /\d+/
31
32
 
32
33
  month = split[3].to_i
33
34
  day = split[2].to_i
@@ -1,3 +1,3 @@
1
1
  module Sidecloq
2
- VERSION = "0.1.0"
2
+ VERSION = '0.1.1'
3
3
  end
data/lib/sidecloq/web.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  module Sidecloq
2
+ # Plugin for sidekiq-web
2
3
  module Web
3
4
  VIEW_PATH = File.expand_path('../../../web/views', __FILE__)
4
5
 
5
6
  def self.registered(app)
6
-
7
7
  app.get '/recurring' do
8
8
  @schedule = Schedule.from_redis
9
9
 
@@ -11,12 +11,13 @@ module Sidecloq
11
11
  end
12
12
 
13
13
  app.post '/recurring/:name/enqueue' do |name|
14
+ # rubocop:disable Lint/AssignmentInCondition
14
15
  if spec = Sidecloq::Schedule.from_redis.job_specs[name]
15
16
  Sidekiq::Client.push(spec)
16
17
  end
18
+ # rubocop:enableLint/AssignmentInCondition
17
19
  redirect "#{root_path}recurring"
18
20
  end
19
-
20
21
  end
21
22
  end
22
23
  end
data/lib/sidecloq.rb CHANGED
@@ -11,6 +11,7 @@ require 'sidecloq/scheduler'
11
11
  require 'sidecloq/runner'
12
12
  require 'sidecloq/version'
13
13
 
14
+ # Sideloq provides a lightweight recurring job scheduler for sidekiq
14
15
  module Sidecloq
15
16
  def self.install
16
17
  Sidekiq.configure_server do |config|
@@ -33,13 +34,12 @@ module Sidecloq
33
34
  end
34
35
 
35
36
  def self.configure
36
- yield self
37
+ yield options
37
38
  end
38
39
 
39
40
  def self.startup
40
- unless options[:scheduler]
41
- options[:schedule] ||= extract_schedule
42
- end
41
+ options[:schedule] ||= extract_schedule unless options[:scheduler]
42
+
43
43
  @runner = Runner.new(options)
44
44
  @runner.run
45
45
  end
@@ -55,13 +55,13 @@ module Sidecloq
55
55
  return options[:schedule] if options[:schedule]
56
56
 
57
57
  # try for a file
58
- options[:schedule_file] ||= "config/sidecloq.yml"
59
- if File.exists?(options[:schedule_file])
58
+ options[:schedule_file] ||= 'config/sidecloq.yml'
59
+ if File.exist?(options[:schedule_file])
60
60
  return Schedule.from_yaml(options[:schedule_file])
61
61
  elsif defined?(Rails)
62
62
  # try rails-root-relative
63
63
  full_path = File.join(Rails.root, options[:schedule_file])
64
- if File.exists?(full_path)
64
+ if File.exist?(full_path)
65
65
  options[:schedule_file] = full_path
66
66
  return Schedule.from_yaml(options[:schedule_file])
67
67
  end
data/screenshot.png ADDED
Binary file
data/sidetoq.gemspec CHANGED
@@ -4,30 +4,30 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'sidecloq/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "sidecloq"
7
+ spec.name = 'sidecloq'
8
8
  spec.version = Sidecloq::VERSION
9
- spec.authors = ["Matt Robinson"]
10
- spec.email = ["robinson.matty@gmail.com"]
9
+ spec.authors = ['Matt Robinson']
10
+ spec.email = ['robinson.matty@gmail.com']
11
11
 
12
- spec.summary = %q{Recurring jobs for Sidekiq}
12
+ spec.summary = 'Recurring jobs for Sidekiq'
13
13
  spec.description = spec.summary
14
- spec.homepage = "http://github.com/mattyr/sidecloq"
15
- spec.license = "MIT"
14
+ spec.homepage = 'http://github.com/mattyr/sidecloq'
15
+ spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "bin"
18
+ spec.bindir = 'bin'
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
21
21
 
22
- spec.add_dependency "sidekiq", "~> 4.0.1"
23
- spec.add_dependency "redlock", "~> 0.1.2"
22
+ spec.add_dependency 'sidekiq', '~> 4.0.1'
23
+ spec.add_dependency 'redlock', '~> 0.1.2'
24
24
  # mimics some dev dependencies of sidekiq:
25
- spec.add_dependency "concurrent-ruby"
26
- spec.add_dependency "sinatra", "~> 1.4", ">= 1.4.6"
27
- spec.add_dependency "redis-namespace", "~> 1.5", ">= 1.5.2"
28
- spec.add_dependency "multi_json", "~> 1.11"
29
- spec.add_dependency "rufus-scheduler", "~> 3.1", ">= 3.1.10"
25
+ spec.add_dependency 'concurrent-ruby'
26
+ spec.add_dependency 'sinatra', '~> 1.4', '>= 1.4.6'
27
+ spec.add_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
28
+ spec.add_dependency 'multi_json', '~> 1.11'
29
+ spec.add_dependency 'rufus-scheduler', '~> 3.1', '>= 3.1.10'
30
30
 
31
- spec.add_development_dependency "rake", "~> 10.0"
32
- spec.add_development_dependency "minitest"
31
+ spec.add_development_dependency 'rake', '~> 10.0'
32
+ spec.add_development_dependency 'minitest'
33
33
  end
@@ -5,8 +5,7 @@
5
5
  <thead>
6
6
  <tr>
7
7
  <th>Name</th>
8
- <th>Description</th>
9
- <th>Interval</th>
8
+ <th>Cron</th>
10
9
  <th>Class</th>
11
10
  <th>Queue</th>
12
11
  <th>Arguments</th>
@@ -18,7 +17,6 @@
18
17
  <% @schedule.job_specs.each do |name, job_spec| %>
19
18
  <tr>
20
19
  <td><%= name %></td>
21
- <td><%= job_spec['description'] %></td>
22
20
  <td><%= job_spec.fetch 'cron', job_spec['every'] %></td>
23
21
  <td><%= job_spec['class'] %></td>
24
22
  <td>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidecloq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Robinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-23 00:00:00.000000000 Z
11
+ date: 2015-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -167,6 +167,7 @@ files:
167
167
  - LICENSE.txt
168
168
  - README.md
169
169
  - Rakefile
170
+ - clock_a_clock_on_the_side.png
170
171
  - lib/sidecloq.rb
171
172
  - lib/sidecloq/locker.rb
172
173
  - lib/sidecloq/runner.rb
@@ -175,6 +176,7 @@ files:
175
176
  - lib/sidecloq/utils.rb
176
177
  - lib/sidecloq/version.rb
177
178
  - lib/sidecloq/web.rb
179
+ - screenshot.png
178
180
  - sidetoq.gemspec
179
181
  - web/views/recurring.erb
180
182
  homepage: http://github.com/mattyr/sidecloq