Perhaps memory is not the right term, but is storing a list of atoms outside the database a viable or recommended pattern?
An example would be if I’ve a table of items that have a status attached to each of them (for example: pending, active and expired). Creating a schema and table for this list of status options seems excessive, as whilst they might be added to further down the line, it will never be in a scenario where one install must be different from another (i.e. the database can handle
Having them hard-coded somewhere in my app feels like the right choice, but how would I go about this? Knowing nothing about ETS tables, would loading these options into an ETS table on application startup be the correct method?
Moreover is this a viable/recommended pattern for such types of data?
Even though it usually is against anything what I have learned about normalisation of databases, but why not just store it as a string in the table? Even a human can then understand what the result of the query means, without having to look up numbers either in another table or even worse, your source code…
That was my first instinct, but just felt that because it’s a list of strings/atoms that is never/rarely going to change that a DB table might be overkill?
That’s it. I didn’t want to just allow string because of the potential problems refactoring, and also wanted to constrain the options to a select few, which is why the reference to another table/list felt like a better option.
If it ‘truly’ is rarely changed but needs to be accessed often then just bake it into a module at compile-time, you can recompile it if needing to update it, the fastglobal library handles that for you too so as to make it a little easier.
But overall I’d probably put it in ETS or so in the general case.
For reference, so that others can see what I was on about, here’s what I’m running with at the moment:
defmodule Tic.Marketplace.ItemStatus do
@statuses [
"live",
"pending_payment",
"archived",
"suspended",
"deleted",
]
def all_status() do
@statuses
end
@doc """
Checks if the supplied status is allowed
"""
@spec check_valid_status(String.t) :: boolean
def check_valid_status(given_status) do
Enum.member?(@statuses, given_status)
end
end
Which I use in my Item as a simple :string in the schema. As part of the Item changeset, I’ve got a function that checks for a valid status, which looks like this:
def validate_status(changeset) do
valid? =
changeset.data.status
|> Tic.Marketplace.ItemStatus.check_valid_status
if valid? do
changeset
else
Ecto.Changeset.add_error(changeset, :status, "invalid status")
end
end
It’s probably not the perfect, or even recommended approach, but I wanted to work out how to accomplish without using a DB to enforce constraints. It was as much an exercise as a solution to a problem.
If they will change rarely or never, then I’d write a function that returns the list and hard-code the list. In the future if you find they change more often then change the function implementation.