Fields
Field Declaration
With NoBrainer, persisted model attributes are called fields or attributes
interchangeably in the documentation.
Declaring a field is done by using the field
method.
For example, the following defines a User
model with a first and last name:
field
accepts the following options:
:index
to specify an index.:default
to specify a default value.:type
to enforce a type.:readonly
to specify if a field cannot be updated.:primary_key
to specify a custom primary key.:store_as
to specify an alias in the database.:lazy_fetch
to specify whether this field should be fetched on demand.
field
also accepts the following validation options:
:validates
to specify validations.:required
as a shorthand for the presence validation (except with Boolean types, for which the not_null validation is used).:uniq
(or:unique
) as a shorthand for the uniqueness validation.:format
as a shorthand for the format validation.:in
as a shorthand for the inclusion validation.:length
as a shorthand for the length validation.:min_length
as a shorthand for the minimum length validation.:max_length
as a shorthand for the maximum length validation.
Accessing Fields
Defined fields can be accessed with the following methods:
Reading an attribute attr
:
self.attr
self.read_attribute(attr)
self[attr]
Writing an attribute attr
:
self.attr = value
self.write_attribute(attr, value)
self[attr] = value
Reading all attributes:
self.attributes
: returns attributes.read_attribute()
is used to compute the values.self.inspectable_attributes
: returns internal attributes used for debugging purposes.
Mass assignment:
self.assign_attributes(attrs_hash)
Methods lower in the list calls the method directly above it.
For example, self[attr]
calls read_attribute(attr)
which calls self.attr
.
Note that there is no attr_protected
method to control mass assignments.
When passing raw params
from Rails controller to NoBrainer, an
ActiveModel::ForbiddenAttributesError
is raised.
params
must be sanitized with
strong parameters.
Overriding attributes
If you wish to override an attribute getter or setter, you may define
the attr
and attr=
methods in your class. super
can be used as usual.
The following shows an example that uses super
in the setter:
Note that the setters are not used when reading a document from the database. Keep this in mind when your database does not match your schema.
Accessing raw attributes
self._read_attribute(attr)
and self._write_attribute(attr, value)
access
internal attributes, bypassing any type checking. These methods should not be
used for regular use. Do not use these methods when overriding getters/setters
(see above).
Default Values
To assign a default value to a field, you may pass a default
option.
You can pass a value or a lambda. The latter will be evaluated at the time of
the assignment in the context of the document, which is useful to set
values depending on other already set attributes.
Defaults values are assigned whenever a model is instantiated in memory, which
happens when Model.new
is called. Reading a model from the database
calls Model.new
and therefore performs default value assignments.
A default value is only assigned when the corresponding attribute has not been
set. For example, calling Model.create(:created_at => nil)
will not trigger
the default value assignment on created_at
. Please create a GitHub issue
if this behavior is a problem for you.
Note that specifying a default value at some point in time does not apply the default value to existing documents in the database. Existing documents must be manually migrated.
Readonly Fields
When declaring a field with :readonly => true
, the field cannot be reassigned
once persisted to the database.
Primary Key
NoBrainer allows custom primary keys with the :primary_key => true
option.
The default primary key is id
and has a format that matches [A-Za-z0-9]{14}
.
It has interesting property compared to UUIDs because these IDs are
monotonically increasing with time. NoBrainer always sort by primary key by
default to give predicable and repeatable results. For example, Model.last
yields the latest created model, which can be quite handy in development mode.
When comparing two models with ==
or eql?
, only the primary keys are
compared, not the other attributes.
Specifying a custom primary key changes the default foreign key names in belongs_to associations.
Dynamic Attributes
Dynamic attributes are supported by NoBrainer, but are not enabled by default.
You must include the NoBrainer::Document::DynamicAttributes
mixin in your model.
By doing so, you will be able to read/write arbitrary attributes to your model with
read_attribute()
/ []
and write_attribute()
/[]=
.
Types
Field types can be declared as such:
Read the Types section to learn more.
Validations
Validations can be declared directly on the field declaration:
Read the Validations section to learn more.
Indexes
A index can be declared on a field as such:
Read the indexes section to learn more.
Aliases
An alias can be specified on a given field as such:
NoBrainer will translate all the references to that field when compiling queries and reading models back from the database.
A simple index declared on an aliased field carries the name of alias in the database,
unless specified otherwise by an :store_as
option on the index.
Warning: When passing raw RQL to NoBrainer, aliases do not get translated.
Lazy Fetching
Some fields can have large content size, for example binary fields. It might be undesirable to fetch them all the time. NoBrainer can fetch certain fields on demand by declaring a field to be lazy fetched. For example:
Virtual Attributes
NoBrainer can merge RQL values which appear as regular attributes on the model
instance. Using virtual_field
allows to specify a custom RQL expression that
will be evaluated by the database when performing queries.
The following example adds a current_user_following
virtual attribute to a
User
model that returns whether the current user follows that user or not.
Reflection
You can access the field definitions with Model.fields
.
It returns a hash of the form {:field_name => options}
.
You can undefine a previously defined field with
Model.remove_field(field_name)
. This feature is needed when removing the
default primary key id
for example.
You may access the name of the current primary key with Model.pk_name
.
You may access the primary key value of a document with instance.pk_value
.