truaddress 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: f9fae5b2d1a641613378d6a24fa8477f01428983bc37234de5eb35f12ec1f3a4
4
+ data.tar.gz: 2687eb72b7182e779bf9be66b3846e0449710d4c4edb9b6feba2db737abeffae
5
+ SHA512:
6
+ metadata.gz: f1a85b789f2a1537f53f6e78f7ed0e7b6d2cf9d9fb1a74cd88e736dd8cd14ad93f03d86e9d15ba62f93e0f3ec16df4fe6b52b077616680ea1a363fe4c17a61e7
7
+ data.tar.gz: bc548b44f3219cf093296bafef7a0532e7f396ef2d086321181c1516f43026e64eccfeeb2966d3c654a9f51706862db9875166cdc3a58056d7db4fd98a575065
data/README.md ADDED
@@ -0,0 +1,311 @@
1
+ # TruAddress Ruby SDK
2
+
3
+ Official Ruby SDK for [TruAddress](https://truaddress.net) - Address validation, autocomplete, and geocoding API.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'truaddress'
11
+ ```
12
+
13
+ Then run:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install directly:
20
+
21
+ ```bash
22
+ gem install truaddress
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```ruby
28
+ require 'truaddress'
29
+
30
+ client = Truaddress::Client.new(api_key: "YOUR_API_KEY")
31
+
32
+ # Validate a US address
33
+ results = client.us_street(
34
+ street: "1600 Pennsylvania Ave NW",
35
+ city: "Washington",
36
+ state: "DC",
37
+ zipcode: "20500"
38
+ )
39
+
40
+ if results.any? && Truaddress::Client.deliverable?(results[0])
41
+ puts "Valid address: #{results[0]['delivery_line_1']}"
42
+ end
43
+ ```
44
+
45
+ ## Configuration
46
+
47
+ ### Per-client Configuration
48
+
49
+ ```ruby
50
+ client = Truaddress::Client.new(
51
+ api_key: "YOUR_API_KEY",
52
+ base_url: "https://truaddress.net" # Optional
53
+ )
54
+ ```
55
+
56
+ ### Global Configuration
57
+
58
+ ```ruby
59
+ Truaddress.configure do |config|
60
+ config.api_key = "YOUR_API_KEY"
61
+ config.base_url = "https://truaddress.net"
62
+ end
63
+
64
+ # Then use the global client
65
+ results = Truaddress.client.us_street(street: "350 5th Ave", city: "New York", state: "NY")
66
+ ```
67
+
68
+ ## Available Methods
69
+
70
+ ### US Endpoints
71
+
72
+ #### `us_street` - US Street Address Validation
73
+
74
+ ```ruby
75
+ results = client.us_street(
76
+ street: "350 5th Avenue",
77
+ city: "New York",
78
+ state: "NY",
79
+ zipcode: "10118"
80
+ )
81
+
82
+ puts results[0]["analysis"]["dpv_match_code"] # => "Y" (Deliverable)
83
+ puts results[0]["metadata"]["latitude"] # => 40.748535
84
+ ```
85
+
86
+ **DPV Match Codes:**
87
+ - `Y` - Confirmed deliverable
88
+ - `S` - Secondary (apt/suite) missing
89
+ - `D` - Secondary invalid
90
+ - `N` - Not deliverable
91
+
92
+ #### `us_zipcode` - ZIP Code Lookup
93
+
94
+ ```ruby
95
+ # By ZIP code
96
+ result = client.us_zipcode(zipcode: "90210")
97
+ puts result[0]["city_states"][0]["city"] # => "Beverly Hills"
98
+
99
+ # By city/state
100
+ result = client.us_zipcode(city: "Austin", state: "TX")
101
+ ```
102
+
103
+ #### `us_autocomplete` - Address Autocomplete
104
+
105
+ ```ruby
106
+ result = client.us_autocomplete(
107
+ search: "350 5th Ave",
108
+ max_results: 5,
109
+ state_filter: ["NY"]
110
+ )
111
+
112
+ result["suggestions"].each do |s|
113
+ puts "#{s['street_line']}, #{s['city']}, #{s['state']} #{s['zipcode']}"
114
+ end
115
+ ```
116
+
117
+ #### `us_extract` - Extract Addresses from Text
118
+
119
+ ```ruby
120
+ result = client.us_extract("Ship to: 350 5th Avenue, New York, NY 10118. Thanks!")
121
+
122
+ puts result["meta"]["address_count"] # => 1
123
+ puts result["addresses"][0]["verified"] # => true
124
+ ```
125
+
126
+ #### `us_reverse_geo` - Reverse Geocoding
127
+
128
+ ```ruby
129
+ result = client.us_reverse_geo(
130
+ latitude: 40.748535,
131
+ longitude: -73.9856571
132
+ )
133
+
134
+ result["results"].each do |r|
135
+ puts "#{r['address']['street']} (#{r['distance'].round}m away)"
136
+ end
137
+ ```
138
+
139
+ ### International Endpoints
140
+
141
+ #### `intl_street` - International Address Validation
142
+
143
+ ```ruby
144
+ results = client.intl_street(
145
+ country: "GBR",
146
+ freeform: "10 Downing Street, London"
147
+ )
148
+
149
+ puts results[0]["components"]["postal_code"] # => "SW1A 2AB"
150
+ puts Truaddress::Client.verified?(results[0]) # => true
151
+ ```
152
+
153
+ #### `intl_autocomplete` - International Autocomplete
154
+
155
+ ```ruby
156
+ result = client.intl_autocomplete(
157
+ search: "Champs Elysees",
158
+ country: "FRA",
159
+ max_results: 5
160
+ )
161
+ ```
162
+
163
+ ### Core Endpoints
164
+
165
+ #### `validate` - Global Address Validation
166
+
167
+ ```ruby
168
+ results = client.validate(
169
+ country: "US",
170
+ address1: "350 5th Avenue",
171
+ locality: "New York",
172
+ administrative_area: "NY",
173
+ postal_code: "10118"
174
+ )
175
+ ```
176
+
177
+ #### `correct` - Address Correction
178
+
179
+ ```ruby
180
+ results = client.correct(freeform: "1600 pennsylvania ave washington dc 20500")
181
+ puts results[0]["address1"] # Corrected address
182
+ ```
183
+
184
+ #### `autocomplete` - Global Autocomplete
185
+
186
+ ```ruby
187
+ result = client.autocomplete(q: "Buckingham Palace London", limit: 5)
188
+ ```
189
+
190
+ ## Helper Methods
191
+
192
+ ```ruby
193
+ # Check if US address is deliverable
194
+ Truaddress::Client.deliverable?(result) # DPV code = 'Y'
195
+
196
+ # Check if US address is a mail drop (CMRA)
197
+ Truaddress::Client.cmra?(result)
198
+
199
+ # Check if US address is vacant
200
+ Truaddress::Client.vacant?(result)
201
+
202
+ # Check if international address is verified
203
+ Truaddress::Client.verified?(result)
204
+
205
+ # Format addresses as strings
206
+ Truaddress::Client.format_us_address(result) # "350 5th Ave, New York NY 10118"
207
+ Truaddress::Client.format_intl_address(result) # "10 Downing Street, London, SW1A 2AB"
208
+ ```
209
+
210
+ ## Error Handling
211
+
212
+ ```ruby
213
+ begin
214
+ results = client.us_street(street: "123 Main St")
215
+ rescue Truaddress::AuthenticationError => e
216
+ puts "Auth failed: #{e.message}"
217
+ rescue Truaddress::ValidationError => e
218
+ puts "Invalid request: #{e.message}"
219
+ rescue Truaddress::RateLimitError => e
220
+ puts "Rate limited: #{e.message}"
221
+ rescue Truaddress::Error => e
222
+ puts "Error: #{e.message} (#{e.status_code})"
223
+ end
224
+ ```
225
+
226
+ ## Rails Integration
227
+
228
+ ### Configuration (config/initializers/truaddress.rb)
229
+
230
+ ```ruby
231
+ Truaddress.configure do |config|
232
+ config.api_key = Rails.application.credentials.truaddress_api_key
233
+ end
234
+ ```
235
+
236
+ ### Controller Example
237
+
238
+ ```ruby
239
+ class AddressesController < ApplicationController
240
+ def validate
241
+ results = Truaddress.client.us_street(
242
+ street: params[:street],
243
+ city: params[:city],
244
+ state: params[:state],
245
+ zipcode: params[:zipcode]
246
+ )
247
+
248
+ if results.any? && Truaddress::Client.deliverable?(results.first)
249
+ render json: { valid: true, address: results.first }
250
+ else
251
+ render json: { valid: false, message: "Address not deliverable" }
252
+ end
253
+ end
254
+ end
255
+ ```
256
+
257
+ ### Model Validation
258
+
259
+ ```ruby
260
+ class Order < ApplicationRecord
261
+ validate :validate_shipping_address
262
+
263
+ private
264
+
265
+ def validate_shipping_address
266
+ return if shipping_street.blank?
267
+
268
+ results = Truaddress.client.us_street(
269
+ street: shipping_street,
270
+ city: shipping_city,
271
+ state: shipping_state,
272
+ zipcode: shipping_zipcode
273
+ )
274
+
275
+ unless results.any? && Truaddress::Client.deliverable?(results.first)
276
+ errors.add(:shipping_address, "is not a valid deliverable address")
277
+ end
278
+ end
279
+ end
280
+ ```
281
+
282
+ ### ActiveJob for Batch Processing
283
+
284
+ ```ruby
285
+ class ValidateAddressesJob < ApplicationJob
286
+ def perform(customer_ids)
287
+ Customer.where(id: customer_ids).find_each do |customer|
288
+ results = Truaddress.client.us_street(
289
+ street: customer.street,
290
+ city: customer.city,
291
+ state: customer.state,
292
+ zipcode: customer.zipcode
293
+ )
294
+
295
+ customer.update(
296
+ address_valid: results.any? && Truaddress::Client.deliverable?(results.first),
297
+ address_validated_at: Time.current
298
+ )
299
+ end
300
+ end
301
+ end
302
+ ```
303
+
304
+ ## Requirements
305
+
306
+ - Ruby >= 2.7.0
307
+ - faraday >= 1.0
308
+
309
+ ## License
310
+
311
+ MIT
@@ -0,0 +1,371 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ module Truaddress
7
+ # TruAddress API Client
8
+ #
9
+ # @example
10
+ # client = Truaddress::Client.new(api_key: "YOUR_API_KEY")
11
+ # results = client.us_street(street: "1600 Pennsylvania Ave NW", city: "Washington", state: "DC")
12
+ #
13
+ class Client
14
+ attr_reader :api_key, :base_url
15
+
16
+ # Initialize a new client
17
+ #
18
+ # @param api_key [String] Your TruAddress API key
19
+ # @param base_url [String] Base URL for the API (default: https://truaddress.net)
20
+ def initialize(api_key:, base_url: nil)
21
+ raise AuthenticationError, "API key is required" if api_key.nil? || api_key.empty?
22
+
23
+ @api_key = api_key
24
+ @base_url = base_url || "https://truaddress.net"
25
+ @conn = build_connection
26
+ end
27
+
28
+ # ==========================================================================
29
+ # US Endpoints
30
+ # ==========================================================================
31
+
32
+ # Validate and standardize US street addresses
33
+ #
34
+ # @param street [String] Primary street address (required)
35
+ # @param street2 [String] Secondary address line
36
+ # @param secondary [String] Secondary designator (apt, suite, etc.)
37
+ # @param city [String] City name
38
+ # @param state [String] State abbreviation
39
+ # @param zipcode [String] ZIP code
40
+ # @param candidates [Integer] Maximum candidates to return (1-10)
41
+ # @param match [String] Match strategy ('strict', 'invalid', 'enhanced')
42
+ # @return [Array<Hash>] Validated address results
43
+ #
44
+ # @example
45
+ # results = client.us_street(street: "350 5th Avenue", city: "New York", state: "NY")
46
+ # puts results[0]["analysis"]["dpv_match_code"] # => "Y"
47
+ #
48
+ def us_street(street:, street2: nil, secondary: nil, city: nil, state: nil, zipcode: nil, candidates: nil, match: nil)
49
+ get("/api/us-street", {
50
+ street: street,
51
+ street2: street2,
52
+ secondary: secondary,
53
+ city: city,
54
+ state: state,
55
+ zipcode: zipcode,
56
+ candidates: candidates,
57
+ match: match
58
+ })
59
+ end
60
+
61
+ # Look up city/state by ZIP code or find ZIP codes by city/state
62
+ #
63
+ # @param zipcode [String] 5-digit ZIP code
64
+ # @param city [String] City name
65
+ # @param state [String] State abbreviation
66
+ # @return [Array<Hash>] Results with city_states and zipcodes
67
+ #
68
+ # @example
69
+ # result = client.us_zipcode(zipcode: "90210")
70
+ # puts result[0]["city_states"][0]["city"] # => "Beverly Hills"
71
+ #
72
+ def us_zipcode(zipcode: nil, city: nil, state: nil)
73
+ get("/api/us-zipcode", { zipcode: zipcode, city: city, state: state })
74
+ end
75
+
76
+ # Get address suggestions as user types
77
+ #
78
+ # @param search [String] Partial address to search
79
+ # @param selected [String] Previously selected suggestion
80
+ # @param max_results [Integer] Maximum suggestions to return
81
+ # @param city_filter [Array<String>] Cities to limit results to
82
+ # @param state_filter [Array<String>] States to limit results to
83
+ # @param zip_filter [Array<String>] ZIP codes to limit results to
84
+ # @return [Hash] Hash with 'suggestions' array
85
+ #
86
+ # @example
87
+ # result = client.us_autocomplete(search: "350 5th Ave", state_filter: ["NY"])
88
+ #
89
+ def us_autocomplete(search:, selected: nil, max_results: nil, city_filter: nil, state_filter: nil, zip_filter: nil,
90
+ prefer_cities: nil, prefer_states: nil, prefer_zips: nil, prefer_ratio: nil,
91
+ prefer_geolocation: nil, source: nil)
92
+ get("/api/us-autocomplete", {
93
+ search: search,
94
+ selected: selected,
95
+ max_results: max_results,
96
+ city_filter: array_param(city_filter),
97
+ state_filter: array_param(state_filter),
98
+ zip_filter: array_param(zip_filter),
99
+ prefer_cities: array_param(prefer_cities),
100
+ prefer_states: array_param(prefer_states),
101
+ prefer_zips: array_param(prefer_zips),
102
+ prefer_ratio: prefer_ratio,
103
+ prefer_geolocation: prefer_geolocation,
104
+ source: source
105
+ })
106
+ end
107
+
108
+ # Extract addresses from unstructured text
109
+ #
110
+ # @param text [String] Text containing addresses
111
+ # @param aggressive [Boolean] Enable aggressive extraction mode
112
+ # @param addr_line_breaks [Boolean] Addresses span multiple lines
113
+ # @param addr_per_line [Integer] Max addresses per line
114
+ # @return [Hash] Hash with 'meta' and 'addresses' arrays
115
+ #
116
+ # @example
117
+ # result = client.us_extract("Ship to 350 5th Ave, New York, NY 10118")
118
+ #
119
+ def us_extract(text, aggressive: nil, addr_line_breaks: nil, addr_per_line: nil)
120
+ post_text("/api/us-extract", text, {
121
+ aggressive: aggressive,
122
+ addr_line_breaks: addr_line_breaks,
123
+ addr_per_line: addr_per_line
124
+ })
125
+ end
126
+
127
+ # Get addresses near GPS coordinates
128
+ #
129
+ # @param latitude [Float] Latitude (-90 to 90)
130
+ # @param longitude [Float] Longitude (-180 to 180)
131
+ # @param source [String] Filter by type ('residential', 'commercial', 'all')
132
+ # @return [Hash] Hash with 'results' array of nearby addresses
133
+ #
134
+ # @example
135
+ # result = client.us_reverse_geo(latitude: 40.748535, longitude: -73.9856571)
136
+ #
137
+ def us_reverse_geo(latitude:, longitude:, source: nil)
138
+ get("/api/us-reverse-geo", { latitude: latitude, longitude: longitude, source: source })
139
+ end
140
+
141
+ # ==========================================================================
142
+ # International Endpoints
143
+ # ==========================================================================
144
+
145
+ # Validate international addresses
146
+ #
147
+ # @param country [String] ISO-3 country code (e.g., 'GBR', 'FRA')
148
+ # @param freeform [String] Full address as single string
149
+ # @param address1 [String] Address line 1
150
+ # @param address2 [String] Address line 2
151
+ # @param address3 [String] Address line 3
152
+ # @param address4 [String] Address line 4
153
+ # @param locality [String] City/town
154
+ # @param administrative_area [String] State/province/region
155
+ # @param postal_code [String] Postal/ZIP code
156
+ # @param geocode [Boolean] Include coordinates
157
+ # @return [Array<Hash>] Validated address results
158
+ #
159
+ # @example
160
+ # results = client.intl_street(country: "GBR", freeform: "10 Downing Street, London")
161
+ #
162
+ def intl_street(country:, freeform: nil, address1: nil, address2: nil, address3: nil, address4: nil,
163
+ locality: nil, administrative_area: nil, postal_code: nil, geocode: nil)
164
+ get("/api/intl-street", {
165
+ country: country,
166
+ freeform: freeform,
167
+ address1: address1,
168
+ address2: address2,
169
+ address3: address3,
170
+ address4: address4,
171
+ locality: locality,
172
+ administrative_area: administrative_area,
173
+ postal_code: postal_code,
174
+ geocode: geocode
175
+ })
176
+ end
177
+
178
+ # Get international address suggestions
179
+ #
180
+ # @param search [String] Partial address to search
181
+ # @param country [String] ISO-3 country code
182
+ # @param max_results [Integer] Maximum suggestions to return
183
+ # @param distance [Integer] Search radius in meters
184
+ # @param latitude [Float] Latitude for location bias
185
+ # @param longitude [Float] Longitude for location bias
186
+ # @return [Hash] Hash with 'candidates' array
187
+ #
188
+ def intl_autocomplete(search:, country:, max_results: nil, distance: nil, latitude: nil, longitude: nil)
189
+ get("/api/intl-autocomplete", {
190
+ search: search,
191
+ country: country,
192
+ max_results: max_results,
193
+ distance: distance,
194
+ latitude: latitude,
195
+ longitude: longitude
196
+ })
197
+ end
198
+
199
+ # ==========================================================================
200
+ # Core Endpoints
201
+ # ==========================================================================
202
+
203
+ # Validate addresses worldwide
204
+ #
205
+ # @param address1 [String] Primary address line (required)
206
+ # @param country [String] Country code (ISO-2, ISO-3, or name)
207
+ # @param address2 [String] Secondary address line
208
+ # @param locality [String] City/town
209
+ # @param administrative_area [String] State/province
210
+ # @param postal_code [String] Postal/ZIP code
211
+ # @return [Array<Hash>] Validated address results
212
+ #
213
+ # @example
214
+ # results = client.validate(
215
+ # country: "US",
216
+ # address1: "350 5th Avenue",
217
+ # locality: "New York",
218
+ # administrative_area: "NY"
219
+ # )
220
+ #
221
+ def validate(address1:, country: nil, address2: nil, locality: nil, administrative_area: nil, postal_code: nil)
222
+ post("/api/validate", {
223
+ country: country,
224
+ address1: address1,
225
+ address2: address2,
226
+ locality: locality,
227
+ administrative_area: administrative_area,
228
+ postal_code: postal_code
229
+ })
230
+ end
231
+
232
+ # Correct and standardize addresses
233
+ #
234
+ # @param freeform [String] Full address as single string
235
+ # @param country [String] Country code
236
+ # @param address1 [String] Address line
237
+ # @param locality [String] City
238
+ # @param administrative_area [String] State/province
239
+ # @param postal_code [String] Postal/ZIP code
240
+ # @return [Array<Hash>] Corrected address results
241
+ #
242
+ # @example
243
+ # results = client.correct(freeform: "1600 pennsylvania ave washington dc 20500")
244
+ #
245
+ def correct(freeform: nil, country: nil, address1: nil, locality: nil, administrative_area: nil, postal_code: nil)
246
+ post("/api/correct", {
247
+ freeform: freeform,
248
+ country: country,
249
+ address1: address1,
250
+ locality: locality,
251
+ administrative_area: administrative_area,
252
+ postal_code: postal_code
253
+ })
254
+ end
255
+
256
+ # Global address autocomplete
257
+ #
258
+ # @param q [String] Search query
259
+ # @param country [String] Filter by country code
260
+ # @param limit [Integer] Maximum suggestions
261
+ # @return [Hash] Hash with 'suggestions' array
262
+ #
263
+ def autocomplete(q:, country: nil, limit: nil)
264
+ get("/api/autocomplete", { q: q, country: country, limit: limit })
265
+ end
266
+
267
+ # ==========================================================================
268
+ # Helper Methods
269
+ # ==========================================================================
270
+
271
+ class << self
272
+ # Check if US address is deliverable (DPV match code = Y)
273
+ def deliverable?(result)
274
+ result.dig("analysis", "dpv_match_code") == "Y"
275
+ end
276
+
277
+ # Check if US address is a CMRA (Commercial Mail Receiving Agency)
278
+ def cmra?(result)
279
+ result.dig("analysis", "dpv_cmra") == "Y"
280
+ end
281
+
282
+ # Check if US address is vacant
283
+ def vacant?(result)
284
+ result.dig("analysis", "dpv_vacant") == "Y"
285
+ end
286
+
287
+ # Check if international address is verified
288
+ def verified?(result)
289
+ result.dig("analysis", "verification_status") == "Verified"
290
+ end
291
+
292
+ # Get formatted US address string
293
+ def format_us_address(result)
294
+ "#{result['delivery_line_1']}, #{result['last_line']}"
295
+ end
296
+
297
+ # Get formatted international address string
298
+ def format_intl_address(result)
299
+ [result["address1"], result["address2"], result["address3"], result["address4"]]
300
+ .compact
301
+ .reject(&:empty?)
302
+ .join(", ")
303
+ end
304
+ end
305
+
306
+ private
307
+
308
+ def build_connection
309
+ Faraday.new(url: base_url) do |f|
310
+ f.request :url_encoded
311
+ f.headers["X-API-Key"] = api_key
312
+ f.headers["User-Agent"] = "TruAddress-Ruby/#{VERSION}"
313
+ f.adapter Faraday.default_adapter
314
+ end
315
+ end
316
+
317
+ def get(path, params = {})
318
+ response = @conn.get(path, compact_params(params))
319
+ handle_response(response)
320
+ end
321
+
322
+ def post(path, body = {})
323
+ response = @conn.post(path) do |req|
324
+ req.headers["Content-Type"] = "application/json"
325
+ req.body = JSON.generate(compact_params(body))
326
+ end
327
+ handle_response(response)
328
+ end
329
+
330
+ def post_text(path, text, params = {})
331
+ query_string = URI.encode_www_form(compact_params(params))
332
+ full_path = query_string.empty? ? path : "#{path}?#{query_string}"
333
+
334
+ response = @conn.post(full_path) do |req|
335
+ req.headers["Content-Type"] = "text/plain"
336
+ req.body = text
337
+ end
338
+ handle_response(response)
339
+ end
340
+
341
+ def handle_response(response)
342
+ body = JSON.parse(response.body)
343
+
344
+ case response.status
345
+ when 200..299
346
+ body
347
+ when 401
348
+ raise AuthenticationError.new(body["error"] || "Authentication failed", status_code: response.status)
349
+ when 400, 422
350
+ raise ValidationError.new(body["error"] || "Validation failed", status_code: response.status)
351
+ when 429
352
+ raise RateLimitError.new(body["error"] || "Rate limit exceeded", status_code: response.status)
353
+ when 500..599
354
+ raise ServerError.new(body["error"] || "Server error", status_code: response.status)
355
+ else
356
+ raise Error.new(body["error"] || "Unknown error", status_code: response.status)
357
+ end
358
+ rescue JSON::ParserError
359
+ raise Error.new("Invalid JSON response", status_code: response.status, response_body: response.body)
360
+ end
361
+
362
+ def compact_params(params)
363
+ params.reject { |_, v| v.nil? }
364
+ end
365
+
366
+ def array_param(value)
367
+ return nil if value.nil?
368
+ value.is_a?(Array) ? value.join(",") : value
369
+ end
370
+ end
371
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truaddress
4
+ # Base error class for TruAddress errors
5
+ class Error < StandardError
6
+ attr_reader :status_code, :response_body
7
+
8
+ def initialize(message = nil, status_code: nil, response_body: nil)
9
+ @status_code = status_code
10
+ @response_body = response_body
11
+ super(message)
12
+ end
13
+ end
14
+
15
+ # Raised when API key is missing or invalid
16
+ class AuthenticationError < Error; end
17
+
18
+ # Raised when request parameters are invalid
19
+ class ValidationError < Error; end
20
+
21
+ # Raised when API rate limit is exceeded
22
+ class RateLimitError < Error; end
23
+
24
+ # Raised when API returns server error
25
+ class ServerError < Error; end
26
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truaddress
4
+ VERSION = "1.0.0"
5
+ end
data/lib/truaddress.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "truaddress/version"
4
+ require_relative "truaddress/error"
5
+ require_relative "truaddress/client"
6
+
7
+ # TruAddress Ruby SDK
8
+ # Official SDK for address validation, autocomplete, and geocoding
9
+ module Truaddress
10
+ class << self
11
+ attr_accessor :api_key, :base_url
12
+
13
+ def configure
14
+ yield self
15
+ end
16
+
17
+ def client
18
+ @client ||= Client.new(api_key: api_key, base_url: base_url)
19
+ end
20
+
21
+ # Reset the client (useful for testing)
22
+ def reset!
23
+ @client = nil
24
+ end
25
+ end
26
+
27
+ # Default base URL
28
+ self.base_url = "https://truaddress.net"
29
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: truaddress
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - TruAddress
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: faraday-net_http
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '13.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: webmock
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.0'
89
+ description: Ruby SDK for TruAddress API. Validate, autocomplete, and geocode addresses
90
+ worldwide with USPS-grade accuracy.
91
+ email:
92
+ - support@truaddress.net
93
+ executables: []
94
+ extensions: []
95
+ extra_rdoc_files: []
96
+ files:
97
+ - README.md
98
+ - lib/truaddress.rb
99
+ - lib/truaddress/client.rb
100
+ - lib/truaddress/error.rb
101
+ - lib/truaddress/version.rb
102
+ homepage: https://truaddress.net
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ homepage_uri: https://truaddress.net
107
+ source_code_uri: https://github.com/truaddress/truaddress-ruby
108
+ documentation_uri: https://truaddress.net/docs
109
+ changelog_uri: https://github.com/truaddress/truaddress-ruby/blob/main/CHANGELOG.md
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 2.7.0
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.5.3
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Official TruAddress SDK for Ruby - Address validation, autocomplete, and
129
+ geocoding
130
+ test_files: []