# `RiderUtils.Gtfs`
[🔗](https://github.com/thecristen/rider_utils/blob/main/lib/gtfs.ex#L1)

A GenServer that downloads and handles updating GTFS files.

Add it to your application and it'll load the files to disk on initialization.

```elixir
children = [
  RiderUtils.Gtfs
]
```

And you can get the values and do things with them:

```elixir
all_inbound_destinations = case RiderUtils.Gtfs.get("directions.txt") do
  {:ok, directions} ->
    directions
    |> Enum.filter(& &1["direction_id"] == "1")
    |> Enum.map(& {&1["route_id"], &1["direction_destination"]})
  other ->
    other
end
```

You probably shouldn't do it, but technically can have more than one of these:

```elixir
children = [
  {RiderUtils.Gtfs, name: Green, gtfs_url: "https://cdn.mbta.com/MBTA_GTFS-dev-green.zip"},
  {RiderUtils.Gtfs, name: Blue, gtfs_url: "https://cdn.mbta.com/MBTA_GTFS-dev-blue.zip"}
]
```

Which'll let you handle multiple sets of files, I guess?

```elixir
# true???
RiderUtils.Gtfs.get("agency.txt", Green) != RiderUtils.Gtfs.get("agency.txt", Blue)
```

## Options

All fields are optional.

  * `:gtfs_url` - The HTTP endpoint from which to download the GTFS archive. Defaults to `"https://cdn.mbta.com/MBTA_GTFS.zip"`.
  * `:on_update` - An MFA tuple that will be invoked after successful GTFS file download.
  * `:refresh_interval` - How frequently to check for GTFS file changes. Defaults to `:timer.minutes(5)`.

### What refreshing means

After the initial download, the Last-Modified response header is saved for
future calls. Subsequent calls leverage the If-Modified-Since request header.
If the file on the `:gtfs_url` server hasn't changed since, we get a 304
response and don't need to update our own data. So, the `:refresh_interval`
merely defines how frequently we are checking, not how often we're downloading
the .zip file.

# `get`

```elixir
@spec get(String.t(), GenServer.server()) :: {:ok, [map()]} | {:error, term()}
```

Get the parsed contents of a GTFS file.

Expects a valid GTFS filename such as `"calendar.txt"`,
`"trips_properties.txt"`, and such. Returns a map per row.

Because the files can be quite large, and the file contents do not change
often, it is recommended to run this prudently.

## Usage

```elixir
{:ok, [%{"feed_version" => version} | _]} = RiderUtils.Gtfs.get("feed_info.txt")
version # "Summer 2026, 2026-08-10T18:56:26+00:00, version D"
```

If using a named GenServer,

```elixir
{:ok, routes} = RiderUtils.Gtfs.get("routes.txt", CustomGtfsServer)
ferry_routes = Enum.find(routes, & &1["route_type"] == "4")
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
