stagger 0.0.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 +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +91 -0
- data/Rakefile +7 -0
- data/lib/stagger/version.rb +3 -0
- data/lib/stagger.rb +69 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/stagger_spec.rb +271 -0
- data/stagger.gemspec +27 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2ae0c775f963616a18e2b349a55dac219f0fd3d6
|
4
|
+
data.tar.gz: 8b7a47c160c80a30ef5f3919aea666baa93d0e13
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e0d494d9034fa8b520391e16be318a79bdd97869bca734aed9807b896dcfe2057c63b98ecabfbfbd78d7cae4075caa359783144ff7826a1e73f35f2237e43e14
|
7
|
+
data.tar.gz: fc9c1c277be8e4764e0f19a1d767aaa5490f0e08c58647525ac9da9808f20e2239f88d1b37371641f98b1247ffe84eb73be80b4dcf67ad08eba936a68ed83820
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Valentin Vasilyev
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Stagger
|
2
|
+
|
3
|
+
[](https://travis-ci.org/Valve/stagger)
|
4
|
+
|
5
|
+
Stagger is a simple gem that evenly distributes items across business
|
6
|
+
days.
|
7
|
+
On the surface, this tasks seems simple, but when you have a lot of
|
8
|
+
tasks,
|
9
|
+
that should be scheduled across business days, that span several weeks,
|
10
|
+
it gets complicated. Stagger has good test coverage, I covered all cases
|
11
|
+
I could think of with specs.
|
12
|
+
|
13
|
+
Stagger has no runtime dependencies.
|
14
|
+
|
15
|
+
Real life business cases:
|
16
|
+
|
17
|
+
Schedule 100 emails to be distributed evenly across next 14 business
|
18
|
+
days:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
emails = get_emails()
|
22
|
+
schedule = Stagger.distribute(emails, 14)
|
23
|
+
```
|
24
|
+
|
25
|
+
Schedule one item to be sent as soon as possible but on business day only:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
email = get_email() # only one email
|
29
|
+
schedule = Stagger.distribute([email], 1) # i.e. distribute across 1
|
30
|
+
business day
|
31
|
+
```
|
32
|
+
|
33
|
+
Schedule 1000 emails to be sent across next 30 business days:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
emails = get_emails()
|
37
|
+
schedule = Stagger.distribute(emails, 30)
|
38
|
+
```
|
39
|
+
|
40
|
+
`schedule` is an array of arrays, each item is a pair of original item, associated with `Time`:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Time.now # 2014-06-27 14:00:00 +0400
|
44
|
+
# 3 items will be distributed today
|
45
|
+
schedule = Stagger.distribute([1,2,3], 1)
|
46
|
+
=> # [[1, 2014-6-27 14:00:00], [2, 2014-6-27 17:20:00], [3, 2014-6-27 20:40:00]]
|
47
|
+
```
|
48
|
+
|
49
|
+
First item is scheduled as soon as possible, while the rest is
|
50
|
+
distributed evenly across business days.
|
51
|
+
|
52
|
+
## Rails integration
|
53
|
+
|
54
|
+
Since this gem has no dependencies, it returns instances of ruby `Time`.
|
55
|
+
But when `ActiveSupport` is available, it returns instances of rails'
|
56
|
+
`ActiveSupport::TimeWithZone` with your current timezone already set.
|
57
|
+
In other words, you don't need to do anything when using this gem with
|
58
|
+
Rails.
|
59
|
+
|
60
|
+
## Plans
|
61
|
+
|
62
|
+
Plans to add support for holidays / initial delay and working hours in the future.**
|
63
|
+
|
64
|
+
|
65
|
+
## Installation
|
66
|
+
|
67
|
+
Add this line to your application's Gemfile:
|
68
|
+
|
69
|
+
gem 'stagger'
|
70
|
+
|
71
|
+
And then execute:
|
72
|
+
|
73
|
+
$ bundle
|
74
|
+
|
75
|
+
Or install it yourself as:
|
76
|
+
|
77
|
+
$ gem install stagger
|
78
|
+
|
79
|
+
### Compatibility
|
80
|
+
|
81
|
+
This gem is tested against MRI 2.1 & JRuby 1.7.11 (1.9 mode) on Travis
|
82
|
+
CI
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it ( https://github.com/[my-github-username]/stagger/fork )
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create a new Pull Request
|
91
|
+
6. Make sure you run tests with `rspec` command
|
data/Rakefile
ADDED
data/lib/stagger.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require "stagger/version"
|
2
|
+
|
3
|
+
module Stagger
|
4
|
+
SECONDS_IN_DAY = 86_400
|
5
|
+
class << self
|
6
|
+
def distribute(items, number_of_days)
|
7
|
+
return [] if Array(items).empty? || number_of_days.to_i < 1
|
8
|
+
time = get_starting_time
|
9
|
+
period_in_seconds = get_period_in_seconds(items.size, number_of_days, time)
|
10
|
+
items = items.reduce [] do |arr, item|
|
11
|
+
if business_day?(time)
|
12
|
+
arr << [item, time]
|
13
|
+
time = time + period_in_seconds
|
14
|
+
arr
|
15
|
+
else
|
16
|
+
time = time + SECONDS_IN_DAY
|
17
|
+
redo
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if active_support_time?
|
21
|
+
items.map{|i,t| [i, t.in_time_zone(Time.zone)]}
|
22
|
+
else
|
23
|
+
items
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def business_day?(time)
|
30
|
+
if time.sunday? || time.saturday?
|
31
|
+
false
|
32
|
+
else
|
33
|
+
time
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_starting_time
|
38
|
+
tc = Time.now
|
39
|
+
if tc.saturday?
|
40
|
+
at_beginning_of_day(tc) + SECONDS_IN_DAY * 2
|
41
|
+
elsif tc.sunday?
|
42
|
+
at_beginning_of_day(tc) + SECONDS_IN_DAY
|
43
|
+
else
|
44
|
+
tc
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_period_in_seconds(items_size, number_of_days, starting_time)
|
49
|
+
now = Time.now
|
50
|
+
total_period = number_of_days * SECONDS_IN_DAY
|
51
|
+
if business_day?(now)
|
52
|
+
total_period -= (now - at_beginning_of_day(now))
|
53
|
+
end
|
54
|
+
total_period / items_size
|
55
|
+
end
|
56
|
+
|
57
|
+
def at_beginning_of_day(time)
|
58
|
+
Time.new(time.year, time.month, time.day)
|
59
|
+
end
|
60
|
+
|
61
|
+
def at_end_of_day(time)
|
62
|
+
at_beginning_of_day(time) + SECONDS_IN_DAY - 0.000000000001
|
63
|
+
end
|
64
|
+
|
65
|
+
def active_support_time?
|
66
|
+
Time.respond_to?(:zone)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
RSpec.describe Stagger do
|
3
|
+
context 'distribute' do
|
4
|
+
context 'invalid args' do
|
5
|
+
it 'returns empty array when given empty array of items' do
|
6
|
+
expect(Stagger.distribute([], 1)).to be_empty
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns empty array when given nil for items' do
|
10
|
+
expect(Stagger.distribute(nil, 1)).to be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns empty array when given nil for days' do
|
14
|
+
expect(Stagger.distribute([1], nil)).to be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns empty array when given value that converts to number <1 for days' do
|
18
|
+
expect(Stagger.distribute([1], 0)).to be_empty
|
19
|
+
expect(Stagger.distribute([1], -1)).to be_empty
|
20
|
+
expect(Stagger.distribute([1], 'hello')).to be_empty
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'valid args' do
|
25
|
+
|
26
|
+
it 'returns same number of results as was given items' do
|
27
|
+
expect(Stagger.distribute([1,2,3], 1).size).to eq(3)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns an array of arrays' do
|
31
|
+
expect(Stagger.distribute([1,2,3], 1).first.class).to eq Array
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'returns array of pairs where first pair item type is given item type and second is Time' do
|
35
|
+
class Foo;end
|
36
|
+
pair = Stagger.distribute([Foo.new], 1).first
|
37
|
+
expect(pair.first).to be_a Foo
|
38
|
+
expect(pair.last).to be_a Time
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'single item' do
|
42
|
+
it 'schedules immediately if today is a business day' do
|
43
|
+
t = Time.local(2014, 6, 27, 18) # 6pm, Friday
|
44
|
+
Timecop.freeze(t) do
|
45
|
+
pair = Stagger.distribute([1], 1).first
|
46
|
+
expect(pair.last).to eq t
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'schedules immediately if today is a business day and number of days > 1' do
|
51
|
+
# a dumb test, actually, but I need to cover it
|
52
|
+
t = Time.local(2014, 6, 27, 18) # 6pm, Friday
|
53
|
+
Timecop.freeze(t) do
|
54
|
+
pair = Stagger.distribute([1], 2).first
|
55
|
+
expect(pair.last).to eq t
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'schedules to run on Monday (00:00) if today is Sunday' do
|
60
|
+
t = Time.local(2014, 6, 29, 14) # - 2pm, Sunday
|
61
|
+
Timecop.freeze(t) do
|
62
|
+
pair = Stagger.distribute([1], 1).first
|
63
|
+
expect(pair.last).to eq Time.local(2014, 6, 30) # Midnight, Monday
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'schedules to run on Monday (00:00) if today is Saturday' do
|
68
|
+
t = Time.local(2014, 6, 28, 14) # - 2pm, Saturday
|
69
|
+
Timecop.freeze(t) do # - 2am, Friday
|
70
|
+
pair = Stagger.distribute([1], 1).first
|
71
|
+
expect(pair.last).to eq Time.local(2014, 6, 30) # Midnight, Monday
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'multiple items' do
|
77
|
+
context 'single day' do
|
78
|
+
it 'schedules two items to run in a single day if today is a business day' do
|
79
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
80
|
+
Timecop.freeze(t) do
|
81
|
+
results = Stagger.distribute([1, 2], 1)
|
82
|
+
expect(results[0][0]).to eq(1)
|
83
|
+
expect(results[0][1]).to eq(t)
|
84
|
+
expect(results[1][0]).to eq(2)
|
85
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 27, 19)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'schedules two items to run in a single day if today is Saturday' do
|
90
|
+
t = Time.local(2014, 6, 28, 14) # - 2pm, Saturday
|
91
|
+
Timecop.freeze(t) do
|
92
|
+
results = Stagger.distribute([1,2], 1)
|
93
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday
|
94
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 12) # Monday
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'schedules two items to run in a single day if today is Sunday' do
|
99
|
+
t = Time.local(2014, 6, 29, 14) # - 2pm, Sunday
|
100
|
+
Timecop.freeze(t) do
|
101
|
+
results = Stagger.distribute([1,2], 1)
|
102
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday
|
103
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 12) # Monday
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'schedules three items to run in a single day if today is a business day' do
|
108
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
109
|
+
Timecop.freeze(t) do
|
110
|
+
results = Stagger.distribute([1, 2, 3], 1)
|
111
|
+
expect(results[0][0]).to eq(1)
|
112
|
+
expect(results[0][1]).to eq(t)
|
113
|
+
expect(results[1][0]).to eq(2)
|
114
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 27, 17, 20)
|
115
|
+
expect(results[2][0]).to eq(3)
|
116
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 27, 20, 40)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'schedules three items to run in a single day of today is Saturday' do
|
121
|
+
t = Time.local(2014, 6, 28, 14) # - 2pm, Saturday
|
122
|
+
Timecop.freeze(t) do
|
123
|
+
results = Stagger.distribute([1, 2, 3], 1)
|
124
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday
|
125
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 8) # Monday
|
126
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 30, 16) # Monday
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'schedules three items to run in a single day of today is Sunday' do
|
131
|
+
t = Time.local(2014, 6, 29, 14) # - 2pm, Sunday
|
132
|
+
Timecop.freeze(t) do
|
133
|
+
results = Stagger.distribute([1, 2, 3], 1)
|
134
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday
|
135
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 8) # Monday
|
136
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 30, 16) # Monday
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'schedules many items to run in a single day if today is a business day' do
|
141
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
142
|
+
Timecop.freeze(t) do
|
143
|
+
results = Stagger.distribute((1..100).to_a, 1)
|
144
|
+
expect(results[0][0]).to eq(1)
|
145
|
+
expect(results[0][1]).to eq(t)
|
146
|
+
expect(results[1][0]).to eq(2)
|
147
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 27, 14, 6) # every 6 mins
|
148
|
+
expect(results[2][0]).to eq(3)
|
149
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 27, 14, 12) # every 6 mins
|
150
|
+
expect(results[99][0]).to eq(100)
|
151
|
+
expect(results[99][1]).to eq Time.local(2014, 6, 27, 23, 54)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'multiple days' do
|
157
|
+
it 'schedules two items in two days if both days are business days' do
|
158
|
+
t = Time.local(2014, 6, 26, 14) # - 2pm, Thursday
|
159
|
+
Timecop.freeze(t) do
|
160
|
+
results = Stagger.distribute([1, 2], 2)
|
161
|
+
expect(results[0][1]).to eq(t)
|
162
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 27, 7)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'schedules two items in two days if today is Friday' do
|
167
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
168
|
+
Timecop.freeze(t) do
|
169
|
+
results = Stagger.distribute([1, 2], 2)
|
170
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 27, 14) # Friday
|
171
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 7) # Monday
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'schedules two items in two days if today is Saturday' do
|
176
|
+
t = Time.local(2014, 6, 28, 14) # - 2pm, Saturday
|
177
|
+
Timecop.freeze(t) do
|
178
|
+
results = Stagger.distribute([1, 2], 2)
|
179
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday
|
180
|
+
expect(results[1][1]).to eq Time.local(2014, 7, 1) # Tuesday
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'schedules two items in 3 days if all days are business days' do
|
185
|
+
t = Time.local(2014, 6, 25, 14) # - 2pm, Wednesday
|
186
|
+
Timecop.freeze(t) do
|
187
|
+
results = Stagger.distribute([1, 2], 3)
|
188
|
+
expect(results[0][1]).to eq(t)
|
189
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 26, 19) # - 7 pm, Thursday
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'schedules two items in 3 days if today is Friday' do
|
194
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
195
|
+
Timecop.freeze(t) do
|
196
|
+
results = Stagger.distribute([1, 2], 3)
|
197
|
+
expect(results[0][1]).to eq t
|
198
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 19) # Monday
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'schedules two items in 3 days if today is Saturday' do
|
203
|
+
t = Time.local(2014, 6, 28, 14) # - 2pm, Friday
|
204
|
+
Timecop.freeze(t) do
|
205
|
+
results = Stagger.distribute([1, 2], 3)
|
206
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday
|
207
|
+
expect(results[1][1]).to eq Time.local(2014, 7, 1, 12) # Wednesday
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'schedules three items in two days if both days are business days' do
|
212
|
+
t = Time.local(2014, 6, 26, 14) # - 2pm, Thursday
|
213
|
+
Timecop.freeze(t) do
|
214
|
+
results = Stagger.distribute([1, 2, 3], 2)
|
215
|
+
expect(results[0][1]).to eq(t)
|
216
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 27, 1, 20) # Friday
|
217
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 27, 12, 40) # Friday
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'schedules three items in two days if today is Friday' do
|
222
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
223
|
+
Timecop.freeze(t) do
|
224
|
+
results = Stagger.distribute([1, 2, 3], 2)
|
225
|
+
expect(results[0][1]).to eq(t) # Friday
|
226
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 1, 20) # Monday
|
227
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 30, 12, 40) # Monday
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'schedules three items in two days if today is Saturday' do
|
232
|
+
t = Time.local(2014, 6, 28, 14) # - 2pm, Saturday
|
233
|
+
Timecop.freeze(t) do
|
234
|
+
results = Stagger.distribute([1, 2, 3], 2)
|
235
|
+
expect(results[0][1]).to eq Time.local(2014, 6, 30) # Monday midnight
|
236
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 30, 16) # Monday 4pm
|
237
|
+
expect(results[2][1]).to eq Time.local(2014, 7, 1, 8) # Tuesday 8 am
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'schedules many items in 30 days' do
|
242
|
+
t = Time.local(2014, 6, 27, 14) # - 2pm, Friday
|
243
|
+
Timecop.freeze(t) do
|
244
|
+
results = Stagger.distribute((1..100).to_a, 30)
|
245
|
+
expect(results[0][1]).to eq t
|
246
|
+
expect(results[1][1]).to eq Time.local(2014, 6, 27, 21, 3, 36)
|
247
|
+
expect(results[2][1]).to eq Time.local(2014, 6, 30, 4, 7, 12)
|
248
|
+
expect(results[18][1]).to eq Time.local(2014, 7, 4, 21, 4, 48)
|
249
|
+
expect(results[19][1]).to eq Time.local(2014, 7, 7, 4, 8, 24)
|
250
|
+
expect(results[35][1]).to eq Time.local(2014, 7, 11, 21, 6)
|
251
|
+
expect(results[99][1]).to eq Time.local(2014, 8, 7, 16, 56, 24)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'active support integration' do
|
257
|
+
it 'returns ActiveSupport::TimeWithZone when ActiveSupport is available' do
|
258
|
+
fork do
|
259
|
+
require 'active_support/all'
|
260
|
+
Time.zone = 'Moscow'
|
261
|
+
pair = Stagger.distribute([1], 1).first
|
262
|
+
expect(pair.first).to eq(1)
|
263
|
+
expect(pair.last).to be_a ::ActiveSupport::TimeWithZone
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
data/stagger.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stagger/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "stagger"
|
8
|
+
spec.version = Stagger::VERSION
|
9
|
+
spec.authors = ["Valentin Vasilyev"]
|
10
|
+
spec.email = ["valentin.vasilyev@outlook.com"]
|
11
|
+
spec.summary = %q{Gem evenly distributes items across business days}
|
12
|
+
spec.homepage = "https://github.com/valve/stagger"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "timecop"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "pry-nav"
|
26
|
+
spec.add_development_dependency "activesupport"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stagger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Valentin Vasilyev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: timecop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-nav
|
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: activesupport
|
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'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- valentin.vasilyev@outlook.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- lib/stagger.rb
|
126
|
+
- lib/stagger/version.rb
|
127
|
+
- spec/spec_helper.rb
|
128
|
+
- spec/stagger_spec.rb
|
129
|
+
- stagger.gemspec
|
130
|
+
homepage: https://github.com/valve/stagger
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.3.0
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Gem evenly distributes items across business days
|
154
|
+
test_files:
|
155
|
+
- spec/spec_helper.rb
|
156
|
+
- spec/stagger_spec.rb
|
157
|
+
has_rdoc:
|