business_calendar 1.0.0 → 3.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 +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/.github/workflows/gem-push.yml +29 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +0 -2
- data/README.md +1 -3
- data/business_calendar.gemspec +1 -5
- data/data/GB.yml +12 -12
- data/data/US.yml +4 -3
- data/gemfiles/ruby_1.9.3.gemfile +6 -0
- data/lib/business_calendar.rb +10 -7
- data/lib/business_calendar/calendar.rb +18 -9
- data/lib/business_calendar/holiday_determiner.rb +20 -2
- data/lib/business_calendar/version.rb +1 -1
- data/spec/acceptance_gb_spec.rb +12 -12
- data/spec/acceptance_us_spec.rb +4 -3
- data/spec/business_calendar/holiday_determiner_spec.rb +1 -1
- data/spec/business_calendar_spec.rb +157 -13
- data/spec/spec_helper.rb +0 -2
- metadata +7 -49
- data/.simplecov +0 -8
- data/.travis.yml +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: beb100bc24dbbd90684a3c07a5201b9608534d9d2a3a26807e59d2b646eb4579
|
4
|
+
data.tar.gz: b456e4a40d119ebf60e822ecd2f07751c4971f5694e8dd0a2496fccc3226768b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b827ee58fe6bec07fc210d3928acea4f5aec538e059ad639cc07dc97dd969837b3c33f778c860a2a5445a89352bac2d706d3ecee6f1a2933016ddb460886ce5
|
7
|
+
data.tar.gz: abe89b5c64c4a84fa1aaa7b21533eed255983efa6da080ddc1911328cc45c83b8cf804dd71bf079a4c433195f3f0eb1665602ba6f9c46c9b3c5a1c0a8d759377
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
continue-on-error: ${{ matrix.experimental }}
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby-version: [2.3.1, 2.7.0]
|
16
|
+
experimental: [false]
|
17
|
+
include:
|
18
|
+
- ruby-version: head
|
19
|
+
experimental: true
|
20
|
+
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v2
|
23
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
24
|
+
uses: ruby/setup-ruby@v1
|
25
|
+
with:
|
26
|
+
ruby-version: ${{ matrix.ruby-version }}
|
27
|
+
- name: Install dependencies
|
28
|
+
run: bundle install
|
29
|
+
- name: Run tests
|
30
|
+
run: bundle exec rspec
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- '*'
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
build:
|
10
|
+
name: Build + Publish
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby 2.7
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: 2.7
|
19
|
+
|
20
|
+
- name: Publish to RubyGems
|
21
|
+
run: |
|
22
|
+
mkdir -p $HOME/.gem
|
23
|
+
touch $HOME/.gem/credentials
|
24
|
+
chmod 0600 $HOME/.gem/credentials
|
25
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
26
|
+
gem build *.gemspec
|
27
|
+
gem push *.gem
|
28
|
+
env:
|
29
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# business_calendar changes by version
|
2
2
|
|
3
|
+
2.0.0
|
4
|
+
---------
|
5
|
+
|
6
|
+
- Remove UK weekend holidays, replace with the actual observed holiday dates [#26]
|
7
|
+
|
8
|
+
1.1.0
|
9
|
+
---------
|
10
|
+
|
11
|
+
- Cache parsed responses from API endpoints for TTL (Time to Live) duration.
|
12
|
+
- Increase default TTL duration from 5 minutes to 1 day.
|
13
|
+
Holidays are not expected to frequently change.
|
14
|
+
- Allow disabling cache clearing by setting `ttl` to `false`.
|
15
|
+
- Set hard limit to size of memoized holidays cache,
|
16
|
+
since large quantities of user supplied dates could consume excessive memory / cause DoS.
|
17
|
+
|
3
18
|
1.0.0
|
4
19
|
---------
|
5
20
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# BusinessCalendar
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/business_calendar)
|
4
|
-
[](https://travis-ci.org/enova/business_calendar)
|
5
|
-
[](https://coveralls.io/github/enova/business_calendar?branch=master)
|
6
4
|
[](https://gemnasium.com/enova/business_calendar)
|
7
5
|
|
8
6
|
Need to know what days you can actually debit a customer on in excruciating detail? Fed up with singleton-based gems
|
@@ -32,7 +30,7 @@ Instantiate a calendar object with:
|
|
32
30
|
bc = BusinessCalendar.for(:US)
|
33
31
|
```
|
34
32
|
|
35
|
-
This will automatically load holidays based on the US banking holiday schedule, as configured in `data/
|
33
|
+
This will automatically load holidays based on the US banking holiday schedule, as configured in `data/US.yml`.
|
36
34
|
Currently, this gem supports `:GB` and `:US` regions.
|
37
35
|
|
38
36
|
Now, you can use it:
|
data/business_calendar.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["rnubel@enova.com"]
|
11
11
|
spec.description = %q{Helper gem for dealing with business days and date adjustment in multiple countries.}
|
12
12
|
spec.summary = %q{Country-aware business-date logic and handling.}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/enova/business_calendar"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -18,15 +18,11 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
22
21
|
spec.add_dependency "holidays", "~> 1.0"
|
23
22
|
spec.add_dependency "faraday"
|
24
|
-
spec.add_dependency "faraday-conductivity"
|
25
23
|
|
26
|
-
spec.add_development_dependency "bundler", "~> 1.3"
|
27
24
|
spec.add_development_dependency "rake"
|
28
25
|
spec.add_development_dependency "rspec", "~> 3.2"
|
29
26
|
spec.add_development_dependency "webmock"
|
30
|
-
spec.add_development_dependency "simplecov"
|
31
27
|
spec.add_development_dependency "pry"
|
32
28
|
end
|
data/data/GB.yml
CHANGED
@@ -26,12 +26,12 @@ GB:
|
|
26
26
|
- '2015-12-26'
|
27
27
|
- '2016-12-25'
|
28
28
|
- '2017-01-01'
|
29
|
-
- '2020-12-
|
30
|
-
- '2021-12-
|
31
|
-
- '2021-12-
|
32
|
-
- '2022-01-
|
33
|
-
- '2022-12-
|
34
|
-
- '2023-01-
|
29
|
+
- '2020-12-28'
|
30
|
+
- '2021-12-27'
|
31
|
+
- '2021-12-28'
|
32
|
+
- '2022-01-03'
|
33
|
+
- '2022-12-27'
|
34
|
+
- '2023-01-02'
|
35
35
|
removals:
|
36
36
|
- '2002-05-28'
|
37
37
|
- '2007-04-08'
|
@@ -44,9 +44,9 @@ GB:
|
|
44
44
|
- '2013-03-31'
|
45
45
|
- '2014-04-20'
|
46
46
|
- '2015-12-28'
|
47
|
-
- '2020-12-
|
48
|
-
- '2021-12-
|
49
|
-
- '2021-12-
|
50
|
-
- '2022-01-
|
51
|
-
- '2022-12-
|
52
|
-
- '2023-01-
|
47
|
+
- '2020-12-26'
|
48
|
+
- '2021-12-25'
|
49
|
+
- '2021-12-26'
|
50
|
+
- '2022-01-01'
|
51
|
+
- '2022-12-25'
|
52
|
+
- '2023-01-01'
|
data/data/US.yml
CHANGED
@@ -182,7 +182,6 @@ US:
|
|
182
182
|
- '2021-01-18'
|
183
183
|
- '2021-02-15'
|
184
184
|
- '2021-05-31'
|
185
|
-
- '2021-07-04'
|
186
185
|
- '2021-07-05'
|
187
186
|
- '2021-09-06'
|
188
187
|
- '2021-10-11'
|
@@ -193,18 +192,18 @@ US:
|
|
193
192
|
- '2022-01-17'
|
194
193
|
- '2022-02-21'
|
195
194
|
- '2022-05-30'
|
195
|
+
- '2022-06-20'
|
196
196
|
- '2022-07-04'
|
197
197
|
- '2022-09-05'
|
198
198
|
- '2022-10-10'
|
199
199
|
- '2022-11-11'
|
200
200
|
- '2022-11-24'
|
201
|
-
- '2022-12-25'
|
202
201
|
- '2022-12-26'
|
203
|
-
- '2023-01-01'
|
204
202
|
- '2023-01-02'
|
205
203
|
- '2023-01-16'
|
206
204
|
- '2023-02-20'
|
207
205
|
- '2023-05-29'
|
206
|
+
- '2023-06-19'
|
208
207
|
- '2023-07-04'
|
209
208
|
- '2023-09-04'
|
210
209
|
- '2023-10-09'
|
@@ -215,6 +214,7 @@ US:
|
|
215
214
|
- '2024-01-15'
|
216
215
|
- '2024-02-19'
|
217
216
|
- '2024-05-27'
|
217
|
+
- '2024-06-19'
|
218
218
|
- '2024-07-04'
|
219
219
|
- '2024-09-02'
|
220
220
|
- '2024-10-14'
|
@@ -225,6 +225,7 @@ US:
|
|
225
225
|
- '2025-01-20'
|
226
226
|
- '2025-02-17'
|
227
227
|
- '2025-05-26'
|
228
|
+
- '2025-06-19'
|
228
229
|
- '2025-07-04'
|
229
230
|
- '2025-09-01'
|
230
231
|
- '2025-10-13'
|
data/lib/business_calendar.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'faraday'
|
2
|
-
require 'faraday/conductivity'
|
3
2
|
|
4
3
|
module BusinessCalendar
|
5
4
|
CountryNotSupported = Class.new(StandardError)
|
@@ -18,8 +17,7 @@ module BusinessCalendar
|
|
18
17
|
end
|
19
18
|
|
20
19
|
def for_endpoint(additions, removals, options = {})
|
21
|
-
|
22
|
-
Calendar.new(holiday_determiner_for_endpoint(additions, removals, options), options.merge({"ttl" => ttl}))
|
20
|
+
Calendar.new(holiday_determiner_for_endpoint(additions, removals, options), options)
|
23
21
|
end
|
24
22
|
|
25
23
|
private
|
@@ -46,18 +44,22 @@ module BusinessCalendar
|
|
46
44
|
:additions_only => cfg['additions_only'] )
|
47
45
|
end
|
48
46
|
|
47
|
+
def holiday_dates_for_endpoint(client, endpoint)
|
48
|
+
Proc.new { JSON.parse(client.get(endpoint).body).fetch('holidays').map { |s| Date.parse s } }
|
49
|
+
end
|
50
|
+
|
49
51
|
def holiday_determiner_for_endpoint(additions_endpoint, removals_endpoint, opts)
|
50
52
|
client = Faraday.new do |conn|
|
51
|
-
conn.response :
|
53
|
+
conn.response :raise_error
|
52
54
|
conn.adapter :net_http
|
53
55
|
end
|
54
56
|
|
55
57
|
additions = if additions_endpoint
|
56
|
-
|
58
|
+
holiday_dates_for_endpoint(client, additions_endpoint)
|
57
59
|
end
|
58
60
|
|
59
61
|
removals = if removals_endpoint
|
60
|
-
|
62
|
+
holiday_dates_for_endpoint(client, removals_endpoint)
|
61
63
|
end
|
62
64
|
|
63
65
|
HolidayDeterminer.new(
|
@@ -65,7 +67,8 @@ module BusinessCalendar
|
|
65
67
|
opts["holiday_names"] || [],
|
66
68
|
:additions => additions,
|
67
69
|
:removals => removals,
|
68
|
-
:additions_only => opts["additions_only"] || []
|
70
|
+
:additions_only => opts["additions_only"] || [],
|
71
|
+
:ttl => opts['ttl']
|
69
72
|
)
|
70
73
|
end
|
71
74
|
|
@@ -1,9 +1,12 @@
|
|
1
1
|
class BusinessCalendar::Calendar
|
2
|
+
DEFAULT_TIME_TO_LIVE = 24 * 60 * 60
|
2
3
|
attr_reader :holiday_determiner
|
3
4
|
|
4
5
|
# @param [Proc[Date -> Boolean]] a proc which returns whether or not a date is a
|
5
6
|
# holiday.
|
6
7
|
def initialize(holiday_determiner, options = {})
|
8
|
+
ttl = options['ttl']
|
9
|
+
@time_to_live = ttl.nil? ? DEFAULT_TIME_TO_LIVE : ttl
|
7
10
|
@options = options
|
8
11
|
@holiday_cache = {}
|
9
12
|
@holiday_determiner = holiday_determiner
|
@@ -14,7 +17,7 @@ class BusinessCalendar::Calendar
|
|
14
17
|
def is_holiday?(date)
|
15
18
|
date = date.send(:to_date) if date.respond_to?(:to_date, true)
|
16
19
|
|
17
|
-
clear_cache if
|
20
|
+
clear_cache if should_clear_cache?
|
18
21
|
|
19
22
|
@holiday_cache[date] ||= holiday_determiner.call(date)
|
20
23
|
end
|
@@ -22,7 +25,7 @@ class BusinessCalendar::Calendar
|
|
22
25
|
# @param [Date] date
|
23
26
|
# @return [Boolean] Whether or not banking can be done on <date>.
|
24
27
|
def is_business_day?(date)
|
25
|
-
return false if !@options[
|
28
|
+
return false if !@options['business_weekends'] && (date.saturday? || date.sunday?)
|
26
29
|
return false if is_holiday?(date)
|
27
30
|
true
|
28
31
|
end
|
@@ -108,6 +111,19 @@ class BusinessCalendar::Calendar
|
|
108
111
|
end
|
109
112
|
|
110
113
|
private
|
114
|
+
|
115
|
+
def should_clear_cache?
|
116
|
+
return false unless @time_to_live
|
117
|
+
|
118
|
+
# limit size using a heuristic, to prevent cache growing arbitrarily large
|
119
|
+
!@last_cleared || (Time.now - @last_cleared) >= @time_to_live || @holiday_cache.size > 365 * 3
|
120
|
+
end
|
121
|
+
|
122
|
+
def clear_cache
|
123
|
+
@last_cleared = Time.now
|
124
|
+
@holiday_cache = {}
|
125
|
+
end
|
126
|
+
|
111
127
|
def with_one_or_many(thing_or_things)
|
112
128
|
if thing_or_things.is_a? Enumerable
|
113
129
|
thing_or_things.collect do |thing|
|
@@ -117,11 +133,4 @@ class BusinessCalendar::Calendar
|
|
117
133
|
yield thing_or_things
|
118
134
|
end
|
119
135
|
end
|
120
|
-
|
121
|
-
def clear_cache
|
122
|
-
if !@issued_at || (Time.now - @issued_at) >= @options["ttl"]
|
123
|
-
@issued_at = Time.now
|
124
|
-
@holiday_cache = {}
|
125
|
-
end
|
126
|
-
end
|
127
136
|
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'holidays'
|
2
2
|
|
3
3
|
class BusinessCalendar::HolidayDeterminer
|
4
|
+
DEFAULT_TIME_TO_LIVE = 24 * 60 * 60
|
4
5
|
attr_reader :regions, :holiday_names, :additions, :removals, :additions_only
|
5
6
|
|
6
7
|
def initialize(regions, holiday_names, opts = {})
|
8
|
+
ttl = opts[:ttl]
|
9
|
+
@time_to_live = ttl.nil? ? DEFAULT_TIME_TO_LIVE : ttl
|
7
10
|
@regions = regions
|
8
11
|
@holiday_names = holiday_names
|
9
12
|
@additions = opts[:additions] || []
|
@@ -12,6 +15,8 @@ class BusinessCalendar::HolidayDeterminer
|
|
12
15
|
end
|
13
16
|
|
14
17
|
def call(date)
|
18
|
+
clear_cache if should_clear_cache?
|
19
|
+
|
15
20
|
if additions.include? date
|
16
21
|
true
|
17
22
|
elsif removals.include? date
|
@@ -23,11 +28,24 @@ class BusinessCalendar::HolidayDeterminer
|
|
23
28
|
end
|
24
29
|
|
25
30
|
private
|
31
|
+
|
32
|
+
def should_clear_cache?
|
33
|
+
return false unless @time_to_live
|
34
|
+
|
35
|
+
!@last_cleared || (Time.now - @last_cleared) >= @time_to_live
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear_cache
|
39
|
+
@last_cleared = Time.now
|
40
|
+
@additions_cache = nil
|
41
|
+
@removals_cache = nil
|
42
|
+
end
|
43
|
+
|
26
44
|
def additions
|
27
|
-
@additions.is_a?(Proc) ? @additions.call : @additions
|
45
|
+
@additions_cache ||= @additions.is_a?(Proc) ? @additions.call : @additions
|
28
46
|
end
|
29
47
|
|
30
48
|
def removals
|
31
|
-
@removals.is_a?(Proc) ? @removals.call : @removals
|
49
|
+
@removals_cache ||= @removals.is_a?(Proc) ? @removals.call : @removals
|
32
50
|
end
|
33
51
|
end
|
data/spec/acceptance_gb_spec.rb
CHANGED
@@ -20,12 +20,12 @@ describe "GB bank holidays" do
|
|
20
20
|
2015-12-26
|
21
21
|
2016-12-25
|
22
22
|
2017-01-01
|
23
|
-
2020-12-
|
24
|
-
2021-12-
|
25
|
-
2021-12-
|
26
|
-
2022-01-
|
27
|
-
2022-12-
|
28
|
-
2023-01-
|
23
|
+
2020-12-28
|
24
|
+
2021-12-27
|
25
|
+
2021-12-28
|
26
|
+
2022-01-03
|
27
|
+
2022-12-27
|
28
|
+
2023-01-02
|
29
29
|
).map { |x| Date.parse x }.each do |expected_holiday|
|
30
30
|
it "treats #{expected_holiday} as a holiday" do
|
31
31
|
expect(BusinessCalendar.for(:GB).is_holiday?(expected_holiday)).to be true
|
@@ -35,12 +35,12 @@ describe "GB bank holidays" do
|
|
35
35
|
%w(
|
36
36
|
2012-05-28
|
37
37
|
2015-12-28
|
38
|
-
2020-12-
|
39
|
-
2021-12-
|
40
|
-
2021-12-
|
41
|
-
2022-01-
|
42
|
-
2022-12-
|
43
|
-
2023-01-
|
38
|
+
2020-12-26
|
39
|
+
2021-12-25
|
40
|
+
2021-12-26
|
41
|
+
2022-01-01
|
42
|
+
2022-12-25
|
43
|
+
2023-01-01
|
44
44
|
).map { |x| Date.parse x }.each do |date|
|
45
45
|
it "treats #{date} as not a holiday" do
|
46
46
|
expect(BusinessCalendar.for(:GB).is_holiday?(date)).to be false
|
data/spec/acceptance_us_spec.rb
CHANGED
@@ -175,7 +175,6 @@ describe "US holidays" do
|
|
175
175
|
2021-01-18
|
176
176
|
2021-02-15
|
177
177
|
2021-05-31
|
178
|
-
2021-07-04
|
179
178
|
2021-07-05
|
180
179
|
2021-09-06
|
181
180
|
2021-10-11
|
@@ -186,18 +185,18 @@ describe "US holidays" do
|
|
186
185
|
2022-01-17
|
187
186
|
2022-02-21
|
188
187
|
2022-05-30
|
188
|
+
2022-06-20
|
189
189
|
2022-07-04
|
190
190
|
2022-09-05
|
191
191
|
2022-10-10
|
192
192
|
2022-11-11
|
193
193
|
2022-11-24
|
194
|
-
2022-12-25
|
195
194
|
2022-12-26
|
196
|
-
2023-01-01
|
197
195
|
2023-01-02
|
198
196
|
2023-01-16
|
199
197
|
2023-02-20
|
200
198
|
2023-05-29
|
199
|
+
2023-06-19
|
201
200
|
2023-07-04
|
202
201
|
2023-09-04
|
203
202
|
2023-10-09
|
@@ -208,6 +207,7 @@ describe "US holidays" do
|
|
208
207
|
2024-01-15
|
209
208
|
2024-02-19
|
210
209
|
2024-05-27
|
210
|
+
2024-06-19
|
211
211
|
2024-07-04
|
212
212
|
2024-09-02
|
213
213
|
2024-10-14
|
@@ -218,6 +218,7 @@ describe "US holidays" do
|
|
218
218
|
2025-01-20
|
219
219
|
2025-02-17
|
220
220
|
2025-05-26
|
221
|
+
2025-06-19
|
221
222
|
2025-07-04
|
222
223
|
2025-09-01
|
223
224
|
2025-10-13
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe BusinessCalendar::HolidayDeterminer do
|
4
4
|
let(:regions) { [ :us ] }
|
5
|
-
let(:opts) {{}}
|
5
|
+
let(:opts) { {} }
|
6
6
|
subject { BusinessCalendar::HolidayDeterminer.new(regions, ["Independence Day"], opts) }
|
7
7
|
|
8
8
|
it "initializes with list of regions and a list of accepted holidays" do
|
@@ -191,38 +191,182 @@ describe BusinessCalendar do
|
|
191
191
|
it_behaves_like "weekends as business days"
|
192
192
|
end
|
193
193
|
|
194
|
-
it 'hits the configured endpoint
|
194
|
+
it 'hits the configured endpoint and then reuses the cached result' do
|
195
|
+
subject.is_business_day?('2014-07-03'.to_date)
|
195
196
|
subject.is_business_day?('2014-07-03'.to_date)
|
196
197
|
subject.is_business_day?('2014-07-04'.to_date)
|
197
198
|
subject.is_holiday?('2014-07-06'.to_date)
|
199
|
+
subject.is_holiday?('2014-07-06'.to_date)
|
198
200
|
subject.is_holiday?('2014-12-24'.to_date)
|
199
201
|
|
200
|
-
expect(a_request(:get, additions)).to have_been_made.times(
|
201
|
-
expect(a_request(:get, removals)).to have_been_made.times(
|
202
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
203
|
+
expect(a_request(:get, removals)).to have_been_made.times(1)
|
202
204
|
end
|
203
205
|
|
204
|
-
|
205
|
-
|
206
|
+
context 'after 24 hours without specifying a Time to Live override' do
|
207
|
+
subject { BusinessCalendar.for_endpoint(additions, removals) }
|
208
|
+
let!(:start) { Time.now }
|
209
|
+
let!(:one_day) { 86400 }
|
206
210
|
|
207
|
-
|
211
|
+
it 'expires the holidays cache' do
|
212
|
+
allow(Time).to receive(:now) { start }
|
208
213
|
|
209
|
-
|
210
|
-
subject.is_business_day?('2014-07-04'.to_date)
|
214
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
211
215
|
|
212
|
-
|
216
|
+
# initial request was made
|
217
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
213
218
|
|
214
|
-
|
219
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
220
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
215
221
|
|
216
|
-
|
222
|
+
# cache from initial request was still used
|
223
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
224
|
+
|
225
|
+
# 24 hours + 1 second have passed
|
226
|
+
# cache should be cleared and fresh API request made
|
227
|
+
allow(Time).to receive(:now) { start + one_day + 1 }
|
228
|
+
|
229
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
230
|
+
|
231
|
+
# 2nd request was made
|
232
|
+
expect(a_request(:get, additions)).to have_been_made.times(2)
|
233
|
+
|
234
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
235
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
236
|
+
|
237
|
+
# 2nd request is now cached, so no new request should have been issued
|
238
|
+
expect(a_request(:get, additions)).to have_been_made.times(2)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context 'turning off Time to Live functionality' do
|
243
|
+
subject { BusinessCalendar.for_endpoint(additions, removals, {'ttl' => false}) }
|
244
|
+
let!(:start) { Time.now }
|
245
|
+
let!(:one_day) { 86400 }
|
246
|
+
|
247
|
+
it 'will never clear the cache' do
|
248
|
+
allow(Time).to receive(:now) { start }
|
249
|
+
|
250
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
251
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
252
|
+
|
253
|
+
allow(Time).to receive(:now) { start + 301 }
|
254
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
255
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
256
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
257
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
258
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
259
|
+
|
260
|
+
allow(Time).to receive(:now) { start + one_day + 1 }
|
261
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
262
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
263
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
264
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'setting Time to Live override to zero' do
|
269
|
+
subject { BusinessCalendar.for_endpoint(additions, removals, {'ttl' => 0}) }
|
270
|
+
let!(:start) { Time.now }
|
271
|
+
|
272
|
+
it 'will always clear the cache' do
|
273
|
+
allow(Time).to receive(:now) { start }
|
274
|
+
|
275
|
+
subject.is_holiday?('2014-01-01'.to_date)
|
276
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
277
|
+
|
278
|
+
allow(Time).to receive(:now) { start + 301 }
|
279
|
+
|
280
|
+
subject.is_holiday?('2014-01-01'.to_date)
|
281
|
+
subject.is_holiday?('2014-07-04'.to_date)
|
282
|
+
subject.is_holiday?('2014-07-04'.to_date)
|
283
|
+
subject.is_holiday?('2014-11-28'.to_date)
|
284
|
+
|
285
|
+
expect(a_request(:get, additions)).to have_been_made.times(5)
|
286
|
+
|
287
|
+
subject.is_holiday?('2014-01-01'.to_date)
|
288
|
+
subject.is_holiday?('2014-01-01'.to_date)
|
289
|
+
subject.is_holiday?('2014-07-04'.to_date)
|
290
|
+
subject.is_holiday?('2014-11-28'.to_date)
|
291
|
+
expect(a_request(:get, additions)).to have_been_made.times(9)
|
292
|
+
end
|
293
|
+
end
|
217
294
|
|
218
|
-
|
295
|
+
context 'using a 5 minute Time to Live override' do
|
296
|
+
subject { BusinessCalendar.for_endpoint(additions, removals, {'ttl' => 300}) }
|
297
|
+
let!(:start) { Time.now }
|
298
|
+
|
299
|
+
it 'expires the holidays cache after the specified time has elapsed' do
|
300
|
+
allow(Time).to receive(:now) { start }
|
301
|
+
|
302
|
+
# initial request made and cached
|
303
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
304
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
305
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
306
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
307
|
+
|
308
|
+
# 120 seconds pass, cache should still be used
|
309
|
+
allow(Time).to receive(:now) { start + 120 }
|
310
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
311
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
312
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
313
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
314
|
+
|
315
|
+
# 301 seconds pass, cache is expired and 2nd request made
|
316
|
+
allow(Time).to receive(:now) { start + 301 }
|
317
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
318
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
319
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
320
|
+
expect(a_request(:get, additions)).to have_been_made.times(2)
|
321
|
+
|
322
|
+
# 2nd request is now cached, so no new request should have been issued
|
323
|
+
subject.is_business_day?('2014-01-01'.to_date)
|
324
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
325
|
+
subject.is_business_day?('2014-11-28'.to_date)
|
326
|
+
expect(a_request(:get, additions)).to have_been_made.times(2)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context 'with a few years in dates filling the cache' do
|
331
|
+
let!(:start) { Time.now }
|
332
|
+
|
333
|
+
# NOTE: this test cheats a bit to test class internals / implementation
|
334
|
+
it 'will clear out the holiday cache but keep the cached API result' do
|
335
|
+
allow(Time).to receive(:now) { start }
|
336
|
+
subject.is_business_day?('2014-07-04'.to_date)
|
337
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
338
|
+
|
339
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
340
|
+
(2014..2017).each do |year|
|
341
|
+
(1..12).each do |month|
|
342
|
+
(1..28).each do |day|
|
343
|
+
subject.is_business_day?("#{year}-#{month}-#{day}".to_date)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
# about to reach cache size threshold
|
349
|
+
expect(subject.instance_variable_get('@holiday_cache').size).to be(960)
|
350
|
+
|
351
|
+
(1..7).each do |month|
|
352
|
+
(1..28).each do |day|
|
353
|
+
subject.is_business_day?("#2018-#{month}-#{day}".to_date)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# now holiday cache will have been cleared and should be small again
|
358
|
+
expect(subject.instance_variable_get('@holiday_cache').size).to be(4)
|
359
|
+
|
360
|
+
# and cached API response was still used
|
361
|
+
expect(a_request(:get, additions)).to have_been_made.times(1)
|
362
|
+
end
|
219
363
|
end
|
220
364
|
|
221
365
|
context 'http request fails' do
|
222
366
|
before { stub_request(:get, additions).to_return(:status => 500) }
|
223
367
|
|
224
368
|
it 'raises an error' do
|
225
|
-
expect { subject.is_business_day?('2014-07-04'.to_date) }.to raise_error Faraday::
|
369
|
+
expect { subject.is_business_day?('2014-07-04'.to_date) }.to raise_error Faraday::Error
|
226
370
|
end
|
227
371
|
end
|
228
372
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: business_calendar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Nubel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: holidays
|
@@ -38,34 +38,6 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: faraday-conductivity
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :runtime
|
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: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '1.3'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '1.3'
|
69
41
|
- !ruby/object:Gem::Dependency
|
70
42
|
name: rake
|
71
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,20 +80,6 @@ dependencies:
|
|
108
80
|
- - ">="
|
109
81
|
- !ruby/object:Gem::Version
|
110
82
|
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: simplecov
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
83
|
- !ruby/object:Gem::Dependency
|
126
84
|
name: pry
|
127
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,10 +102,10 @@ executables: []
|
|
144
102
|
extensions: []
|
145
103
|
extra_rdoc_files: []
|
146
104
|
files:
|
105
|
+
- ".github/workflows/ci.yml"
|
106
|
+
- ".github/workflows/gem-push.yml"
|
147
107
|
- ".gitignore"
|
148
108
|
- ".rspec"
|
149
|
-
- ".simplecov"
|
150
|
-
- ".travis.yml"
|
151
109
|
- CHANGELOG.md
|
152
110
|
- Gemfile
|
153
111
|
- LICENSE.txt
|
@@ -160,6 +118,7 @@ files:
|
|
160
118
|
- data/US.yml
|
161
119
|
- data/org/captalys.yml
|
162
120
|
- gemfiles/ree.gemfile
|
121
|
+
- gemfiles/ruby_1.9.3.gemfile
|
163
122
|
- lib/business_calendar.rb
|
164
123
|
- lib/business_calendar/calendar.rb
|
165
124
|
- lib/business_calendar/holiday_determiner.rb
|
@@ -173,7 +132,7 @@ files:
|
|
173
132
|
- spec/business_calendar/holiday_determiner_spec.rb
|
174
133
|
- spec/business_calendar_spec.rb
|
175
134
|
- spec/spec_helper.rb
|
176
|
-
homepage:
|
135
|
+
homepage: https://github.com/enova/business_calendar
|
177
136
|
licenses:
|
178
137
|
- MIT
|
179
138
|
metadata: {}
|
@@ -192,8 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
151
|
- !ruby/object:Gem::Version
|
193
152
|
version: '0'
|
194
153
|
requirements: []
|
195
|
-
|
196
|
-
rubygems_version: 2.7.8
|
154
|
+
rubygems_version: 3.1.6
|
197
155
|
signing_key:
|
198
156
|
specification_version: 4
|
199
157
|
summary: Country-aware business-date logic and handling.
|
data/.simplecov
DELETED
data/.travis.yml
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
sudo: false
|
2
|
-
language: ruby
|
3
|
-
cache: bundler
|
4
|
-
|
5
|
-
matrix:
|
6
|
-
include:
|
7
|
-
- rmv: ree
|
8
|
-
gemfile: gemfiles/ree.gemfile
|
9
|
-
script: bundle exec rspec
|
10
|
-
- rvm: 1.9.3
|
11
|
-
script: bundle exec rspec
|
12
|
-
- rvm: 2.3.1
|
13
|
-
script: bundle exec rspec
|
14
|
-
|
15
|
-
env:
|
16
|
-
global:
|
17
|
-
secure: klQ3BQNdKkGIq3Zfv8Sr6oZqaMg9GHx+LGhynQ5xt7A6SGvxbMuoXJlE9wW4me6u+B9S2D+q+pdKgGPE/+fHsVt/d0zDXRnrjVyo1eexT220AMfpjiFDAtya8sAcuuAICLhw8AmzTjns8yAWInv5U5vC6oejkLA71FXtHU//210=
|
18
|
-
|
19
|
-
deploy:
|
20
|
-
provider: rubygems
|
21
|
-
api_key:
|
22
|
-
secure: "c9QcNX+nk0Yzl22ZVknZtv+/G4fUeLph0TH9PS6hQ92zywM61wtXTJExZdBHulvKAaQbKaq20LcFjLw41qwJc6tvIEYXpOinfOaiemTKaGmKscDrIi3wUAxfjmTacXZAaTdEORQVqTOmppqsuKnP7cqmSNxTSI901Sz5Cy6Jpl0="
|
23
|
-
on:
|
24
|
-
tags: true
|
25
|
-
repo: enova/business_calendar
|