Class: Vehicles::Model

Inherits:
Object
  • Object
show all
Defined in:
lib/vehicles/model.rb,
sig/vehicles.rbs

Overview

A single vehicle nameplate, e.g. "Volkswagen Golf". A lightweight, immutable value object — no ActiveRecord, no mutation. Reads like English on purpose.

car = Vehicles.find("vw golf")
car.make        # => "Volkswagen"
car.name        # => "Golf"
car.full_name   # => "Volkswagen Golf"
car.body_type   # => :hatchback
car.suv?        # => false

Constant Summary collapse

KINDS =

Vehicle kinds (RDW voertuigsoort). Today the bundled data is all :car; the shape already supports the rest for when those packs land.

Returns:

  • (Array[Symbol])
%i[car motorcycle van truck pickup trailer bus moped quad trike].freeze
BODY_TYPES =

Body types — the sub-classification within a kind. For cars these are the familiar shapes; motorcycle styles (:naked, :adventure, …) reuse this field.

Returns:

  • (Array[Symbol])
%i[
  hatchback sedan wagon suv mpv coupe convertible roadster pickup van
].freeze
REGIONS =

Continent codes (the model regions rollup). Order = rough market size.

%i[eu na as sa oc af].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs, make:, make_slug:) ⇒ Model

Returns a new instance of Model.

Parameters:

  • attrs (Hash)

    one model entry from the dataset (name/slug/kind + optional body_type/global_decile/availability/regions/aliases)

  • make (String)

    the parent make's display name

  • make_slug (String)

    the parent make's slug (for the composite slug)



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/vehicles/model.rb', line 34

def initialize(attrs, make:, make_slug:)
  @make       = make
  @make_slug  = make_slug
  @name       = attrs["name"]
  @model_slug = attrs["slug"]
  @kind       = (attrs["kind"] || "car").to_sym
  # nil = no body vocabulary for this record yet (most non-car kinds).
  # A default would be a lie — predicates just return false instead.
  @body_type  = attrs["body_type"]&.to_sym
  # Popularity decile 1 (most popular) … 10, averaged over every country
  # with registration counts. nil = catalog-only evidence, no counts yet.
  @global_decile = attrs["global_decile"]
  # ISO-3166-1 alpha-2 codes where an official source evidences the model.
  # Evidence of PRESENCE, not proof of official marketing (grey imports
  # count — they're real vehicles on real roads).
  @availability  = (attrs["availability"] || []).freeze
  # Continent rollup of availability (:eu/:na/:sa/:as/:oc/:af).
  @regions       = (attrs["regions"] || []).map(&:to_sym).freeze
  # Documented alternate names (nicknames, native scripts, market names).
  @aliases       = (attrs["aliases"] || []).freeze
  freeze
end

Instance Attribute Details

#aliasesObject (readonly)

Returns the value of attribute aliases.



27
28
29
# File 'lib/vehicles/model.rb', line 27

def aliases
  @aliases
end

#availabilityObject (readonly)

Returns the value of attribute availability.



27
28
29
# File 'lib/vehicles/model.rb', line 27

def availability
  @availability
end

#body_typeSymbol (readonly)

Returns the value of attribute body_type.

Returns:

  • (Symbol)


27
28
29
# File 'lib/vehicles/model.rb', line 27

def body_type
  @body_type
end

#global_decileObject (readonly)

Returns the value of attribute global_decile.



27
28
29
# File 'lib/vehicles/model.rb', line 27

def global_decile
  @global_decile
end

#kindSymbol (readonly)

Returns the value of attribute kind.

Returns:

  • (Symbol)


27
28
29
# File 'lib/vehicles/model.rb', line 27

def kind
  @kind
end

#makeString (readonly)

Returns the value of attribute make.

Returns:

  • (String)


27
28
29
# File 'lib/vehicles/model.rb', line 27

def make
  @make
end

#make_slugString (readonly)

Returns the value of attribute make_slug.

Returns:

  • (String)


27
28
29
# File 'lib/vehicles/model.rb', line 27

def make_slug
  @make_slug
end

#model_slugString (readonly)

The bare model slug ("golf"), used as the value in model_options.

Returns:

  • (String)


69
70
71
# File 'lib/vehicles/model.rb', line 69

def model_slug
  @model_slug
end

#nameString (readonly)

Returns the value of attribute name.

Returns:

  • (String)


27
28
29
# File 'lib/vehicles/model.rb', line 27

def name
  @name
end

#regionsObject (readonly)

Returns the value of attribute regions.



27
28
29
# File 'lib/vehicles/model.rb', line 27

def regions
  @regions
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?

Value-object equality — two models with the same slug are equal.



133
134
135
# File 'lib/vehicles/model.rb', line 133

def ==(other)
  other.is_a?(Model) && other.slug == slug
end

#available_in?(country) ⇒ Boolean

