concrete_holidays 1.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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +25 -0
  9. data/LICENSE +22 -0
  10. data/README.md +58 -0
  11. data/Rakefile +11 -0
  12. data/concrete_holidays.gemspec +17 -0
  13. data/lib/concrete_holidays.rb +35 -0
  14. data/lib/concrete_holidays/black_friday.rb +7 -0
  15. data/lib/concrete_holidays/calculations.rb +42 -0
  16. data/lib/concrete_holidays/christmas.rb +5 -0
  17. data/lib/concrete_holidays/christmas_eve.rb +5 -0
  18. data/lib/concrete_holidays/christmas_observed.rb +7 -0
  19. data/lib/concrete_holidays/holidays.rb +12 -0
  20. data/lib/concrete_holidays/independence_day.rb +5 -0
  21. data/lib/concrete_holidays/independence_day_observed.rb +7 -0
  22. data/lib/concrete_holidays/labor_day.rb +7 -0
  23. data/lib/concrete_holidays/memorial_day.rb +7 -0
  24. data/lib/concrete_holidays/new_years_day.rb +5 -0
  25. data/lib/concrete_holidays/new_years_day_observed.rb +7 -0
  26. data/lib/concrete_holidays/new_years_eve.rb +5 -0
  27. data/lib/concrete_holidays/thanksgiving.rb +11 -0
  28. data/spec/concrete_holidays/black_friday_spec.rb +22 -0
  29. data/spec/concrete_holidays/christmas_eve_spec.rb +8 -0
  30. data/spec/concrete_holidays/christmas_observed_spec.rb +17 -0
  31. data/spec/concrete_holidays/christmas_spec.rb +8 -0
  32. data/spec/concrete_holidays/independence_day_observed_spec.rb +17 -0
  33. data/spec/concrete_holidays/independence_day_spec.rb +8 -0
  34. data/spec/concrete_holidays/labor_day_spec.rb +43 -0
  35. data/spec/concrete_holidays/memorial_day_spec.rb +43 -0
  36. data/spec/concrete_holidays/new_years_day_observed_spec.rb +17 -0
  37. data/spec/concrete_holidays/new_years_day_spec.rb +8 -0
  38. data/spec/concrete_holidays/new_years_eve_spec.rb +8 -0
  39. data/spec/concrete_holidays/thanksgiving_spec.rb +22 -0
  40. data/spec/concrete_holidays_spec.rb +48 -0
  41. data/spec/spec_helper.rb +93 -0
  42. metadata +98 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b9cc21063b5c325b7b581b8fabda431133026f6e
