holidays 3.3.0 → 4.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/CHANGELOG.md +44 -0
- data/CONTRIBUTING.md +37 -0
- data/README.md +16 -25
- data/REFERENCES +4 -1
- data/Rakefile +38 -8
- data/benchmark.rb +8 -0
- data/definitions/README.md +187 -8
- data/definitions/ar.yaml +2 -1
- data/definitions/at.yaml +17 -13
- data/definitions/au.yaml +65 -60
- data/definitions/be_fr.yaml +14 -10
- data/definitions/be_nl.yaml +8 -4
- data/definitions/bg.yaml +12 -6
- data/definitions/br.yaml +6 -3
- data/definitions/ca.yaml +8 -6
- data/definitions/ch.yaml +21 -15
- data/definitions/cl.yaml +4 -2
- data/definitions/cr.yaml +5 -3
- data/definitions/cz.yaml +4 -2
- data/definitions/de.yaml +25 -14
- data/definitions/dk.yaml +26 -17
- data/definitions/ecb_target.yaml +4 -2
- data/definitions/el.yaml +23 -18
- data/definitions/es.yaml +31 -28
- data/definitions/federal_reserve.yaml +12 -12
- data/definitions/fedex.yaml +6 -6
- data/definitions/fi.yaml +26 -25
- data/definitions/fr.yaml +8 -4
- data/definitions/gb.yaml +9 -7
- data/definitions/hr.yaml +8 -6
- data/definitions/hu.yaml +8 -6
- data/definitions/ie.yaml +17 -16
- data/definitions/index.yaml +1 -1
- data/definitions/is.yaml +29 -19
- data/definitions/it.yaml +10 -9
- data/definitions/jp.yaml +92 -44
- data/definitions/li.yaml +25 -20
- data/definitions/lt.yaml +2 -1
- data/definitions/ma.yaml +7 -7
- data/definitions/mx.yaml +11 -11
- data/definitions/nerc.yaml +6 -6
- data/definitions/nl.yaml +22 -18
- data/definitions/no.yaml +19 -11
- data/definitions/north_america_informal.yaml +6 -6
- data/definitions/nyse.yaml +9 -8
- data/definitions/nz.yaml +33 -29
- data/definitions/ph.yaml +15 -8
- data/definitions/pl.yaml +27 -17
- data/definitions/pt.yaml +4 -2
- data/definitions/ro.yaml +21 -18
- data/definitions/se.yaml +24 -18
- data/definitions/sg.yaml +10 -9
- data/definitions/si.yaml +4 -2
- data/definitions/sk.yaml +4 -2
- data/definitions/united_nations.yaml +12 -12
- data/definitions/ups.yaml +6 -6
- data/definitions/us.yaml +12 -11
- data/definitions/ve.yaml +8 -4
- data/definitions/vi.yaml +6 -6
- data/definitions/za.yaml +26 -24
- data/holidays.gemspec +3 -1
- data/lib/generated_definitions/MANIFEST +1 -1
- data/lib/generated_definitions/ar.rb +8 -6
- data/lib/generated_definitions/at.rb +11 -9
- data/lib/generated_definitions/au.rb +75 -99
- data/lib/generated_definitions/be.rb +12 -10
- data/lib/generated_definitions/be_fr.rb +12 -10
- data/lib/generated_definitions/be_nl.rb +12 -10
- data/lib/generated_definitions/bg.rb +14 -13
- data/lib/generated_definitions/br.rb +11 -9
- data/lib/generated_definitions/ca.rb +20 -20
- data/lib/generated_definitions/ch.rb +41 -44
- data/lib/generated_definitions/cl.rb +9 -7
- data/lib/generated_definitions/cr.rb +9 -7
- data/lib/generated_definitions/cz.rb +9 -6
- data/lib/generated_definitions/de.rb +25 -25
- data/lib/generated_definitions/dk.rb +17 -15
- data/lib/generated_definitions/ecb_target.rb +9 -7
- data/lib/generated_definitions/el.rb +13 -11
- data/lib/generated_definitions/es.rb +35 -33
- data/lib/generated_definitions/europe.rb +234 -247
- data/lib/generated_definitions/federal_reserve.rb +11 -9
- data/lib/generated_definitions/fedex.rb +42 -0
- data/lib/generated_definitions/fi.rb +32 -36
- data/lib/generated_definitions/fr.rb +12 -10
- data/lib/generated_definitions/gb.rb +15 -13
- data/lib/generated_definitions/hr.rb +10 -8
- data/lib/generated_definitions/hu.rb +9 -7
- data/lib/generated_definitions/ie.rb +17 -17
- data/lib/generated_definitions/is.rb +26 -26
- data/lib/generated_definitions/it.rb +9 -7
- data/lib/generated_definitions/jp.rb +112 -89
- data/lib/generated_definitions/li.rb +14 -12
- data/lib/generated_definitions/lt.rb +9 -7
- data/lib/generated_definitions/ma.rb +7 -5
- data/lib/generated_definitions/mx.rb +7 -5
- data/lib/generated_definitions/nerc.rb +10 -8
- data/lib/generated_definitions/nl.rb +15 -13
- data/lib/generated_definitions/no.rb +16 -14
- data/lib/generated_definitions/north_america.rb +34 -37
- data/lib/generated_definitions/nyse.rb +10 -8
- data/lib/generated_definitions/nz.rb +40 -40
- data/lib/generated_definitions/ph.rb +17 -13
- data/lib/generated_definitions/pl.rb +25 -27
- data/lib/generated_definitions/pt.rb +10 -8
- data/lib/generated_definitions/ro.rb +11 -9
- data/lib/generated_definitions/scandinavia.rb +92 -102
- data/lib/generated_definitions/se.rb +25 -27
- data/lib/generated_definitions/sg.rb +11 -9
- data/lib/generated_definitions/si.rb +10 -8
- data/lib/generated_definitions/sk.rb +9 -7
- data/lib/generated_definitions/united_nations.rb +7 -5
- data/lib/generated_definitions/ups.rb +13 -12
- data/lib/generated_definitions/us.rb +20 -21
- data/lib/generated_definitions/ve.rb +11 -9
- data/lib/generated_definitions/vi.rb +7 -5
- data/lib/generated_definitions/za.rb +19 -17
- data/lib/holidays.rb +20 -83
- data/lib/holidays/date_calculator/weekend_modifier.rb +22 -5
- data/lib/holidays/definition/context/generator.rb +67 -29
- data/lib/holidays/definition/context/merger.rb +8 -8
- data/lib/holidays/definition/decorator/custom_method_proc.rb +28 -0
- data/lib/holidays/definition/decorator/custom_method_source.rb +30 -0
- data/lib/holidays/definition/entity/custom_method.rb +11 -0
- data/lib/holidays/definition/parser/custom_method.rb +69 -0
- data/lib/holidays/definition/repository/custom_methods.rb +27 -0
- data/lib/holidays/definition/repository/holidays_by_month.rb +1 -1
- data/lib/holidays/definition/repository/{proc_cache.rb → proc_result_cache.rb} +19 -4
- data/lib/holidays/definition/validator/custom_method.rb +31 -0
- data/lib/holidays/definition_factory.rb +42 -6
- data/lib/holidays/errors.rb +6 -0
- data/lib/holidays/load_all_definitions.rb +57 -0
- data/lib/holidays/option/context/parse_options.rb +26 -16
- data/lib/holidays/option_factory.rb +1 -0
- data/lib/holidays/use_case/context/between.rb +41 -14
- data/lib/holidays/use_case_factory.rb +2 -1
- data/lib/holidays/version.rb +1 -1
- data/test/data/test_single_custom_holiday_with_custom_procs.yaml +24 -0
- data/test/defs/test_defs_at.rb +1 -1
- data/test/defs/test_defs_au.rb +3 -2
- data/test/defs/test_defs_cr.rb +1 -0
- data/test/defs/test_defs_cz.rb +1 -0
- data/test/defs/test_defs_dk.rb +2 -2
- data/test/defs/test_defs_el.rb +7 -6
- data/test/defs/test_defs_europe.rb +40 -33
- data/test/defs/test_defs_fedex.rb +24 -0
- data/test/defs/test_defs_fi.rb +4 -3
- data/test/defs/test_defs_hr.rb +2 -2
- data/test/defs/test_defs_hu.rb +2 -2
- data/test/defs/test_defs_is.rb +2 -1
- data/test/defs/test_defs_it.rb +2 -1
- data/test/defs/test_defs_jp.rb +1 -1
- data/test/defs/test_defs_li.rb +1 -1
- data/test/defs/test_defs_ma.rb +2 -1
- data/test/defs/test_defs_mx.rb +4 -3
- data/test/defs/test_defs_nl.rb +7 -6
- data/test/defs/test_defs_no.rb +1 -0
- data/test/defs/test_defs_north_america.rb +4 -3
- data/test/defs/test_defs_nyse.rb +2 -1
- data/test/defs/test_defs_pl.rb +1 -0
- data/test/defs/test_defs_ro.rb +11 -11
- data/test/defs/test_defs_scandinavia.rb +12 -8
- data/test/defs/test_defs_se.rb +3 -2
- data/test/defs/test_defs_sg.rb +2 -1
- data/test/defs/test_defs_vi.rb +1 -1
- data/test/defs/test_defs_za.rb +3 -2
- data/test/holidays/date_calculator/test_weekend_modifier.rb +11 -0
- data/test/holidays/definition/context/test_generator.rb +64 -5
- data/test/holidays/definition/context/test_merger.rb +5 -2
- data/test/holidays/definition/decorator/test_custom_method_proc.rb +113 -0
- data/test/holidays/definition/decorator/test_custom_method_source.rb +96 -0
- data/test/holidays/definition/parser/test_custom_method.rb +79 -0
- data/test/holidays/definition/repository/test_custom_methods.rb +43 -0
- data/test/holidays/definition/repository/test_holidays_by_month.rb +0 -32
- data/test/holidays/definition/repository/test_proc_result_cache.rb +84 -0
- data/test/holidays/definition/validator/test_custom_method.rb +89 -0
- data/test/holidays/option/context/test_parse_options.rb +5 -0
- data/test/holidays/test_definition_factory.rb +17 -2
- data/test/holidays/use_case/context/test_between.rb +2 -0
- data/test/test_all_regions.rb +7 -49
- data/test/test_custom_holidays.rb +8 -2
- data/test/test_helper.rb +9 -2
- data/test/test_holidays.rb +9 -29
- metadata +46 -11
- data/lib/generated_definitions/fed_ex.rb +0 -41
- data/test/holidays/definition/repository/test_proc_cache.rb +0 -29
- data/test/test_parse_definitions.rb +0 -30
|
@@ -20,16 +20,31 @@ module Holidays
|
|
|
20
20
|
# 0100 0.125000 0.000000 0.125000 ( 0.125000)
|
|
21
21
|
# 1000 1.234000 0.000000 1.234000 ( 1.234000)
|
|
22
22
|
# 5000 6.094000 0.031000 6.125000 ( 6.141000)
|
|
23
|
-
class
|
|
23
|
+
class ProcResultCache
|
|
24
24
|
def initialize
|
|
25
25
|
@proc_cache = {}
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def lookup(function,
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
def lookup(function, *function_arguments)
|
|
29
|
+
validate!(function, function_arguments)
|
|
30
|
+
|
|
31
|
+
proc_key = build_proc_key(function, function_arguments)
|
|
32
|
+
@proc_cache[proc_key] = function.call(*function_arguments) unless @proc_cache[proc_key]
|
|
31
33
|
@proc_cache[proc_key]
|
|
32
34
|
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def validate!(function, function_arguments)
|
|
39
|
+
raise ArgumentError.new("function must be a proc") unless function.is_a?(Proc)
|
|
40
|
+
function_arguments.each do |arg|
|
|
41
|
+
raise ArgumentError.new("function arguments '#{function_arguments}' must contain either integers or dates") unless arg.is_a?(Integer) || arg.is_a?(Date)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def build_proc_key(function, function_arguments)
|
|
46
|
+
Digest::MD5.hexdigest("#{function.to_s}_#{function_arguments.join('_')}")
|
|
47
|
+
end
|
|
33
48
|
end
|
|
34
49
|
end
|
|
35
50
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Validator
|
|
4
|
+
class CustomMethod
|
|
5
|
+
VALID_ARGUMENTS = ["date", "year", "month", "day"]
|
|
6
|
+
|
|
7
|
+
def valid?(m)
|
|
8
|
+
valid_name?(m[:name]) &&
|
|
9
|
+
valid_arguments?(m[:arguments]) &&
|
|
10
|
+
valid_source?(m[:source])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def valid_name?(name)
|
|
16
|
+
!name.nil? && !name.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def valid_arguments?(arguments)
|
|
20
|
+
arguments.split(",").all? { |arg|
|
|
21
|
+
arg == arg.chomp && VALID_ARGUMENTS.include?(arg.strip)
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def valid_source?(source)
|
|
26
|
+
!source.nil? && !source.empty?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,35 +1,67 @@
|
|
|
1
1
|
require 'holidays/definition/context/generator'
|
|
2
2
|
require 'holidays/definition/context/merger'
|
|
3
|
+
require 'holidays/definition/decorator/custom_method_proc'
|
|
4
|
+
require 'holidays/definition/decorator/custom_method_source'
|
|
5
|
+
require 'holidays/definition/parser/custom_method'
|
|
3
6
|
require 'holidays/definition/repository/holidays_by_month'
|
|
4
7
|
require 'holidays/definition/repository/regions'
|
|
5
8
|
require 'holidays/definition/repository/cache'
|
|
6
|
-
require 'holidays/definition/repository/
|
|
9
|
+
require 'holidays/definition/repository/proc_result_cache'
|
|
10
|
+
require 'holidays/definition/repository/custom_methods'
|
|
11
|
+
require 'holidays/definition/validator/custom_method'
|
|
7
12
|
require 'holidays/definition/validator/region'
|
|
8
13
|
|
|
9
14
|
module Holidays
|
|
10
15
|
module DefinitionFactory
|
|
11
16
|
class << self
|
|
12
17
|
def file_parser
|
|
13
|
-
Definition::Context::Generator.new
|
|
18
|
+
Definition::Context::Generator.new(
|
|
19
|
+
custom_method_parser,
|
|
20
|
+
custom_method_source_decorator,
|
|
21
|
+
custom_methods_repository,
|
|
22
|
+
)
|
|
14
23
|
end
|
|
15
24
|
|
|
16
25
|
def source_generator
|
|
17
|
-
Definition::Context::Generator.new
|
|
26
|
+
Definition::Context::Generator.new(
|
|
27
|
+
custom_method_parser,
|
|
28
|
+
custom_method_source_decorator,
|
|
29
|
+
custom_methods_repository,
|
|
30
|
+
)
|
|
18
31
|
end
|
|
19
32
|
|
|
20
33
|
def merger
|
|
21
34
|
Definition::Context::Merger.new(
|
|
22
35
|
holidays_by_month_repository,
|
|
23
|
-
regions_repository
|
|
36
|
+
regions_repository,
|
|
37
|
+
custom_methods_repository,
|
|
24
38
|
)
|
|
25
39
|
end
|
|
26
40
|
|
|
41
|
+
def custom_method_parser
|
|
42
|
+
Definition::Parser::CustomMethod.new(
|
|
43
|
+
custom_method_validator,
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def custom_method_proc_decorator
|
|
48
|
+
Definition::Decorator::CustomMethodProc.new
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def custom_method_source_decorator
|
|
52
|
+
Definition::Decorator::CustomMethodSource.new
|
|
53
|
+
end
|
|
54
|
+
|
|
27
55
|
def region_validator
|
|
28
56
|
Definition::Validator::Region.new(
|
|
29
57
|
regions_repository
|
|
30
58
|
)
|
|
31
59
|
end
|
|
32
60
|
|
|
61
|
+
def custom_method_validator
|
|
62
|
+
Definition::Validator::CustomMethod.new
|
|
63
|
+
end
|
|
64
|
+
|
|
33
65
|
def holidays_by_month_repository
|
|
34
66
|
@holidays_repo ||= Definition::Repository::HolidaysByMonth.new
|
|
35
67
|
end
|
|
@@ -42,8 +74,12 @@ module Holidays
|
|
|
42
74
|
@cache_repo ||= Definition::Repository::Cache.new
|
|
43
75
|
end
|
|
44
76
|
|
|
45
|
-
def
|
|
46
|
-
@
|
|
77
|
+
def proc_result_cache_repository
|
|
78
|
+
@proc_result_cache_repo ||= Definition::Repository::ProcResultCache.new
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def custom_methods_repository
|
|
82
|
+
@custom_methods_repository ||= Definition::Repository::CustomMethods.new
|
|
47
83
|
end
|
|
48
84
|
end
|
|
49
85
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
class LoadAllDefinitions
|
|
3
|
+
class << self
|
|
4
|
+
def call
|
|
5
|
+
path = File.expand_path(File.dirname(__FILE__)) + "/../#{Holidays::DEFINITIONS_PATH}/"
|
|
6
|
+
|
|
7
|
+
Dir.foreach(path) do |item|
|
|
8
|
+
next if item == '.' or item == '..'
|
|
9
|
+
|
|
10
|
+
target = path+item
|
|
11
|
+
next if File.extname(target) != '.rb'
|
|
12
|
+
|
|
13
|
+
require target
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#FIXME I need a better way to do this. I'm thinking of putting these 'common' methods
|
|
17
|
+
# into some kind of definition file so it can be loaded automatically but I'm afraid
|
|
18
|
+
# of making that big of a breaking API change since these are public. For the time
|
|
19
|
+
# being I'll load them manually like this.
|
|
20
|
+
global_methods = {
|
|
21
|
+
"easter(year)" => gregorian_easter.method(:calculate_easter_for).to_proc,
|
|
22
|
+
"orthodox_easter(year)" => gregorian_easter.method(:calculate_orthodox_easter_for).to_proc,
|
|
23
|
+
"orthodox_easter_julian(year)" => julian_easter.method(:calculate_orthodox_easter_for).to_proc,
|
|
24
|
+
"to_monday_if_sunday(date)" => weekend_modifier.method(:to_monday_if_sunday).to_proc,
|
|
25
|
+
"to_monday_if_weekend(date)" => weekend_modifier.method(:to_monday_if_weekend).to_proc,
|
|
26
|
+
"to_weekday_if_boxing_weekend(date)" => weekend_modifier.method(:to_weekday_if_boxing_weekend).to_proc,
|
|
27
|
+
"to_weekday_if_boxing_weekend_from_year(year)" => weekend_modifier.method(:to_weekday_if_boxing_weekend_from_year).to_proc,
|
|
28
|
+
"to_weekday_if_weekend(date)" => weekend_modifier.method(:to_weekday_if_weekend).to_proc,
|
|
29
|
+
"calculate_day_of_month(year, month, day, wday)" => day_of_month_calculator.method(:call).to_proc,
|
|
30
|
+
"to_weekday_if_boxing_weekend_from_year_or_to_tuesday_if_monday(year)" => weekend_modifier.method(:to_weekday_if_boxing_weekend_from_year_or_to_tuesday_if_monday).to_proc,
|
|
31
|
+
"xmas_to_weekday_if_weekend(year)" => weekend_modifier.method(:xmas_to_weekday_if_weekend).to_proc,
|
|
32
|
+
"to_tuesday_if_sunday_or_monday_if_saturday(date)" => weekend_modifier.method(:to_tuesday_if_sunday_or_monday_if_saturday).to_proc,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
Holidays::DefinitionFactory.custom_methods_repository.add(global_methods)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def gregorian_easter
|
|
41
|
+
DateCalculatorFactory::Easter::Gregorian.easter_calculator
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def julian_easter
|
|
45
|
+
DateCalculatorFactory::Easter::Julian.easter_calculator
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def weekend_modifier
|
|
49
|
+
DateCalculatorFactory.weekend_modifier
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def day_of_month_calculator
|
|
53
|
+
DateCalculatorFactory.day_of_month_calculator
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -2,9 +2,10 @@ module Holidays
|
|
|
2
2
|
module Option
|
|
3
3
|
module Context
|
|
4
4
|
class ParseOptions
|
|
5
|
-
def initialize(regions_repo, region_validator)
|
|
5
|
+
def initialize(regions_repo, region_validator, definition_merger)
|
|
6
6
|
@regions_repo = regions_repo
|
|
7
7
|
@region_validator = region_validator
|
|
8
|
+
@definition_merger = definition_merger
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
# Returns [(arr)regions, (bool)observed, (bool)informal]
|
|
@@ -23,12 +24,12 @@ module Holidays
|
|
|
23
24
|
|
|
24
25
|
private
|
|
25
26
|
|
|
26
|
-
attr_reader :regions_repo, :region_validator
|
|
27
|
+
attr_reader :regions_repo, :region_validator, :definition_merger
|
|
27
28
|
|
|
28
29
|
# Check regions against list of supported regions and return an array of
|
|
29
30
|
# symbols.
|
|
30
31
|
#
|
|
31
|
-
# If a wildcard region is found (e.g.
|
|
32
|
+
# If a wildcard region is found (e.g. :ca_) it is expanded into all
|
|
32
33
|
# of its available sub regions.
|
|
33
34
|
def parse_regions!(regions)
|
|
34
35
|
regions = [regions] unless regions.kind_of?(Array)
|
|
@@ -49,20 +50,19 @@ module Holidays
|
|
|
49
50
|
|
|
50
51
|
regions.flatten!
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
load_definition_data("north_america") if regions.include?(:us) # special case for north_america/US cross-linking
|
|
53
54
|
|
|
54
55
|
regions.each do |region|
|
|
55
|
-
unless region == :any
|
|
56
|
-
region_definition = "#{DEFINITIONS_PATH}/#{region.to_s}"
|
|
56
|
+
unless region == :any || regions_repo.exists?(region)
|
|
57
57
|
begin
|
|
58
|
-
|
|
59
|
-
rescue
|
|
58
|
+
load_definition_data(region.to_s)
|
|
59
|
+
rescue NameError => e
|
|
60
60
|
# This could be a sub region that does not have any holiday
|
|
61
61
|
# definitions of its own; try to load the containing region instead.
|
|
62
62
|
if region.to_s =~ /_/
|
|
63
63
|
load_containing_region(region.to_s)
|
|
64
64
|
else
|
|
65
|
-
raise UnknownRegionError, "Could not load #{
|
|
65
|
+
raise UnknownRegionError.new(e), "Could not load #{region.to_s}"
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
end
|
|
@@ -81,15 +81,25 @@ module Holidays
|
|
|
81
81
|
# and load its definition. (Common code factored out from parse_regions)
|
|
82
82
|
def load_containing_region(sub_reg)
|
|
83
83
|
prefix = sub_reg.split('_').first
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
|
|
85
|
+
return if regions_repo.exists?(prefix.to_sym)
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
load_definition_data(prefix)
|
|
89
|
+
rescue NameError => e
|
|
90
|
+
raise UnknownRegionError.new(e), "Could not load region prefix: #{prefix.to_s}, original subregion: #{sub_reg.to_s}"
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
|
+
|
|
94
|
+
def load_definition_data(region)
|
|
95
|
+
target_region_module = Module.const_get("Holidays").const_get(region.upcase)
|
|
96
|
+
|
|
97
|
+
definition_merger.call(
|
|
98
|
+
target_region_module.defined_regions,
|
|
99
|
+
target_region_module.holidays_by_month,
|
|
100
|
+
target_region_module.custom_methods,
|
|
101
|
+
)
|
|
102
|
+
end
|
|
93
103
|
end
|
|
94
104
|
end
|
|
95
105
|
end
|
|
@@ -2,10 +2,11 @@ module Holidays
|
|
|
2
2
|
module UseCase
|
|
3
3
|
module Context
|
|
4
4
|
class Between
|
|
5
|
-
def initialize(holidays_by_month_repo, day_of_month_calculator,
|
|
5
|
+
def initialize(holidays_by_month_repo, day_of_month_calculator, custom_methods_repo, proc_result_cache_repo)
|
|
6
6
|
@holidays_by_month_repo = holidays_by_month_repo
|
|
7
7
|
@day_of_month_calculator = day_of_month_calculator
|
|
8
|
-
@
|
|
8
|
+
@custom_methods_repo = custom_methods_repo
|
|
9
|
+
@proc_result_cache_repo = proc_result_cache_repo
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def call(start_date, end_date, dates_driver, regions, observed, informal)
|
|
@@ -18,9 +19,7 @@ module Holidays
|
|
|
18
19
|
next unless hbm = holidays_by_month_repo.find_by_month(month)
|
|
19
20
|
hbm.each do |h|
|
|
20
21
|
next unless in_region?(regions, h[:regions])
|
|
21
|
-
|
|
22
|
-
# Skip informal holidays unless they have been requested
|
|
23
|
-
next if h[:type] == :informal and not informal
|
|
22
|
+
next if h[:type] == :informal && !informal
|
|
24
23
|
|
|
25
24
|
# range check feature.
|
|
26
25
|
if h[:year_ranges]
|
|
@@ -45,18 +44,42 @@ module Holidays
|
|
|
45
44
|
next unless valid_range_year
|
|
46
45
|
end
|
|
47
46
|
|
|
47
|
+
#FIXME I don't like this entire if/else. If it's a function, do something, else do some
|
|
48
|
+
# weird mday logic? Bollocks. I think this should be a refactor target.
|
|
48
49
|
if h[:function]
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
function_arguments = []
|
|
51
|
+
|
|
52
|
+
#FIXME This is a refactor target. We should also allow 'date'. Right now these are the only
|
|
53
|
+
# three things that we allow in. I think having a more testable, robust approach here is vital.
|
|
54
|
+
if h[:function_arguments].include?(:year)
|
|
55
|
+
function_arguments << year
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if h[:function_arguments].include?(:month)
|
|
59
|
+
function_arguments << month
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if h[:function_arguments].include?(:day)
|
|
63
|
+
function_arguments << h[:mday]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
result = call_proc(h[:function], *function_arguments)
|
|
67
|
+
|
|
68
|
+
#FIXME This is a dangerous assumption. We should raise an error or something
|
|
69
|
+
# if these procs return something unexpected.
|
|
70
|
+
#
|
|
51
71
|
# Procs may return either Date or an integer representing mday
|
|
52
72
|
if result.kind_of?(Date)
|
|
73
|
+
if h[:function_modifier]
|
|
74
|
+
result = result + h[:function_modifier] # NOTE: This could be a positive OR negative number.
|
|
75
|
+
end
|
|
76
|
+
|
|
53
77
|
month = result.month
|
|
54
78
|
mday = result.mday
|
|
55
79
|
else
|
|
56
80
|
mday = result
|
|
57
81
|
end
|
|
58
82
|
else
|
|
59
|
-
# Calculate the mday
|
|
60
83
|
mday = h[:mday] || day_of_month_calculator.call(year, month, h[:week], h[:wday])
|
|
61
84
|
end
|
|
62
85
|
|
|
@@ -65,9 +88,10 @@ module Holidays
|
|
|
65
88
|
date = Date.civil(year, month, mday)
|
|
66
89
|
rescue; next; end
|
|
67
90
|
|
|
68
|
-
#
|
|
69
|
-
# is
|
|
70
|
-
|
|
91
|
+
#FIXME We should be checking the function arguments and passing in what is specified.
|
|
92
|
+
# Right now all 'observed' functions require 'date' but that is by convention. Nothing
|
|
93
|
+
# is requiring that. We should be more explicit.
|
|
94
|
+
if observed && h[:observed]
|
|
71
95
|
date = call_proc(h[:observed], date)
|
|
72
96
|
end
|
|
73
97
|
|
|
@@ -83,7 +107,7 @@ module Holidays
|
|
|
83
107
|
|
|
84
108
|
private
|
|
85
109
|
|
|
86
|
-
attr_reader :holidays_by_month_repo, :day_of_month_calculator, :
|
|
110
|
+
attr_reader :holidays_by_month_repo, :day_of_month_calculator, :custom_methods_repo, :proc_result_cache_repo
|
|
87
111
|
|
|
88
112
|
def validate!(start_date, end_date, dates_driver, regions)
|
|
89
113
|
raise ArgumentError unless start_date
|
|
@@ -98,8 +122,11 @@ module Holidays
|
|
|
98
122
|
raise ArgumentError if regions.nil? || regions.empty?
|
|
99
123
|
end
|
|
100
124
|
|
|
101
|
-
def call_proc(
|
|
102
|
-
|
|
125
|
+
def call_proc(function_id, *arguments)
|
|
126
|
+
function = custom_methods_repo.find(function_id)
|
|
127
|
+
raise Holidays::FunctionNotFound.new("Unable to find function with id '#{function_id}'") if function.nil?
|
|
128
|
+
|
|
129
|
+
proc_result_cache_repo.lookup(function, *arguments)
|
|
103
130
|
end
|
|
104
131
|
|
|
105
132
|
# Check sub regions.
|
|
@@ -8,7 +8,8 @@ module Holidays
|
|
|
8
8
|
UseCase::Context::Between.new(
|
|
9
9
|
DefinitionFactory.holidays_by_month_repository,
|
|
10
10
|
DateCalculatorFactory.day_of_month_calculator,
|
|
11
|
-
DefinitionFactory.
|
|
11
|
+
DefinitionFactory.custom_methods_repository,
|
|
12
|
+
DefinitionFactory.proc_result_cache_repository,
|
|
12
13
|
)
|
|
13
14
|
end
|
|
14
15
|
|
data/lib/holidays/version.rb
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
months:
|
|
2
|
+
0:
|
|
3
|
+
- name: Custom Holiday
|
|
4
|
+
regions: [custom_single_file]
|
|
5
|
+
function: custom_method(year, month)
|
|
6
|
+
function_modifier: 5
|
|
7
|
+
6:
|
|
8
|
+
- name: Company Founding
|
|
9
|
+
regions: [custom_single_file]
|
|
10
|
+
mday: 20
|
|
11
|
+
methods:
|
|
12
|
+
custom_method:
|
|
13
|
+
arguments: year, month
|
|
14
|
+
source: |
|
|
15
|
+
d = Date.civil(year, month, 1)
|
|
16
|
+
d + 2
|
|
17
|
+
tests: |
|
|
18
|
+
{Date.civil(2013,6,20) => 'Company Founding'}.each do |date, name|
|
|
19
|
+
assert_equal name, (Holidays.on(date, :custom_single_file)[0] || {})[:name]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
{Date.civil(2015, 1, 1) => 'Custom Holiday'}.each do |date, name|
|
|
23
|
+
assert_equal name, (Holidays.on(date, :custom_single_file)[0] || {}[:name]
|
|
24
|
+
end
|