Evidence of presence in a country (see availability for semantics):

Vehicles.find("vw golf").available_in?(:nl)  # => true

Returns:

  • (Boolean)


85
86
87
# File 'lib/vehicles/model.rb', line 85

def available_in?(country)
  availability.include?(country.to_s.downcase)
end

#available_in_region?(region) ⇒ Boolean

Evidence of presence on a continent (:eu/:na/:sa/:as/:oc/:af):

Vehicles.find("perodua myvi").available_in_region?(:as)  # => true

Returns:

  • (Boolean)


91
92
93
# File 'lib/vehicles/model.rb', line 91

def available_in_region?(region)
  regions.include?(region.to_sym)
end

#car?Boolean

predicate sugar for each kind/body type: car?, suv?, hatchback?, coupe?, ...

Returns:

  • (Boolean)


87
# File 'sig/vehicles.rbs', line 87

def car?: () -> bool

#common?Boolean

Returns:

  • (Boolean)


123
# File 'lib/vehicles/model.rb', line 123

def common?  = rarity == :common

#full_nameString Also known as: to_s

"Volkswagen Golf" — the natural label for a model on its own.

Returns:

  • (String)


58
59
60
# File 'lib/vehicles/model.rb', line 58

def full_name
  "#{make} #{name}"
end

#hashObject



138
139
140
# File 'lib/vehicles/model.rb', line 138

def hash
  slug.hash
end

#hatchback?Boolean

Returns:

  • (Boolean)


89
# File 'sig/vehicles.rbs', line 89

def hatchback?: () -> bool

#image(year: nil, color: nil) ⇒ String?

Image URL, optionally year-/color-accurate. nil without hosted data.

Parameters:

  • year: (Integer, nil) (defaults to: nil)
  • color: (Object) (defaults to: nil)

Returns:

  • (String, nil)


163
164
165
# File 'lib/vehicles/model.rb', line 163

def image(year: nil, color: nil)
  Vehicles.resolve(:image, self, year: year, color: color)
end

#inspectObject



142
143
144
# File 'lib/vehicles/model.rb', line 142

def inspect
  %(#<Vehicles::Model "#{full_name}" #{body_type || kind}>)
end

#popular?Boolean

Top-20% popularity across covered markets. false when unranked (nil decile) — "we don't know" must never read as "popular".

Returns:

  • (Boolean)


104
105
106
# File 'lib/vehicles/model.rb', line 104

def popular?
  !global_decile.nil? && global_decile <= 2
end

#rare?Boolean

Returns:

  • (Boolean)


124
# File 'lib/vehicles/model.rb', line 124

def rare?    = rarity == :rare

#rarityObject

Rarity tier from the popularity decile — the coarse "how much data do I want to show" knob. Deciles are the moat's measured signal; this buckets them into three names apps can filter on WITHOUT us exposing raw counts:

:common  deciles 1-3   (the names everyone knows — safe default filter)
:average deciles 4-7
:rare    deciles 8-10
:unknown unranked (catalog-only evidence — no popularity signal yet)


115
116
117
118
119
120
121
# File 'lib/vehicles/model.rb', line 115

def rarity
  return :unknown if global_decile.nil?
  return :common  if global_decile <= 3
  return :average if global_decile <= 7

  :rare
end

#segmentSymbol?

Editorial market segment, e.g. :hot_hatch, :supercar. nil without hosted data.

Returns:

  • (Symbol, nil)


158
159
160
# File 'lib/vehicles/model.rb', line 158

def segment
  Vehicles.resolve(:segment, self)
end

#slugString

Composite, stable slug: "volkswagen-golf".

Returns:

  • (String)


64
65
66
# File 'lib/vehicles/model.rb', line 64

def slug
  "#{make_slug}-#{@model_slug}"
end

#suv?Boolean

Returns:

  • (Boolean)


88
# File 'sig/vehicles.rbs', line 88

def suv?: () -> bool

#to_hHash[Symbol, untyped]

Returns:

  • (Hash[Symbol, untyped])


126
127
128
129
130
# File 'lib/vehicles/model.rb', line 126

def to_h
  { make: make, model: name, slug: slug, kind: kind, body_type: body_type,
    global_decile: global_decile, rarity: rarity,
    availability: availability, regions: regions, aliases: aliases }
end

#two_wheeler?Boolean

Motorcycle OR moped — the union pickers usually want ("two-wheeler section" in an insurance/marketplace form).

Returns:

  • (Boolean)


79
80
81
# File 'lib/vehicles/model.rb', line 79

def two_wheeler?
  %i[motorcycle moped].include?(@kind)
end

#yearsRange[Integer]?

Production years as a Range, e.g. 1974..2024. nil without hosted data.

Returns:

  • (Range[Integer], nil)


153
154
155
# File 'lib/vehicles/model.rb', line 153

def years
  Vehicles.resolve(:years, self)
end