|
| 1 | +# ADR 1: Custom Methods Format Change |
| 2 | + |
| 3 | +## Context |
| 4 | + |
| 5 | +We would like these definitions to be usable by any language. The original `holidays` project was written purely in `ruby` but the definitions were generally plain `YAML`. |
| 6 | + |
| 7 | +The issue is that `ruby` has been sprinkled into the otherwise plain `YAML` when it made sense with no plan for use outside of `ruby`. This makes sense when you are never planning on using the `YAML` files in other languages. |
| 8 | + |
| 9 | +Over time we have [been working](https://github.com/holidays/definitions/issues/7) to make the syntax more generic so that other language implementations could consume them. The last hurdle was custom methods. |
| 10 | + |
| 11 | +An example of the original format: |
| 12 | + |
| 13 | +```yaml |
| 14 | +methods: |
| 15 | + ca_victoria_day: |
| 16 | + arguments: year |
| 17 | + source: | |
| 18 | + date = Date.civil(year, 5, 24) |
| 19 | + if date.wday > 1 |
| 20 | + date -= (date.wday - 1) |
| 21 | + elsif date.wday == 0 |
| 22 | + date -= 6 |
| 23 | + end |
| 24 | +
|
| 25 | + date |
| 26 | +``` |
| 27 | +
|
| 28 | +As you can see the actual function is just plain `ruby`. |
| 29 | + |
| 30 | +After lots of trial and error I have decided that I cannot see a generic format for this logic that would satisfy all use cases for existing custom methods in our definitions. While some custom methods are relatively simple `if/else` statements there are many that are much more complicated. |
| 31 | + |
| 32 | +An example of a 'complicated' custom method from the `ch` (Swiss) region: |
| 33 | + |
| 34 | +```yaml |
| 35 | + ch_vd_lundi_du_jeune_federal: |
| 36 | + # Monday after the third Sunday of September |
| 37 | + arguments: year |
| 38 | + ruby: | |
| 39 | + date = Date.civil(year,9,1) |
| 40 | + # Find the first Sunday of September |
| 41 | + until date.wday.eql? 0 do |
| 42 | + date += 1 |
| 43 | + end |
| 44 | + # There are 15 days between the first Sunday |
| 45 | + # and the Monday after the third Sunday |
| 46 | + date + 15 |
| 47 | +``` |
| 48 | + |
| 49 | +The logic itself is not hard to follow but coming up with a generic way to phrase this seems like a complex problem. Every attempt that was made devolved into very complex parsers of nested `YAML` so that we correctly handled each new edge case that appeared. It was very slow going and the complexity was growing and growing. |
| 50 | + |
| 51 | +Additionally, having a complex `YAML` syntax for custom methods would require each downstream repository to implement the 'standard'. That seems pretty scary to think about maintaining. |
| 52 | + |
| 53 | +The other option is to just make each future language provide their own implementations. |
| 54 | + |
| 55 | +## Decision |
| 56 | + |
| 57 | +The decision is to simply require language-specific implementations of custom methods. Since all custom methods are currently in `ruby` we are changing every `source` field to `ruby`. In the future new languages will need to provide their own implementations. For example, we could add a `golang` or `swift` section next to the existing `ruby` section. |
| 58 | + |
| 59 | +There are three significant advantages: |
| 60 | + |
| 61 | + - It is very easy to understand |
| 62 | + - All holidays using custom methods have tests so each downstream project will have built-in protection in case a bug is introduced in only one language implementation |
| 63 | + - It is very easy to implement for the current `ruby` implementation (which is our only project currently) |
| 64 | + |
| 65 | +There are significant downsides: |
| 66 | + |
| 67 | + - Possible divergence between languages due to separate implementations, causing confusion and frustration |
| 68 | + - More pressure on maintainers to handle the various implementations, ensuring they can build without issues when new custom methods are added |
| 69 | + - Confusion for new contributors who may only be comfortable in a single language |
| 70 | + - New downstream languages will have a higher hurdle to overcome since they will need to implement the existing logic in their own language |
| 71 | + |
| 72 | +In the end I don't want to hold things up because of _possible_ new language implementations that might show up in the future. I personally want to create a new `golang` version of `holidays` but beyond that maybe no one else will consume these definitions! |
| 73 | + |
| 74 | +If the `holidays` projects become wildly popular in the future and this becomes a huge problem then I can address it with the (presumably huge) community to find a solution. |
| 75 | + |
| 76 | +## Consequences |
| 77 | + |
| 78 | +We might lose contributions due to confusion or fear. |
| 79 | + |
| 80 | +We might burn out maintainers if the juggling of languages becomes too much of a burden. |
| 81 | + |
| 82 | +This puts more pressure for the completion of an updated [test framework](https://github.com/holidays/definitions/issues/42) for downstream repositories. |
| 83 | + |
| 84 | +## Status |
| 85 | + |
| 86 | +Accepted. |
0 commit comments