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
|
@@ -2,18 +2,19 @@ module Holidays
|
|
|
2
2
|
module DateCalculator
|
|
3
3
|
class WeekendModifier
|
|
4
4
|
# Move date to Monday if it occurs on a Saturday on Sunday.
|
|
5
|
+
# Does not modify date if it is not a weekend.
|
|
5
6
|
# Used as a callback function.
|
|
6
7
|
def to_monday_if_weekend(date)
|
|
7
|
-
date
|
|
8
|
-
date
|
|
9
|
-
date
|
|
8
|
+
return date unless date.wday == 6 || date.wday == 0
|
|
9
|
+
to_next_weekday(date)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
# Move date to Monday if it occurs on a Sunday.
|
|
13
|
+
# Does not modify the date if it is not a Sunday.
|
|
13
14
|
# Used as a callback function.
|
|
14
15
|
def to_monday_if_sunday(date)
|
|
15
|
-
date
|
|
16
|
-
date
|
|
16
|
+
return date unless date.wday == 0
|
|
17
|
+
to_next_weekday(date)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
# if Christmas falls on a Sunday, move it to the next Tuesday (Boxing Day will go on Monday)
|
|
@@ -65,6 +66,22 @@ module Holidays
|
|
|
65
66
|
date -= 1 if date.wday == 6
|
|
66
67
|
date
|
|
67
68
|
end
|
|
69
|
+
|
|
70
|
+
# Finds the next weekday. For example, if a 'Friday' date is received
|
|
71
|
+
# it will return the following Monday. If Sunday then return Monday,
|
|
72
|
+
# if Saturday return Monday, if Tuesday return Wednesday, etc.
|
|
73
|
+
def to_next_weekday(date)
|
|
74
|
+
case date.wday
|
|
75
|
+
when 6
|
|
76
|
+
date += 2
|
|
77
|
+
when 5
|
|
78
|
+
date += 3
|
|
79
|
+
else
|
|
80
|
+
date += 1
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
date
|
|
84
|
+
end
|
|
68
85
|
end
|
|
69
86
|
end
|
|
70
87
|
end
|
|
@@ -4,6 +4,11 @@ module Holidays
|
|
|
4
4
|
module Definition
|
|
5
5
|
module Context
|
|
6
6
|
class Generator
|
|
7
|
+
def initialize(custom_method_parser, custom_method_source_decorator, custom_methods_repository)
|
|
8
|
+
@custom_method_parser = custom_method_parser
|
|
9
|
+
@custom_method_source_decorator = custom_method_source_decorator
|
|
10
|
+
@custom_methods_repository = custom_methods_repository
|
|
11
|
+
end
|
|
7
12
|
|
|
8
13
|
def parse_definition_files(files)
|
|
9
14
|
raise ArgumentError, "Must have at least one file to parse" if files.nil? || files.empty?
|
|
@@ -18,7 +23,9 @@ module Holidays
|
|
|
18
23
|
files.each do |file|
|
|
19
24
|
definition_file = YAML.load_file(file)
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
custom_methods = custom_method_parser.call(definition_file['methods'])
|
|
27
|
+
|
|
28
|
+
regions, rules_by_month = parse_month_definitions(definition_file['months'], custom_methods)
|
|
22
29
|
|
|
23
30
|
all_regions << regions.flatten
|
|
24
31
|
|
|
@@ -27,7 +34,10 @@ module Holidays
|
|
|
27
34
|
existing.flatten!
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
#FIXME This is a problem. We will have a 'global' list of methods. That's always bad. What effects will this have?
|
|
38
|
+
# This is an existing problem (just so we are clear). An issue would be extremely rare because we are generally parsing
|
|
39
|
+
# single files/custom files. But it IS possible that we would parse a bunch of things at the same time and step
|
|
40
|
+
# on each other so we need a solution.
|
|
31
41
|
all_custom_methods.merge!(custom_methods)
|
|
32
42
|
|
|
33
43
|
all_tests << parse_test_definitions(definition_file['tests'])
|
|
@@ -39,12 +49,12 @@ module Holidays
|
|
|
39
49
|
end
|
|
40
50
|
|
|
41
51
|
def generate_definition_source(module_name, files, regions, rules_by_month, custom_methods, tests)
|
|
42
|
-
month_strings = generate_month_definition_strings(rules_by_month)
|
|
52
|
+
month_strings = generate_month_definition_strings(rules_by_month, custom_methods)
|
|
43
53
|
|
|
44
54
|
# Build the custom methods string
|
|
45
55
|
custom_method_string = ''
|
|
46
56
|
custom_methods.each do |key, code|
|
|
47
|
-
custom_method_string << code + "
|
|
57
|
+
custom_method_string << custom_method_source_decorator.call(code) + ",\n\n"
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
module_src = generate_module_src(module_name, files, regions, month_strings, custom_method_string)
|
|
@@ -55,7 +65,10 @@ module Holidays
|
|
|
55
65
|
|
|
56
66
|
private
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
attr_reader :custom_method_parser, :custom_method_source_decorator, :custom_methods_repository
|
|
69
|
+
|
|
70
|
+
#FIXME This should be a 'month_definitions_parser' like the above parser
|
|
71
|
+
def parse_month_definitions(month_definitions, parsed_custom_methods)
|
|
59
72
|
regions = []
|
|
60
73
|
rules_by_month = {}
|
|
61
74
|
|
|
@@ -82,6 +95,12 @@ module Holidays
|
|
|
82
95
|
end
|
|
83
96
|
|
|
84
97
|
unless exists
|
|
98
|
+
# This will add in the custom method arguments so they are immediately
|
|
99
|
+
# available for 'on the fly' def loading.
|
|
100
|
+
if rule[:function]
|
|
101
|
+
rule[:function_arguments] = get_function_arguments(rule[:function], parsed_custom_methods)
|
|
102
|
+
end
|
|
103
|
+
|
|
85
104
|
rules_by_month[month] << rule
|
|
86
105
|
end
|
|
87
106
|
end
|
|
@@ -91,18 +110,6 @@ module Holidays
|
|
|
91
110
|
[regions, rules_by_month]
|
|
92
111
|
end
|
|
93
112
|
|
|
94
|
-
def parse_method_definitions(methods)
|
|
95
|
-
custom_methods = {}
|
|
96
|
-
|
|
97
|
-
if methods
|
|
98
|
-
methods.each do |name, code|
|
|
99
|
-
custom_methods[name] = code
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
custom_methods
|
|
104
|
-
end
|
|
105
|
-
|
|
106
113
|
def parse_test_definitions(tests)
|
|
107
114
|
test_strings = []
|
|
108
115
|
|
|
@@ -113,7 +120,8 @@ module Holidays
|
|
|
113
120
|
test_strings
|
|
114
121
|
end
|
|
115
122
|
|
|
116
|
-
|
|
123
|
+
#FIXME This should really be split out and tested with its own unit tests.
|
|
124
|
+
def generate_month_definition_strings(rules_by_month, parsed_custom_methods)
|
|
117
125
|
month_strings = []
|
|
118
126
|
|
|
119
127
|
rules_by_month.each do |month, rules|
|
|
@@ -123,13 +131,28 @@ module Holidays
|
|
|
123
131
|
string = '{'
|
|
124
132
|
if rule[:mday]
|
|
125
133
|
string << ":mday => #{rule[:mday]}, "
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if rule[:function]
|
|
137
|
+
string << ":function => \"#{rule[:function].to_s}\", "
|
|
138
|
+
|
|
139
|
+
# We need to add in the arguments so we can know what to send in when calling the custom proc during holiday lookups.
|
|
140
|
+
# NOTE: the allowed arguments are enforced in the custom methods parser.
|
|
141
|
+
string << ":function_arguments => #{get_function_arguments(rule[:function], parsed_custom_methods)}, "
|
|
142
|
+
|
|
143
|
+
if rule[:function_modifier]
|
|
144
|
+
string << ":function_modifier => #{rule[:function_modifier].to_s}, "
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# This is the 'else'. It is possible for mday AND function
|
|
149
|
+
# to be set but this is the fallback. This whole area
|
|
150
|
+
# needs to be reworked!
|
|
151
|
+
if string == '{'
|
|
130
152
|
string << ":wday => #{rule[:wday]}, :week => #{rule[:week]}, "
|
|
131
153
|
end
|
|
132
154
|
|
|
155
|
+
#FIXME I think this should be split out into its own file.
|
|
133
156
|
if rule[:year_ranges] && rule[:year_ranges].kind_of?(Array)
|
|
134
157
|
year_string = " :year_ranges => ["
|
|
135
158
|
len = rule[:year_ranges].length
|
|
@@ -145,8 +168,8 @@ module Holidays
|
|
|
145
168
|
end
|
|
146
169
|
|
|
147
170
|
if rule[:observed]
|
|
148
|
-
string << ":observed =>
|
|
149
|
-
string << ":
|
|
171
|
+
string << ":observed => \"#{rule[:observed].to_s}\", "
|
|
172
|
+
string << ":observed_arguments => #{get_function_arguments(rule[:observed], parsed_custom_methods)}, "
|
|
150
173
|
end
|
|
151
174
|
|
|
152
175
|
if rule[:type]
|
|
@@ -164,6 +187,19 @@ module Holidays
|
|
|
164
187
|
return month_strings
|
|
165
188
|
end
|
|
166
189
|
|
|
190
|
+
# This method sucks. The issue here is that the custom methods repo has the 'general' methods (like easter)
|
|
191
|
+
# but the 'parsed_custom_methods' have the recently parsed stuff. We don't load those until they are needed later.
|
|
192
|
+
# This entire file is a refactor target so I am adding some tech debt to get me over the hump.
|
|
193
|
+
# What we should do is ensure that all custom methods are loaded into the repo as soon as they are parsed
|
|
194
|
+
# so we only have one place to look.
|
|
195
|
+
def get_function_arguments(function_id, parsed_custom_methods)
|
|
196
|
+
if method = custom_methods_repository.find(function_id)
|
|
197
|
+
method.parameters.collect { |arg| arg[1] }
|
|
198
|
+
elsif method = parsed_custom_methods[function_id]
|
|
199
|
+
method.arguments.collect { |arg| arg.to_sym }
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
167
203
|
def generate_module_src(module_name, files, regions, month_strings, custom_methods)
|
|
168
204
|
module_src = ""
|
|
169
205
|
|
|
@@ -180,7 +216,7 @@ module Holidays
|
|
|
180
216
|
# require 'holidays'
|
|
181
217
|
# require '#{DEFINITIONS_PATH}/#{module_name.to_s.downcase}'
|
|
182
218
|
#
|
|
183
|
-
# All the definitions are available at https://github.com/
|
|
219
|
+
# All the definitions are available at https://github.com/holidays/holidays
|
|
184
220
|
module #{module_name.to_s.upcase} # :nodoc:
|
|
185
221
|
def self.defined_regions
|
|
186
222
|
[:#{regions.join(', :')}]
|
|
@@ -191,12 +227,14 @@ module Holidays
|
|
|
191
227
|
#{month_strings.join(",\n")}
|
|
192
228
|
}
|
|
193
229
|
end
|
|
194
|
-
end
|
|
195
230
|
|
|
196
|
-
|
|
231
|
+
def self.custom_methods
|
|
232
|
+
{
|
|
233
|
+
#{custom_methods}
|
|
234
|
+
}
|
|
235
|
+
end
|
|
236
|
+
end
|
|
197
237
|
end
|
|
198
|
-
|
|
199
|
-
Holidays.merge_defs(Holidays::#{module_name.to_s.upcase}.defined_regions, Holidays::#{module_name.to_s.upcase}.holidays_by_month)
|
|
200
238
|
EOM
|
|
201
239
|
|
|
202
240
|
return module_src
|
|
@@ -7,19 +7,19 @@ module Holidays
|
|
|
7
7
|
# files. This is accomplished because the Generator class generates the
|
|
8
8
|
# definition source with this class explicitly.
|
|
9
9
|
class Merger
|
|
10
|
-
def initialize(holidays_by_month_repo, regions_repo)
|
|
10
|
+
def initialize(holidays_by_month_repo, regions_repo, custom_methods_repo)
|
|
11
11
|
@holidays_repo = holidays_by_month_repo
|
|
12
12
|
@regions_repo = regions_repo
|
|
13
|
+
@custom_methods_repo = custom_methods_repo
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
def call(target_regions, target_holidays)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
def call(target_regions, target_holidays, target_custom_methods)
|
|
17
|
+
#FIXME Does this need to come in this exact order? God I hope not.
|
|
18
|
+
# If not then we should swap the order so it matches the init.
|
|
19
|
+
@regions_repo.add(target_regions)
|
|
20
|
+
@holidays_repo.add(target_holidays)
|
|
21
|
+
@custom_methods_repo.add(target_custom_methods)
|
|
18
22
|
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
attr_reader :holidays_repo, :regions_repo
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Decorator
|
|
4
|
+
class CustomMethodProc
|
|
5
|
+
def call(proc)
|
|
6
|
+
validate!(proc)
|
|
7
|
+
|
|
8
|
+
eval("Proc.new { |#{parse_arguments(proc.arguments)}|
|
|
9
|
+
#{proc.source}
|
|
10
|
+
}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def validate!(proc)
|
|
16
|
+
raise ArgumentError if proc.name.nil? || proc.name.empty?
|
|
17
|
+
raise ArgumentError if proc.arguments.nil? || proc.arguments.empty?
|
|
18
|
+
raise ArgumentError if proc.source.nil? || proc.source.empty?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parse_arguments(args)
|
|
22
|
+
a = args.join(", ")
|
|
23
|
+
a[0..-1]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Decorator
|
|
4
|
+
class CustomMethodSource
|
|
5
|
+
def call(proc)
|
|
6
|
+
validate!(proc)
|
|
7
|
+
|
|
8
|
+
method_name = proc.name
|
|
9
|
+
args = args_string(proc.arguments)
|
|
10
|
+
source = proc.source
|
|
11
|
+
|
|
12
|
+
"\"#{method_name.to_s}(#{args})\" => Proc.new { |#{args}|\n#{source}}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def validate!(proc)
|
|
18
|
+
raise ArgumentError if proc.name.nil? || proc.name == ""
|
|
19
|
+
raise ArgumentError if proc.arguments.nil? || !proc.arguments.is_a?(Array) || proc.arguments.empty?
|
|
20
|
+
raise ArgumentError if proc.source.nil? || proc.source == ""
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def args_string(args)
|
|
24
|
+
a = args.join(", ")
|
|
25
|
+
a[0..-1]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'holidays/definition/entity/custom_method'
|
|
2
|
+
|
|
3
|
+
module Holidays
|
|
4
|
+
module Definition
|
|
5
|
+
module Parser
|
|
6
|
+
class CustomMethod
|
|
7
|
+
def initialize(validator)
|
|
8
|
+
@validator = validator
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(methods)
|
|
12
|
+
return {} if methods.nil? || methods.empty?
|
|
13
|
+
|
|
14
|
+
validate!(methods)
|
|
15
|
+
|
|
16
|
+
custom_methods = {}
|
|
17
|
+
|
|
18
|
+
methods.each do |name, pieces|
|
|
19
|
+
arguments = parse_arguments!(pieces["arguments"])
|
|
20
|
+
|
|
21
|
+
custom_methods[method_key(name, arguments)] = Entity::CustomMethod.new({
|
|
22
|
+
name: name,
|
|
23
|
+
arguments: arguments,
|
|
24
|
+
source: pieces["source"],
|
|
25
|
+
})
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
custom_methods
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_reader :validator
|
|
34
|
+
|
|
35
|
+
def validate!(methods)
|
|
36
|
+
raise ArgumentError unless methods.all? do |name, pieces|
|
|
37
|
+
validator.valid?(
|
|
38
|
+
{
|
|
39
|
+
:name => name,
|
|
40
|
+
:arguments => pieces["arguments"],
|
|
41
|
+
:source => pieces["source"]
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def parse_arguments!(arguments)
|
|
48
|
+
splitArgs = arguments.split(",")
|
|
49
|
+
parsedArgs = []
|
|
50
|
+
|
|
51
|
+
splitArgs.each do |arg|
|
|
52
|
+
parsedArgs << arg.strip
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
parsedArgs
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def method_key(name, args)
|
|
59
|
+
"#{name.to_s}(#{args_string(args)})"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def args_string(args)
|
|
63
|
+
a = args.join(", ")
|
|
64
|
+
a[0..-1]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Repository
|
|
4
|
+
class CustomMethods
|
|
5
|
+
def initialize
|
|
6
|
+
@custom_methods = {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# This performs a merge that overwrites any conflicts.
|
|
10
|
+
# While this is not ideal I'm leaving it as-is since I have no
|
|
11
|
+
# evidence of any current definitions that will cause an issue.
|
|
12
|
+
#
|
|
13
|
+
# FIXME: this should probably return an error if a method with the
|
|
14
|
+
# same ID already exists.
|
|
15
|
+
def add(new_custom_methods)
|
|
16
|
+
raise ArgumentError if new_custom_methods.nil?
|
|
17
|
+
@custom_methods.merge!(new_custom_methods)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def find(method_id)
|
|
21
|
+
raise ArgumentError if method_id.nil? || method_id.empty?
|
|
22
|
+
@custom_methods[method_id]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -41,7 +41,7 @@ module Holidays
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def definition_exists?(existing_def, target_def)
|
|
44
|
-
existing_def[:name] == target_def[:name] && existing_def[:wday] == target_def[:wday] && existing_def[:mday] == target_def[:mday] && existing_def[:week] == target_def[:week] && existing_def[:
|
|
44
|
+
existing_def[:name] == target_def[:name] && existing_def[:wday] == target_def[:wday] && existing_def[:mday] == target_def[:mday] && existing_def[:week] == target_def[:week] && existing_def[:function] == target_def[:function] && existing_def[:type] == target_def[:type] && existing_def[:observed] == target_def[:observed] && existing_def[:year_ranges] == target_def[:year_ranges]
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|