holidays 3.3.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|