holidays 2.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +22 -8
  4. data/Rakefile +26 -8
  5. data/bin/console +7 -0
  6. data/bin/setup +5 -0
  7. data/{data → definitions}/SYNTAX.rdoc +0 -0
  8. data/{data → definitions}/ar.yaml +0 -0
  9. data/{data → definitions}/at.yaml +1 -1
  10. data/{data → definitions}/au.yaml +80 -51
  11. data/{data → definitions}/be.yaml +0 -0
  12. data/{data → definitions}/br.yaml +0 -0
  13. data/{data → definitions}/ca.yaml +0 -0
  14. data/{data → definitions}/ch.yaml +0 -0
  15. data/{data → definitions}/cl.yaml +0 -0
  16. data/{data → definitions}/cr.yaml +0 -0
  17. data/{data → definitions}/cz.yaml +0 -0
  18. data/{data → definitions}/de.yaml +25 -34
  19. data/{data → definitions}/dk.yaml +0 -0
  20. data/{data → definitions}/ecb_target.yaml +2 -2
  21. data/{data → definitions}/el.yaml +0 -0
  22. data/{data → definitions}/es.yaml +37 -26
  23. data/{data → definitions}/federal_reserve.yaml +0 -0
  24. data/{data → definitions}/fedex.yaml +9 -3
  25. data/{data → definitions}/fi.yaml +0 -0
  26. data/{data → definitions}/fr.yaml +0 -0
  27. data/{data → definitions}/gb.yaml +19 -19
  28. data/{data → definitions}/hr.yaml +0 -0
  29. data/{data → definitions}/hu.yaml +1 -1
  30. data/{data → definitions}/ie.yaml +0 -0
  31. data/{data → definitions}/index.yaml +0 -0
  32. data/{data → definitions}/is.yaml +0 -0
  33. data/{data → definitions}/it.yaml +0 -0
  34. data/{data → definitions}/jp.yaml +3 -3
  35. data/{data → definitions}/li.yaml +3 -3
  36. data/{data → definitions}/lt.yaml +0 -0
  37. data/{data → definitions}/ma.yaml +0 -0
  38. data/{data → definitions}/mx.yaml +0 -0
  39. data/{data → definitions}/nerc.yaml +0 -0
  40. data/{data → definitions}/nl.yaml +0 -0
  41. data/{data → definitions}/no.yaml +0 -0
  42. data/{data → definitions}/north_america_informal.yaml +0 -0
  43. data/{data → definitions}/nyse.yaml +0 -0
  44. data/{data → definitions}/nz.yaml +21 -10
  45. data/{data → definitions}/ph.yaml +0 -0
  46. data/{data → definitions}/pl.yaml +0 -0
  47. data/{data → definitions}/pt.yaml +0 -0
  48. data/{data → definitions}/ro.yaml +1 -2
  49. data/{data → definitions}/se.yaml +0 -0
  50. data/{data → definitions}/sg.yaml +0 -0
  51. data/{data → definitions}/si.yaml +6 -6
  52. data/{data → definitions}/sk.yaml +0 -0
  53. data/{data → definitions}/united_nations.yaml +0 -0
  54. data/{data → definitions}/ups.yaml +17 -11
  55. data/{data → definitions}/us.yaml +20 -15
  56. data/{data → definitions}/ve.yaml +0 -0
  57. data/{data → definitions}/vi.yaml +0 -0
  58. data/{data → definitions}/za.yaml +0 -0
  59. data/holidays.gemspec +2 -1
  60. data/lib/generated_definitions/MANIFEST +56 -0
  61. data/lib/generated_definitions/REGIONS.rb +4 -0
  62. data/lib/{holidays → generated_definitions}/ar.rb +2 -2
  63. data/lib/{holidays → generated_definitions}/at.rb +2 -2
  64. data/lib/{holidays → generated_definitions}/au.rb +19 -10
  65. data/lib/{holidays → generated_definitions}/be.rb +2 -2
  66. data/lib/{holidays → generated_definitions}/br.rb +2 -2
  67. data/lib/{holidays → generated_definitions}/ca.rb +2 -2
  68. data/lib/{holidays → generated_definitions}/ch.rb +2 -2
  69. data/lib/{holidays → generated_definitions}/cl.rb +2 -2
  70. data/lib/{holidays → generated_definitions}/cr.rb +2 -2
  71. data/lib/{holidays → generated_definitions}/cz.rb +2 -2
  72. data/lib/{holidays → generated_definitions}/de.rb +5 -4
  73. data/lib/{holidays → generated_definitions}/dk.rb +2 -2
  74. data/lib/{holidays → generated_definitions}/ecb_target.rb +2 -2
  75. data/lib/{holidays → generated_definitions}/el.rb +2 -2
  76. data/lib/{holidays → generated_definitions}/es.rb +6 -6
  77. data/lib/{holidays → generated_definitions}/europe.rb +7 -6
  78. data/lib/{holidays → generated_definitions}/fed_ex.rb +8 -3
  79. data/lib/{holidays → generated_definitions}/federal_reserve.rb +2 -2
  80. data/lib/{holidays → generated_definitions}/fi.rb +2 -2
  81. data/lib/{holidays → generated_definitions}/fr.rb +2 -2
  82. data/lib/{holidays → generated_definitions}/gb.rb +2 -2
  83. data/lib/{holidays → generated_definitions}/hr.rb +2 -2
  84. data/lib/{holidays → generated_definitions}/hu.rb +2 -2
  85. data/lib/{holidays → generated_definitions}/ie.rb +2 -2
  86. data/lib/{holidays → generated_definitions}/is.rb +2 -2
  87. data/lib/{holidays → generated_definitions}/it.rb +2 -2
  88. data/lib/{holidays → generated_definitions}/jp.rb +5 -5
  89. data/lib/{holidays → generated_definitions}/li.rb +2 -2
  90. data/lib/{holidays → generated_definitions}/lt.rb +2 -2
  91. data/lib/{holidays → generated_definitions}/ma.rb +2 -2
  92. data/lib/{holidays → generated_definitions}/mx.rb +2 -2
  93. data/lib/{holidays → generated_definitions}/nerc.rb +2 -2
  94. data/lib/{holidays → generated_definitions}/nl.rb +2 -2
  95. data/lib/{holidays → generated_definitions}/no.rb +2 -2
  96. data/lib/{holidays → generated_definitions}/north_america.rb +8 -3
  97. data/lib/{holidays → generated_definitions}/nyse.rb +2 -2
  98. data/lib/{holidays → generated_definitions}/nz.rb +5 -5
  99. data/lib/{holidays → generated_definitions}/ph.rb +2 -2
  100. data/lib/{holidays → generated_definitions}/pl.rb +2 -2
  101. data/lib/{holidays → generated_definitions}/pt.rb +2 -2
  102. data/lib/{holidays → generated_definitions}/ro.rb +2 -2
  103. data/lib/{holidays → generated_definitions}/scandinavia.rb +2 -2
  104. data/lib/{holidays → generated_definitions}/se.rb +2 -2
  105. data/lib/{holidays → generated_definitions}/sg.rb +2 -2
  106. data/lib/{holidays → generated_definitions}/si.rb +2 -2
  107. data/lib/{holidays → generated_definitions}/sk.rb +2 -2
  108. data/lib/{holidays → generated_definitions}/united_nations.rb +2 -2
  109. data/lib/{holidays → generated_definitions}/ups.rb +8 -3
  110. data/lib/{holidays → generated_definitions}/us.rb +8 -3
  111. data/lib/{holidays → generated_definitions}/ve.rb +2 -2
  112. data/lib/{holidays → generated_definitions}/vi.rb +2 -2
  113. data/lib/{holidays → generated_definitions}/za.rb +2 -2
  114. data/lib/holidays.rb +120 -665
  115. data/lib/holidays/core_extensions/date.rb +39 -0
  116. data/lib/holidays/date_calculator/day_of_month.rb +68 -0
  117. data/lib/holidays/date_calculator/easter.rb +58 -0
  118. data/lib/holidays/date_calculator/weekend_modifier.rb +49 -0
  119. data/lib/holidays/date_calculator_factory.rb +21 -0
  120. data/lib/holidays/definition/context/generator.rb +216 -0
  121. data/lib/holidays/definition/context/merger.rb +26 -0
  122. data/lib/holidays/definition/repository/cache.rb +33 -0
  123. data/lib/holidays/definition/repository/holidays_by_month.rb +49 -0
  124. data/lib/holidays/definition/repository/proc_cache.rb +36 -0
  125. data/lib/holidays/definition/repository/regions.rb +36 -0
  126. data/lib/holidays/definition/validator/region.rb +45 -0
  127. data/lib/holidays/definition_factory.rb +50 -0
  128. data/lib/holidays/option/context/parse_options.rb +96 -0
  129. data/lib/holidays/option_factory.rb +14 -0
  130. data/lib/holidays/use_case/context/between.rb +105 -0
  131. data/lib/holidays/use_case/context/dates_driver_builder.rb +64 -0
  132. data/lib/holidays/use_case_factory.rb +20 -0
  133. data/lib/holidays/version.rb +1 -1
  134. data/test/defs/test_defs_ar.rb +1 -1
  135. data/test/defs/test_defs_at.rb +2 -2
  136. data/test/defs/test_defs_au.rb +61 -43
  137. data/test/defs/test_defs_be.rb +1 -1
  138. data/test/defs/test_defs_br.rb +1 -1
  139. data/test/defs/test_defs_ca.rb +1 -1
  140. data/test/defs/test_defs_ch.rb +1 -1
  141. data/test/defs/test_defs_cl.rb +1 -1
  142. data/test/defs/test_defs_cr.rb +1 -1
  143. data/test/defs/test_defs_cz.rb +1 -1
  144. data/test/defs/test_defs_de.rb +18 -30
  145. data/test/defs/test_defs_dk.rb +1 -1
  146. data/test/defs/test_defs_ecb_target.rb +3 -3
  147. data/test/defs/test_defs_el.rb +1 -1
  148. data/test/defs/test_defs_es.rb +36 -25
  149. data/test/defs/test_defs_europe.rb +82 -84
  150. data/test/defs/test_defs_fed_ex.rb +4 -1
  151. data/test/defs/test_defs_federal_reserve.rb +1 -1
  152. data/test/defs/test_defs_fi.rb +1 -1
  153. data/test/defs/test_defs_fr.rb +1 -1
  154. data/test/defs/test_defs_gb.rb +20 -20
  155. data/test/defs/test_defs_hr.rb +1 -1
  156. data/test/defs/test_defs_hu.rb +2 -2
  157. data/test/defs/test_defs_ie.rb +1 -1
  158. data/test/defs/test_defs_is.rb +1 -1
  159. data/test/defs/test_defs_it.rb +1 -1
  160. data/test/defs/test_defs_jp.rb +1 -1
  161. data/test/defs/test_defs_li.rb +4 -4
  162. data/test/defs/test_defs_lt.rb +1 -1
  163. data/test/defs/test_defs_ma.rb +1 -1
  164. data/test/defs/test_defs_mx.rb +1 -1
  165. data/test/defs/test_defs_nerc.rb +1 -1
  166. data/test/defs/test_defs_nl.rb +1 -1
  167. data/test/defs/test_defs_no.rb +1 -1
  168. data/test/defs/test_defs_north_america.rb +5 -3
  169. data/test/defs/test_defs_nyse.rb +1 -1
  170. data/test/defs/test_defs_nz.rb +19 -9
  171. data/test/defs/test_defs_ph.rb +1 -1
  172. data/test/defs/test_defs_pl.rb +1 -1
  173. data/test/defs/test_defs_pt.rb +1 -1
  174. data/test/defs/test_defs_ro.rb +2 -3
  175. data/test/defs/test_defs_scandinavia.rb +1 -1
  176. data/test/defs/test_defs_se.rb +1 -1
  177. data/test/defs/test_defs_sg.rb +1 -1
  178. data/test/defs/test_defs_si.rb +7 -7
  179. data/test/defs/test_defs_sk.rb +1 -1
  180. data/test/defs/test_defs_united_nations.rb +1 -1
  181. data/test/defs/test_defs_ups.rb +5 -2
  182. data/test/defs/test_defs_us.rb +5 -3
  183. data/test/defs/test_defs_ve.rb +1 -1
  184. data/test/defs/test_defs_vi.rb +1 -1
  185. data/test/defs/test_defs_za.rb +1 -1
  186. data/test/{test_date.rb → holidays/core_extensions/test_date.rb} +8 -2
  187. data/test/holidays/date_calculator/test_day_of_month.rb +27 -0
  188. data/test/holidays/date_calculator/test_easter.rb +29 -0
  189. data/test/holidays/date_calculator/test_weekend_modifier.rb +33 -0
  190. data/test/holidays/definition/context/test_generator.rb +84 -0
  191. data/test/holidays/definition/context/test_merger.rb +22 -0
  192. data/test/holidays/definition/repository/test_cache.rb +82 -0
  193. data/test/holidays/definition/repository/test_holidays_by_month.rb +187 -0
  194. data/test/holidays/definition/repository/test_proc_cache.rb +29 -0
  195. data/test/holidays/definition/repository/test_regions.rb +86 -0
  196. data/test/holidays/definition/validator/test_region.rb +50 -0
  197. data/test/holidays/option/context/test_parse_options.rb +69 -0
  198. data/test/holidays/test_date_calculator_factory.rb +21 -0
  199. data/test/holidays/test_definition_factory.rb +34 -0
  200. data/test/holidays/test_option_factory.rb +9 -0
  201. data/test/holidays/test_use_case_factory.rb +13 -0
  202. data/test/holidays/use_case/context/test_between.rb +75 -0
  203. data/test/holidays/use_case/context/test_dates_driver_builder.rb +91 -0
  204. data/test/test_all_regions.rb +14 -3
  205. data/test/test_helper.rb +2 -1
  206. data/test/test_holidays.rb +19 -24
  207. data/test/test_holidays_between.rb +85 -0
  208. data/test/test_multiple_regions.rb +2 -2
  209. data/test/test_parse_definitions.rb +10 -4
  210. metadata +181 -111
  211. data/.coveralls.yml +0 -1
  212. data/lib/holidays/MANIFEST +0 -55
