VinDecoder
vin_decoder is a lightweight Ruby client for the NHTSA vPIC API. It decodes 17-character VINs and exposes a tidy domain object so you can access make, model, year, powertrain data, and more without sifting through raw JSON.
Installation
Add the gem to your project (when published):
gem 'vin_decoder'Or install directly from source:
git clone https://github.com/Wenmar-Pro/vin_decoder.git
cd vin_decoder
bundle installConfiguration
Use the top-level VinDecoder.configure block to customize the base URL (handy for mocking) or request timeout. Defaults target the official NHTSA API with a 10-second timeout.
VinDecoder.configure do |config|
config.base_url = 'https://vpic.nhtsa.dot.gov/api/vehicles'
config.timeout = 5
endUsage
client = VinDecoder::Client.new
vehicle = client.decode('1HGCM82633A004352')
if vehicle.valid?
puts [vehicle.year, vehicle.make, vehicle.model].compact.join(' ')
puts "Engine: #{vehicle.engine} (#{vehicle.engine_cylinders} cylinders)"
puts "Fuel: #{vehicle.fuel_type}"
puts "Fuel type (raw accessor): #{vehicle['FuelTypePrimary']}"
puts "Lot size (dynamic): #{vehicle.lot_size}"
else
warn 'VIN could not be decoded'
endThe client raises:
-
VinDecoder::NotFoundErrorwhen the API responds with HTTP 404. -
VinDecoder::ApiErrorfor any other non-200 HTTP status code.
Each VinDecoder::Vehicle exposes the entire response payload:
- Common helper methods (
make,model,trim,engine, etc.). - Hash-style access with string or symbol keys (
vehicle['ModelYear']/vehicle[:ModelYear]). - Dynamic snake_case readers derived from the raw keys (
vehicle.vehicle_descriptor,vehicle.fuel_type_secondary). Missing fields raiseNoMethodErrorso mistakes are caught quickly.
Rails Integration
Add the gem to your Rails app (from RubyGems or a local path while developing).
# Gemfile
gem 'vin_decoder', path: '../path/to/vin_decoder'Create an initializer to configure timeouts and base URL:
# config/initializers/vin_decoder.rb
VinDecoder.configure do |config|
config.timeout = ENV.fetch('VIN_DECODER_TIMEOUT', 5).to_i
config.base_url = ENV.fetch('VIN_DECODER_BASE_URL', 'https://vpic.nhtsa.dot.gov/api/vehicles')
endWrap the client in a PORO/service for controllers or jobs:
# app/services/vin_lookup.rb
class VinLookup
def initialize(client: VinDecoder::Client.new)
@client = client
end
def call(vin)
vehicle = @client.decode(vin)
raise VinDecoder::NotFoundError unless vehicle.valid?
vehicle
end
endExample controller usage:
class VehiclesController < ApplicationController
def show
@vehicle = VinLookup.new.call(params[:vin])
rescue VinDecoder::NotFoundError
redirect_to root_path, alert: 'VIN not found'
end
endYou can also enqueue lookups via ActiveJob/Sidekiq and cache results to avoid repeated external calls.
Testing & VCR Cassettes
Tests run via RSpec and rely on VCR/WebMock for deterministic HTTP interactions.
bundle exec rspecTo refresh the live cassette, delete spec/fixtures/vcr_cassettes/decode_success.yml and rerun the specs. VCR will connect to the real API once, capture the response, and reuse it on subsequent runs.
Publishing Checklist
-
Ensure version in
lib/vin_decoder/version.rbreflects the release. -
Update
CHANGELOGor release notes as needed (add one if not yet created). -
Build the gem and inspect the package contents:
gem build vin_decoder.gemspec gem install vin_decoder-<version>.gem
-
Push the artifact to RubyGems:
gem push vin_decoder-<version>.gem
-
Tag the release in git and push tags to GitHub for reference.
That’s it—you now have a fully tested VIN decoder client ready for reuse or publication.