countriesdb-validator 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: fe17836873cfdf5121f0ed749ae6ab13302c3f2341533bfef346a5254fff8e24
4
+ data.tar.gz: be1c0723cf2f27ba93c8565d7dba18c42dc33a75a67ed7b3426c7060544216e2
5
+ SHA512:
6
+ metadata.gz: 53fa0e51b86cc0ef0d255fb8da6583ecf65b0e7c3f56bfe27d8a89b47663861193bdf9fb6cba4ec96ca47982a0f5811e60f5cefc4265244e2fe13bd2093c3838
7
+ data.tar.gz: 55bb861b16609100657fd11fa4f60903a341e0c057d2451f0edbaa537af412a3960fc327e901726bd44f20ce9d6a27a3bea1009d443e332086f37bf9f292d5e7
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # countriesdb-validator
2
+
3
+ **Backend validation package for CountriesDB.** Provides server-side validation for country and subdivision codes using ISO 3166-1 and ISO 3166-2 standards.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/countriesdb-validator.svg)](https://badge.fury.io/rb/countriesdb-validator)
6
+ 📖 **[Full Documentation](https://countriesdb.com/docs/backend-api)** | 🌐 **[Website](https://countriesdb.com)** | 📦 **[GitHub Repository](https://github.com/countriesdb/validator-ruby)**
7
+
8
+ **Important**: This package only provides validation methods. Data fetching for frontend widgets must be done through frontend packages ([`@countriesdb/widget-core`](https://www.npmjs.com/package/@countriesdb/widget-core), [`@countriesdb/widget`](https://www.npmjs.com/package/@countriesdb/widget)).
9
+
10
+ ## Getting Started
11
+
12
+ **⚠️ API Key Required:** This package requires a CountriesDB **private** API key to function. You must create an account at [countriesdb.com](https://countriesdb.com) to obtain your private API key. Test accounts are available with limited functionality.
13
+
14
+ - 🔑 [Get your API key](https://countriesdb.com) - Create an account and get your private key
15
+ - 📚 [View documentation](https://countriesdb.com/docs/backend-api) - Complete API reference and examples
16
+ - 💬 [Support](https://countriesdb.com) - Get help and support
17
+
18
+ ## Features
19
+
20
+ - ✅ **ISO 3166 Compliant** - Validates ISO 3166-1 (countries) and ISO 3166-2 (subdivisions) codes
21
+ - ✅ **Multiple Validation Options** - Support for `follow_upward`, `follow_related`, and `allow_parent_selection`
22
+ - ✅ **Batch Validation** - Validate multiple countries or subdivisions in a single request
23
+ - ✅ **Rails Integration** - Built-in ActiveModel validators for seamless Rails integration
24
+ - ✅ **Detailed Error Messages** - Returns specific error messages from the CountriesDB API
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ gem install countriesdb-validator
30
+ ```
31
+
32
+ Or add to your Gemfile:
33
+
34
+ ```ruby
35
+ gem 'countriesdb-validator'
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ### Standalone Validator
41
+
42
+ ```ruby
43
+ require 'countriesdb-validator'
44
+
45
+ validator = CountriesDB::Validator.new(api_key: 'YOUR_API_KEY')
46
+
47
+ # Validate a single country
48
+ result = validator.validate_country('US')
49
+ if result[:valid]
50
+ puts "Valid country"
51
+ else
52
+ puts "Invalid: #{result[:message]}"
53
+ end
54
+
55
+ # Validate a single subdivision
56
+ result = validator.validate_subdivision('US-CA', 'US')
57
+ if result[:valid]
58
+ puts "Valid subdivision"
59
+ end
60
+
61
+ # Validate multiple countries
62
+ results = validator.validate_countries(['US', 'CA', 'MX'])
63
+ results.each do |result|
64
+ puts "#{result[:code]}: #{result[:valid] ? 'Valid' : 'Invalid'}"
65
+ end
66
+
67
+ # Validate multiple subdivisions
68
+ results = validator.validate_subdivisions(
69
+ ['US-CA', 'US-NY', 'US-TX'],
70
+ 'US'
71
+ )
72
+ ```
73
+
74
+ ### Rails Validation
75
+
76
+ #### Configuration
77
+
78
+ Add to `config/application.rb` or `config/environments/*.rb`:
79
+
80
+ ```ruby
81
+ config.countriesdb = {
82
+ api_key: 'YOUR_API_KEY'
83
+ }
84
+ ```
85
+
86
+ #### Using Validators
87
+
88
+ ```ruby
89
+ class User < ApplicationRecord
90
+ validates :country, countriesdb: true
91
+ validates :subdivision, countriesdb: { subdivision: true, country: :country }
92
+ end
93
+
94
+ # With options
95
+ class User < ApplicationRecord
96
+ validates :country, countriesdb: { follow_upward: true }
97
+ validates :subdivision, countriesdb: {
98
+ subdivision: true,
99
+ country: :country,
100
+ follow_related: true,
101
+ allow_parent_selection: true
102
+ }
103
+ end
104
+ ```
105
+
106
+ ## API Reference
107
+
108
+ ### `CountriesDB::Validator`
109
+
110
+ Main validator class.
111
+
112
+ #### Constructor
113
+
114
+ ```ruby
115
+ CountriesDB::Validator.new(api_key:)
116
+ ```
117
+
118
+ **Parameters:**
119
+ - `api_key` (required): Your CountriesDB API key
120
+
121
+ #### Methods
122
+
123
+ ##### `validate_country(code, follow_upward: false)`
124
+
125
+ Validate a single country code.
126
+
127
+ **Parameters:**
128
+ - `code` (String): ISO 3166-1 alpha-2 country code
129
+ - `follow_upward` (Boolean): Check if country is referenced in a subdivision (default: `false`)
130
+
131
+ **Returns:** Hash with `:valid` (Boolean) and optional `:message` (String)
132
+
133
+ ##### `validate_countries(codes, follow_upward: false)`
134
+
135
+ Validate multiple country codes.
136
+
137
+ **Parameters:**
138
+ - `codes` (Array<String>): Array of ISO 3166-1 alpha-2 country codes
139
+ - `follow_upward` (Boolean): Always false for multi-value (disabled)
140
+
141
+ **Returns:** Array of results with `:code`, `:valid`, and optional `:message`
142
+
143
+ ##### `validate_subdivision(code, country, follow_related: false, allow_parent_selection: false)`
144
+
145
+ Validate a single subdivision code.
146
+
147
+ **Parameters:**
148
+ - `code` (String, nil): Subdivision code (e.g., 'US-CA') or nil/empty string
149
+ - `country` (String): ISO 3166-1 alpha-2 country code
150
+ - `follow_related` (Boolean): Check if subdivision redirects to another country (default: `false`)
151
+ - `allow_parent_selection` (Boolean): Allow selecting parent subdivisions (default: `false`)
152
+
153
+ **Returns:** Hash with `:valid` (Boolean) and optional `:message` (String)
154
+
155
+ ##### `validate_subdivisions(codes, country, follow_related: false, allow_parent_selection: false)`
156
+
157
+ Validate multiple subdivision codes.
158
+
159
+ **Parameters:**
160
+ - `codes` (Array<String, nil>): Array of subdivision codes or nil/empty strings
161
+ - `country` (String): ISO 3166-1 alpha-2 country code
162
+ - `follow_related` (Boolean): Always false for multi-value (disabled)
163
+ - `allow_parent_selection` (Boolean): Allow selecting parent subdivisions (default: `false`)
164
+
165
+ **Returns:** Array of results with `:code`, `:valid`, and optional `:message`
166
+
167
+ ## Examples
168
+
169
+ Runnable examples using this package are available in the [countriesdb/examples](https://github.com/countriesdb/examples) repository:
170
+
171
+ - [`ruby/backend-faraday`](https://github.com/countriesdb/examples/tree/main/ruby/backend-faraday) – HTTP client examples using Faraday
172
+
173
+ ### Sinatra Example
174
+
175
+ ```ruby
176
+ require 'sinatra'
177
+ require 'countriesdb-validator'
178
+
179
+ validator = CountriesDB::Validator.new(api_key: 'YOUR_API_KEY')
180
+
181
+ post '/api/user' do
182
+ country = params[:country]
183
+ subdivision = params[:subdivision]
184
+
185
+ country_result = validator.validate_country(country)
186
+ unless country_result[:valid]
187
+ status 422
188
+ return { error: country_result[:message] }.to_json
189
+ end
190
+
191
+ subdivision_result = validator.validate_subdivision(subdivision, country)
192
+ unless subdivision_result[:valid]
193
+ status 422
194
+ return { error: subdivision_result[:message] }.to_json
195
+ end
196
+
197
+ # Validation passed
198
+ { message: 'Valid data' }.to_json
199
+ end
200
+ ```
201
+
202
+ ## Requirements
203
+
204
+ - Ruby 2.7+
205
+ - Valid CountriesDB API key
206
+
207
+ ## License
208
+
209
+ Proprietary
210
+ Copyright (c) NAYEE LLC. All rights reserved.
211
+
212
+ This software is the proprietary property of NAYEE LLC. For licensing inquiries, please contact [NAYEE LLC](https://nayee.net).
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
@@ -0,0 +1,58 @@
1
+ # Rails validators for CountriesDB
2
+ require 'active_model'
3
+
4
+ module CountriesDB
5
+ module Rails
6
+ # ActiveModel validator for country codes
7
+ class CountriesdbValidator < ActiveModel::EachValidator
8
+ def validate_each(record, attribute, value)
9
+ api_key = Rails.application.config.countriesdb&.dig(:api_key) ||
10
+ ENV['COUNTRIESDB_API_KEY']
11
+ backend_url = Rails.application.config.countriesdb&.dig(:backend_url) ||
12
+ ENV['COUNTRIESDB_BACKEND_URL'] ||
13
+ 'https://api.countriesdb.com'
14
+
15
+ return if api_key.nil? || api_key.empty?
16
+
17
+ validator = CountriesDB::Validator.new(api_key: api_key, backend_url: backend_url)
18
+
19
+ # Determine validation type
20
+ if options[:subdivision]
21
+ country_attr = options[:country] || :country
22
+ country = record.send(country_attr)
23
+ result = validator.validate_subdivision(
24
+ value,
25
+ country,
26
+ follow_related: options[:follow_related] || false,
27
+ allow_parent_selection: options[:allow_parent_selection] || false
28
+ )
29
+ else
30
+ result = validator.validate_country(
31
+ value,
32
+ follow_upward: options[:follow_upward] || false
33
+ )
34
+ end
35
+
36
+ unless result[:valid]
37
+ record.errors.add(attribute, result[:message] || 'is invalid')
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # Register the validator
45
+ ActiveModel::Validations::HelperMethods.module_eval do
46
+ def validates_countriesdb(*attr_names)
47
+ validates_with CountriesDB::Rails::CountriesdbValidator, _merge_attributes(attr_names)
48
+ end
49
+ end
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
@@ -0,0 +1,163 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ module CountriesDB
6
+ # Validator class for CountriesDB backend API
7
+ class Validator
8
+ attr_reader :api_key
9
+
10
+ def initialize(api_key:, backend_url: nil)
11
+ raise ArgumentError, 'API key is required' if api_key.nil? || api_key.empty?
12
+
13
+ @api_key = api_key
14
+ @backend_url = backend_url || 'https://api.countriesdb.com'
15
+ end
16
+
17
+ # Validate a single country code
18
+ #
19
+ # @param code [String] ISO 3166-1 alpha-2 country code
20
+ # @param follow_upward [Boolean] Whether to check if country is referenced in a subdivision
21
+ # @return [Hash] Result with :valid (Boolean) and optional :message (String)
22
+ def validate_country(code, follow_upward: false)
23
+ return invalid_result('Invalid country code.') if code.nil? || code.length != 2
24
+
25
+ begin
26
+ response = make_request('/api/validate/country', {
27
+ code: code.upcase,
28
+ follow_upward: follow_upward
29
+ })
30
+ response
31
+ rescue StandardError => e
32
+ invalid_result(e.message)
33
+ end
34
+ end
35
+
36
+ # Validate multiple country codes
37
+ #
38
+ # @param codes [Array<String>] Array of ISO 3166-1 alpha-2 country codes
39
+ # @return [Array<Hash>] Array of results with :code, :valid, and optional :message
40
+ def validate_countries(codes, follow_upward: false)
41
+ return [] if codes.nil? || codes.empty?
42
+
43
+ # Validate format
44
+ codes.each do |code|
45
+ if code.nil? || code.length != 2
46
+ raise ArgumentError, 'Invalid country code format. All codes must be 2-character strings.'
47
+ end
48
+ end
49
+
50
+ begin
51
+ response = make_request('/api/validate/country', {
52
+ code: codes.map(&:upcase),
53
+ follow_upward: false # Disabled for multi-select
54
+ })
55
+ response['results'] || []
56
+ rescue StandardError => e
57
+ raise ValidationError, "Failed to validate countries: #{e.message}"
58
+ end
59
+ end
60
+
61
+ # Validate a single subdivision code
62
+ #
63
+ # @param code [String, nil] Subdivision code (e.g., 'US-CA') or nil/empty string
64
+ # @param country [String] ISO 3166-1 alpha-2 country code
65
+ # @param follow_related [Boolean] Whether to check if subdivision redirects to another country
66
+ # @param allow_parent_selection [Boolean] Whether to allow selecting parent subdivisions
67
+ # @return [Hash] Result with :valid (Boolean) and optional :message (String)
68
+ def validate_subdivision(code, country, follow_related: false, allow_parent_selection: false)
69
+ return invalid_result('Invalid country code.') if country.nil? || country.length != 2
70
+
71
+ begin
72
+ response = make_request('/api/validate/subdivision', {
73
+ code: code || '',
74
+ country: country.upcase,
75
+ follow_related: follow_related,
76
+ allow_parent_selection: allow_parent_selection
77
+ })
78
+ response
79
+ rescue StandardError => e
80
+ invalid_result(e.message)
81
+ end
82
+ end
83
+
84
+ # Validate multiple subdivision codes
85
+ #
86
+ # @param codes [Array<String, nil>] Array of subdivision codes or nil/empty strings
87
+ # @param country [String] ISO 3166-1 alpha-2 country code
88
+ # @param follow_related [Boolean] Whether to check if subdivision redirects to another country
89
+ # @param allow_parent_selection [Boolean] Whether to allow selecting parent subdivisions
90
+ # @return [Array<Hash>] Array of results with :code, :valid, and optional :message
91
+ def validate_subdivisions(codes, country, follow_related: false, allow_parent_selection: false)
92
+ raise ArgumentError, 'Invalid country code.' if country.nil? || country.length != 2
93
+ return [] if codes.nil? || codes.empty?
94
+
95
+ begin
96
+ response = make_request('/api/validate/subdivision', {
97
+ code: codes.map { |c| c || '' },
98
+ country: country.upcase,
99
+ follow_related: false, # Disabled for multi-select
100
+ allow_parent_selection: allow_parent_selection
101
+ })
102
+ response['results'] || []
103
+ rescue StandardError => e
104
+ raise ValidationError, "Failed to validate subdivisions: #{e.message}"
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def make_request(endpoint, data)
111
+ uri = URI.join(@backend_url, endpoint)
112
+ http = Net::HTTP.new(uri.host, uri.port)
113
+ http.use_ssl = uri.scheme == 'https'
114
+ http.open_timeout = 5
115
+ http.read_timeout = 10
116
+
117
+ request = Net::HTTP::Post.new(uri.path)
118
+ request['Content-Type'] = 'application/json'
119
+ request['Authorization'] = "Bearer #{@api_key}"
120
+ request.body = data.to_json
121
+
122
+ response = http.request(request)
123
+
124
+ unless response.code == '200'
125
+ error_data = JSON.parse(response.body) rescue {}
126
+ raise StandardError, error_data['message'] || "HTTP Error: #{response.code}"
127
+ end
128
+
129
+ JSON.parse(response.body)
130
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
131
+ raise TimeoutError, "Request timeout: #{e.message}"
132
+ rescue Net::HTTPError, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e
133
+ raise ConnectionError, "Connection failed: #{e.message}"
134
+ rescue JSON::ParserError => e
135
+ raise StandardError, "Invalid JSON response: #{e.message}"
136
+ end
137
+
138
+ def invalid_result(message)
139
+ { valid: false, message: message }
140
+ end
141
+ end
142
+
143
+ # Exception for validation errors
144
+ class ValidationError < StandardError
145
+ end
146
+
147
+ # Exception for timeout errors
148
+ class TimeoutError < StandardError
149
+ end
150
+
151
+ # Exception for connection errors
152
+ class ConnectionError < StandardError
153
+ end
154
+ end
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
@@ -0,0 +1,12 @@
1
+ module CountriesDB
2
+ VERSION = '1.0.0'
3
+ end
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
@@ -0,0 +1,4 @@
1
+ # Compatibility file for countriesdb-validator gem
2
+ # Allows both require 'countriesdb' and require 'countriesdb-validator'
3
+ require_relative 'countriesdb'
4
+
@@ -0,0 +1,22 @@
1
+ # Main CountriesDB module
2
+ require_relative 'countriesdb/version'
3
+ require_relative 'countriesdb/validator'
4
+
5
+ # Conditionally load Rails validators if ActiveModel is available
6
+ if defined?(ActiveModel)
7
+ require_relative 'countriesdb/rails/validators'
8
+ end
9
+
10
+ module CountriesDB
11
+ # Main module for CountriesDB Ruby gem
12
+ # Provides backend validation for country and subdivision codes
13
+ end
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: countriesdb-validator
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - NAYEE LLC
8
+ - CountriesDB
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2025-12-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '13.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '13.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ description: Server-side validation for country and subdivision codes
57
+ email:
58
+ - contact@nayee.net
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - README.md
64
+ - lib/countriesdb-validator.rb
65
+ - lib/countriesdb.rb
66
+ - lib/countriesdb/rails/validators.rb
67
+ - lib/countriesdb/validator.rb
68
+ - lib/countriesdb/version.rb
69
+ homepage: https://countriesdb.com
70
+ licenses:
71
+ - Nonstandard
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 2.7.0
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.4.20
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Backend validation package for CountriesDB
92
+ test_files: []