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:
class User
include NoBrainer::Document
field :first_name
field :last_name
endfield accepts the following options:
:indexto specify an index.:defaultto specify a default value.:typeto enforce a type.:readonlyto specify if a field cannot be updated.:primary_keyto specify a custom primary key.:store_asto specify an alias in the database.:lazy_fetchto specify whether this field should be fetched on demand.
field also accepts the following validation options:
:validatesto specify validations.:requiredas 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.:formatas a shorthand for the format validation.:inas a shorthand for the inclusion validation.:lengthas a shorthand for the length validation.:min_lengthas a shorthand for the minimum length validation.:max_lengthas a shorthand for the maximum length validation.
Accessing Fields
Defined fields can be accessed with the following methods:
Reading an attribute attr:
self.attrself.read_attribute(attr)self[attr]
Writing an attribute attr:
self.attr = valueself.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:
class User
include NoBrainer::Document
field :email
def email=(value)
super(value.strip.downcase)
end
endNote 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.
field :num_friends, :default => 0
field :created_at, :default => ->{ Time.now }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:
field :email, :type => StringRead the Types section to learn more.
Validations
Validations can be declared directly on the field declaration:
field :email, :validates => { :format => { :with => /@/ } }Read the Validations section to learn more.
Indexes
A index can be declared on a field as such:
field :email, :index => trueRead the indexes section to learn more.
Aliases
An alias can be specified on a given field as such:
field :email, :store_as => :eNoBrainer 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:
class User
field :email, :type => String
field :avatar, :type => Binary, :lazy_fetch => true
end
user = User.first
user.email # In memory access.
user.avatar # Performs an extra query to fetch the data.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.
class User
virtual_field :current_user_following do |doc|
if Thread.current[:current_user]
Followship.where(:follower_id => doc[:id], :following_id => Thread.current[:current_user].id).to_rql.is_empty.not
end
end
endReflection
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.