View Source Carve.View (Carve v0.5.0)
Carve.View provides a DSL for quickly building JSON API views in Phoenix applications.
It automatically creates show and index functions for Phoenix controllers,
handles ID hashing, and manages links between different entities.
Usage
In your Phoenix JSON view module:
defmodule MyApp.UserJSON do
use Carve.View, :user
links fn user ->
%{
MyApp.TeamJSON => user.team_id,
MyApp.ProfileJSON => user.profile_id
}
end
get fn id ->
MyApp.Users.get_by_id!(id)
end
view fn user ->
%{
id: hash(user.id),
name: user.name,
team_id: MyApp.TeamJSON.hash(user.team_id),
profile_id: MyApp.ProfileJSON.hash(user.profile_id)
}
end
endThis will automatically create show/1 and index/1 functions that can be used
in your Phoenix controllers, handling both data rendering and link generation.
Caching Expensive Computations
When both links and view need the same expensive data, use the cache macro
to compute it once and pass it to both:
defmodule MyApp.UserJSON do
use Carve.View, :user
get fn id -> MyApp.Users.get!(id) end
cache fn user ->
%{plan: MyApp.Plans.get_active_plan(user.id)}
end
links fn user, cached ->
%{MyApp.PlanJSON => cached.plan.id}
end
view fn user, cached ->
%{
id: hash(user.id),
plan_id: MyApp.PlanJSON.hash(cached.plan.id)
}
end
endThe cached result is also shared across entities within the same request, so if the same user is linked from multiple parents, the cache function runs only once.
Generated Functions
The use Carve.View, :type macro generates several functions at compile time:
index/1: Handles rendering a list of entities with their links.show/1: Handles rendering a single entity with its links.hash/1: Alias of encode_id.encode_id/1: Encodes an ID using the view type as a salt.decode_id/1: Decodes an ID using the view type as a salt.type_name/0: Returns the type of the view.declare_links/1: Processes links for an entity (iflinksmacro is used).
Here's an example of what these functions might look like at runtime:
def index(%{result: data}) when is_list(data) do
results = Enum.map(data, &prepare_for_view/1)
links = Carve.Links.get_links_by_data(__MODULE__, data)
%{result: results, links: links}
end
def show(%{result: data}) do
result = prepare_for_view(data)
links = Carve.Links.get_links_by_data(__MODULE__, data)
%{result: result, links: links}
end
def hash(id) when is_integer(id), do: Carve.HashIds.encode(:user, id)
def hash(%{id: id}), do: hash(id)
def type_name, do: :user
def declare_links(data) do
%{
MyApp.TeamJSON => data.team_id,
MyApp.ProfileJSON => data.profile_id
}
endThese generated functions work together to provide a seamless API for rendering JSON views with proper linking and ID hashing.
Summary
Functions
Sets up the view module with the given type.
Caches expensive computations that are shared between links and view.
Generates a runtime case statement to return a loaded Ecto association or fall back to its ID.
Defines how to retrieve an entity by its ID.
Defines the links for the current view.
Defines how to render the view for the current entity.
Functions
Sets up the view module with the given type.
This macro is called when you use use Carve.View, :type in your module.
It imports Carve.View functions and sets up the necessary module attributes.
Parameters
type: An atom representing the type of the view (e.g., :user, :post).
Example
defmodule MyApp.UserJSON do
use Carve.View, :user
# ... rest of the module
end
Caches expensive computations that are shared between links and view.
The function receives the entity data and should return a map of precomputed values.
This map is then passed as the second argument to links and view functions.
Results are cached per entity (by module + id) within a single request.
Example
cache fn user ->
%{
plan: Plans.get_active_user_plan(user.id),
team: Teams.get_team(user.team_id)
}
end
links fn user, cached ->
%{PlanJSON => cached.plan.id}
end
view fn user, cached ->
%{id: hash(user.id), plan_id: PlanJSON.hash(cached.plan.id)}
end
Generates a runtime case statement to return a loaded Ecto association or fall back to its ID.
Defines how to retrieve an entity by its ID.
This macro specifies a function to fetch an entity given its ID.
Parameters
func: A function that takes an ID and returns the corresponding entity.
Example
get fn id ->
MyApp.Users.get_by_id!(id)
end
Defines the links for the current view.
This macro allows you to specify how to generate links for the current entity.
Accepts a 1-arity function, or a 2-arity function when used with cache.
Parameters
func: A function that takes the entity data (and optionally cached data) and returns a map of links.
Example
links fn user ->
%{
MyApp.TeamJSON => user.team_id,
MyApp.ProfileJSON => user.profile_id
}
end
Defines how to render the view for the current entity.
This macro specifies how to format the entity data for JSON output.
Accepts a 1-arity function, or a 2-arity function when used with cache.
Parameters
func: A function that takes the entity data (and optionally cached data) and returns a map for JSON rendering.
Example
view fn user ->
%{
id: hash(user.id),
name: user.name,
team_id: MyApp.TeamJSON.hash(user.team_id)
}
end