@@ -0,0 +1,39 @@
1
+ module Holidays
2
+ module CoreExtensions
3
+ module Date
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ # Get holidays on the current date.
9
+ #
10
+ # Returns an array of hashes or nil. See Holidays#between for options
11
+ # and the output format.
12
+ #
13
+ # Date.civil('2008-01-01').holidays(:ca_)
14
+ # => [{:name => 'New Year\'s Day',...}]
15
+ #
16
+ # Also available via Holidays#on.
17
+ def holidays(*options)
18
+ Holidays.on(self, options)
19
+ end
20
+
21
+ # Check if the current date is a holiday.
22
+ #
23
+ # Returns true or false.
24
+ #
25
+ # Date.civil('2008-01-01').holiday?(:ca)
26
+ # => true
27
+ def holiday?(*options)
28
+ holidays = self.holidays(options)
29
+ holidays && !holidays.empty?
30
+ end
31
+
32
+ module ClassMethods
33
+ def calculate_mday(year, month, week, wday)
34
+ Holidays::DateCalculatorFactory.day_of_month_calculator.call(year, month, week, wday)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,68 @@
1
+ module Holidays
2
+ module DateCalculator
3
+ # Calculate day of the month based on the week number and the day of the
4
+ # week.
5
+ #
6
+ # ==== Parameters
7
+ # [<tt>year</tt>] Integer.
8
+ # [<tt>month</tt>] Integer from 1-12.
9
+ # [<tt>week</tt>] One of <tt>:first</tt>, <tt>:second</tt>, <tt>:third</tt>,
10
+ # <tt>:fourth</tt>, <tt>:fifth</tt> or <tt>:last</tt>.
11
+ # [<tt>wday</tt>] Day of the week as an integer from 0 (Sunday) to 6
12
+ # (Saturday) or as a symbol (e.g. <tt>:monday</tt>).
13
+ #
14
+ # Returns an integer.
15
+ #
16
+ # ===== Examples
17
+ # First Monday of January, 2008:
18
+ # Date.calculate_mday(2008, 1, :first, :monday)
19
+ # => 7
20
+ #
21
+ # Third Thursday of December, 2008:
22
+ # Date.calculate_mday(2008, 12, :third, :thursday)
23
+ # => 18
24
+ #
25
+ # Last Monday of January, 2008:
26
+ # Date.calculate_mday(2008, 1, :last, 1)
27
+ # => 28
28
+ #--
29
+ # see http://www.irt.org/articles/js050/index.htm
30
+ class DayOfMonth
31
+ def call(year, month, week, wday)
32
+ raise ArgumentError, "Week parameter must be one of Holidays::WEEKS (provided #{week})." unless weeks.include?(week) or weeks.has_value?(week)
33
+
34
+ unless wday.kind_of?(Numeric) and wday.between?(0,6) or day_symbols.index(wday)
35
+ raise ArgumentError, "Wday parameter must be an integer between 0 and 6 or one of Holidays::DAY_SYMBOLS."
36
+ end
37
+
38
+ week = weeks[week] if week.kind_of?(Symbol)
39
+ wday = day_symbols.index(wday) if wday.kind_of?(Symbol)
40
+
41
+ # :first, :second, :third, :fourth or :fifth
42
+ if week > 0
43
+ return ((week - 1) * 7) + 1 + ((wday - Date.civil(year, month,(week-1)*7 + 1).wday) % 7)
44
+ end
45
+
46
+ days = month_lengths[month-1]
47
+
48
+ days = 29 if month == 2 and Date.leap?(year)
49
+
50
+ return days - ((Date.civil(year, month, days).wday - wday + 7) % 7) - (7 * (week.abs - 1))
51
+ end
52
+
53
+ private
54
+
55
+ def weeks
56
+ Holidays::WEEKS
57
+ end
58
+
59
+ def day_symbols
60
+ Holidays::DAY_SYMBOLS
61
+ end
62
+
63
+ def month_lengths
64
+ Holidays::MONTH_LENGTHS
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,58 @@
1
+ module Holidays
2
+ module DateCalculator
3
+ class Easter
4
+ # Get the date of Easter Sunday in a given year. From Easter Sunday, it is
5
+ # possible to calculate many traditional holidays in Western countries.
6
+ # Returns a Date object.
7
+ def calculate_easter_for(year)
8
+ y = year
9
+ a = y % 19
10
+ b = y / 100
11
+ c = y % 100
12
+ d = b / 4
13
+ e = b % 4
14
+ f = (b + 8) / 25
15
+ g = (b - f + 1) / 3
16
+ h = (19 * a + b - d - g + 15) % 30
17
+ i = c / 4
18
+ k = c % 4
19
+ l = (32 + 2 * e + 2 * i - h - k) % 7
20
+ m = (a + 11 * h + 22 * l) / 451
21
+
22
+ month = (h + l - 7 * m + 114) / 31
23
+ day = ((h + l - 7 * m + 114) % 31) + 1
24
+
25
+ Date.civil(year, month, day)
26
+ end
27
+
28
+ # A method to calculate the orthodox easter date, returns date in the Gregorian (western) calendar
29
+ # Safe until appr. 4100 AD, when one leap day will be removed.
30
+ # Returns a Date object.
31
+ def calculate_orthodox_easter_for(year)
32
+ y = year
33
+ g = y % 19
34
+ i = (19 * g + 15) % 30
35
+ j = (year + year/4 + i) % 7
36
+ j_month = 3 + (i - j + 40) / 44
37
+ j_day = i - j + 28 - 31 * (j_month / 4)
38
+ j_date = Date.civil(year, j_month, j_day)
39
+
40
+ case
41
+ # up until 1582, julian and gregorian easter dates were identical
42
+ when year <= 1582
43
+ offset = 0
44
+ # between the years 1583 and 1699 10 days are added to the julian day count
45
+ when (year >= 1583 and year <= 1699)
46
+ offset = 10
47
+ # after 1700, 1 day is added for each century, except if the century year is exactly divisible by 400 (in which case no days are added).
48
+ # Safe until 4100 AD, when one leap day will be removed.
49
+ when year >= 1700
50
+ offset = (year - 1700).divmod(100)[0] + ((year - year.divmod(100)[1]).divmod(400)[1] == 0 ? 0 : 1) - (year - year.divmod(100)[1] - 1700).divmod(400)[0] + 10
51
+ end
52
+
53
+ # add offset to the julian day
54
+ return Date.jd(j_date.jd + offset)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ require 'date'
2
+
3
+ module Holidays
4
+ module DateCalculator
5
+ class WeekendModifier
6
+ # Move date to Monday if it occurs on a Saturday on Sunday.
7
+ # Used as a callback function.
8
+ def to_monday_if_weekend(date)
9
+ date += 1 if date.wday == 0
10
+ date += 2 if date.wday == 6
11
+ date
12
+ end
13
+
14
+ # Move date to Monday if it occurs on a Sunday.
15
+ # Used as a callback function.
16
+ def to_monday_if_sunday(date)
17
+ date += 1 if date.wday == 0
18
+ date
19
+ end
20
+
21
+ # Move Boxing Day if it falls on a weekend, leaving room for Christmas.
22
+ # Used as a callback function.
23
+ def to_weekday_if_boxing_weekend(date)
24
+ if date.wday == 6 || date.wday == 0
25
+ date += 2
26
+ elsif date.wday == 1
27
+ date += 1
28
+ end
29
+
30
+ date
31
+ end
32
+
33
+ # Call to_weekday_if_boxing_weekend but first get date based on year
34
+ # Used as a callback function.
35
+ def to_weekday_if_boxing_weekend_from_year(year)
36
+ to_weekday_if_boxing_weekend(Date.civil(year, 12, 26))
37
+ end
38
+
39
+ # Move date to Monday if it occurs on a Sunday or to Friday if it occurs on a
40
+ # Saturday.
41
+ # Used as a callback function.
42
+ def to_weekday_if_weekend(date)
43
+ date += 1 if date.wday == 0
44
+ date -= 1 if date.wday == 6
45
+ date
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ require 'holidays/date_calculator/easter'
2
+ require 'holidays/date_calculator/weekend_modifier'
3
+ require 'holidays/date_calculator/day_of_month'
4
+
5
+ module Holidays
6
+ module DateCalculatorFactory
7
+ class << self
8
+ def easter_calculator
9
+ DateCalculator::Easter.new
10
+ end
11
+
12
+ def weekend_modifier
13
+ DateCalculator::WeekendModifier.new
14
+ end
15
+
16
+ def day_of_month_calculator
17
+ DateCalculator::DayOfMonth.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,216 @@
1
+ require 'yaml'
2
+
3
+ module Holidays
4
+ module Definition
5
+ module Context
6
+ class Generator
7
+
8
+ def parse_definition_files(files)
9
+ raise ArgumentError, "Must have at least one file to parse" if files.nil? || files.empty?
10
+
11
+ all_regions = []
12
+ all_rules_by_month = {}
13
+ all_custom_methods = {}
14
+ all_tests = []
15
+
16
+ files.flatten!
17
+
18
+ files.each do |file|
19
+ definition_file = YAML.load_file(file)
20
+
21
+ regions, rules_by_month = parse_month_definitions(definition_file['months'])
22
+
23
+ all_regions << regions.flatten
24
+
25
+ all_rules_by_month.merge!(rules_by_month) { |month, existing, new|
26
+ existing << new
27
+ existing.flatten!
28
+ }
29
+
30
+ custom_methods = parse_method_definitions(definition_file['methods'])
31
+ all_custom_methods.merge!(custom_methods)
32
+
33
+ all_tests << parse_test_definitions(definition_file['tests'])
34
+ end
35
+
36
+ all_regions.flatten!.uniq!
37
+
38
+ [all_regions, all_rules_by_month, all_custom_methods, all_tests]
39
+ end
40
+
41
+ def generate_definition_source(module_name, files, regions, rules_by_month, custom_methods, tests)
42
+ month_strings = generate_month_definition_strings(rules_by_month)
43
+
44
+ # Build the custom methods string
45
+ custom_method_string = ''
46
+ custom_methods.each do |key, code|
47
+ custom_method_string << code + "\n\n"
48
+ end
49
+
50
+ module_src = generate_module_src(module_name, files, regions, month_strings, custom_method_string)
51
+ test_src = generate_test_src(module_name, files, tests)
52
+
53
+ return module_src, test_src || ''
54
+ end
55
+
56
+ private
57
+
58
+ def parse_month_definitions(month_definitions)
59
+ regions = []
60
+ rules_by_month = {}
61
+
62
+ if month_definitions
63
+ month_definitions.each do |month, definitions|
64
+ rules_by_month[month] = [] unless rules_by_month[month]
65
+ definitions.each do |definition|
66
+ rule = {}
67
+
68
+ definition.each do |key, val|
69
+ rule[key.to_sym] = val
70
+ end
71
+
72
+ rule[:regions] = rule[:regions].collect { |r| r.to_sym }
73
+
74
+ regions << rule[:regions]
75
+
76
+ exists = false
77
+ rules_by_month[month].each do |ex|
78
+ if ex[:name] == rule[:name] and ex[:wday] == rule[:wday] and ex[:mday] == rule[:mday] and ex[:week] == rule[:week] and ex[:type] == rule[:type] and ex[:function] == rule[:function] and ex[:observed] == rule[:observed]
79
+ ex[:regions] << rule[:regions].flatten
80
+ exists = true
81
+ end
82
+ end
83
+
84
+ unless exists
85
+ rules_by_month[month] << rule
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ [regions, rules_by_month]
92
+ end
93
+
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
+ def parse_test_definitions(tests)
107
+ test_strings = []
108
+
109
+ if tests
110
+ test_strings << tests
111
+ end
112
+
113
+ test_strings
114
+ end
115
+
116
+ def generate_month_definition_strings(rules_by_month)
117
+ month_strings = []
118
+
119
+ rules_by_month.each do |month, rules|
120
+ month_string = " #{month.to_s} => ["
121
+ rule_strings = []
122
+ rules.each do |rule|
123
+ string = '{'
124
+ if rule[:mday]
125
+ string << ":mday => #{rule[:mday]}, "
126
+ elsif rule[:function]
127
+ string << ":function => lambda { |year| Holidays.#{rule[:function]} }, "
128
+ string << ":function_id => \"#{rule[:function].to_s}\", "
129
+ else
130
+ string << ":wday => #{rule[:wday]}, :week => #{rule[:week]}, "
131
+ end
132
+
133
+ if rule[:observed]
134
+ string << ":observed => lambda { |date| Holidays.#{rule[:observed]}(date) }, "
135
+ string << ":observed_id => \"#{rule[:observed].to_s}\", "
136
+ end
137
+
138
+ if rule[:type]
139
+ string << ":type => :#{rule[:type]}, "
140
+ end
141
+
142
+ # shouldn't allow the same region twice
143
+ string << ":name => \"#{rule[:name]}\", :regions => [:" + rule[:regions].uniq.join(', :') + "]}"
144
+ rule_strings << string
145
+ end
146
+ month_string << rule_strings.join(",\n ") + "]"
147
+ month_strings << month_string
148
+ end
149
+
150
+ return month_strings
151
+ end
152
+
153
+ def generate_module_src(module_name, files, regions, month_strings, custom_methods)
154
+ module_src = ""
155
+
156
+ module_src =<<-EOM
157
+ # encoding: utf-8
158
+ module Holidays
159
+ # This file is generated by the Ruby Holidays gem.
160
+ #
161
+ # Definitions loaded: #{files.join(', ')}
162
+ #
163
+ # To use the definitions in this file, load it right after you load the
164
+ # Holiday gem:
165
+ #
166
+ # require 'holidays'
167
+ # require '#{DEFINITIONS_PATH}/#{module_name.to_s.downcase}'
168
+ #
169
+ # All the definitions are available at https://github.com/alexdunae/holidays
170
+ module #{module_name.to_s.upcase} # :nodoc:
171
+ def self.defined_regions
172
+ [:#{regions.join(', :')}]
173
+ end
174
+
175
+ def self.holidays_by_month
176
+ {
177
+ #{month_strings.join(",\n")}
178
+ }
179
+ end
180
+ end
181
+
182
+ #{custom_methods}
183
+ end
184
+
185
+ Holidays.merge_defs(Holidays::#{module_name.to_s.upcase}.defined_regions, Holidays::#{module_name.to_s.upcase}.holidays_by_month)
186
+ EOM
187
+
188
+ return module_src
189
+ end
190
+
191
+ def generate_test_src(module_name, files, tests)
192
+ unless tests.empty?
193
+ test_src = ""
194
+
195
+ test_src =<<-EndOfTests
196
+ # encoding: utf-8
197
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
198
+
199
+ # This file is generated by the Ruby Holiday gem.
200
+ #
201
+ # Definitions loaded: #{files.join(', ')}
202
+ class #{module_name.to_s.capitalize}DefinitionTests < Test::Unit::TestCase # :nodoc:
203
+
204
+ def test_#{module_name.to_s.downcase}
205
+ #{tests.join("\n\n")}
206
+ end
207
+ end
208
+ EndOfTests
209
+ end
210
+
211
+ return test_src
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end