Module: Vehicles::Providers::HostedProvider
- Defined in:
- lib/vehicles/providers/hosted_provider.rb
Overview
Talks to the hosted VehiclesDB API to enrich models with years, images, and
segments. It is STRICTLY OPTIONAL: available? is true only when an api_key
is configured, so a key-less install never reaches the network. Every call
is error-isolated — a slow, missing, or failing API yields nil and the
resolver falls back to the local data. Tracking/enrichment must never break
the host app, so this never raises out.
The images endpoint is LIVE (since 2026-07): mint a key at
https://vehiclesdb.com/settings/api-keys, set config.api_key, and
model.image / model.images answer with rendered vehicle imagery.
The enrichment endpoints (years/segment) are still the wired-up seam —
they safely return nil until the service ships them.
Class Method Summary collapse
- .available? ⇒ Boolean
-
.fetch(model) ⇒ Object
Fetch + memoize the full model payload for this process.
-
.get(path, params) ⇒ Object
Issue a GET and parse JSON.
-
.image(model, year: nil, color: nil, size: :md) ⇒ Object
One variant URL — the common "just give me an
" case.
-
.images(model, color: nil) ⇒ Object
The full images payload for a model — palette, every variant with dimensions, provenance, and the honest color fallback:
coloris what the API actually served,requested_colorwhat you asked for (a color that isn't rendered yet falls back rather than 404ing). - .reset! ⇒ Object
- .segment(model) ⇒ Object
- .years(model) ⇒ Object
Class Method Details
.available? ⇒ Boolean
22 23 24 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 22 def available? !Vehicles.configuration.api_key.to_s.empty? end |
.fetch(model) ⇒ Object
Fetch + memoize the full model payload for this process. Keyed by slug.
64 65 66 67 68 69 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 64 def fetch(model) @cache ||= {} return @cache[model.slug] if @cache.key?(model.slug) @cache[model.slug] = get("/v1/models/#{model.slug}", {}) end |
.get(path, params) ⇒ Object
Issue a GET and parse JSON. Returns nil on ANY failure (network, timeout, non-200, bad JSON) — callers treat nil as "fall back to local data".
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 77 def get(path, params) # Lazy-required: the gem is standalone-first, and this whole module is # inert without an api_key, so we don't pay net/http at load time. require "net/http" require "uri" config = Vehicles.configuration uri = URI.join(config.api_base_url, path) uri.query = URI.encode_www_form(params) unless params.empty? http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == "https" http.open_timeout = config.api_timeout http.read_timeout = config.api_timeout request = Net::HTTP::Get.new(uri) request["Authorization"] = "Bearer #{config.api_key}" request["Accept"] = "application/json" response = http.request(request) return nil unless response.is_a?(Net::HTTPSuccess) JSON.parse(response.body) rescue StandardError => e warn "[vehicles] hosted lookup failed (#{e.class}); using local data" if $DEBUG nil end |
.image(model, year: nil, color: nil, size: :md) ⇒ Object
One variant URL — the common "just give me an " case.
size picks from the API's rendered variants (:sm 320×180, :md
640×360, :lg 1280×720; webp).
40 41 42 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 40 def image(model, year: nil, color: nil, size: :md) images(model, color: color)&.dig("variants", size.to_s, "url") end |
.images(model, color: nil) ⇒ Object
The full images payload for a model — palette, every variant with
dimensions, provenance, and the honest color fallback: color is what
the API actually served, requested_color what you asked for (a color
that isn't rendered yet falls back rather than 404ing).
GET /v1/vehicles/:kind/:make_slug/:model_slug/images?color=
year/trim filters are RESERVED server-side today (the API 422s on
them by contract, so callers can't silently build on unimplemented
semantics) — which is why image accepts year: but never sends it:
serving the current rendering beats an error until the filter ships.
55 56 57 58 59 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 55 def images(model, color: nil) params = {} params[:color] = color.to_s unless color.to_s.empty? get("/v1/vehicles/#{model.kind}/#{model.make_slug}/#{model.model_slug}/images", params) end |
.reset! ⇒ Object
71 72 73 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 71 def reset! @cache = {} end |
.segment(model) ⇒ Object
33 34 35 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 33 def segment(model) fetch(model)&.dig("segment")&.to_sym end |
.years(model) ⇒ Object
26 27 28 29 30 31 |
# File 'lib/vehicles/providers/hosted_provider.rb', line 26 def years(model) data = fetch(model) return nil unless data && data["year_start"] (data["year_start"]..data["year_end"]) # year_end may be nil => endless range end |