Packages

An Elixir client library for the Hacker News API

Current section

Files

Jump to
orange_site lib orange_site.ex
Raw

lib/orange_site.ex

defmodule OrangeSite do
@moduledoc """
An Elixir client library for the Hacker News API.
This module provides functions to interact with the Hacker News API,
including fetching items (stories, comments, jobs, polls), user data,
and various story feeds.
## Examples
# Fetch a story
{:ok, item} = OrangeSite.get_item(8863)
# Fetch a user
{:ok, user} = OrangeSite.get_user("pg")
# Get top stories
{:ok, story_ids} = OrangeSite.get_top_stories()
# Get the maximum item id
{:ok, max_id} = OrangeSite.get_max_item()
"""
alias OrangeSite.{Client, Item, User}
@doc """
Fetches an item by its ID.
Returns `{:ok, %Item{}}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_item(8863)
# {:ok, %OrangeSite.Item{id: 8863, type: "story", ...}}
"""
@spec get_item(integer()) :: {:ok, Item.t()} | {:error, term()}
def get_item(id) when is_integer(id) do
case Client.get("/item/#{id}.json") do
{:ok, nil} -> {:error, :not_found}
{:ok, data} -> {:ok, Item.new(data)}
error -> error
end
end
@doc """
Fetches an item by its ID, raising on error.
## Examples
OrangeSite.get_item!(8863)
# %OrangeSite.Item{id: 8863, type: "story", ...}
"""
@spec get_item!(integer()) :: Item.t()
def get_item!(id) when is_integer(id) do
case get_item(id) do
{:ok, item} -> item
{:error, reason} -> raise "Failed to fetch item #{id}: #{inspect(reason)}"
end
end
@doc """
Fetches a user by their username.
Returns `{:ok, %User{}}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_user("pg")
# {:ok, %OrangeSite.User{id: "pg", ...}}
"""
@spec get_user(String.t()) :: {:ok, User.t()} | {:error, term()}
def get_user(username) when is_binary(username) do
case Client.get("/user/#{username}.json") do
{:ok, nil} -> {:error, :not_found}
{:ok, data} -> {:ok, User.new(data)}
error -> error
end
end
@doc """
Fetches a user by their username, raising on error.
## Examples
OrangeSite.get_user!("pg")
# %OrangeSite.User{id: "pg", ...}
"""
@spec get_user!(String.t()) :: User.t()
def get_user!(username) when is_binary(username) do
case get_user(username) do
{:ok, user} -> user
{:error, reason} -> raise "Failed to fetch user #{username}: #{inspect(reason)}"
end
end
@doc """
Gets the current largest item ID.
Returns `{:ok, integer()}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_max_item()
# {:ok, 37839134}
"""
@spec get_max_item() :: {:ok, integer()} | {:error, term()}
def get_max_item do
Client.get("/maxitem.json")
end
@doc """
Gets the current largest item ID, raising on error.
"""
@spec get_max_item!() :: integer()
def get_max_item! do
Client.get!("/maxitem.json")
end
@doc """
Gets up to 500 top story IDs.
Returns `{:ok, [integer()]}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_top_stories()
# {:ok, [37839134, 37838989, ...]}
"""
@spec get_top_stories() :: {:ok, list(integer())} | {:error, term()}
def get_top_stories do
Client.get("/topstories.json")
end
@doc """
Gets up to 500 top story IDs, raising on error.
"""
@spec get_top_stories!() :: list(integer())
def get_top_stories! do
Client.get!("/topstories.json")
end
@doc """
Gets up to 500 new story IDs.
Returns `{:ok, [integer()]}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_new_stories()
# {:ok, [37839200, 37839150, ...]}
"""
@spec get_new_stories() :: {:ok, list(integer())} | {:error, term()}
def get_new_stories do
Client.get("/newstories.json")
end
@doc """
Gets up to 500 new story IDs, raising on error.
"""
@spec get_new_stories!() :: list(integer())
def get_new_stories! do
Client.get!("/newstories.json")
end
@doc """
Gets up to 500 best story IDs.
Returns `{:ok, [integer()]}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_best_stories()
# {:ok, [37835820, 37834999, ...]}
"""
@spec get_best_stories() :: {:ok, list(integer())} | {:error, term()}
def get_best_stories do
Client.get("/beststories.json")
end
@doc """
Gets up to 500 best story IDs, raising on error.
"""
@spec get_best_stories!() :: list(integer())
def get_best_stories! do
Client.get!("/beststories.json")
end
@doc """
Gets up to 200 Ask HN story IDs.
Returns `{:ok, [integer()]}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_ask_stories()
# {:ok, [37832000, 37831500, ...]}
"""
@spec get_ask_stories() :: {:ok, list(integer())} | {:error, term()}
def get_ask_stories do
Client.get("/askstories.json")
end
@doc """
Gets up to 200 Ask HN story IDs, raising on error.
"""
@spec get_ask_stories!() :: list(integer())
def get_ask_stories! do
Client.get!("/askstories.json")
end
@doc """
Gets up to 200 Show HN story IDs.
Returns `{:ok, [integer()]}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_show_stories()
# {:ok, [37830000, 37829500, ...]}
"""
@spec get_show_stories() :: {:ok, list(integer())} | {:error, term()}
def get_show_stories do
Client.get("/showstories.json")
end
@doc """
Gets up to 200 Show HN story IDs, raising on error.
"""
@spec get_show_stories!() :: list(integer())
def get_show_stories! do
Client.get!("/showstories.json")
end
@doc """
Gets up to 200 job story IDs.
Returns `{:ok, [integer()]}` on success, or `{:error, reason}` on failure.
## Examples
OrangeSite.get_job_stories()
# {:ok, [37825000, 37824500, ...]}
"""
@spec get_job_stories() :: {:ok, list(integer())} | {:error, term()}
def get_job_stories do
Client.get("/jobstories.json")
end
@doc """
Gets up to 200 job story IDs, raising on error.
"""
@spec get_job_stories!() :: list(integer())
def get_job_stories! do
Client.get!("/jobstories.json")
end
@doc """
Fetches multiple items in parallel.
Returns a list of `{:ok, item}` or `{:error, reason}` tuples.
## Examples
OrangeSite.get_items([8863, 8864])
# [{:ok, %OrangeSite.Item{...}}, {:ok, %OrangeSite.Item{...}}]
"""
@spec get_items(list(integer())) :: list({:ok, Item.t()} | {:error, term()})
def get_items(ids) when is_list(ids) do
ids
|> Task.async_stream(&get_item/1, ordered: true)
|> Enum.map(fn {:ok, result} -> result end)
end
@doc """
Fetches the top N stories with their full data.
## Examples
OrangeSite.fetch_top_stories(10)
# {:ok, [%OrangeSite.Item{...}, ...]}
"""
@spec fetch_top_stories(integer()) :: {:ok, list(Item.t())} | {:error, term()}
def fetch_top_stories(limit \\ 30) do
with {:ok, ids} <- get_top_stories() do
stories =
ids
|> Enum.take(limit)
|> get_items()
|> Enum.filter(fn
{:ok, _} -> true
_ -> false
end)
|> Enum.map(fn {:ok, item} -> item end)
{:ok, stories}
end
end
@doc """
Fetches the top N stories with their full data, raising on error.
"""
@spec fetch_top_stories!(integer()) :: list(Item.t())
def fetch_top_stories!(limit \\ 30) do
case fetch_top_stories(limit) do
{:ok, stories} -> stories
{:error, reason} -> raise "Failed to fetch top stories: #{inspect(reason)}"
end
end
end