holidays-rest 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e0dac7d315f912c59e080ba784b8cf3e468843d3899d7a3f6a9172f20775f649
4
+ data.tar.gz: 153118ea22cada7f0e902ed77f024c7ca7b3cd7fa6b08309beea5a204a30e4dc
5
+ SHA512:
6
+ metadata.gz: 5937099897ea4b539d0c5a0529009f93685c3d5b87bc31b3e67da8977c5b9d082e869312afcccf7b67f9310549aa93ac89ae2a2d77dbd0a134e785ac01b07c0c
7
+ data.tar.gz: bea79bf17ce3e3b2872b76c76e0c6f3265b848e91b195b96968675a19c2e3b4b9d8767435f516fdac1e9b1cd5d26ced856418b4865908d8a9c070cb2e6544b80
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # holidays.rest Ruby SDK
2
+
3
+ Official Ruby SDK for the [holidays.rest](https://www.holidays.rest) API.
4
+
5
+ ## Requirements
6
+
7
+ - Ruby 3.2+
8
+ - Zero runtime dependencies — uses only the standard library (`net/http`, `json`, `uri`)
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ gem install holidays-rest
14
+ ```
15
+
16
+ Or in your `Gemfile`:
17
+
18
+ ```ruby
19
+ gem "holidays-rest"
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```ruby
25
+ require "holidays_rest"
26
+
27
+ client = HolidaysRest::Client.new(api_key: "YOUR_API_KEY")
28
+
29
+ holidays = client.holidays(country: "US", year: 2024)
30
+ holidays.each { |h| puts "#{h.date} — #{h.name}" }
31
+ ```
32
+
33
+ Get an API key at [holidays.rest/dashboard](https://www.holidays.rest/dashboard).
34
+
35
+ ---
36
+
37
+ ## API
38
+
39
+ ### `HolidaysRest::Client.new`
40
+
41
+ ```ruby
42
+ client = HolidaysRest::Client.new(
43
+ api_key: "YOUR_API_KEY", # required
44
+ open_timeout: 5, # optional, default 5s
45
+ read_timeout: 15, # optional, default 15s
46
+ base_url: "https://..." # optional, override for testing
47
+ )
48
+ ```
49
+
50
+ ---
51
+
52
+ ### `client.holidays(...)` → `Array<Holiday>`
53
+
54
+ | Parameter | Type | Required | Description |
55
+ |------------|-----------------------|----------|--------------------------------------------------|
56
+ | `country` | `String` | yes | ISO 3166 alpha-2 code (e.g. `"US"`) |
57
+ | `year` | `Integer \| String` | yes | Four-digit year (e.g. `2024`) |
58
+ | `month` | `Integer \| String` | no | Month filter (1–12) |
59
+ | `day` | `Integer \| String` | no | Day filter (1–31) |
60
+ | `type` | `String \| Array` | no | `"religious"`, `"national"`, `"local"` |
61
+ | `religion` | `Integer \| Array` | no | Religion code(s) 1–11 |
62
+ | `region` | `String \| Array` | no | Subdivision code(s) — from `#country` |
63
+ | `lang` | `String \| Array` | no | Language code(s) — from `#languages` |
64
+ | `response` | `String` | no | `"json"` (default) \| `"xml"` \| `"yaml"` \| `"csv"` |
65
+
66
+ ```ruby
67
+ # All US holidays in 2024
68
+ client.holidays(country: "US", year: 2024)
69
+
70
+ # National holidays only
71
+ client.holidays(country: "DE", year: 2024, type: "national")
72
+
73
+ # Multiple types
74
+ client.holidays(country: "TR", year: 2024, type: ["national", "religious"])
75
+
76
+ # Filter by month and day
77
+ client.holidays(country: "GB", year: 2024, month: 12, day: 25)
78
+
79
+ # Specific region
80
+ client.holidays(country: "US", year: 2024, region: "US-CA")
81
+
82
+ # Multiple regions
83
+ client.holidays(country: "US", year: 2024, region: ["US-CA", "US-NY"])
84
+ ```
85
+
86
+ ---
87
+
88
+ ### `client.countries` → `Array<Country>`
89
+
90
+ ```ruby
91
+ client.countries.each { |c| puts "#{c.alpha2} — #{c.name}" }
92
+ ```
93
+
94
+ ---
95
+
96
+ ### `client.country(country_code)` → `Country`
97
+
98
+ Returns country details including subdivision codes usable as `region:` filters.
99
+
100
+ ```ruby
101
+ us = client.country("US")
102
+ us.subdivisions.each { |s| puts "#{s.code} — #{s.name}" }
103
+ ```
104
+
105
+ ---
106
+
107
+ ### `client.languages` → `Array<Language>`
108
+
109
+ ```ruby
110
+ client.languages.each { |l| puts "#{l.code} — #{l.name}" }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Models
116
+
117
+ All responses are deserialized into immutable `Data` objects.
118
+
119
+ ```ruby
120
+ Holiday
121
+ .country_code # String — ISO 3166 alpha-2, e.g. "DE"
122
+ .country_name # String — e.g. "Germany"
123
+ .date # String — ISO 8601, e.g. "2026-01-01"
124
+ .name # Hash — language code => name, e.g. { "en" => "New Year's Day" }
125
+ .is_national # Boolean
126
+ .is_religious # Boolean
127
+ .is_local # Boolean
128
+ .is_estimate # Boolean
129
+ .day # HolidayDay
130
+ .religion # String — e.g. "Christianity", or "" if not applicable
131
+ .regions # Array<String> — subdivision codes, e.g. ["BW", "BY"]
132
+
133
+ HolidayDay
134
+ .actual # String — day of week the holiday actually falls on
135
+ .observed # String — day of week the holiday is observed
136
+
137
+ Country # .name, .alpha2, .subdivisions → Array<Subdivision>
138
+ Subdivision # .code, .name
139
+ Language # .code, .name
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Error Handling
145
+
146
+ Non-2xx responses raise `HolidaysRest::ApiError`:
147
+
148
+ ```ruby
149
+ begin
150
+ client.holidays(country: "US", year: 2024)
151
+ rescue HolidaysRest::ApiError => e
152
+ puts e.status_code # HTTP status code (Integer)
153
+ puts e.message # Error message (String)
154
+ puts e.body # Raw response body (String)
155
+ end
156
+ ```
157
+
158
+ | Status | Meaning |
159
+ |--------|---------------------|
160
+ | 400 | Bad request |
161
+ | 401 | Invalid API key |
162
+ | 404 | Not found |
163
+ | 500 | Server error |
164
+ | 503 | Service unavailable |
165
+
166
+ ---
167
+
168
+ ## License
169
+
170
+ MIT
@@ -0,0 +1,24 @@
1
+ require_relative "lib/holidays_rest/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "holidays-rest"
5
+ spec.version = HolidaysRest::VERSION
6
+ spec.authors = ["msdundar"]
7
+ spec.summary = "Official Ruby SDK for the holidays.rest API"
8
+ spec.description = "Fetch public holidays by country, year, region, type, and more."
9
+ spec.homepage = "https://www.holidays.rest"
10
+ spec.license = "MIT"
11
+
12
+ spec.required_ruby_version = ">= 3.2"
13
+
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = "https://github.com/holidays-rest/sdk-ruby"
16
+ spec.metadata["documentation_uri"] = "https://docs.holidays.rest"
17
+
18
+ spec.files = Dir["lib/**/*.rb", "README.md", "holidays-rest.gemspec"]
19
+
20
+ # Zero runtime dependencies — uses only Ruby stdlib (net/http, json, uri)
21
+
22
+ spec.add_development_dependency "minitest", "~> 5"
23
+ spec.add_development_dependency "webmock", "~> 3"
24
+ end
@@ -0,0 +1,109 @@
1
+ require "json"
2
+ require "net/http"
3
+ require "uri"
4
+
5
+ module HolidaysRest
6
+ class Client
7
+ BASE_URL = "https://api.holidays.rest/v1"
8
+
9
+ # @param api_key [String] Bearer token from https://www.holidays.rest/dashboard
10
+ # @param base_url [String] Override base URL (useful for testing)
11
+ # @param open_timeout [Integer] Seconds to wait for connection (default 5)
12
+ # @param read_timeout [Integer] Seconds to wait for response (default 15)
13
+ def initialize(api_key:, base_url: BASE_URL, open_timeout: 5, read_timeout: 15)
14
+ raise ArgumentError, "api_key must not be empty" if api_key.nil? || api_key.empty?
15
+
16
+ @api_key = api_key
17
+ @base_url = base_url.chomp("/")
18
+ @open_timeout = open_timeout
19
+ @read_timeout = read_timeout
20
+ end
21
+
22
+ # Fetch public holidays.
23
+ #
24
+ # @param country [String] ISO 3166 alpha-2 code, e.g. "US" (required)
25
+ # @param year [Integer, String] Four-digit year (required)
26
+ # @param month [Integer, String] 1–12 (optional)
27
+ # @param day [Integer, String] 1–31 (optional)
28
+ # @param type [String, Array<String>] "religious", "national", "local"
29
+ # @param religion [Integer, Array<Integer>] Religion code(s) 1–11
30
+ # @param region [String, Array<String>] Subdivision code(s)
31
+ # @param lang [String, Array<String>] Language code(s)
32
+ # @param response [String] "json" | "xml" | "yaml" | "csv"
33
+ # @return [Array<Holiday>]
34
+ def holidays(country:, year:, month: nil, day: nil, type: nil,
35
+ religion: nil, region: nil, lang: nil, response: nil)
36
+ params = build_params(
37
+ country: country,
38
+ year: year,
39
+ month: month,
40
+ day: day,
41
+ type: type,
42
+ religion: religion,
43
+ region: region,
44
+ lang: lang,
45
+ response: response
46
+ )
47
+ get("/holidays", params).map { Holiday.from_hash(_1) }
48
+ end
49
+
50
+ # Return all supported countries.
51
+ # @return [Array<Country>]
52
+ def countries
53
+ get("/countries", {}).map { Country.from_hash(_1) }
54
+ end
55
+
56
+ # Return details for one country, including subdivision codes.
57
+ # @param country_code [String] ISO 3166 alpha-2 code, e.g. "US"
58
+ # @return [Country]
59
+ def country(country_code)
60
+ raise ArgumentError, "country_code must not be empty" if country_code.nil? || country_code.empty?
61
+
62
+ Country.from_hash(get("/country/#{URI.encode_uri_component(country_code)}", {}))
63
+ end
64
+
65
+ # Return all supported language codes.
66
+ # @return [Array<Language>]
67
+ def languages
68
+ get("/languages", {}).map { Language.from_hash(_1) }
69
+ end
70
+
71
+ private
72
+
73
+ def build_params(**kwargs)
74
+ kwargs.each_with_object({}) do |(key, value), out|
75
+ next if value.nil?
76
+
77
+ out[key] = value.is_a?(Array) ? value.join(",") : value.to_s
78
+ end
79
+ end
80
+
81
+ def get(path, params)
82
+ uri = URI("#{@base_url}#{path}")
83
+ uri.query = URI.encode_www_form(params) unless params.empty?
84
+
85
+ http = Net::HTTP.new(uri.host, uri.port)
86
+ http.use_ssl = uri.scheme == "https"
87
+ http.open_timeout = @open_timeout
88
+ http.read_timeout = @read_timeout
89
+
90
+ request = Net::HTTP::Get.new(uri)
91
+ request["Authorization"] = "Bearer #{@api_key}"
92
+ request["Accept"] = "application/json"
93
+
94
+ response = http.request(request)
95
+ body = response.body
96
+
97
+ unless response.is_a?(Net::HTTPSuccess)
98
+ message = begin
99
+ JSON.parse(body).fetch("message", response.message)
100
+ rescue JSON::ParserError
101
+ response.message
102
+ end
103
+ raise ApiError.new(message, response.code.to_i, body)
104
+ end
105
+
106
+ JSON.parse(body)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,15 @@
1
+ module HolidaysRest
2
+ class ApiError < StandardError
3
+ attr_reader :status_code, :body
4
+
5
+ def initialize(message, status_code, body)
6
+ super(message)
7
+ @status_code = status_code
8
+ @body = body
9
+ end
10
+
11
+ def inspect
12
+ "#<HolidaysRest::ApiError status_code=#{@status_code} message=#{message.inspect}>"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ module HolidaysRest
2
+ HolidayDay = Data.define(:actual, :observed) do
3
+ def self.from_hash(h)
4
+ new(
5
+ actual: h.fetch("actual", ""),
6
+ observed: h.fetch("observed", "")
7
+ )
8
+ end
9
+ end
10
+
11
+ Holiday = Data.define(:country_code, :country_name, :date, :name,
12
+ :is_national, :is_religious, :is_local, :is_estimate,
13
+ :day, :religion, :regions) do
14
+ def self.from_hash(h)
15
+ new(
16
+ country_code: h.fetch("country_code", ""),
17
+ country_name: h.fetch("country_name", ""),
18
+ date: h.fetch("date", ""),
19
+ name: h.fetch("name", {}),
20
+ is_national: h.fetch("isNational", false),
21
+ is_religious: h.fetch("isReligious", false),
22
+ is_local: h.fetch("isLocal", false),
23
+ is_estimate: h.fetch("isEstimate", false),
24
+ day: HolidayDay.from_hash(h.fetch("day", {})),
25
+ religion: h.fetch("religion", ""),
26
+ regions: Array(h["regions"])
27
+ )
28
+ end
29
+ end
30
+
31
+ Subdivision = Data.define(:code, :name) do
32
+ def self.from_hash(h)
33
+ new(code: h.fetch("code", ""), name: h.fetch("name", ""))
34
+ end
35
+ end
36
+
37
+ Country = Data.define(:name, :alpha2, :subdivisions) do
38
+ def self.from_hash(h)
39
+ new(
40
+ name: h.fetch("name", ""),
41
+ alpha2: h.fetch("alpha2", ""),
42
+ subdivisions: Array(h["subdivisions"]).map { Subdivision.from_hash(_1) }
43
+ )
44
+ end
45
+ end
46
+
47
+ Language = Data.define(:code, :name) do
48
+ def self.from_hash(h)
49
+ new(code: h.fetch("code", ""), name: h.fetch("name", ""))
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module HolidaysRest
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "holidays_rest/version"
2
+ require_relative "holidays_rest/errors"
3
+ require_relative "holidays_rest/models"
4
+ require_relative "holidays_rest/client"
5
+
6
+ # holidays.rest Ruby SDK
7
+ # https://docs.holidays.rest
8
+ module HolidaysRest
9
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: holidays-rest
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - msdundar
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: minitest
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5'
26
+ - !ruby/object:Gem::Dependency
27
+ name: webmock
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3'
40
+ description: Fetch public holidays by country, year, region, type, and more.
41
+ executables: []
42
+ extensions: []
43
+ extra_rdoc_files: []
44
+ files:
45
+ - README.md
46
+ - holidays-rest.gemspec
47
+ - lib/holidays_rest.rb
48
+ - lib/holidays_rest/client.rb
49
+ - lib/holidays_rest/errors.rb
50
+ - lib/holidays_rest/models.rb
51
+ - lib/holidays_rest/version.rb
52
+ homepage: https://www.holidays.rest
53
+ licenses:
54
+ - MIT
55
+ metadata:
56
+ homepage_uri: https://www.holidays.rest
57
+ source_code_uri: https://github.com/holidays-rest/sdk-ruby
58
+ documentation_uri: https://docs.holidays.rest
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '3.2'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.6.9
74
+ specification_version: 4
75
+ summary: Official Ruby SDK for the holidays.rest API
76
+ test_files: []