Realm is a simple model layer in Elixir with validation functions.
It is database-independent. Any interaction with databases are left to be implemented by the user (if required).
- Define your record with a field called
__errors__
with a default value as an empty list - In your record, to add validations, define a
validate
function which takes a record as the first argument and also returns the record.
Once you have done those, you can use the valid?
function on the record. It returns true
or false
.
All validation functions return the record. The record is modified, with the errors added whenever necessary, so you will have to keep the modified record. To make things easier, use Elixir's pipes. Look at the example below:
defrecord Student, name: nil, phone: nil, country: nil, __errors__: [] do
use Realm
def validate(record) do
record
|> validates_length(:name, [min: 1])
|> validates_length(:country, [max: 2, message: "use a valid 2-letter country code"])
|> validates_format(:phone, [format: %r/[0-9]+/], fn(record)-> record.country == "US" end)
end
end
john = Student[name: "John Doe"]
john.valid? #=> false
john.attributes #=> [name: "John Doe", phone: nil, country: nil]
john.validate.errors #=> [country: "use a valid 2-letter country code"]
## We have a condition for the validating phone number
## So let us update the country to US and validate again
john.country("US").validate.errors #=> [phone: "does not match format"]
Note that the validate
MUST return a record.
When you have a record of the type User
, to get attributes do record.attributes
.
It will return all fields and their values, except the field names that start and end with __
. Like the __errors__
field, which isn't returned.
In your validate
function, you can use the following helpers.
In all the helper functions, the first two arguments are
record
- record to be validatedfield_name
- the field name in the record to validate
Each validation helper can also be passed a function, that accepts a record, as a 4th argument. If such a function is passed, then the validation is performed only if the function returns true.
Validates length of Elixir strings (binaries) or lists (including single quoted strings).
Accepts the following options:
min
- minimum length of the fieldmax
- maximum length of the fieldmessage
- the error message incase the validation fails
Either min
or max
should be passed. Passing both is also fine.
If you do not pass a message
, a standard error message will be used.
validates_length(record, :name, [min: 3, max: 30])
validates_length(record, :name, [min: 3, message: "Name is not long enough"])
validates_length(record, :phone, [min: 10], fn(record)-> record.country == "US" end)
Validates the value of the field is non-nil.
The only option is message
, which is the custom error message.
validates_presence(record, :age)
validates_presence(record, :age, [message: "Age must be entered"])
# In this case you can skip the options arg
validates_presence(record, :age, fn(record)-> record.country == "US" end)
Validates if the field contains only any of values from a specified list.
Valid options:
in
- list of valid values. This is a required optionmessage
- custom error message
validates_inclusion(record, :role, [in: ["admin", "member"]])
validates_inclusion(record, :role, [in: ["admin", "member"], message: "must have a valid role"])
validates_inclusion(record, :costume, fn(record)-> record.role == "superhero" end)
Validates if the field doesn't contain any of values from a specified list.
Valid options:
from
- list of invalid values. this is a required option.message
- custom error message
validates_exclusion(record, :role, [from: ["superman", "batman"]])
validates_exclusion(record, :role, [from: ["superman", "batman"], message: "You cannot be a superhero."])
validates_exclusion(record, :role, [from: ["ironman", "batman"]], fn(record)-> record.status != "billionaire" end)
Validates if the format of the field's value matches the specified format.
Valid options:
format
- regexp that specifies the format. This is a required option.message
- custom error message
validates_format(record, :serial_number, [format: %r/[a-z][0-9]{3}/])
validates_format(record, :serial_number, [format: %r/[a-z][0-9]{3}/], message: "Invalid serial number"])
validates_format(record, :serial_number, [format: %r/[a-z][0-9]{3}/]], fn(record)->
record.expiry_year < 2011
end)
Validates if the uniqueness of the record, by using the condition passed. Ideally you'll be using checking the database or some source to validate if this record is unique in some way.
Valid options:
condition
- function to check the uniqueness. It should accept a record and should returntrue
if the record's field's value is unique.message
- custom error message
This is the only function without a variant, which accepts a fourth argument (like other validation helpers). If you want any other condition, just specify it in the condition option itself.
Easy peasy... add your custom validations to the validate
function. Make sure you return the record at the end.
When writing your own validation, to add your own errors for fields, use the following helpers
Add error to a record, for a field, after validation
example:
add_error(record, :username, "already been taken")
Clear errors in a validated record
clear_errors(record)
Author: Akash Manohar
Copyright © 2013 under the MIT License