ETS-backed storage for Price resources.
Uses the shared store pattern via use PaperTiger.Store which provides:
- GenServer wraps ETS table
- Reads go directly to ETS (concurrent, fast)
- Writes go through GenServer (serialized, safe)
Architecture
- ETS Table:
:paper_tiger_prices(public, read_concurrency: true) - GenServer: Serializes writes, handles initialization
- Shared Implementation: All CRUD operations via PaperTiger.Store
Examples
# Direct read (no GenServer bottleneck)
{:ok, price} = PaperTiger.Store.Prices.get("price_123")
# Serialized write
price = %{id: "price_123", product: "prod_123", active: true, ...}
{:ok, price} = PaperTiger.Store.Prices.insert(price)
# Query helpers (direct ETS access)
prices = PaperTiger.Store.Prices.find_by(:product, "prod_123")
active_prices = PaperTiger.Store.Prices.find_by(:active, true)
Summary
Functions
Returns a specification to start this module under a supervisor.
Clears all prices from the store (all namespaces).
Clears all prices for a specific namespace.
Counts total prices in current namespace.
Deletes a price from the store.
Finds all prices in the current namespace whose field equals value.
Retrieves a price by ID.
Fetches a price by ID, or builds a minimal placeholder when the ID is unknown.
Fetches a price by ID, accepting a legacy plan ID in its place.
Retrieves a price only when it belongs to the given owner.
Inserts a price into the store.
Lists all prices with optional pagination.
Returns all items in a specific namespace.
Validates and applies child-resource mutations in one serialized store call.
Lists every storage namespace that currently holds prices.
Returns the ID prefix for this resource.
Starts the price store GenServer.
Returns the ETS table name for this store.
Updates a price in the store.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec clear() :: :ok
Clears all prices from the store (all namespaces).
Serialized write - goes through GenServer.
Useful for test cleanup. Note: This clears ALL data, not just
the current namespace. For namespace-specific cleanup, use
clear_namespace/1.
Clears all prices for a specific namespace.
Used by PaperTiger.Test to clean up after each test.
@spec count() :: non_neg_integer()
Counts total prices in current namespace.
Direct ETS access - does not go through GenServer.
@spec delete(String.t()) :: :ok
Deletes a price from the store.
Serialized write - goes through GenServer. Data is scoped to the current test namespace.
Finds all prices in the current namespace whose field equals value.
Direct ETS access - does not go through GenServer.
Returns an empty list when value is nil, since a nil reference never
identifies a parent resource.
Examples
find_by(:customer, "cus_123")
find_by(:status, "active")
Retrieves a price by ID.
Direct ETS access - does not go through GenServer. Data is scoped to the current test namespace.
Fetches a price by ID, or builds a minimal placeholder when the ID is unknown.
Callers that embed a price inside another resource (subscription items,
checkout line items) commonly receive ad-hoc price IDs that were never
created. They get a well-formed price object either way. A price map passes
through unchanged; anything else returns nil.
Direct ETS access - does not go through GenServer.
Fetches a price by ID, accepting a legacy plan ID in its place.
Stripe accepts plan IDs wherever a price ID is expected. A plan comes back
converted to price shape, with its amount as unit_amount and its interval
under recurring.
Direct ETS access - does not go through GenServer.
Retrieves a price only when it belongs to the given owner.
Missing resources and resources owned by a different parent both return
{:error, :not_found} so nested endpoints do not disclose foreign IDs.
Inserts a price into the store.
Serialized write - goes through GenServer to prevent race conditions. Data is scoped to the current test namespace.
@spec list(keyword() | map()) :: PaperTiger.List.t()
Lists all prices with optional pagination.
Direct ETS access - does not go through GenServer. Data is scoped to the current test namespace.
Options
:limit- Number of items (default: 10, max: 100):starting_after- Cursor for pagination:ending_before- Reverse cursor
Returns all items in a specific namespace.
Useful for debugging test isolation.
@spec mutate_owned(atom(), term(), [ {:insert | :update, map()} | {:delete, String.t()} ]) :: :ok | {:error, {:already_exists | :duplicate_operation | :not_found | :not_owned, String.t()}}
Validates and applies child-resource mutations in one serialized store call.
Every operation is checked before any write occurs. Inserts must use new
IDs, updates and deletes must target existing resources owned by
owner_id, and an ID may appear only once in the batch.
@spec namespaces() :: [PaperTiger.Connect.storage_namespace()]
Lists every storage namespace that currently holds prices.
Direct ETS access - does not go through GenServer.
@spec prefix() :: String.t() | nil
Returns the ID prefix for this resource.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the price store GenServer.
@spec table_name() :: atom()
Returns the ETS table name for this store.
Updates a price in the store.
Serialized write - goes through GenServer. Data is scoped to the current test namespace.