ultra_marathon 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 +4 -4
- data/README.md +25 -3
- data/lib/ultra_marathon/abstract_runner.rb +5 -4
- data/lib/ultra_marathon/instrumentation.rb +8 -32
- data/lib/ultra_marathon/instrumentation/profile.rb +51 -0
- data/lib/ultra_marathon/sub_runner.rb +30 -9
- data/lib/ultra_marathon/version.rb +1 -1
- data/spec/ultra_marathon/instrumentation/profile_spec.rb +58 -0
- data/spec/ultra_marathon/instrumentation_spec.rb +8 -47
- data/spec/ultra_marathon/sub_runner_spec.rb +17 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4dc515ceea5248b2ac05129ad6b2f5d074742819
|
4
|
+
data.tar.gz: e68e4458184d953024a5efe23083dfc4a3f9ba8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c32534cc602929cfcc7b0d98d47f868e8838adc1de117ffe31a2f73e64bcd9b6679cf09af40daf8ec5e2b4bd9da86106c4fa05f02e8a350e536d606f4ef9d19
|
7
|
+
data.tar.gz: 932ebf6722c3e7fafe391b0e725eb2d2ef280b9187ccc365bf76ca39bc78d2100e5db415caa183de646ed2d9967f9a8c4b290b87203295af45666c299dd1bea1
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ running complex jobs. It is best inheirited to fully customize.
|
|
18
18
|
A simple DSL, currently consisting of only the `run` command, are used to
|
19
19
|
specify independent chunks of work. The first argument is the name of the run
|
20
20
|
block. Omitted, this defaults to ':main', though names must be unique within a
|
21
|
-
given Runner
|
21
|
+
given Runner. E.g. for a runner with N run blocks, N - 1 must be manually named.
|
22
22
|
|
23
23
|
```ruby
|
24
24
|
class MailRunner < UltraMarathon::AbstractRunner
|
@@ -29,12 +29,19 @@ class MailRunner < UltraMarathon::AbstractRunner
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# :eat run block
|
32
|
+
# Must be named because there is already a :main block (one without
|
33
|
+
# a name explicitly defined)
|
32
34
|
run :eat do
|
33
35
|
add_butter
|
34
36
|
add_syrup
|
35
37
|
eat!
|
36
38
|
end
|
37
39
|
|
40
|
+
# Will throw an error, since the first runner is implicitly named :main
|
41
|
+
run :main do
|
42
|
+
puts 'nope nope nope!'
|
43
|
+
end
|
44
|
+
|
38
45
|
# Omitted for brevity
|
39
46
|
def add_butter; end
|
40
47
|
def add_syrup; end
|
@@ -137,6 +144,19 @@ class MercurialRunner < UltraMarathon::AbstractRunner
|
|
137
144
|
end
|
138
145
|
```
|
139
146
|
|
147
|
+
### Instrumentation
|
148
|
+
|
149
|
+
The entire `run!` is instrumented and logged automatically. Additionally,
|
150
|
+
individual run blocks are instrumented and logged by default.
|
151
|
+
|
152
|
+
You can also choose to not instrument a block:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
run :embarrassingly_slowly, instrument: false do
|
156
|
+
sleep(rand(10))
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
140
160
|
### Success, Failure, and Reseting
|
141
161
|
|
142
162
|
If any part of a runner fails, either by raising an error or explicitly setting
|
@@ -144,8 +164,10 @@ If any part of a runner fails, either by raising an error or explicitly setting
|
|
144
164
|
which rely on an unsuccessful run block will also be considered failed.
|
145
165
|
|
146
166
|
A failed runner can be reset, which essentially changes the failed runners to
|
147
|
-
being unrun and returns the success flag to true.
|
148
|
-
|
167
|
+
being unrun and returns the success flag to true. Runners that have been
|
168
|
+
successfully run _will not_ be rerun, though any failed dependencies will.
|
169
|
+
|
170
|
+
The runner will then execute any `on_reset` callbacks before returning itself.
|
149
171
|
|
150
172
|
```ruby
|
151
173
|
class WatRunner < UltraMarathon::AbstractRunner
|
@@ -24,7 +24,7 @@ module UltraMarathon
|
|
24
24
|
begin
|
25
25
|
self.success = true
|
26
26
|
invoke_before_run_callbacks
|
27
|
-
instrument { run_unrun_sub_runners }
|
27
|
+
instrument(:run_unrun_sub_runners) { run_unrun_sub_runners }
|
28
28
|
self.success = failed_sub_runners.empty?
|
29
29
|
rescue StandardError => error
|
30
30
|
invoke_on_error_callbacks(error)
|
@@ -180,12 +180,13 @@ module UltraMarathon
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def summary
|
183
|
+
run_instrumentation = instrumentations[:run_unrun_sub_runners]
|
183
184
|
"""
|
184
185
|
|
185
186
|
Status: #{status}
|
186
|
-
Start Time: #{formatted_start_time}
|
187
|
-
End Time: #{formatted_end_time}
|
188
|
-
Total Time: #{formatted_total_time}
|
187
|
+
Run Start Time: #{run_instrumentation.formatted_start_time}
|
188
|
+
End Time: #{run_instrumentation.formatted_end_time}
|
189
|
+
Total Time: #{run_instrumentation.formatted_total_time}
|
189
190
|
|
190
191
|
Successful SubRunners: #{successful_sub_runners.size}
|
191
192
|
Failed SubRunners: #{failed_sub_runners.size}
|
@@ -1,49 +1,25 @@
|
|
1
|
+
require 'ultra_marathon/instrumentation/profile'
|
2
|
+
|
1
3
|
module UltraMarathon
|
2
4
|
module Instrumentation
|
3
5
|
TIME_FORMAT = "%02d:%02d:%02d"
|
4
|
-
attr_reader :start_time, :end_time
|
5
6
|
|
6
7
|
## Public Instance Methods
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
(end_time - start_time).to_i
|
11
|
-
end
|
12
|
-
|
13
|
-
def formatted_total_time
|
14
|
-
duration = total_time
|
15
|
-
seconds = (duration % 60).floor
|
16
|
-
minutes = (duration / 60).floor
|
17
|
-
hours = (duration / 3600).floor
|
18
|
-
sprintf(TIME_FORMAT, hours, minutes, seconds)
|
19
|
-
end
|
20
|
-
|
21
|
-
def formatted_start_time
|
22
|
-
format_time(start_time)
|
23
|
-
end
|
24
|
-
|
25
|
-
def formatted_end_time
|
26
|
-
format_time(end_time)
|
9
|
+
def instrumentations
|
10
|
+
@instrumentations ||= Hash.new
|
27
11
|
end
|
28
12
|
|
29
13
|
private
|
30
14
|
|
31
15
|
## Private Instance Methods
|
32
16
|
|
33
|
-
def format_time(time)
|
34
|
-
sprintf(TIME_FORMAT, time.hour, time.min, time.sec)
|
35
|
-
end
|
36
|
-
|
37
17
|
# Instruments given block, setting its start time and end time
|
38
18
|
# Returns the result of the block
|
39
|
-
def instrument(&block)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
ensure
|
44
|
-
@end_time = Time.now
|
45
|
-
end
|
46
|
-
return_value
|
19
|
+
def instrument(name, &block)
|
20
|
+
profile = Profile.new(name, &block)
|
21
|
+
instrumentations[name] = profile
|
22
|
+
profile.call
|
47
23
|
end
|
48
24
|
end
|
49
25
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module UltraMarathon
|
2
|
+
module Instrumentation
|
3
|
+
class Profile
|
4
|
+
attr_reader :name, :instrument_block, :start_time, :end_time
|
5
|
+
|
6
|
+
## Public Instance Methods
|
7
|
+
|
8
|
+
def initialize(name, &block)
|
9
|
+
@name = name
|
10
|
+
@instrument_block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
@start_time = Time.now
|
15
|
+
begin
|
16
|
+
return_value = instrument_block.call
|
17
|
+
ensure
|
18
|
+
@end_time = Time.now
|
19
|
+
end
|
20
|
+
return_value
|
21
|
+
end
|
22
|
+
|
23
|
+
# returns the total time, in seconds
|
24
|
+
def total_time
|
25
|
+
(end_time - start_time).to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted_total_time
|
29
|
+
duration = total_time
|
30
|
+
seconds = (duration % 60).floor
|
31
|
+
minutes = (duration / 60).floor
|
32
|
+
hours = (duration / 3600).floor
|
33
|
+
sprintf(TIME_FORMAT, hours, minutes, seconds)
|
34
|
+
end
|
35
|
+
|
36
|
+
def formatted_start_time
|
37
|
+
format_time(start_time)
|
38
|
+
end
|
39
|
+
|
40
|
+
def formatted_end_time
|
41
|
+
format_time(end_time)
|
42
|
+
end
|
43
|
+
|
44
|
+
## Private Instance Methods
|
45
|
+
|
46
|
+
def format_time(time)
|
47
|
+
sprintf(TIME_FORMAT, time.hour, time.min, time.sec)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'active_support/core_ext/proc'
|
3
3
|
require 'ultra_marathon/callbacks'
|
4
|
+
require 'ultra_marathon/instrumentation'
|
4
5
|
require 'ultra_marathon/logging'
|
5
6
|
|
6
7
|
module UltraMarathon
|
7
8
|
class SubRunner
|
8
9
|
include Callbacks
|
10
|
+
include Instrumentation
|
9
11
|
include Logging
|
10
12
|
attr_accessor :run_block, :success
|
11
13
|
attr_reader :sub_context, :options, :name
|
@@ -22,18 +24,32 @@ module UltraMarathon
|
|
22
24
|
# other class, but do other things (like log) in the context of this one.
|
23
25
|
def initialize(options, run_block)
|
24
26
|
@name = options[:name]
|
25
|
-
@options =
|
27
|
+
@options = {
|
28
|
+
instrument: true
|
29
|
+
}.merge(options)
|
26
30
|
@sub_context = SubContext.new(options[:context], run_block)
|
27
31
|
end
|
28
32
|
|
29
33
|
def run!
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
instrument(:run) do
|
35
|
+
begin
|
36
|
+
self.success = true
|
37
|
+
run_sub_context
|
38
|
+
rescue StandardError => error
|
39
|
+
invoke_on_error_callbacks(error)
|
40
|
+
ensure
|
41
|
+
invoke_after_run_callbacks
|
42
|
+
end
|
43
|
+
end
|
44
|
+
log_instrumentation
|
45
|
+
end
|
46
|
+
|
47
|
+
def log_instrumentation
|
48
|
+
if options[:instrument]
|
49
|
+
run_profile = instrumentations[:run]
|
50
|
+
logger.info """
|
51
|
+
Total Time: #{run_profile.formatted_total_time}
|
52
|
+
"""
|
37
53
|
end
|
38
54
|
end
|
39
55
|
|
@@ -76,9 +92,14 @@ module UltraMarathon
|
|
76
92
|
|
77
93
|
def initialize(context, run_block)
|
78
94
|
@context = context
|
95
|
+
@run_block = run_block
|
79
96
|
# Ruby cannot marshal procs or lambdas, so we need to define a method.
|
80
97
|
# Binding to self allows us to intercept logging calls.
|
81
|
-
define_singleton_method
|
98
|
+
define_singleton_method(:call, &bound_block)
|
99
|
+
end
|
100
|
+
|
101
|
+
def bound_block
|
102
|
+
run_block.bind(self)
|
82
103
|
end
|
83
104
|
|
84
105
|
# If the original context responds, including private methods,
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe UltraMarathon::Instrumentation::Profile do
|
3
|
+
let(:start_time) { Time.local(1991, 1, 31, 15, 15, 00) }
|
4
|
+
let(:end_time) { Time.local(1991, 1, 31, 16, 00, 20) }
|
5
|
+
let(:run_block) do
|
6
|
+
# This is going to be evaluated in context of the instance
|
7
|
+
# so we need to wrap in an IIF to access `end_time`
|
8
|
+
Proc.new do |end_time|
|
9
|
+
Proc.new do
|
10
|
+
Timecop.travel(end_time)
|
11
|
+
'Bubbles'
|
12
|
+
end
|
13
|
+
end.call(end_time)
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:profile) { described_class.new(:speedy, &run_block) }
|
17
|
+
|
18
|
+
subject(:run) { profile.call }
|
19
|
+
|
20
|
+
before(:each) { Timecop.freeze(start_time) }
|
21
|
+
after(:each) { Timecop.return }
|
22
|
+
|
23
|
+
it 'should set its name' do
|
24
|
+
profile.name.should be :speedy
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#call' do
|
28
|
+
it 'should return the result of the block' do
|
29
|
+
run.should eq 'Bubbles'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should set the start_time' do
|
33
|
+
run
|
34
|
+
profile.start_time.should eq start_time
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should set the end_time' do
|
38
|
+
run
|
39
|
+
profile.end_time.to_i.should eq end_time.to_i
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'total_time' do
|
44
|
+
before(:each) { run }
|
45
|
+
|
46
|
+
it 'should return the total seconds elapsed' do
|
47
|
+
profile.total_time.should eq 2720
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#formatted_total_time' do
|
52
|
+
before(:each) { run }
|
53
|
+
|
54
|
+
it 'should return the total time, formatted' do
|
55
|
+
profile.formatted_total_time.should eq '00:45:20'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -6,65 +6,26 @@ describe UltraMarathon::Instrumentation do
|
|
6
6
|
include UltraMarathon::Instrumentation
|
7
7
|
|
8
8
|
def run(&block)
|
9
|
-
instrument { block.call }
|
9
|
+
instrument(:run) { block.call }
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
let(:test_instance) { test_class.new }
|
15
|
-
let(:start_time) { Time.local(1991, 1, 31, 15, 15, 00) }
|
16
|
-
let(:end_time) { Time.local(1991, 1, 31, 16, 00, 20) }
|
17
|
-
let(:run_block) do
|
18
|
-
# This is going to be evaluated in context of the instance
|
19
|
-
# so we need to wrap in an IIF to access `end_time`
|
20
|
-
Proc.new do |end_time|
|
21
|
-
Proc.new do
|
22
|
-
Timecop.travel(end_time)
|
23
|
-
end
|
24
|
-
end.call(end_time)
|
25
|
-
end
|
26
15
|
|
27
16
|
subject(:run) { test_instance.run &run_block }
|
28
17
|
|
29
|
-
before(:each) { Timecop.freeze(start_time) }
|
30
|
-
after(:each) { Timecop.return }
|
31
|
-
|
32
18
|
describe '#instrument' do
|
19
|
+
let(:run_block) { Proc.new { 'Bubbles!' } }
|
33
20
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
it 'should return the result of the passed in block' do
|
38
|
-
run.should eq 'Bubbles!'
|
39
|
-
end
|
21
|
+
it 'should return the result of the passed in block' do
|
22
|
+
run.should eq 'Bubbles!'
|
40
23
|
end
|
41
24
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
test_instance.start_time.should eq start_time
|
47
|
-
end
|
48
|
-
|
49
|
-
it 'should set the end_time' do
|
50
|
-
test_instance.end_time.to_i.should eq end_time.to_i
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe 'total_time' do
|
56
|
-
before(:each) { run }
|
57
|
-
|
58
|
-
it 'should return the total seconds elapsed' do
|
59
|
-
test_instance.total_time.should eq 2720
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe '#formatted_total_time' do
|
64
|
-
before(:each) { run }
|
65
|
-
|
66
|
-
it 'should return the total time, formatted' do
|
67
|
-
test_instance.formatted_total_time.should eq '00:45:20'
|
25
|
+
it 'should add the instrumentation information to the #instrumentations hash' do
|
26
|
+
run
|
27
|
+
profile = test_instance.instrumentations[:run]
|
28
|
+
profile.should be_an UltraMarathon::Instrumentation::Profile
|
68
29
|
end
|
69
30
|
end
|
70
31
|
end
|
@@ -50,5 +50,22 @@ describe UltraMarathon::SubRunner do
|
|
50
50
|
test_instance.logger.contents.should include 'Draws those who are willing, drags those who are not.'
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
describe 'instrumentation' do
|
55
|
+
it 'should log the total time' do
|
56
|
+
subject
|
57
|
+
test_instance.logger.contents.should include 'Total Time:'
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'when instrument: false is set' do
|
61
|
+
let(:options) do
|
62
|
+
{ context: context, name: name, instrument: false }
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should not log total time' do
|
66
|
+
test_instance.logger.contents.should_not include 'Total Time:'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
53
70
|
end
|
54
71
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ultra_marathon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Maddox
|
@@ -22,6 +22,7 @@ files:
|
|
22
22
|
- lib/ultra_marathon/abstract_runner.rb
|
23
23
|
- lib/ultra_marathon/callbacks.rb
|
24
24
|
- lib/ultra_marathon/instrumentation.rb
|
25
|
+
- lib/ultra_marathon/instrumentation/profile.rb
|
25
26
|
- lib/ultra_marathon/logger.rb
|
26
27
|
- lib/ultra_marathon/logging.rb
|
27
28
|
- lib/ultra_marathon/store.rb
|
@@ -32,6 +33,7 @@ files:
|
|
32
33
|
- spec/support/test_helpers.rb
|
33
34
|
- spec/ultra_marathon/abstract_runner_spec.rb
|
34
35
|
- spec/ultra_marathon/callbacks_spec.rb
|
36
|
+
- spec/ultra_marathon/instrumentation/profile_spec.rb
|
35
37
|
- spec/ultra_marathon/instrumentation_spec.rb
|
36
38
|
- spec/ultra_marathon/logging_spec.rb
|
37
39
|
- spec/ultra_marathon/store_spec.rb
|
@@ -67,6 +69,7 @@ test_files:
|
|
67
69
|
- spec/support/test_helpers.rb
|
68
70
|
- spec/ultra_marathon/abstract_runner_spec.rb
|
69
71
|
- spec/ultra_marathon/callbacks_spec.rb
|
72
|
+
- spec/ultra_marathon/instrumentation/profile_spec.rb
|
70
73
|
- spec/ultra_marathon/instrumentation_spec.rb
|
71
74
|
- spec/ultra_marathon/logging_spec.rb
|
72
75
|
- spec/ultra_marathon/store_spec.rb
|