4
+ data.tar.gz: 5bb6cba7ba38d0e263c2f0187f5acbb802acd515
5
+ SHA512:
6
+ metadata.gz: 2d492b8f2ca6b2c673302831a883b2ab977fd4676fde048d99332fcaf575891dccf562f27923f33b65b968768f767221baeddfe0b6b9881e57d7436d73c7316f
7
+ data.tar.gz: ca8fd69c0cc08ba7f97f364746a37a56b6f517502bf1a517a39e4ea2339bdda1ef4bc1fabc66f3d3f2e31e58c494772bbcbd85c4aaf4dcab290d666ae904def6
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ concrete_holidays
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.2
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.1.6
5
+ - 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rspec'
4
+ gem 'rake'
data/Gemfile.lock ADDED
@@ -0,0 +1,25 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.2.5)
5
+ rake (10.1.0)
6
+ rspec (3.2.0)
7
+ rspec-core (~> 3.2.0)
8
+ rspec-expectations (~> 3.2.0)
9
+ rspec-mocks (~> 3.2.0)
10
+ rspec-core (3.2.3)
11
+ rspec-support (~> 3.2.0)
12
+ rspec-expectations (3.2.1)
13
+ diff-lcs (>= 1.2.0, < 2.0)
14
+ rspec-support (~> 3.2.0)
15
+ rspec-mocks (3.2.1)
16
+ diff-lcs (>= 1.2.0, < 2.0)
17
+ rspec-support (~> 3.2.0)
18
+ rspec-support (3.2.2)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ rake
25
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Matthew Lineen
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,58 @@
1
+ # ConcreteHolidays
2
+ [![Gem Version](https://badge.fury.io/rb/concrete_holidays.svg)](https://rubygems.org/gems/concrete_holidays)
3
+ [![Build Status](https://travis-ci.org/tablexi/concrete_holidays.svg?branch=master)](https://travis-ci.org/tablexi/concrete_holidays)
4
+
5
+ The ConcreteHolidays gem was built to calculate if a given date is a holiday using plain old ruby objects.
6
+
7
+ ## Example Usage
8
+
9
+ ```ruby
10
+ class MyHolidays < ConcreteHolidays
11
+ def default_holidays
12
+ add_holiday ConcreteHolidays::NewYearsDayObserved
13
+ add_holiday ConcreteHolidays::MemorialDay
14
+ add_holiday ConcreteHolidays::IndependenceDayObserved
15
+ add_holiday ConcreteHolidays::LaborDay
16
+ add_holiday ConcreteHolidays::Thanksgiving
17
+ add_holiday ConcreteHolidays::BlackFriday
18
+ add_holiday ConcreteHolidays::ChristmasEve
19
+ add_holiday ConcreteHolidays::ChristmasObserved
20
+ add_holiday ConcreteHolidays::NewYearsEve
21
+ end
22
+ end
23
+
24
+ holidays = MyHolidays.new
25
+ holidays.holiday?(date)
26
+ ```
27
+
28
+ ## Create a custom holiday (class)
29
+
30
+ The class must implement `self.date(year)` and return a date or array of dates.
31
+
32
+ ```ruby
33
+ class MyBirthday
34
+ def self.date(year)
35
+ Date.new(year,3,14)
36
+ end
37
+ end
38
+
39
+ holidays = ConcreteHolidays.new
40
+ holidays.add_holiday MyBirthday
41
+ holidays.holiday?(date)
42
+ ```
43
+
44
+ ## Create a custom holiday (instance)
45
+
46
+ The instance must respond to `date(year)` and return a date or array of dates.
47
+
48
+ ```ruby
49
+ class MyBirthday
50
+ def date(year)
51
+ Date.new(year,3,14)
52
+ end
53
+ end
54
+
55
+ holidays = ConcreteHolidays.new
56
+ holidays.add_holiday MyBirthday.new
57
+ holidays.holiday?(date)
58
+ ```
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+ rescue LoadError
10
+ # no rspec available
11
+ end
@@ -0,0 +1,17 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "concrete_holidays"
6
+ gem.version = '1.0.0'
7
+ gem.authors = ["Matthew Lineen"]
8
+ gem.email = ["matthew@lineen.com"]
9
+ gem.description = "Holiday calculations using concrete classes"
10
+ gem.summary = "Holiday calculations using concrete classes"
11
+ gem.homepage = "https://github.com/tablexi/concrete_holidays"
12
+ gem.license = "MIT"
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.require_paths = ["lib"]
17
+ end
@@ -0,0 +1,35 @@
1
+ require 'concrete_holidays/calculations'
2
+ require 'concrete_holidays/holidays'
3
+
4
+ class ConcreteHolidays
5
+ attr_reader :holidays
6
+
7
+ def initialize
8
+ @holidays = []
9
+ initialize_cache
10
+ default_holidays
11
+ end
12
+
13
+ def default_holidays
14
+ # override to set initial holidays
15
+ end
16
+
17
+ def add_holiday(holiday)
18
+ initialize_cache
19
+ @holidays << holiday
20
+ end
21
+
22
+ def holiday?(date)
23
+ holidays_in_year(date.year).include?(date)
24
+ end
25
+
26
+ private
27
+
28
+ def initialize_cache
29
+ @cache_by_year = {}
30
+ end
31
+
32
+ def holidays_in_year(year)
33
+ @cache_by_year[year] ||= holidays.flat_map { |holiday| holiday.date(year) }
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ require 'concrete_holidays/thanksgiving'
2
+
3
+ class ConcreteHolidays::BlackFriday
4
+ def self.date(year) # the day after Thanksgiving
5
+ ConcreteHolidays::Thanksgiving.date(year) + 1
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ class ConcreteHolidays
2
+ module Calculations
3
+ WDAY_SYMBOLS = {
4
+ sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6,
5
+ sunday: 0, monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6
6
+ }
7
+
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def to_weekday_if_weekend(date)
14
+ date += 1 if date.wday == wday_from_symbol(:sun)
15
+ date -= 1 if date.wday == wday_from_symbol(:sat)
16
+ date
17
+ end
18
+
19
+ def this_or_prev(day_symbol, from_date)
20
+ from_date -= 1 until from_date.wday == wday_from_symbol(day_symbol)
21
+ from_date
22
+ end
23
+
24
+ def this_or_next(day_symbol, from_date)
25
+ from_date += 1 until from_date.wday == wday_from_symbol(day_symbol)
26
+ from_date
27
+ end
28
+
29
+ def the_next(day_symbol, from_date)
30
+ loop do
31
+ from_date += 1
32
+ break if from_date.wday == wday_from_symbol(day_symbol)
33
+ end
34
+ from_date
35
+ end
36
+
37
+ def wday_from_symbol(symbol)
38
+ WDAY_SYMBOLS.fetch(symbol)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ class ConcreteHolidays::Christmas
2
+ def self.date(year) # December 25th
3
+ Date.new(year,12,25)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class ConcreteHolidays::ChristmasEve
2
+ def self.date(year) # December 24th
3
+ Date.new(year,12,24)
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class ConcreteHolidays::ChristmasObserved
2
+ include ConcreteHolidays::Calculations
3
+
4
+ def self.date(year) # December 25th
5
+ to_weekday_if_weekend(Date.new(year,12,25))
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ require 'concrete_holidays/new_years_day'
2
+ require 'concrete_holidays/new_years_day_observed'
3
+ require 'concrete_holidays/memorial_day'
4
+ require 'concrete_holidays/independence_day'
5
+ require 'concrete_holidays/independence_day_observed'
6
+ require 'concrete_holidays/labor_day'
7
+ require 'concrete_holidays/thanksgiving'
8
+ require 'concrete_holidays/black_friday'
9
+ require 'concrete_holidays/christmas_eve'
10
+ require 'concrete_holidays/christmas'
11
+ require 'concrete_holidays/christmas_observed'
12
+ require 'concrete_holidays/new_years_eve'
@@ -0,0 +1,5 @@
1
+ class ConcreteHolidays::IndependenceDay
2
+ def self.date(year) # July 4th
3
+ Date.new(year,7,4)
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class ConcreteHolidays::IndependenceDayObserved
2
+ include ConcreteHolidays::Calculations
3
+
4
+ def self.date(year) # July 4th
5
+ to_weekday_if_weekend(Date.new(year,7,4))
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class ConcreteHolidays::LaborDay
2
+ include ConcreteHolidays::Calculations
3
+
4
+ def self.date(year) # the first Monday of September
5
+ this_or_next(:mon, Date.new(year,9,1))
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class ConcreteHolidays::MemorialDay
2
+ include ConcreteHolidays::Calculations
3
+
4
+ def self.date(year) # the last Monday of May
5
+ this_or_prev(:mon, Date.new(year,5,31))
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class ConcreteHolidays::NewYearsDay
2
+ def self.date(year) # January 1st
3
+ Date.new(year,1,1)
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class ConcreteHolidays::NewYearsDayObserved
2
+ include ConcreteHolidays::Calculations
3
+
4
+ def self.date(year) # January 1st
5
+ to_weekday_if_weekend(Date.new(year,1,1))
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class ConcreteHolidays::NewYearsEve
2
+ def self.date(year) # December 31st
3
+ Date.new(year,12,31)
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ class ConcreteHolidays::Thanksgiving
2
+ include ConcreteHolidays::Calculations
3
+
4
+ def self.date(year) # the fourth Thursday in November
5
+ date = this_or_next(:thu, Date.new(year,11,1))
6
+ 3.times do
7
+ date = the_next(:thu, date)
8
+ end
9
+ date
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ RSpec.describe ConcreteHolidays::BlackFriday do
2
+ let(:holiday) { ConcreteHolidays::BlackFriday }
3
+
4
+ # http://en.wikipedia.org/wiki/Thanksgiving_%28United_States%29#Thanksgiving_dates_1942.E2.80.932099
5
+ test_cases = [
6
+ { day: 22, years: [1945, 1951, 1956, 1962, 1973, 1979, 1984, 1990, 2001, 2007, 2012, 2018, 2029, 2035, 2040, 2046, 2057, 2063, 2068, 2074, 2085, 2091, 2096] },
7
+ { day: 23, years: [1944, 1950, 1961, 1967, 1972, 1978, 1989, 1995, 2000, 2006, 2017, 2023, 2028, 2034, 2045, 2051, 2056, 2062, 2073, 2079, 2084, 2090] },
8
+ { day: 24, years: [1949, 1955, 1960, 1966, 1977, 1983, 1988, 1994, 2005, 2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095] },
9
+ { day: 25, years: [1943, 1948, 1954, 1965, 1971, 1976, 1982, 1993, 1999, 2004, 2010, 2021, 2027, 2032, 2038, 2049, 2055, 2060, 2066, 2077, 2083, 2088, 2094] },
10
+ { day: 26, years: [1942, 1953, 1959, 1964, 1970, 1981, 1987, 1992, 1998, 2009, 2015, 2020, 2026, 2037, 2043, 2048, 2054, 2065, 2071, 2076, 2082, 2093, 2099] },
11
+ { day: 27, years: [1947, 1952, 1958, 1969, 1975, 1980, 1986, 1997, 2003, 2008, 2014, 2025, 2031, 2036, 2042, 2053, 2059, 2064, 2070, 2081, 2087, 2092, 2098] },
12
+ { day: 28, years: [1946, 1957, 1963, 1968, 1974, 1985, 1991, 1996, 2002, 2013, 2019, 2024, 2030, 2041, 2047, 2052, 2058, 2069, 2075, 2080, 2086, 2097] },
13
+ ]
14
+
15
+ test_cases.each do |test|
16
+ test[:years].each do |year|
17
+ it "calculates black friday for #{year}" do
18
+ expect(holiday.date(year)).to eq(Date.new(year,11,test[:day]) + 1)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.describe ConcreteHolidays::ChristmasEve do
2
+ let(:holiday) { ConcreteHolidays::ChristmasEve }
3
+ let(:year) { 2015 }
4
+
5
+ it "calculates christmas eve" do
6
+ expect(holiday.date(year)).to eq(Date.new(year,12,24))
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.describe ConcreteHolidays::ChristmasObserved do
2
+ let(:holiday) { ConcreteHolidays::ChristmasObserved }
3
+
4
+ test_cases = [
5
+ { day: 24, years: [1993, 1999, 2004] },
6
+ { day: 25, years: [1990, 1991, 1992, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003] },
7
+ { day: 26, years: [1994, 2005] },
8
+ ]
9
+
10
+ test_cases.each do |test|
11
+ test[:years].each do |year|
12
+ it "calculates observed christmas day for #{year}" do
13
+ expect(holiday.date(year)).to eq(Date.new(year,12,test[:day]))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.describe ConcreteHolidays::Christmas do
2
+ let(:holiday) { ConcreteHolidays::Christmas }
3
+ let(:year) { 2015 }
4
+
5
+ it "calculates christmas day" do
6
+ expect(holiday.date(year)).to eq(Date.new(year,12,25))
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.describe ConcreteHolidays::IndependenceDayObserved do
2
+ let(:holiday) { ConcreteHolidays::IndependenceDayObserved }
3
+
4
+ test_cases = [
5
+ { day: 3, years: [1992, 1998, 2009] },
6
+ { day: 4, years: [1990, 1991, 1994, 1995, 1996, 1997, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008] },
7
+ { day: 5, years: [1993, 1999, 2004] },
8
+ ]
9
+
10
+ test_cases.each do |test|
11
+ test[:years].each do |year|
12
+ it "calculates observed independence day for #{year}" do
13
+ expect(holiday.date(year)).to eq(Date.new(year,7,test[:day]))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.describe ConcreteHolidays::IndependenceDay do
2
+ let(:holiday) { ConcreteHolidays::IndependenceDay }
3
+ let(:year) { 2015 }
4
+
5
+ it "calculates independence day" do
6
+ expect(holiday.date(year)).to eq(Date.new(year,7,4))
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ RSpec.describe ConcreteHolidays::LaborDay do
2
+ let(:holiday) { ConcreteHolidays::LaborDay }
3
+
4
+ # http://en.wikipedia.org/wiki/Labor_Day#Dates
5
+ test_cases = [
6
+ { day: 3, years: [1900, 1928, 1956, 1984, 2012, 2040, 2068, 2096] },
7
+ { day: 2, years: [1901, 1929, 1957, 1985, 2013, 2041, 2069, 2097] },
8
+ { day: 1, years: [1902, 1930, 1958, 1986, 2014, 2042, 2070, 2098] },
9
+ { day: 7, years: [1903, 1931, 1959, 1987, 2015, 2043, 2071, 2099] },
10
+ { day: 5, years: [1904, 1932, 1960, 1988, 2016, 2044, 2072] },
11
+ { day: 4, years: [1905, 1933, 1961, 1989, 2017, 2045, 2073] },
12
+ { day: 3, years: [1906, 1934, 1962, 1990, 2018, 2046, 2074] },
13
+ { day: 2, years: [1907, 1935, 1963, 1991, 2019, 2047, 2075] },
14
+ { day: 7, years: [1908, 1936, 1964, 1992, 2020, 2048, 2076] },
15
+ { day: 6, years: [1909, 1937, 1965, 1993, 2021, 2049, 2077] },
16
+ { day: 5, years: [1910, 1938, 1966, 1994, 2022, 2050, 2078] },
17
+ { day: 4, years: [1911, 1939, 1967, 1995, 2023, 2051, 2079] },
18
+ { day: 2, years: [1912, 1940, 1968, 1996, 2024, 2052, 2080] },
19
+ { day: 1, years: [1913, 1941, 1969, 1997, 2025, 2053, 2081] },
20
+ { day: 7, years: [1914, 1942, 1970, 1998, 2026, 2054, 2082] },
21
+ { day: 6, years: [1915, 1943, 1971, 1999, 2027, 2055, 2083] },
22
+ { day: 4, years: [1916, 1944, 1972, 2000, 2028, 2056, 2084] },
23
+ { day: 3, years: [1917, 1945, 1973, 2001, 2029, 2057, 2085] },
24
+ { day: 2, years: [1918, 1946, 1974, 2002, 2030, 2058, 2086] },
25
+ { day: 1, years: [1919, 1947, 1975, 2003, 2031, 2059, 2087] },
26
+ { day: 6, years: [1920, 1948, 1976, 2004, 2032, 2060, 2088] },
27
+ { day: 5, years: [1921, 1949, 1977, 2005, 2033, 2061, 2089] },
28
+ { day: 4, years: [1922, 1950, 1978, 2006, 2034, 2062, 2090] },
29
+ { day: 3, years: [1923, 1951, 1979, 2007, 2035, 2063, 2091] },
30
+ { day: 1, years: [1924, 1952, 1980, 2008, 2036, 2064, 2092] },
31
+ { day: 7, years: [1925, 1953, 1981, 2009, 2037, 2065, 2093] },
32
+ { day: 6, years: [1926, 1954, 1982, 2010, 2038, 2066, 2094] },
33
+ { day: 5, years: [1927, 1955, 1983, 2011, 2039, 2067, 2095] },
34
+ ]
35
+
36
+ test_cases.each do |test|
37
+ test[:years].each do |year|
38
+ it "calculates labor day for #{year}" do
39
+ expect(holiday.date(year)).to eq(Date.new(year,9,test[:day]))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ RSpec.describe ConcreteHolidays::MemorialDay do
2
+ let(:holiday) { ConcreteHolidays::MemorialDay }
3
+
4
+ # http://en.wikipedia.org/wiki/Memorial_Day#Dates
5
+ test_cases = [
6
+ { years: [1971, 1999, 2027, 2055, 2083], day: 31 },
7
+ { years: [1972, 2000, 2028, 2056, 2084], day: 29 },
8
+ { years: [1973, 2001, 2029, 2057, 2085], day: 28 },
9
+ { years: [1974, 2002, 2030, 2058, 2086], day: 27 },
10
+ { years: [1975, 2003, 2031, 2059, 2087], day: 26 },
11
+ { years: [1976, 2004, 2032, 2060, 2088], day: 31 },
12
+ { years: [1977, 2005, 2033, 2061, 2089], day: 30 },
13
+ { years: [1978, 2006, 2034, 2062, 2090], day: 29 },
14
+ { years: [1979, 2007, 2035, 2063, 2091], day: 28 },
15
+ { years: [1980, 2008, 2036, 2064, 2092], day: 26 },
16
+ { years: [1981, 2009, 2037, 2065, 2093], day: 25 },
17
+ { years: [1982, 2010, 2038, 2066, 2094], day: 31 },
18
+ { years: [1983, 2011, 2039, 2067, 2095], day: 30 },
19
+ { years: [1984, 2012, 2040, 2068, 2096], day: 28 },
20
+ { years: [1985, 2013, 2041, 2069, 2097], day: 27 },
21
+ { years: [1986, 2014, 2042, 2070, 2098], day: 26 },
22
+ { years: [1987, 2015, 2043, 2071, 2099], day: 25 },
23
+ { years: [1988, 2016, 2044, 2072], day: 30 },
24
+ { years: [1989, 2017, 2045, 2073], day: 29 },
25
+ { years: [1990, 2018, 2046, 2074], day: 28 },
26
+ { years: [1991, 2019, 2047, 2075], day: 27 },
27
+ { years: [1992, 2020, 2048, 2076], day: 25 },
28
+ { years: [1993, 2021, 2049, 2077], day: 31 },
29
+ { years: [1994, 2022, 2050, 2078], day: 30 },
30
+ { years: [1995, 2023, 2051, 2079], day: 29 },
31
+ { years: [1996, 2024, 2052, 2080], day: 27 },
32
+ { years: [1997, 2025, 2053, 2081], day: 26 },
33
+ { years: [1998, 2026, 2054, 2082], day: 25 },
34
+ ]
35
+
36
+ test_cases.each do |test|
37
+ test[:years].each do |year|
38
+ it "calculates memorial day for #{year}" do
39
+ expect(holiday.date(year)).to eq(Date.new(year,5,test[:day]))
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.describe ConcreteHolidays::NewYearsDayObserved do
2
+ let(:holiday) { ConcreteHolidays::NewYearsDayObserved }
3
+
4
+ test_cases = [
5
+ { offset: -1, years: [1994, 2000, 2005] },
6
+ { offset: 0, years: [1990, 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004] },
7
+ { offset: 1, years: [1995, 2006] },
8
+ ]
9
+
10
+ test_cases.each do |test|
11
+ test[:years].each do |year|
12
+ it "calculates observed new year's day for #{year}" do
13
+ expect(holiday.date(year)).to eq(Date.new(year,1,1) + test[:offset])
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.describe ConcreteHolidays::NewYearsDay do
2
+ let(:holiday) { ConcreteHolidays::NewYearsDay }
3
+ let(:year) { 2015 }
4
+
5
+ it "calculates new year's day" do
6
+ expect(holiday.date(year)).to eq(Date.new(year,1,1))
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.describe ConcreteHolidays::NewYearsEve do
2
+ let(:holiday) { ConcreteHolidays::NewYearsEve }
3
+ let(:year) { 2015 }
4
+
5
+ it "calculates new years eve" do
6
+ expect(holiday.date(year)).to eq(Date.new(year,12,31))
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ RSpec.describe ConcreteHolidays::Thanksgiving do
2
+ let(:holiday) { ConcreteHolidays::Thanksgiving }
3
+
4
+ # http://en.wikipedia.org/wiki/Thanksgiving_%28United_States%29#Thanksgiving_dates_1942.E2.80.932099
5
+ test_cases = [
6
+ { day: 22, years: [1945, 1951, 1956, 1962, 1973, 1979, 1984, 1990, 2001, 2007, 2012, 2018, 2029, 2035, 2040, 2046, 2057, 2063, 2068, 2074, 2085, 2091, 2096] },
7
+ { day: 23, years: [1944, 1950, 1961, 1967, 1972, 1978, 1989, 1995, 2000, 2006, 2017, 2023, 2028, 2034, 2045, 2051, 2056, 2062, 2073, 2079, 2084, 2090] },
8
+ { day: 24, years: [1949, 1955, 1960, 1966, 1977, 1983, 1988, 1994, 2005, 2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095] },
9
+ { day: 25, years: [1943, 1948, 1954, 1965, 1971, 1976, 1982, 1993, 1999, 2004, 2010, 2021, 2027, 2032, 2038, 2049, 2055, 2060, 2066, 2077, 2083, 2088, 2094] },
10
+ { day: 26, years: [1942, 1953, 1959, 1964, 1970, 1981, 1987, 1992, 1998, 2009, 2015, 2020, 2026, 2037, 2043, 2048, 2054, 2065, 2071, 2076, 2082, 2093, 2099] },
11
+ { day: 27, years: [1947, 1952, 1958, 1969, 1975, 1980, 1986, 1997, 2003, 2008, 2014, 2025, 2031, 2036, 2042, 2053, 2059, 2064, 2070, 2081, 2087, 2092, 2098] },
12
+ { day: 28, years: [1946, 1957, 1963, 1968, 1974, 1985, 1991, 1996, 2002, 2013, 2019, 2024, 2030, 2041, 2047, 2052, 2058, 2069, 2075, 2080, 2086, 2097] },
13
+ ]
14
+
15
+ test_cases.each do |test|
16
+ test[:years].each do |year|
17
+ it "calculates thanksgiving for #{year}" do
18
+ expect(holiday.date(year)).to eq(Date.new(year,11,test[:day]))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ require 'concrete_holidays'
2
+
3
+ RANGE = Date.new(2015)...Date.new(2016)
4
+
5
+ class VacationTrue
6
+ def self.date(year); RANGE.to_a; end
7
+ def date(year); RANGE.to_a; end
8
+ end
9
+
10
+ RSpec.describe ConcreteHolidays do
11
+ context "with no holidays defined" do
12
+ let(:holidays) { ConcreteHolidays.new }
13
+
14
+ RANGE.each do |date|
15
+ it "#holiday? returns false for #{date}" do
16
+ expect(holidays.holiday?(date)).to be false
17
+ end
18
+ end
19
+ end
20
+
21
+ context "with a holiday via class method" do
22
+ let(:holidays) { ConcreteHolidays.new }
23
+
24
+ before(:each) do
25
+ holidays.add_holiday VacationTrue
26
+ end
27
+
28
+ RANGE.each do |date|
29
+ it "#holiday? returns true for #{date}" do
30
+ expect(holidays.holiday?(date)).to be true
31
+ end
32
+ end
33
+ end
34
+
35
+ context "with a holiday via instance method" do
36
+ let(:holidays) { ConcreteHolidays.new }
37
+
38
+ before(:each) do
39
+ holidays.add_holiday VacationTrue.new
40
+ end
41
+
42
+ RANGE.each do |date|
43
+ it "#holiday? returns true for #{date}" do
44
+ expect(holidays.holiday?(date)).to be true
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,93 @@
1
+ require 'concrete_holidays'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
6
+ # this file to always be loaded, without a need to explicitly require it in any
7
+ # files.
8
+ #
9
+ # Given that it is always loaded, you are encouraged to keep this file as
10
+ # light-weight as possible. Requiring heavyweight dependencies from this file
11
+ # will add to the boot time of your test suite on EVERY test run, even for an
12
+ # individual file that may not need all of that loaded. Instead, consider making
13
+ # a separate helper file that requires the additional dependencies and performs
14
+ # the additional setup, and require it from the spec files that actually need
15
+ # it.
16
+ #
17
+ # The `.rspec` file also contains a few flags that are not defaults but that
18
+ # users commonly want.
19
+ #
20
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
+ RSpec.configure do |config|
22
+ # rspec-expectations config goes here. You can use an alternate
23
+ # assertion/expectation library such as wrong or the stdlib/minitest
24
+ # assertions if you prefer.
25
+ config.expect_with :rspec do |expectations|
26
+ # This option will default to `true` in RSpec 4. It makes the `description`
27
+ # and `failure_message` of custom matchers include text for helper methods
28
+ # defined using `chain`, e.g.:
29
+ # be_bigger_than(2).and_smaller_than(4).description
30
+ # # => "be bigger than 2 and smaller than 4"
31
+ # ...rather than:
32
+ # # => "be bigger than 2"
33
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
34
+ end
35
+
36
+ # rspec-mocks config goes here. You can use an alternate test double
37
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
38
+ config.mock_with :rspec do |mocks|
39
+ # Prevents you from mocking or stubbing a method that does not exist on
40
+ # a real object. This is generally recommended, and will default to
41
+ # `true` in RSpec 4.
42
+ mocks.verify_partial_doubles = true
43
+ end
44
+
45
+ # The settings below are suggested to provide a good initial experience
46
+ # with RSpec, but feel free to customize to your heart's content.
47
+ =begin
48
+ # These two settings work together to allow you to limit a spec run
49
+ # to individual examples or groups you care about by tagging them with
50
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
51
+ # get run.
52
+ config.filter_run :focus
53
+ config.run_all_when_everything_filtered = true
54
+
55
+ # Limits the available syntax to the non-monkey patched syntax that is
56
+ # recommended. For more details, see:
57
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
58
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
59
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
60
+ config.disable_monkey_patching!
61
+
62
+ # This setting enables warnings. It's recommended, but in some cases may
63
+ # be too noisy due to issues in dependencies.
64
+ config.warnings = true
65
+
66
+ # Many RSpec users commonly either run the entire suite or an individual
67
+ # file, and it's useful to allow more verbose output when running an
68
+ # individual spec file.
69
+ if config.files_to_run.one?
70
+ # Use the documentation formatter for detailed output,
71
+ # unless a formatter has already been configured
72
+ # (e.g. via a command-line flag).
73
+ config.default_formatter = 'doc'
74
+ end
75
+
76
+ # Print the 10 slowest examples and example groups at the
77
+ # end of the spec run, to help surface which specs are running
78
+ # particularly slow.
79
+ config.profile_examples = 10
80
+
81
+ # Run specs in random order to surface order dependencies. If you find an
82
+ # order dependency and want to debug it, you can fix the order by providing
83
+ # the seed, which is printed after each run.
84
+ # --seed 1234
85
+ config.order = :random
86
+
87
+ # Seed global randomization in this process using the `--seed` CLI option.
88
+ # Setting this allows you to use `--seed` to deterministically reproduce
89
+ # test failures related to randomization by passing the same `--seed` value
90
+ # as the one that triggered the failure.
91
+ Kernel.srand config.seed
92
+ =end
93
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: concrete_holidays
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Lineen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Holiday calculations using concrete classes
14
+ email:
15
+ - matthew@lineen.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - ".ruby-gemset"
23
+ - ".ruby-version"
24
+ - ".travis.yml"
25
+ - Gemfile
26
+ - Gemfile.lock
27
+ - LICENSE
28
+ - README.md
29
+ - Rakefile
30
+ - concrete_holidays.gemspec
31
+ - lib/concrete_holidays.rb
32
+ - lib/concrete_holidays/black_friday.rb
33
+ - lib/concrete_holidays/calculations.rb
34
+ - lib/concrete_holidays/christmas.rb
35
+ - lib/concrete_holidays/christmas_eve.rb
36
+ - lib/concrete_holidays/christmas_observed.rb
37
+ - lib/concrete_holidays/holidays.rb
38
+ - lib/concrete_holidays/independence_day.rb
39
+ - lib/concrete_holidays/independence_day_observed.rb
40
+ - lib/concrete_holidays/labor_day.rb
41
+ - lib/concrete_holidays/memorial_day.rb
42
+ - lib/concrete_holidays/new_years_day.rb
43
+ - lib/concrete_holidays/new_years_day_observed.rb
44
+ - lib/concrete_holidays/new_years_eve.rb
45
+ - lib/concrete_holidays/thanksgiving.rb
46
+ - spec/concrete_holidays/black_friday_spec.rb
47
+ - spec/concrete_holidays/christmas_eve_spec.rb
48
+ - spec/concrete_holidays/christmas_observed_spec.rb
49
+ - spec/concrete_holidays/christmas_spec.rb
50
+ - spec/concrete_holidays/independence_day_observed_spec.rb
51
+ - spec/concrete_holidays/independence_day_spec.rb
52
+ - spec/concrete_holidays/labor_day_spec.rb
53
+ - spec/concrete_holidays/memorial_day_spec.rb
54
+ - spec/concrete_holidays/new_years_day_observed_spec.rb
55
+ - spec/concrete_holidays/new_years_day_spec.rb
56
+ - spec/concrete_holidays/new_years_eve_spec.rb
57
+ - spec/concrete_holidays/thanksgiving_spec.rb
58
+ - spec/concrete_holidays_spec.rb
59
+ - spec/spec_helper.rb
60
+ homepage: https://github.com/tablexi/concrete_holidays
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.4.6
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Holiday calculations using concrete classes
84
+ test_files:
85
+ - spec/concrete_holidays/black_friday_spec.rb
86
+ - spec/concrete_holidays/christmas_eve_spec.rb
87
+ - spec/concrete_holidays/christmas_observed_spec.rb
88
+ - spec/concrete_holidays/christmas_spec.rb
89
+ - spec/concrete_holidays/independence_day_observed_spec.rb
90
+ - spec/concrete_holidays/independence_day_spec.rb
91
+ - spec/concrete_holidays/labor_day_spec.rb
92
+ - spec/concrete_holidays/memorial_day_spec.rb
93
+ - spec/concrete_holidays/new_years_day_observed_spec.rb
94
+ - spec/concrete_holidays/new_years_day_spec.rb
95
+ - spec/concrete_holidays/new_years_eve_spec.rb
96
+ - spec/concrete_holidays/thanksgiving_spec.rb
97
+ - spec/concrete_holidays_spec.rb
98
+ - spec/spec_helper.rb