fist_of_fury 0.0.1 → 0.1.0
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 +4 -4
- data/.travis.yml +11 -0
- data/README.md +18 -5
- data/Rakefile +11 -0
- data/fist_of_fury.gemspec +5 -0
- data/lib/fist_of_fury/actor/clock.rb +2 -26
- data/lib/fist_of_fury/actor/dispatcher.rb +1 -26
- data/lib/fist_of_fury/clock.rb +29 -0
- data/lib/fist_of_fury/config.rb +20 -0
- data/lib/fist_of_fury/dispatcher.rb +29 -0
- data/lib/fist_of_fury/recurrent.rb +4 -0
- data/lib/fist_of_fury/schedule.rb +24 -18
- data/lib/fist_of_fury/supervisor.rb +0 -4
- data/lib/fist_of_fury/version.rb +1 -1
- data/lib/fist_of_fury.rb +3 -2
- data/spec/fist_of_fury/actor_sharedspec.rb +7 -0
- data/spec/fist_of_fury/actors/clock_spec.rb +20 -0
- data/spec/fist_of_fury/actors/dispatcher_spec.rb +8 -0
- data/spec/fist_of_fury/api_sharedspec.rb +19 -0
- data/spec/fist_of_fury/clock_sharedspec.rb +43 -0
- data/spec/fist_of_fury/config_spec.rb +47 -0
- data/spec/fist_of_fury/dispatcher_sharedspec.rb +21 -0
- data/spec/fist_of_fury/job_spec.rb +6 -0
- data/spec/fist_of_fury/logging_sharedspec.rb +9 -0
- data/spec/fist_of_fury/railtie_spec.rb +20 -0
- data/spec/fist_of_fury/recurrent_sharedspec.rb +56 -0
- data/spec/fist_of_fury/schedule_spec.rb +89 -0
- data/spec/fist_of_fury/subclass_tracking_sharedspec.rb +25 -0
- data/spec/fist_of_fury/version_spec.rb +23 -0
- data/spec/fist_of_fury_spec.rb +37 -5
- data/spec/fixtures/awesome_job.rb +7 -0
- data/spec/spec_helper.rb +25 -0
- metadata +64 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30fd5d339ee339c1294b986583f7a0abed1cb08a
|
4
|
+
data.tar.gz: 593d60d97908fa37af0f13192fc68487950d8a71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01928d25ec21fece7ac05657327f503ac07f287067b0bf979823241bce3e2a324e9124401343e9cff4668cd18187c620e33f96587284e67318d9094a5aec67a0
|
7
|
+
data.tar.gz: f2697fdaf69a3f99e0b14a70f365f10a3cb8588d80b8c4b327d4bb4d1a0775d7ecaaafc98290331737e02611f6ef66005e85356e3067f01115bb5e26ad173534
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# Fist of Fury
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/facto/fist_of_fury)
|
4
|
+
[](https://gemnasium.com/facto/fist_of_fury)
|
5
|
+
[](https://coveralls.io/r/facto/fist_of_fury)
|
6
|
+
[](https://codeclimate.com/github/facto/fist_of_fury)
|
7
|
+
[](http://badge.fury.io/rb/fist_of_fury)
|
4
8
|
|
5
|
-
|
9
|
+
Recurring jobs for [Sucker Punch](https://github.com/brandonhilkert/sucker_punch).
|
6
10
|
|
7
11
|
## Why
|
8
12
|
|
9
|
-
Sucker Punch offers the
|
13
|
+
Sucker Punch offers the ability to run background jobs *within the web server process*. Yes, this means you can host your app on Heroku without paying for an extra dyno for background jobs. However, Sucker Punch does not implement recurring jobs. Enter Fist of Fury!
|
10
14
|
|
11
15
|
## What
|
12
16
|
|
@@ -16,7 +20,7 @@ Fist of Fury is heavily inspired by [Sidetiq](https://github.com/tobiassvn/sidet
|
|
16
20
|
|
17
21
|
Add this line to your application's Gemfile:
|
18
22
|
|
19
|
-
gem 'fist_of_fury', '~> 0.1'
|
23
|
+
gem 'fist_of_fury', '~> 0.1.0'
|
20
24
|
|
21
25
|
And then execute:
|
22
26
|
|
@@ -69,10 +73,19 @@ unless defined?(Rails::Console)
|
|
69
73
|
end
|
70
74
|
```
|
71
75
|
|
76
|
+
## Configuration Options
|
77
|
+
|
78
|
+
In the initializer:
|
79
|
+
|
80
|
+
```Ruby
|
81
|
+
FistOfFury.configure do |config|
|
82
|
+
config.utc = true # false by default; makes all time within Fist of Fury UTC
|
83
|
+
end
|
84
|
+
```
|
72
85
|
|
73
86
|
## Contributing
|
74
87
|
|
75
|
-
1. Fork it ( http://github.com
|
88
|
+
1. Fork it ( http://github.com/facto/fist_of_fury/fork )
|
76
89
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
77
90
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
78
91
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -1 +1,12 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new('spec')
|
5
|
+
|
6
|
+
# If you want to make this the default task
|
7
|
+
task :default => :spec
|
8
|
+
task :test => :spec
|
9
|
+
|
10
|
+
task :console do
|
11
|
+
exec "irb -r fist_of_fury -I ./lib"
|
12
|
+
end
|
data/fist_of_fury.gemspec
CHANGED
@@ -23,6 +23,11 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_development_dependency 'rspec'
|
24
24
|
gem.add_development_dependency 'guard'
|
25
25
|
gem.add_development_dependency 'guard-rspec'
|
26
|
+
gem.add_development_dependency 'ruby_gntp'
|
27
|
+
|
28
|
+
if RUBY_PLATFORM != "java"
|
29
|
+
gem.add_development_dependency 'coveralls'
|
30
|
+
end
|
26
31
|
|
27
32
|
gem.add_dependency 'sucker_punch', '>= 1.0.2'
|
28
33
|
gem.add_dependency 'celluloid', '>= 0.15.2'
|
@@ -1,33 +1,15 @@
|
|
1
1
|
module FistOfFury
|
2
2
|
module Actor
|
3
|
-
class Clock
|
4
|
-
include Logging
|
3
|
+
class Clock < FistOfFury::Clock
|
5
4
|
include FistOfFury::Actor
|
6
5
|
|
7
|
-
attr_reader :schedules
|
8
|
-
|
9
6
|
def initialize(*args, &block)
|
10
7
|
after(0) do
|
11
|
-
debug 'FistOfFury::Clock
|
8
|
+
debug 'FistOfFury::Clock starting loop...'
|
12
9
|
loop!
|
13
10
|
end
|
14
11
|
end
|
15
12
|
|
16
|
-
def schedule_for(worker)
|
17
|
-
worker.schedule if worker.respond_to? :schedule
|
18
|
-
end
|
19
|
-
|
20
|
-
def tick(tick = current_time)
|
21
|
-
FistOfFury.workers.each do |worker|
|
22
|
-
FistOfFury.dispatcher.dispatch(worker, tick)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def current_time
|
27
|
-
# TODO: configurable time zone support
|
28
|
-
Time.now
|
29
|
-
end
|
30
|
-
|
31
13
|
private
|
32
14
|
|
33
15
|
def loop!
|
@@ -39,12 +21,6 @@ module FistOfFury
|
|
39
21
|
# handle_exception(e, context: 'FistOfFury::Clock#loop!')
|
40
22
|
raise e
|
41
23
|
end
|
42
|
-
|
43
|
-
def time
|
44
|
-
start = current_time
|
45
|
-
yield
|
46
|
-
1 - (current_time.to_f - start.to_f)
|
47
|
-
end
|
48
24
|
end
|
49
25
|
end
|
50
26
|
end
|
@@ -1,32 +1,7 @@
|
|
1
1
|
module FistOfFury
|
2
2
|
module Actor
|
3
|
-
class Dispatcher
|
4
|
-
include Logging
|
3
|
+
class Dispatcher < FistOfFury::Dispatcher
|
5
4
|
include FistOfFury::Actor
|
6
|
-
|
7
|
-
def dispatch(worker, tick)
|
8
|
-
schedule = worker.schedule
|
9
|
-
return unless schedule.schedule_next?(tick)
|
10
|
-
enqueue(worker, schedule.next_occurrence(tick))
|
11
|
-
rescue StandardError => e
|
12
|
-
# TODO: exception handling support
|
13
|
-
raise e
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def enqueue(worker, next_run_time)
|
19
|
-
klass = worker.to_s
|
20
|
-
info "Enqueue: #{klass}"
|
21
|
-
last_run_time = FistOfFury.store["#{klass}:next"] || -1
|
22
|
-
next_run_time = next_run_time.to_f
|
23
|
-
FistOfFury.store["#{klass}:last"] = last_run_time
|
24
|
-
FistOfFury.store["#{klass}:next"] = next_run_time
|
25
|
-
worker.new.async.perform
|
26
|
-
rescue StandardError => e
|
27
|
-
# TODO: exception handling support
|
28
|
-
raise e
|
29
|
-
end
|
30
5
|
end
|
31
6
|
end
|
32
7
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FistOfFury
|
2
|
+
class Clock
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
attr_reader :schedules
|
6
|
+
|
7
|
+
def schedule_for(worker)
|
8
|
+
worker.schedule if worker.respond_to? :schedule
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_time
|
12
|
+
FistOfFury.config.utc ? Time.now.utc : Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def time
|
18
|
+
start = current_time
|
19
|
+
yield
|
20
|
+
1 - (current_time.to_f - start.to_f)
|
21
|
+
end
|
22
|
+
|
23
|
+
def tick(time = current_time)
|
24
|
+
FistOfFury.workers.each do |worker|
|
25
|
+
FistOfFury.dispatcher.dispatch(worker, time)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FistOfFury
|
2
|
+
class << self
|
3
|
+
attr_writer :config
|
4
|
+
|
5
|
+
def configure
|
6
|
+
yield config
|
7
|
+
end
|
8
|
+
|
9
|
+
def configure_with_defaults
|
10
|
+
config.utc = false
|
11
|
+
yield config if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
def config
|
15
|
+
@config ||= OpenStruct.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
configure_with_defaults
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FistOfFury
|
2
|
+
class Dispatcher
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
def dispatch(worker, time)
|
6
|
+
worker.schedule_next(time) do
|
7
|
+
enqueue(worker, worker.next_occurrence(time))
|
8
|
+
end
|
9
|
+
rescue StandardError => e
|
10
|
+
# TODO: exception handling support
|
11
|
+
raise e
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def enqueue(worker, next_run_time)
|
17
|
+
klass = worker.to_s
|
18
|
+
info "Enqueue: #{klass}"
|
19
|
+
last_run_time = FistOfFury.store["#{klass}:next"] || -1
|
20
|
+
next_run_time = next_run_time.to_f
|
21
|
+
FistOfFury.store["#{klass}:last"] = last_run_time
|
22
|
+
FistOfFury.store["#{klass}:next"] = next_run_time
|
23
|
+
worker.new.async.perform
|
24
|
+
rescue StandardError => e
|
25
|
+
# TODO: exception handling support
|
26
|
+
raise e
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,39 +1,45 @@
|
|
1
1
|
module FistOfFury
|
2
2
|
class Schedule
|
3
|
-
attr_accessor :options
|
4
|
-
attr_reader :last_occurrence
|
5
|
-
|
6
|
-
# TODO: configurable timezone support
|
7
|
-
START_TIME = Time.local(2010, 1, 1)
|
3
|
+
attr_accessor :options, :ice_cube_schedule, :last_occurrence
|
8
4
|
|
9
5
|
def initialize
|
10
|
-
|
6
|
+
self.ice_cube_schedule = IceCube::Schedule.new(start_time)
|
11
7
|
end
|
12
8
|
|
13
9
|
def method_missing(meth, *args, &block)
|
14
10
|
if IceCube::Rule.respond_to?(meth)
|
15
11
|
rule = IceCube::Rule.send(meth, *args, &block)
|
16
|
-
|
12
|
+
ice_cube_schedule.add_recurrence_rule(rule)
|
17
13
|
rule
|
18
|
-
elsif
|
19
|
-
|
14
|
+
elsif ice_cube_schedule.respond_to?(meth)
|
15
|
+
ice_cube_schedule.send(meth, *args, &block)
|
20
16
|
else
|
21
17
|
super
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
|
-
def schedule_next
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
true
|
30
|
-
else
|
31
|
-
false
|
32
|
-
end
|
21
|
+
def schedule_next(time)
|
22
|
+
return unless schedule_next?(time)
|
23
|
+
yield
|
24
|
+
self.last_occurrence = next_occurrence(time)
|
33
25
|
end
|
34
26
|
|
35
27
|
def to_s
|
36
|
-
|
28
|
+
ice_cube_schedule.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_time
|
32
|
+
@start_time ||= FistOfFury.config.utc ? Time.utc(2010, 1, 1) : Time.local(2010, 1, 1)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def next_occurrence(time)
|
38
|
+
ice_cube_schedule.next_occurrence(time)
|
39
|
+
end
|
40
|
+
|
41
|
+
def schedule_next?(time)
|
42
|
+
last_occurrence != next_occurrence(time)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
data/lib/fist_of_fury/version.rb
CHANGED
data/lib/fist_of_fury.rb
CHANGED
@@ -10,10 +10,13 @@ require 'sucker_punch'
|
|
10
10
|
require 'celluloid'
|
11
11
|
|
12
12
|
# internal
|
13
|
+
require 'fist_of_fury/config'
|
13
14
|
require 'fist_of_fury/subclass_tracking'
|
14
15
|
require 'fist_of_fury/api'
|
15
16
|
require 'fist_of_fury/logging'
|
16
17
|
require 'fist_of_fury/schedule'
|
18
|
+
require 'fist_of_fury/clock'
|
19
|
+
require 'fist_of_fury/dispatcher'
|
17
20
|
require 'fist_of_fury/recurrent'
|
18
21
|
require 'fist_of_fury/version'
|
19
22
|
|
@@ -49,10 +52,8 @@ module FistOfFury
|
|
49
52
|
end
|
50
53
|
|
51
54
|
def attack!
|
52
|
-
return true if @attacking
|
53
55
|
yield if block_given?
|
54
56
|
FistOfFury::Supervisor.run!
|
55
|
-
@attacking = true
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fist_of_fury/actor_sharedspec'
|
3
|
+
require 'fist_of_fury/clock_sharedspec'
|
4
|
+
|
5
|
+
describe FistOfFury::Actor::Clock do
|
6
|
+
it_should_behave_like 'an actor'
|
7
|
+
it_should_behave_like 'a clock'
|
8
|
+
|
9
|
+
let(:clock) { subject }
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
expect_any_instance_of(described_class).to receive(:after)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#initialize' do
|
16
|
+
it 'makes a deferred call' do
|
17
|
+
clock
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for 'the api' do
|
4
|
+
describe '#workers' do
|
5
|
+
it 'returns the deep subclass list from Recurrent' do
|
6
|
+
expect(FistOfFury::Recurrent).to receive(:subclasses).with(true)
|
7
|
+
subject.workers
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#schedules' do
|
12
|
+
it 'returns the worker schedules' do
|
13
|
+
worker_list = double('worker_list')
|
14
|
+
expect(worker_list).to receive(:map)
|
15
|
+
allow_any_instance_of(described_class).to receive(:workers).and_return(worker_list)
|
16
|
+
subject.schedules
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fist_of_fury/logging_sharedspec'
|
3
|
+
|
4
|
+
shared_examples_for 'a clock' do
|
5
|
+
it_should_behave_like 'a logger'
|
6
|
+
|
7
|
+
describe '#schedule_for' do
|
8
|
+
it "delegates to the worker's schedule" do
|
9
|
+
worker = double('worker')
|
10
|
+
worker.stub(:schedule)
|
11
|
+
expect(worker).to receive(:schedule)
|
12
|
+
subject.schedule_for(worker)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#current_time' do
|
17
|
+
context 'when UTC is enabled' do
|
18
|
+
before :each do
|
19
|
+
FistOfFury.configure { |config| config.utc = true }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns the current time in UTC' do
|
23
|
+
now = double('now')
|
24
|
+
utc = double('utc')
|
25
|
+
now.stub(:utc).and_return(utc)
|
26
|
+
Time.stub(:now).and_return(now)
|
27
|
+
expect(subject.current_time).to eq utc
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when UTC is not enabled' do
|
32
|
+
before :each do
|
33
|
+
FistOfFury.configure { |config| config.utc = false }
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns the current time in the default time zone' do
|
37
|
+
now = double('now')
|
38
|
+
Time.stub(:now).and_return(now)
|
39
|
+
expect(subject.current_time).to eq now
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FistOfFury do
|
4
|
+
describe 'config' do
|
5
|
+
describe '#configure' do
|
6
|
+
let(:config) { OpenStruct.new }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
described_class.config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'calls the block and yields the config ' do
|
13
|
+
block_called = false
|
14
|
+
described_class.configure do |config|
|
15
|
+
expect(config).to eq(config)
|
16
|
+
block_called = true
|
17
|
+
end
|
18
|
+
expect(block_called).to be_true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#configure_with_defaults' do
|
23
|
+
let(:config) { OpenStruct.new }
|
24
|
+
|
25
|
+
before :each do
|
26
|
+
described_class.config = config
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'calls the block and yields the config ' do
|
30
|
+
block_called = false
|
31
|
+
described_class.configure_with_defaults do |config|
|
32
|
+
expect(config).to eq(config)
|
33
|
+
block_called = true
|
34
|
+
end
|
35
|
+
expect(block_called).to be_true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#config' do
|
40
|
+
it 'has some default values' do
|
41
|
+
expect(described_class.config).to eq OpenStruct.new(
|
42
|
+
utc: false
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fist_of_fury/logging_sharedspec'
|
3
|
+
|
4
|
+
shared_examples_for 'a dispatcher' do
|
5
|
+
it_should_behave_like 'a logger'
|
6
|
+
|
7
|
+
describe '#dispatch' do
|
8
|
+
|
9
|
+
let(:time) { double('time') }
|
10
|
+
|
11
|
+
let(:worker) { double('worker') }
|
12
|
+
|
13
|
+
it 'delegates scheduling to the worker' do
|
14
|
+
expect(worker).to receive(:schedule_next) do |*args, &block|
|
15
|
+
expect(args).to include(time)
|
16
|
+
expect(block).to_not be_nil
|
17
|
+
end
|
18
|
+
subject.dispatch(worker, time)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ::Rails
|
4
|
+
class Railtie
|
5
|
+
def self.initializer(name)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative '../../lib/fist_of_fury/railtie'
|
11
|
+
|
12
|
+
describe FistOfFury::Railtie do
|
13
|
+
after :all do
|
14
|
+
Object.send(:remove_const, :Rails)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'derives from ::Rails::Railtie' do
|
18
|
+
expect(described_class.ancestors).to include(::Rails::Railtie)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fist_of_fury/subclass_tracking_sharedspec'
|
3
|
+
|
4
|
+
shared_examples_for 'a job with recurrence' do
|
5
|
+
it_should_behave_like 'a class that tracks its subclasses'
|
6
|
+
|
7
|
+
after :each do
|
8
|
+
Celluloid::Actor.clear_registry
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '::schedule' do
|
12
|
+
it 'returns a new schedule' do
|
13
|
+
schedule = double('schedule')
|
14
|
+
FistOfFury::Schedule.stub(:new).and_return(schedule)
|
15
|
+
expect(described_class.schedule).to eq schedule
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '::last_scheduled_occurrence' do
|
20
|
+
it 'calls scheduled_occurrence with the right arguments' do
|
21
|
+
expect(described_class).to receive(:scheduled_occurrence).with('last')
|
22
|
+
described_class.last_scheduled_occurrence
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '::next_scheduled_occurrence' do
|
27
|
+
it 'calls scheduled_occurrence with the right arguments' do
|
28
|
+
expect(described_class).to receive(:scheduled_occurrence).with('next')
|
29
|
+
described_class.next_scheduled_occurrence
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '::recurs' do
|
34
|
+
it 'evaluate the block within the context of the schedule and sets the options' do
|
35
|
+
block = lambda {}
|
36
|
+
schedule = double('schedule')
|
37
|
+
options = double('optionss')
|
38
|
+
described_class.schedule = schedule
|
39
|
+
expect(schedule).to receive(:instance_eval)
|
40
|
+
expect(schedule).to receive(:options=).with(options)
|
41
|
+
described_class.recurs(options, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '::schedule_next' do
|
46
|
+
let(:block) { lambda {} }
|
47
|
+
let(:time) { double('time') }
|
48
|
+
let(:schedule) { double('schedule') }
|
49
|
+
|
50
|
+
it 'delegates to the schedule' do
|
51
|
+
described_class.schedule = schedule
|
52
|
+
expect(schedule).to receive(:schedule_next).with(time)
|
53
|
+
described_class.schedule_next(time, &block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FistOfFury::Schedule do
|
4
|
+
let(:schedule) { subject }
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
it 'creates an IceCube schedule' do
|
8
|
+
ice_cube_schedule = double('ice_cube_schedule')
|
9
|
+
start_time = double('start_time')
|
10
|
+
allow_any_instance_of(described_class).to receive(:start_time).and_return(start_time)
|
11
|
+
allow(IceCube::Schedule).to receive(:new).with(start_time).and_return(ice_cube_schedule)
|
12
|
+
expect_any_instance_of(described_class).to receive(:ice_cube_schedule=).with(ice_cube_schedule)
|
13
|
+
schedule
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#schedule_next' do
|
18
|
+
let(:time) { double('time') }
|
19
|
+
let(:block) { lambda {} }
|
20
|
+
let(:next_occurrence) { double('next_occurrence') }
|
21
|
+
|
22
|
+
before :each do
|
23
|
+
allow(schedule).to receive(:next_occurrence).and_return(next_occurrence)
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when it should be scheduled' do
|
27
|
+
before :each do
|
28
|
+
allow(schedule).to receive(:schedule_next?).and_return(true)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'calls the block' do
|
32
|
+
block_called = false
|
33
|
+
schedule.schedule_next(time) { block_called = true }
|
34
|
+
expect(block_called).to be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'sets the last occurrence' do
|
38
|
+
expect(schedule).to receive(:last_occurrence=).with(next_occurrence)
|
39
|
+
schedule.schedule_next(time, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when it should not be scheduled' do
|
44
|
+
before :each do
|
45
|
+
allow(schedule).to receive(:schedule_next?).and_return(false)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'does not call the block' do
|
49
|
+
block_called = false
|
50
|
+
schedule.schedule_next(time) { block_called = true }
|
51
|
+
expect(block_called).to be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'does not set the last occurrence' do
|
55
|
+
expect(schedule).not_to receive(:last_occurrence=).with(next_occurrence)
|
56
|
+
schedule.schedule_next(time, &block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#to_s' do
|
62
|
+
it 'delegates to ice_cube_schedule' do
|
63
|
+
expect(schedule.ice_cube_schedule).to receive(:to_s)
|
64
|
+
schedule.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#start_time' do
|
69
|
+
context 'when utc is enabled' do
|
70
|
+
before :each do
|
71
|
+
FistOfFury.configure do |config|
|
72
|
+
config.utc = true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns the utc start time' do
|
77
|
+
expect(Time).to receive(:utc)
|
78
|
+
schedule
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when utc is not enabled' do
|
83
|
+
it 'returns the local start time' do
|
84
|
+
expect(Time).to receive(:local)
|
85
|
+
schedule
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for 'a class that tracks its subclasses' do
|
4
|
+
class ClassThatTracksItsSubclasses < described_class
|
5
|
+
end
|
6
|
+
|
7
|
+
class AnotherClassThatTracksItsSubclasses < ClassThatTracksItsSubclasses
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#subclasses' do
|
11
|
+
context 'when deep is specified' do
|
12
|
+
it 'returns the class' do
|
13
|
+
expect(described_class.subclasses()).to include(ClassThatTracksItsSubclasses)
|
14
|
+
expect(described_class.subclasses(true)).to include(AnotherClassThatTracksItsSubclasses)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when deep is not specified' do
|
19
|
+
it 'returns the class' do
|
20
|
+
expect(described_class.subclasses(false)).to include(ClassThatTracksItsSubclasses)
|
21
|
+
expect(described_class.subclasses(false)).not_to include(AnotherClassThatTracksItsSubclasses)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FistOfFury do
|
4
|
+
it 'has a version' do
|
5
|
+
expect(FistOfFury::VERSION).to_not be_nil
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'has a version that matches the version in the README' do
|
9
|
+
readme_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'README.md'))
|
10
|
+
readme = File.read(readme_path)
|
11
|
+
readme_matches = readme.scan(/gem 'fist_of_fury', '~> (\d+).(\d+).+'/)
|
12
|
+
readme_matches
|
13
|
+
readme_major = readme_matches[0][0]
|
14
|
+
readme_minor = readme_matches[0][1]
|
15
|
+
|
16
|
+
version_matches = FistOfFury::VERSION.scan(/(\d+).(\d+).+/)
|
17
|
+
version_major = version_matches[0][0]
|
18
|
+
version_minor = version_matches[0][1]
|
19
|
+
|
20
|
+
expect(readme_major).to eq(version_major)
|
21
|
+
expect(readme_minor).to eq(version_minor)
|
22
|
+
end
|
23
|
+
end
|
data/spec/fist_of_fury_spec.rb
CHANGED
@@ -1,22 +1,54 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'fist_of_fury/api_sharedspec'
|
2
3
|
|
3
4
|
describe FistOfFury do
|
4
|
-
|
5
|
-
|
5
|
+
it_should_behave_like 'the api'
|
6
|
+
|
7
|
+
describe '#logger' do
|
8
|
+
before :each do
|
9
|
+
FistOfFury.logger = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "delegates to Sucker Punch's logger" do
|
6
13
|
expect(FistOfFury.logger).to eq SuckerPunch.logger
|
7
14
|
end
|
15
|
+
|
16
|
+
after :each do
|
17
|
+
FistOfFury.logger = Logger.new(nil)
|
18
|
+
end
|
8
19
|
end
|
9
20
|
|
10
|
-
describe 'clock' do
|
11
|
-
it "delegates
|
21
|
+
describe '#clock' do
|
22
|
+
it "delegates to the supervisor's clock" do
|
12
23
|
expect(FistOfFury::Supervisor).to receive(:clock)
|
13
24
|
FistOfFury.clock
|
14
25
|
end
|
15
26
|
end
|
16
27
|
|
17
|
-
describe 'store' do
|
28
|
+
describe '#store' do
|
18
29
|
it 'starts out as an empty hash' do
|
19
30
|
expect(FistOfFury.store).to eq Hash.new
|
20
31
|
end
|
21
32
|
end
|
33
|
+
|
34
|
+
describe '#dispatcher' do
|
35
|
+
it "delegates to the supervisor's dispatcher" do
|
36
|
+
expect(FistOfFury::Supervisor).to receive(:dispatcher)
|
37
|
+
FistOfFury.dispatcher
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#attack!' do
|
42
|
+
it 'calls the block when provided' do
|
43
|
+
allow(FistOfFury::Supervisor).to receive(:run!)
|
44
|
+
block_called = false
|
45
|
+
FistOfFury.attack! { block_called = true }
|
46
|
+
expect(block_called).to be_true
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'calls #run! on the supervisor' do
|
50
|
+
expect(FistOfFury::Supervisor).to receive(:run!)
|
51
|
+
FistOfFury.attack!
|
52
|
+
end
|
53
|
+
end
|
22
54
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,11 +3,36 @@ begin
|
|
3
3
|
rescue LoadError
|
4
4
|
end
|
5
5
|
|
6
|
+
if RUBY_PLATFORM != "java"
|
7
|
+
require 'coveralls'
|
8
|
+
Coveralls.wear!
|
9
|
+
end
|
10
|
+
|
6
11
|
require 'fist_of_fury'
|
7
12
|
|
13
|
+
Dir[File.join(File.dirname(__FILE__), 'fixtures/**/*.rb')].each do |fixture|
|
14
|
+
require fixture
|
15
|
+
end
|
16
|
+
|
17
|
+
FistOfFury.logger = Logger.new(nil)
|
18
|
+
# Celluloid.logger = Logger.new(nil)
|
19
|
+
|
8
20
|
RSpec.configure do |config|
|
9
21
|
config.after(:each) do
|
10
22
|
# Clean up the master queue list
|
11
23
|
SuckerPunch::Queues.instance_variable_set(:@queues, Set.new)
|
24
|
+
|
25
|
+
# Make sure supervisor is fake
|
26
|
+
class ::FistOfFury::Supervisor
|
27
|
+
def self.clock
|
28
|
+
@clock ||= FistOfFury::Clock.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.dispatcher
|
32
|
+
@dispatcher ||= FistOfFury::Dispatcher.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
FistOfFury.configure_with_defaults
|
12
37
|
end
|
13
38
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fist_of_fury
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Rieken
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: ruby_gntp
|
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: coveralls
|
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'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: sucker_punch
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,6 +159,7 @@ extra_rdoc_files: []
|
|
131
159
|
files:
|
132
160
|
- ".gitignore"
|
133
161
|
- ".rspec"
|
162
|
+
- ".travis.yml"
|
134
163
|
- Gemfile
|
135
164
|
- Guardfile
|
136
165
|
- LICENSE.txt
|
@@ -142,6 +171,9 @@ files:
|
|
142
171
|
- lib/fist_of_fury/actor/clock.rb
|
143
172
|
- lib/fist_of_fury/actor/dispatcher.rb
|
144
173
|
- lib/fist_of_fury/api.rb
|
174
|
+
- lib/fist_of_fury/clock.rb
|
175
|
+
- lib/fist_of_fury/config.rb
|
176
|
+
- lib/fist_of_fury/dispatcher.rb
|
145
177
|
- lib/fist_of_fury/logging.rb
|
146
178
|
- lib/fist_of_fury/railtie.rb
|
147
179
|
- lib/fist_of_fury/recurrent.rb
|
@@ -149,7 +181,22 @@ files:
|
|
149
181
|
- lib/fist_of_fury/subclass_tracking.rb
|
150
182
|
- lib/fist_of_fury/supervisor.rb
|
151
183
|
- lib/fist_of_fury/version.rb
|
184
|
+
- spec/fist_of_fury/actor_sharedspec.rb
|
185
|
+
- spec/fist_of_fury/actors/clock_spec.rb
|
186
|
+
- spec/fist_of_fury/actors/dispatcher_spec.rb
|
187
|
+
- spec/fist_of_fury/api_sharedspec.rb
|
188
|
+
- spec/fist_of_fury/clock_sharedspec.rb
|
189
|
+
- spec/fist_of_fury/config_spec.rb
|
190
|
+
- spec/fist_of_fury/dispatcher_sharedspec.rb
|
191
|
+
- spec/fist_of_fury/job_spec.rb
|
192
|
+
- spec/fist_of_fury/logging_sharedspec.rb
|
193
|
+
- spec/fist_of_fury/railtie_spec.rb
|
194
|
+
- spec/fist_of_fury/recurrent_sharedspec.rb
|
195
|
+
- spec/fist_of_fury/schedule_spec.rb
|
196
|
+
- spec/fist_of_fury/subclass_tracking_sharedspec.rb
|
197
|
+
- spec/fist_of_fury/version_spec.rb
|
152
198
|
- spec/fist_of_fury_spec.rb
|
199
|
+
- spec/fixtures/awesome_job.rb
|
153
200
|
- spec/spec_helper.rb
|
154
201
|
homepage: https://github.com/facto/fist_of_fury
|
155
202
|
licenses:
|
@@ -176,5 +223,20 @@ signing_key:
|
|
176
223
|
specification_version: 4
|
177
224
|
summary: Recurring jobs for Sucker Punch
|
178
225
|
test_files:
|
226
|
+
- spec/fist_of_fury/actor_sharedspec.rb
|
227
|
+
- spec/fist_of_fury/actors/clock_spec.rb
|
228
|
+
- spec/fist_of_fury/actors/dispatcher_spec.rb
|
229
|
+
- spec/fist_of_fury/api_sharedspec.rb
|
230
|
+
- spec/fist_of_fury/clock_sharedspec.rb
|
231
|
+
- spec/fist_of_fury/config_spec.rb
|
232
|
+
- spec/fist_of_fury/dispatcher_sharedspec.rb
|
233
|
+
- spec/fist_of_fury/job_spec.rb
|
234
|
+
- spec/fist_of_fury/logging_sharedspec.rb
|
235
|
+
- spec/fist_of_fury/railtie_spec.rb
|
236
|
+
- spec/fist_of_fury/recurrent_sharedspec.rb
|
237
|
+
- spec/fist_of_fury/schedule_spec.rb
|
238
|
+
- spec/fist_of_fury/subclass_tracking_sharedspec.rb
|
239
|
+
- spec/fist_of_fury/version_spec.rb
|
179
240
|
- spec/fist_of_fury_spec.rb
|
241
|
+
- spec/fixtures/awesome_job.rb
|
180
242
|
- spec/spec_helper.rb
|