timeloop 1.0.3 → 2.0.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 +5 -13
- data/.travis.yml +1 -0
- data/README.md +21 -6
- data/lib/timeloop.rb +88 -31
- data/lib/timeloop/version.rb +2 -2
- data/spec/unit/timeloop_spec.rb +129 -0
- metadata +18 -16
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ODYxODZmNWFlOTM5Njc0OTkxZjc5YTg3OWFhOTNjZjZlNjVkY2ZiNQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f87b83fb56ecfca6fa89255b53b1e07da53d9963
|
4
|
+
data.tar.gz: a6f76643df4419b586da4df4726003db238b6ae8
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZDgyNDI4OWRhYWY0NzJiNTRmYTYxMTMxZTZhNGIwZTg5ZTdiOTdhZmJhMjFi
|
11
|
-
NGNjZjYyMjRiZDc2ODRmMzA0MmViMjI5NmE0NGU3YmY0ZDUwNjA=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YzUxOTg2ZGJkNjFiZGZlM2VkNTUxNmRjNDMwMGRhY2IyMmJmMTkwN2ZhOTI5
|
14
|
-
YzgwYzZkNWJjNzAwYzRkNzNlNWNkNGRhZWVlYjcxNTY4ODY4NzMxNTk1ZWUy
|
15
|
-
YTFhNjMyYWViZWRhNmMwMDAzYTJjYzM5OTBmNmMwNWJjMjk0NGQ=
|
6
|
+
metadata.gz: 68ebd000097745cfb4f9de25a4ba3152a44876a9987b5a6361eec557f3600a6da5f943e0e5c45ffad39faa2dcf08a90ccf33da5e3ff8c9f72d4a8372016f6b8a
|
7
|
+
data.tar.gz: df2377a796a83f2cb6431fd7ef607f9432e7c48e80cc52e6776d314bbf6168313a2402d31ca8226dd8d636f997d7bf136a25e85e7b428b62e97be56fb5536fc6
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Timeloop [](https://codeclimate.com/github/pinoss/timeloop) [](https://travis-ci.org/pinoss/timeloop) [](https://coveralls.io/r/pinoss/timeloop?branch=master) [](http://badge.fury.io/rb/timeloop)
|
2
2
|
|
3
|
-
Timeloop is a simple Ruby gem that provides loop with time interval inspired on 'every' method from whenever gem.
|
3
|
+
Timeloop is a simple Ruby gem that provides loop with time interval inspired on 'every' method from whenever gem.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -23,7 +23,7 @@ Require gem in you Ruby file:
|
|
23
23
|
```ruby
|
24
24
|
require 'timeloop'
|
25
25
|
|
26
|
-
every 10.seconds do
|
26
|
+
Timeloop.every 10.seconds do
|
27
27
|
puts '10 seconds delay'
|
28
28
|
end
|
29
29
|
|
@@ -34,7 +34,7 @@ end
|
|
34
34
|
# 10 seconds delay
|
35
35
|
# ...
|
36
36
|
|
37
|
-
every 3.hours do
|
37
|
+
Timeloop.every 3.hours do
|
38
38
|
Mail.deliver do
|
39
39
|
from 'me@test.email'
|
40
40
|
to 'you@test.email'
|
@@ -43,7 +43,7 @@ every 3.hours do
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
every :minute do
|
46
|
+
Timeloop.every :minute do
|
47
47
|
product = 2 * 2
|
48
48
|
puts "and 2 * 2 is still #{product}"
|
49
49
|
end
|
@@ -59,7 +59,7 @@ end
|
|
59
59
|
You can also specify how many times your block will be evaluated:
|
60
60
|
|
61
61
|
```ruby
|
62
|
-
every 10.seconds, maximum: 4.times do |i|
|
62
|
+
Timeloop.every 10.seconds, maximum: 4.times do |i|
|
63
63
|
puts i
|
64
64
|
end
|
65
65
|
|
@@ -71,7 +71,7 @@ end
|
|
71
71
|
# 3
|
72
72
|
# => 4
|
73
73
|
|
74
|
-
every 'second', maximum: 3 do |i|
|
74
|
+
Timeloop.every 'second', maximum: 3 do |i|
|
75
75
|
puts 'You will see me only 3 times.'
|
76
76
|
end
|
77
77
|
|
@@ -83,6 +83,21 @@ end
|
|
83
83
|
# => 3
|
84
84
|
```
|
85
85
|
|
86
|
+
If you want to include the `every` method in your own classes simply
|
87
|
+
include the `Timeloop::Helper` module.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
class MyClass
|
91
|
+
include Timeloop::Helper
|
92
|
+
|
93
|
+
def doit
|
94
|
+
every 10.seconds, max: 4 do |i|
|
95
|
+
puts i
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
86
101
|
## Contributing
|
87
102
|
|
88
103
|
1. Fork it ( https://github.com/pinoss/timeloop/fork )
|
data/lib/timeloop.rb
CHANGED
@@ -1,28 +1,69 @@
|
|
1
1
|
require 'timeloop/core_ext'
|
2
2
|
require 'timeloop/version'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
4
|
+
# Execute a block of code periodically. The runtime of the block is
|
5
|
+
# taken in to account so a delay in one run does not impact the start
|
6
|
+
# times of future runs.
|
7
|
+
class Timeloop
|
8
|
+
# Runs provided block periodically.
|
9
|
+
#
|
10
|
+
# Yields Integer index of the iteration to the provide block every `period`.
|
11
|
+
def loop
|
12
|
+
i = -1
|
13
|
+
super() do
|
14
|
+
run_started_at = Time.now
|
15
|
+
i += 1
|
16
|
+
logger.debug("#{to_s}: starting #{i}th run")
|
17
|
+
yield(i) if block_given?
|
18
|
+
|
19
|
+
break if i+1 >= max
|
20
|
+
|
21
|
+
sleep til_next_start_time(Time.now - run_started_at)
|
22
|
+
.tap{|s| logger.debug "#{to_s}: sleeping #{s} seconds until next run" }
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
+
# Returns string representation of self,
|
27
|
+
def to_s
|
28
|
+
["#<Timeloop period=#{period}",
|
29
|
+
max < Float::INFINITY ? ", max=#{max}" : "",
|
30
|
+
">"].join
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
attr_reader :period, :max, :logger
|
36
|
+
|
37
|
+
# Initialize a new Timeloop object.
|
38
|
+
#
|
39
|
+
# Options
|
40
|
+
# period - seconds or name of time period (eg, 'second', 'hour')
|
41
|
+
# between iterations
|
42
|
+
# max - the maximum number of executions of the block. Default:
|
43
|
+
# INFINITY
|
44
|
+
# maximum - synonym of `max`
|
45
|
+
# logger - logger to which debugging info should be sent. Default:
|
46
|
+
# no logging
|
47
|
+
def initialize(opts)
|
48
|
+
@period = parse_frequency(opts.fetch(:period))
|
49
|
+
@max = parse_maximum_value(opts.fetch(:maximum){opts.fetch(:max, Float::INFINITY)})
|
50
|
+
@logger = opts.fetch(:logger) { NULL_LOGGER }
|
51
|
+
end
|
52
|
+
|
53
|
+
def til_next_start_time(current_run_duration)
|
54
|
+
[0, period - current_run_duration].max
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_maximum_value(maximum)
|
58
|
+
case maximum
|
59
|
+
when Enumerator
|
60
|
+
maximum.count
|
61
|
+
when Integer, Float::INFINITY
|
62
|
+
maximum
|
63
|
+
else
|
64
|
+
fail ArgumentError.new('wrong type of argument (should be an Enumerator or Integer)')
|
65
|
+
end
|
66
|
+
end
|
26
67
|
|
27
68
|
def parse_frequency(frequency)
|
28
69
|
case frequency
|
@@ -30,7 +71,7 @@ module Kernel
|
|
30
71
|
frequency
|
31
72
|
when :second, 'second'
|
32
73
|
1.second
|
33
|
-
when :minute, '
|
74
|
+
when :minute, 'minute'
|
34
75
|
1.minute
|
35
76
|
when :hour, 'hour'
|
36
77
|
1.hour
|
@@ -45,18 +86,34 @@ module Kernel
|
|
45
86
|
end
|
46
87
|
end
|
47
88
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
89
|
+
# Useful to avoid conditionals in logging code.
|
90
|
+
NULL_LOGGER = Class.new do
|
91
|
+
def debug(*args); end
|
92
|
+
end.new
|
93
|
+
|
94
|
+
# Conveniences methods that provide access to the Timeloop
|
95
|
+
# functionality to be included in other classes and modules.
|
96
|
+
module Helper
|
97
|
+
# Provides a way to execute a block of code periodically. The
|
98
|
+
# runtime of the block is taken in to account so a delay in one run
|
99
|
+
# does not impact the start times of future runs.
|
100
|
+
#
|
101
|
+
# Arguments
|
102
|
+
# period - seconds or name of time period (eg,
|
103
|
+
# 'second', 'hour') between iterations
|
104
|
+
#
|
105
|
+
# Options
|
106
|
+
# max - the maximum number of executions of the block. Default:
|
107
|
+
# INFINITY
|
108
|
+
# maximum - synonym of `max`
|
109
|
+
# logger - logger to which debugging info should be sent. Default:
|
110
|
+
# no logging
|
111
|
+
#
|
112
|
+
# Yields Integer index of the iteration to the provide block every `period`.
|
113
|
+
def every(period, opts = {}, &blk)
|
114
|
+
Timeloop.new(opts.merge(period: period)).loop(&blk)
|
56
115
|
end
|
57
116
|
end
|
58
|
-
end
|
59
117
|
|
60
|
-
|
61
|
-
include Kernel
|
118
|
+
extend Helper
|
62
119
|
end
|
data/lib/timeloop/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '
|
1
|
+
class Timeloop
|
2
|
+
VERSION = '2.0.0'
|
3
3
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
describe Timeloop do
|
5
|
+
describe ".new" do
|
6
|
+
it "accepts all the options" do
|
7
|
+
expect(described_class.new(period: 0.1, maximum: 3, logger: a_logger))
|
8
|
+
.to be_kind_of described_class
|
9
|
+
end
|
10
|
+
|
11
|
+
it "works with minimal options set" do
|
12
|
+
expect(described_class.new(period: 0.1))
|
13
|
+
.to be_kind_of described_class
|
14
|
+
end
|
15
|
+
|
16
|
+
it "understands english periods" do
|
17
|
+
expect(described_class.new(period: :second))
|
18
|
+
.to be_kind_of described_class
|
19
|
+
end
|
20
|
+
|
21
|
+
it "rejects bogus periods" do
|
22
|
+
expect{described_class.new(period: :circle)}
|
23
|
+
.to raise_error ArgumentError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".every(period, max: 3)" do
|
28
|
+
it "invokes block on the specified period" do
|
29
|
+
start_times = []
|
30
|
+
Timeloop.every(0.1, max: 3) { start_times << Time.now }
|
31
|
+
|
32
|
+
expect(start_times[1] - start_times[0]).to be_within(0.01).of(0.1)
|
33
|
+
expect(start_times[2] - start_times[1]).to be_within(0.01).of(0.1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".every(period, maximum: 3)" do
|
38
|
+
it "invokes block on the specified period" do
|
39
|
+
start_times = []
|
40
|
+
Timeloop.every(0.1, maximum: 3) { start_times << Time.now }
|
41
|
+
|
42
|
+
expect(start_times[1] - start_times[0]).to be_within(0.01).of(0.1)
|
43
|
+
expect(start_times[2] - start_times[1]).to be_within(0.01).of(0.1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe ".every(period)" do
|
48
|
+
it "loops forever" do
|
49
|
+
expect {
|
50
|
+
Timeout.timeout(1) do
|
51
|
+
expect{|b| Timeloop.every(0.1, &b)}.to yield_control.exactly(10).times
|
52
|
+
end
|
53
|
+
}.to raise_error(Timeout::Error)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
subject { described_class.new(period: 0.1, maximum: 3, logger: a_logger) }
|
58
|
+
|
59
|
+
describe "logging" do
|
60
|
+
it "records sleeps" do
|
61
|
+
subject.loop {}
|
62
|
+
|
63
|
+
expect(a_logger).to have_received(:debug).with(/sleeping/i).exactly(2)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "records starts" do
|
67
|
+
subject.loop {}
|
68
|
+
|
69
|
+
expect(a_logger).to have_received(:debug).with(/starting/i).exactly(3)
|
70
|
+
expect(a_logger).to have_received(:debug).with(/starting.*0/i)
|
71
|
+
expect(a_logger).to have_received(:debug).with(/starting.*1/i)
|
72
|
+
expect(a_logger).to have_received(:debug).with(/starting.*2/i)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "invokes block on the specified period" do
|
77
|
+
start_times = []
|
78
|
+
subject.loop { start_times << Time.now }
|
79
|
+
|
80
|
+
expect(start_times[1] - start_times[0]).to be_within(0.01).of(0.1)
|
81
|
+
expect(start_times[2] - start_times[1]).to be_within(0.01).of(0.1)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "invokes block on the specified period even if block takes a (relatively) long time" do
|
85
|
+
start_times = []
|
86
|
+
subject.loop { start_times << Time.now; sleep 0.08 }
|
87
|
+
|
88
|
+
expect(start_times[1] - start_times[0]).to be_within(0.01).of(0.1)
|
89
|
+
expect(start_times[2] - start_times[1]).to be_within(0.01).of(0.1)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "invokes block continuously without any delays if block takes longer than period" do
|
93
|
+
start_times = []
|
94
|
+
subject.loop { start_times << Time.now; sleep 0.18 }
|
95
|
+
|
96
|
+
expect(start_times[1] - start_times[0]).to be_within(0.01).of(0.18)
|
97
|
+
expect(start_times[2] - start_times[1]).to be_within(0.01).of(0.18)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "provides run count on iteration" do
|
101
|
+
expect{|b| subject.loop(&b)}.to yield_successive_args(0, 1, 2)
|
102
|
+
end
|
103
|
+
|
104
|
+
context "w/ max" do
|
105
|
+
specify { expect(subject.to_s).to eq "#<Timeloop period=0.1, max=3>" }
|
106
|
+
|
107
|
+
it "stops looping at maximum number of time" do
|
108
|
+
expect{|b| subject.loop(&b)}.to yield_control.exactly(3).times
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "w/o max" do
|
113
|
+
subject { described_class.new(period: 0.1) }
|
114
|
+
|
115
|
+
specify { expect(subject.to_s).to eq "#<Timeloop period=0.1>" }
|
116
|
+
|
117
|
+
it "loops forever" do
|
118
|
+
expect {
|
119
|
+
Timeout.timeout(1) do
|
120
|
+
subject.loop { }
|
121
|
+
end
|
122
|
+
}.to raise_error(Timeout::Error)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Background
|
127
|
+
|
128
|
+
let(:a_logger) { spy(instance_double(Logger)) }
|
129
|
+
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timeloop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maciej Paruszewski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.6'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: coveralls
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description:
|
@@ -73,9 +73,9 @@ executables: []
|
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
|
-
- .gitignore
|
77
|
-
- .rspec
|
78
|
-
- .travis.yml
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
79
|
- Gemfile
|
80
80
|
- LICENSE.txt
|
81
81
|
- README.md
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- spec/spec_helper.rb
|
91
91
|
- spec/unit/core_ext/integer_spec.rb
|
92
92
|
- spec/unit/core_ext/numeric_spec.rb
|
93
|
+
- spec/unit/timeloop_spec.rb
|
93
94
|
- timeloop.gemspec
|
94
95
|
homepage: https://github.com/pinoss/timeloop
|
95
96
|
licenses:
|
@@ -101,17 +102,17 @@ require_paths:
|
|
101
102
|
- lib
|
102
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
103
104
|
requirements:
|
104
|
-
- -
|
105
|
+
- - ">="
|
105
106
|
- !ruby/object:Gem::Version
|
106
107
|
version: '0'
|
107
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
109
|
requirements:
|
109
|
-
- -
|
110
|
+
- - ">="
|
110
111
|
- !ruby/object:Gem::Version
|
111
112
|
version: '0'
|
112
113
|
requirements: []
|
113
114
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.
|
115
|
+
rubygems_version: 2.4.6
|
115
116
|
signing_key:
|
116
117
|
specification_version: 4
|
117
118
|
summary: Timeloop is a simple Ruby gem that provides loop with time interval.
|
@@ -119,3 +120,4 @@ test_files:
|
|
119
120
|
- spec/spec_helper.rb
|
120
121
|
- spec/unit/core_ext/integer_spec.rb
|
121
122
|
- spec/unit/core_ext/numeric_spec.rb
|
123
|
+
- spec/unit/timeloop_spec.rb
|