1 Resumen de las validaciones
Aquí tienes un ejemplo de una validación muy simple:
class Person < ApplicationRecord
validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false
Como puedes ver, nuestra validación nos indica que nuestra Person
no es válida sin un atributo name
. La segunda Person
no se guardará en la base de datos.
Antes de entrar en más detalles, hablemos de cómo encajan las validaciones en el panorama general de tu aplicación.
1.1 ¿Por qué utilizar validaciones?
Las validaciones se utilizan para asegurarse de que solo se guarde en la base de datos datos válidos. Por ejemplo, puede ser importante para tu aplicación asegurarse de que cada usuario proporcione una dirección de correo electrónico y una dirección postal válidas. Las validaciones a nivel de modelo son la mejor manera de asegurarse de que solo se guarde en la base de datos datos válidos. Son independientes de la base de datos, no pueden ser evitadas por los usuarios finales y son convenientes de probar y mantener. Rails proporciona funciones incorporadas para necesidades comunes y te permite crear tus propios métodos de validación también.
Hay varias otras formas de validar datos antes de que se guarden en la base de datos, incluyendo restricciones nativas de la base de datos, validaciones en el lado del cliente y validaciones a nivel de controlador. Aquí tienes un resumen de las ventajas y desventajas:
- Las restricciones de la base de datos y/o los procedimientos almacenados hacen que los mecanismos de validación dependan de la base de datos y pueden dificultar las pruebas y el mantenimiento. Sin embargo, si tu base de datos es utilizada por otras aplicaciones, puede ser una buena idea utilizar algunas restricciones a nivel de base de datos. Además, las validaciones a nivel de base de datos pueden manejar de forma segura algunas cosas (como la unicidad en tablas muy utilizadas) que pueden ser difíciles de implementar de otra manera.
- Las validaciones en el lado del cliente pueden ser útiles, pero generalmente no son fiables si se utilizan solas. Si se implementan utilizando JavaScript, pueden ser evitadas si JavaScript está desactivado en el navegador del usuario. Sin embargo, si se combinan con otras técnicas, la validación en el lado del cliente puede ser una forma conveniente de proporcionar a los usuarios una retroalimentación inmediata mientras utilizan tu sitio.
- Las validaciones a nivel de controlador pueden ser tentadoras de utilizar, pero a menudo se vuelven difíciles de manejar y difíciles de probar y mantener. Siempre que sea posible, es una buena idea mantener tus controladores simples, ya que hará que tu aplicación sea un placer de trabajar a largo plazo.
Elige estas en casos específicos. Es la opinión del equipo de Rails que las validaciones a nivel de modelo son las más apropiadas en la mayoría de las circunstancias.
1.2 ¿Cuándo ocurre la validación?
Hay dos tipos de objetos de Active Record: aquellos que corresponden a una fila dentro de tu base de datos y aquellos que no lo hacen. Cuando creas un objeto nuevo, por ejemplo utilizando el método new
, ese objeto aún no pertenece a la base de datos. Una vez que llamas a save
en ese objeto, se guardará en la tabla de la base de datos correspondiente. Active Record utiliza el método de instancia new_record?
para determinar si un objeto ya está en la base de datos o no. Considera la siguiente clase de Active Record:
class Person < ApplicationRecord
end
Podemos ver cómo funciona mirando la salida de bin/rails console
:
irb> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
irb> p.new_record?
=> true
irb> p.save
=> true
irb> p.new_record?
=> false
Crear y guardar un nuevo registro enviará una operación SQL INSERT
a la base de datos. Actualizar un registro existente enviará una operación SQL UPDATE
en su lugar. Las validaciones se ejecutan típicamente antes de que se envíen estos comandos a la base de datos. Si alguna validación falla, el objeto se marcará como no válido y Active Record no realizará la operación INSERT
o UPDATE
. Esto evita almacenar un objeto no válido en la base de datos. Puedes elegir que se ejecuten validaciones específicas cuando se crea, guarda o actualiza un objeto.
PRECAUCIÓN: Hay muchas formas de cambiar el estado de un objeto en la base de datos. Algunos métodos activarán las validaciones, pero otros no. Esto significa que es posible guardar un objeto en la base de datos en un estado no válido si no tienes cuidado. Los siguientes métodos activan las validaciones y guardarán el objeto en la base de datos solo si el objeto es válido:
create
create!
save
save!
update
update!
Las versiones con signo de exclamación (por ejemplo, save!
) lanzan una excepción si el registro no es válido. Las versiones sin signo de exclamación no lo hacen: save
y update
devuelven false
, y create
devuelve el objeto.
1.3 Omitir validaciones
Los siguientes métodos omiten las validaciones y guardarán el objeto en la base de datos sin importar su validez. Deben usarse con precaución.
decrement!
decrement_counter
increment!
increment_counter
insert
insert!
insert_all
insert_all!
toggle!
touch
touch_all
update_all
update_attribute
update_column
update_columns
update_counters
upsert
upsert_all
Tenga en cuenta que save
también tiene la capacidad de omitir las validaciones si se le pasa validate: false
como argumento. Esta técnica debe usarse con precaución.
save(validate: false)
1.4 valid?
e invalid?
Antes de guardar un objeto Active Record, Rails ejecuta las validaciones. Si estas validaciones producen algún error, Rails no guarda el objeto.
También puedes ejecutar estas validaciones por tu cuenta. valid?
activa tus validaciones y devuelve true
si no se encontraron errores en el objeto, y false
en caso contrario. Como viste anteriormente:
class Person < ApplicationRecord
validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false
Después de que Active Record haya realizado las validaciones, cualquier error se puede acceder a través del método de instancia errors
. Este método devuelve una colección de errores. Por definición, un objeto es válido si esta colección está vacía después de ejecutar las validaciones.
Tenga en cuenta que un objeto instanciado con new
no informará errores incluso si es técnicamente inválido, porque las validaciones se ejecutan automáticamente solo cuando se guarda el objeto, como con los métodos create
o save
.
class Person < ApplicationRecord
validates :name, presence: true
end
irb> p = Person.new
=> #<Person id: nil, name: nil>
irb> p.errors.size
=> 0
irb> p.valid?
=> false
irb> p.errors.objects.first.full_message
=> "Name can’t be blank"
irb> p = Person.create
=> #<Person id: nil, name: nil>
irb> p.errors.objects.first.full_message
=> "Name can’t be blank"
irb> p.save
=> false
irb> p.save!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank
irb> Person.create!
ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank
invalid?
es el inverso de valid?
. Activa tus validaciones y devuelve true
si se encontraron errores en el objeto, y false
en caso contrario.
1.5 errors[]
Para verificar si un atributo particular de un objeto es válido o no, puedes usar errors[:atributo]
. Devuelve una matriz de todos los mensajes de error para :atributo
. Si no hay errores en el atributo especificado, se devuelve una matriz vacía.
Este método solo es útil después de que se hayan ejecutado las validaciones, porque solo inspecciona la colección de errores y no activa las validaciones en sí. Es diferente del método ActiveRecord::Base#invalid?
explicado anteriormente porque no verifica la validez del objeto en su conjunto. Solo verifica si se encontraron errores en un atributo individual del objeto.
class Person < ApplicationRecord
validates :name, presence: true
end
irb> Person.new.errors[:name].any?
=> false
irb> Person.create.errors[:name].any?
=> true
Trataremos los errores de validación con más detalle en la sección Trabajando con errores de validación.
2 Ayudantes de validación
Active Record ofrece muchos ayudantes de validación predefinidos que puedes usar directamente dentro de las definiciones de tus clases. Estos ayudantes proporcionan reglas de validación comunes. Cada vez que una validación falla, se agrega un error a la colección errors
del objeto, y esto se asocia con el atributo que se está validando.
Cada ayudante acepta un número arbitrario de nombres de atributos, por lo que con una sola línea de código puedes agregar el mismo tipo de validación a varios atributos.
Todos ellos aceptan las opciones :on
y :message
, que definen cuándo se debe ejecutar la validación y qué mensaje se debe agregar a la colección errors
si falla, respectivamente. La opción :on
toma uno de los valores :create
o :update
. Hay un mensaje de error predeterminado para cada uno de los ayudantes de validación. Estos mensajes se utilizan cuando no se especifica la opción :message
. Veamos cada uno de los ayudantes disponibles.
Para ver una lista de los ayudantes predeterminados disponibles, echa un vistazo a ActiveModel::Validations::HelperMethods
.
2.1 aceptación
Este método valida que se haya marcado una casilla de verificación en la interfaz de usuario cuando se envía un formulario. Esto se utiliza típicamente cuando el usuario necesita aceptar los términos de servicio de su aplicación, confirmar que se ha leído un texto o cualquier concepto similar.
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
Esta verificación se realiza solo si terms_of_service
no es nil
. El mensaje de error predeterminado para este ayudante es "debe ser aceptado". También puede pasar un mensaje personalizado a través de la opción message
.
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { message: 'debe ser cumplido' }
end
También puede recibir una opción :accept
, que determina los valores permitidos que se considerarán aceptables. Por defecto, es ['1', true]
y se puede cambiar fácilmente.
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: 'sí' }
validates :eula, acceptance: { accept: ['VERDADERO', 'aceptado'] }
end
Esta validación es muy específica para aplicaciones web y esta 'aceptación' no necesita ser registrada en ninguna parte de su base de datos. Si no tiene un campo para ello, el ayudante creará un atributo virtual. Si el campo existe en su base de datos, la opción accept
debe establecerse en o incluir true
, de lo contrario, la validación no se ejecutará.
2.2 confirmación
Debe utilizar este ayudante cuando tenga dos campos de texto que deben recibir exactamente el mismo contenido. Por ejemplo, es posible que desee confirmar una dirección de correo electrónico o una contraseña. Esta validación crea un atributo virtual cuyo nombre es el nombre del campo que debe confirmarse con "_confirmation" agregado.
class Person < ApplicationRecord
validates :email, confirmation: true
end
En su plantilla de vista, podría usar algo como esto:
<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>
NOTA: Esta verificación se realiza solo si email_confirmation
no es nil
. Para requerir confirmación, asegúrese de agregar una verificación de presencia para el atributo de confirmación (veremos presence
más adelante en esta guía):
class Person < ApplicationRecord
validates :email, confirmation: true
validates :email_confirmation, presence: true
end
También hay una opción :case_sensitive
que puede utilizar para definir si la restricción de confirmación será sensible a mayúsculas y minúsculas o no. Esta opción tiene un valor predeterminado de verdadero.
class Person < ApplicationRecord
validates :email, confirmation: { case_sensitive: false }
end
El mensaje de error predeterminado para este ayudante es "no coincide con la confirmación". También puede pasar un mensaje personalizado a través de la opción message
.
Generalmente, al usar este validador, querrá combinarlo con la opción :if
para validar solo el campo "_confirmation" cuando el campo inicial haya cambiado y no cada vez que guarde el registro. Más sobre validaciones condicionales más adelante.
class Person < ApplicationRecord
validates :email, confirmation: true
validates :email_confirmation, presence: true, if: :email_changed?
end
2.3 comparación
Esta verificación validará una comparación entre dos valores comparables.
class Promotion < ApplicationRecord
validates :end_date, comparison: { greater_than: :start_date }
end
El mensaje de error predeterminado para este ayudante es "comparación fallida". También puede pasar un mensaje personalizado a través de la opción message
.
Estas opciones son compatibles:
:greater_than
- Especifica que el valor debe ser mayor que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser mayor que %{count}".:greater_than_or_equal_to
- Especifica que el valor debe ser mayor o igual que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser mayor o igual que %{count}".:equal_to
- Especifica que el valor debe ser igual al valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser igual a %{count}".:less_than
- Especifica que el valor debe ser menor que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser menor que %{count}".:less_than_or_equal_to
- Especifica que el valor debe ser menor o igual que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser menor o igual que %{count}".:other_than
- Especifica que el valor debe ser distinto del valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser distinto de %{count}".
NOTA: El validador requiere que se suministre una opción de comparación. Cada opción acepta un valor, un proc o un símbolo. Cualquier clase que incluya Comparable se puede comparar.
2.4 formato
Este ayudante valida los valores de los atributos probando si coinciden con una expresión regular dada, que se especifica utilizando la opción :with
.
class Product < ApplicationRecord
validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
message: "solo permite letras" }
end
Inversamente, al usar la opción :without
en su lugar, puedes requerir que el atributo especificado no coincida con la expresión regular.
En ambos casos, la opción :with
o :without
proporcionada debe ser una expresión regular o un proc o lambda que devuelva una.
El mensaje de error predeterminado es "no es válido".
ADVERTENCIA. usa \A
y \z
para coincidir con el inicio y el final de la cadena, ^
y $
coinciden con el inicio / fin de una línea. Debido al uso frecuente incorrecto de ^
y $
, debes pasar la opción multiline: true
en caso de que uses cualquiera de estos dos anclajes en la expresión regular proporcionada. En la mayoría de los casos, deberías usar \A
y \z
.
2.5 inclusión
Este ayudante valida que los valores de los atributos estén incluidos en un conjunto dado. De hecho, este conjunto puede ser cualquier objeto enumerable.
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} no es un tamaño válido" }
end
El ayudante inclusion
tiene una opción :in
que recibe el conjunto de valores que se aceptarán. La opción :in
tiene un alias llamado :within
que puedes usar con el mismo propósito, si lo deseas. El ejemplo anterior usa la opción :message
para mostrar cómo puedes incluir el valor del atributo. Para ver todas las opciones, consulta la documentación sobre mensajes.
El mensaje de error predeterminado para este ayudante es "no está incluido en la lista".
2.6 exclusión
¡Lo opuesto a inclusión
es... exclusión
!
Este ayudante valida que los valores de los atributos no estén incluidos en un conjunto dado. De hecho, este conjunto puede ser cualquier objeto enumerable.
class Account < ApplicationRecord
validates :subdomain, exclusion: { in: %w(www us ca jp),
message: "%{value} está reservado." }
end
El ayudante exclusion
tiene una opción :in
que recibe el conjunto de valores que no se aceptarán para los atributos validados. La opción :in
tiene un alias llamado :within
que puedes usar con el mismo propósito, si lo deseas. Este ejemplo usa la opción :message
para mostrar cómo puedes incluir el valor del atributo. Para ver todas las opciones del argumento del mensaje, consulta la documentación sobre mensajes.
El mensaje de error predeterminado es "está reservado".
Alternativamente a un enumerable tradicional (como un Array), puedes proporcionar un proc, lambda o símbolo que devuelva un enumerable. Si el enumerable es un rango numérico, de tiempo o de fecha y hora, la prueba se realiza con Range#cover?
, de lo contrario, se utiliza include?
. Cuando se utiliza un proc o lambda, se pasa la instancia en validación como argumento.
2.7 longitud
Este ayudante valida la longitud de los valores de los atributos. Proporciona una variedad de opciones para que puedas especificar restricciones de longitud de diferentes formas:
class Person < ApplicationRecord
validates :name, length: { minimum: 2 }
validates :bio, length: { maximum: 500 }
validates :password, length: { in: 6..20 }
validates :registration_number, length: { is: 6 }
end
Las posibles opciones de restricción de longitud son:
:minimum
- El atributo no puede tener menos de la longitud especificada.:maximum
- El atributo no puede tener más de la longitud especificada.:in
(o:within
) - La longitud del atributo debe estar incluida en un intervalo dado. El valor para esta opción debe ser un rango.:is
- La longitud del atributo debe ser igual al valor dado.
Los mensajes de error predeterminados dependen del tipo de validación de longitud que se esté realizando. Puedes personalizar estos mensajes utilizando las opciones :wrong_length
, :too_long
y :too_short
, y %{count}
como marcador de posición para el número correspondiente a la restricción de longitud que se está utilizando. Aún puedes usar la opción :message
para especificar un mensaje de error.
class Person < ApplicationRecord
validates :bio, length: { maximum: 1000,
too_long: "%{count} caracteres es el máximo permitido" }
end
Ten en cuenta que los mensajes de error predeterminados están en plural (por ejemplo, "es demasiado corto (el mínimo es %{count} caracteres)"). Por esta razón, cuando :minimum
es 1, debes proporcionar un mensaje personalizado o usar presence: true
en su lugar. Cuando :in
o :within
tienen un límite inferior de 1, debes proporcionar un mensaje personalizado o llamar a presence
antes de length
.
NOTA: Solo se puede usar una opción de restricción a la vez, aparte de las opciones :minimum
y :maximum
que se pueden combinar juntas.
2.8 numericality
Este ayudante valida que tus atributos tengan solo valores numéricos. Por defecto, coincidirá con un signo opcional seguido de un número entero o decimal.
Para especificar que solo se permiten números enteros, establece :only_integer
en true. Luego usará la siguiente expresión regular para validar el valor del atributo.
/\A[+-]?\d+\z/
De lo contrario, intentará convertir el valor a un número usando Float
. Los Float
se convierten a BigDecimal
utilizando el valor de precisión de la columna o un máximo de 15 dígitos.
class Player < ApplicationRecord
validates :points, numericality: true
validates :games_played, numericality: { only_integer: true }
end
El mensaje de error predeterminado para :only_integer
es "debe ser un número entero".
Además de :only_integer
, este ayudante también acepta la opción :only_numeric
, que especifica que el valor debe ser una instancia de Numeric
e intenta analizar el valor si es una String
.
NOTA: Por defecto, numericality
no permite valores nil
. Puedes usar la opción allow_nil: true
para permitirlo. Ten en cuenta que para las columnas Integer
y Float
, las cadenas vacías se convierten en nil
.
El mensaje de error predeterminado cuando no se especifican opciones es "no es un número".
También hay muchas opciones que se pueden usar para agregar restricciones a los valores aceptables:
:greater_than
- Especifica que el valor debe ser mayor que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser mayor que %{count}".:greater_than_or_equal_to
- Especifica que el valor debe ser mayor o igual que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser mayor o igual que %{count}".:equal_to
- Especifica que el valor debe ser igual al valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser igual a %{count}".:less_than
- Especifica que el valor debe ser menor que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser menor que %{count}".:less_than_or_equal_to
- Especifica que el valor debe ser menor o igual que el valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser menor o igual que %{count}".:other_than
- Especifica que el valor debe ser distinto del valor suministrado. El mensaje de error predeterminado para esta opción es "debe ser distinto de %{count}".:in
- Especifica que el valor debe estar dentro del rango suministrado. El mensaje de error predeterminado para esta opción es "debe estar en %{count}".:odd
- Especifica que el valor debe ser un número impar. El mensaje de error predeterminado para esta opción es "debe ser impar".:even
- Especifica que el valor debe ser un número par. El mensaje de error predeterminado para esta opción es "debe ser par".
2.9 presence
Este ayudante valida que los atributos especificados no estén vacíos. Utiliza el método Object#blank?
para verificar si el valor es nil
o una cadena vacía, es decir, una cadena que está vacía o consiste solo de espacios en blanco.
class Person < ApplicationRecord
validates :name, :login, :email, presence: true
end
Si quieres asegurarte de que una asociación esté presente, debes probar si el objeto asociado en sí está presente, y no la clave externa utilizada para mapear la asociación. De esta manera, no solo se verifica que la clave externa no esté vacía, sino que también existe el objeto referenciado.
class Supplier < ApplicationRecord
has_one :account
validates :account, presence: true
end
Para validar registros asociados cuya presencia es requerida, debes especificar la opción :inverse_of
para la asociación:
class Order < ApplicationRecord
has_many :line_items, inverse_of: :order
end
NOTA: Si deseas asegurarte de que la asociación esté presente y sea válida, también debes usar validates_associated
. Más información sobre eso abajo
Si validas la presencia de un objeto asociado a través de una relación has_one
o has_many
, se verificará que el objeto no esté blank?
ni marked_for_destruction?
.
Dado que false.blank?
es verdadero, si deseas validar la presencia de un campo booleano, debes usar una de las siguientes validaciones:
# El valor _debe_ ser verdadero o falso
validates :nombre_campo_booleano, inclusion: [true, false]
# El valor _no debe_ ser nulo, es decir, verdadero o falso
validates :nombre_campo_booleano, exclusion: [nil]
Al utilizar una de estas validaciones, asegurará que el valor NO sea nil
, lo que resultaría en un valor NULL
en la mayoría de los casos.
El mensaje de error predeterminado es "no puede estar en blanco".
2.10 absence
Este ayudante valida que los atributos especificados estén ausentes. Utiliza el método Object#present?
para verificar si el valor no es ni nil
ni una cadena en blanco, es decir, una cadena que esté vacía o consista solo en espacios en blanco.
class Person < ApplicationRecord
validates :name, :login, :email, absence: true
end
Si desea asegurarse de que una asociación esté ausente, deberá verificar si el objeto asociado en sí está ausente y no la clave externa utilizada para mapear la asociación.
class LineItem < ApplicationRecord
belongs_to :order
validates :order, absence: true
end
Para validar registros asociados cuya ausencia es requerida, debe especificar la opción :inverse_of
para la asociación:
class Order < ApplicationRecord
has_many :line_items, inverse_of: :order
end
NOTA: Si desea asegurarse de que la asociación esté presente y sea válida, también debe utilizar validates_associated
. Más información sobre eso a continuación.
Si valida la ausencia de un objeto asociado a través de una relación has_one
o has_many
, se verificará que el objeto no esté present?
ni marked_for_destruction?
.
Dado que false.present?
es falso, si desea validar la ausencia de un campo booleano, debe usar validates :nombre_campo, exclusion: { in: [true, false] }
.
El mensaje de error predeterminado es "debe estar en blanco".
2.11 uniqueness
Este ayudante valida que el valor del atributo sea único justo antes de que se guarde el objeto.
class Account < ApplicationRecord
validates :email, uniqueness: true
end
La validación se realiza realizando una consulta SQL en la tabla del modelo, buscando un registro existente con el mismo valor en ese atributo.
Hay una opción :scope
que puede usar para especificar uno o más atributos que se utilizan para limitar la verificación de unicidad:
class Holiday < ApplicationRecord
validates :name, uniqueness: { scope: :year,
message: "debe ocurrir una vez al año" }
end
ADVERTENCIA. Esta validación no crea una restricción de unicidad en la base de datos, por lo que puede suceder que dos conexiones de base de datos diferentes creen dos registros con el mismo valor para una columna que se supone que debe ser única. Para evitar eso, debe crear un índice único en esa columna en su base de datos.
Para agregar una restricción de unicidad en la base de datos, use la declaración add_index
en una migración e incluya la opción unique: true
.
Si desea crear una restricción de base de datos para evitar posibles violaciones de una validación de unicidad utilizando la opción :scope
, debe crear un índice único en ambas columnas de su base de datos. Consulte el manual de MySQL para obtener más detalles sobre índices de varias columnas o el manual de PostgreSQL para ver ejemplos de restricciones únicas que se refieren a un grupo de columnas.
También hay una opción :case_sensitive
que puede usar para definir si la restricción de unicidad será sensible a mayúsculas y minúsculas, insensible a mayúsculas y minúsculas o respetará la intercalación predeterminada de la base de datos. Esta opción tiene como valor predeterminado respetar la intercalación predeterminada de la base de datos.
class Person < ApplicationRecord
validates :name, uniqueness: { case_sensitive: false }
end
ADVERTENCIA. Tenga en cuenta que algunas bases de datos están configuradas para realizar búsquedas insensibles a mayúsculas y minúsculas de todos modos.
Hay una opción :conditions
que puede especificar condiciones adicionales como un fragmento SQL WHERE
para limitar la búsqueda de la restricción de unicidad (por ejemplo, conditions: -> { where(status: 'active') }
).
El mensaje de error predeterminado es "ya ha sido tomado".
Consulte validates_uniqueness_of
para obtener más información.
2.12 validates_associated
Debe utilizar este ayudante cuando su modelo tenga asociaciones que siempre deben validarse. Cada vez que intente guardar su objeto, se llamará a valid?
en cada uno de los objetos asociados.
class Library < ApplicationRecord
has_many :books
validates_associated :books
end
Esta validación funcionará con todos los tipos de asociaciones.
PRECAUCIÓN: No utilice validates_associated
en ambos extremos de sus asociaciones. Se llamarían entre sí en un bucle infinito.
El mensaje de error predeterminado para validates_associated
es "no es válido". Tenga en cuenta que cada objeto asociado contendrá su propia colección de errors
; los errores no se propagan al modelo que llama.
NOTA: validates_associated
solo se puede usar con objetos ActiveRecord, todo hasta ahora también se puede usar en cualquier objeto que incluya ActiveModel::Validations
.
2.13 validates_each
Este ayudante valida los atributos contra un bloque. No tiene una función de validación predefinida. Debe crear una usando un bloque, y cada atributo pasado a validates_each
se probará contra él.
En el siguiente ejemplo, rechazaremos nombres y apellidos que comiencen con minúsculas.
class Person < ApplicationRecord
validates_each :name, :surname do |record, attr, value|
record.errors.add(attr, 'debe comenzar con mayúscula') if /\A[[:lower:]]/.match?(value)
end
end
El bloque recibe el registro, el nombre del atributo y el valor del atributo.
Puede hacer cualquier cosa que desee para verificar datos válidos dentro del bloque. Si su validación falla, debe agregar un error al modelo, lo que lo hace inválido.
2.14 validates_with
Este ayudante pasa el registro a una clase separada para su validación.
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if record.first_name == "Evil"
record.errors.add :base, "Esta persona es malvada"
end
end
end
class Person < ApplicationRecord
validates_with GoodnessValidator
end
No hay un mensaje de error predeterminado para validates_with
. Debe agregar manualmente errores a la colección de errores del registro en la clase validadora.
NOTA: Los errores agregados a record.errors[:base]
se relacionan con el estado del registro en su conjunto.
Para implementar el método de validación, debe aceptar un parámetro record
en la definición del método, que es el registro que se va a validar.
Si desea agregar un error en un atributo específico, páselo como primer argumento, como record.errors.add(:first_name, "por favor elija otro nombre")
. Cubriremos los [errores de validación][] con más detalle más adelante.
def validate(record)
if record.some_field != "aceptable"
record.errors.add :some_field, "este campo no es aceptable"
end
end
El ayudante validates_with
toma una clase o una lista de clases para usar en la validación.
class Person < ApplicationRecord
validates_with MyValidator, MyOtherValidator, on: :create
end
Al igual que todas las demás validaciones, validates_with
toma las opciones :if
, :unless
y :on
. Si pasa cualquier otra opción, las enviará a la clase validadora como options
:
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if options[:fields].any? { |field| record.send(field) == "Evil" }
record.errors.add :base, "Esta persona es malvada"
end
end
end
class Person < ApplicationRecord
validates_with GoodnessValidator, fields: [:first_name, :last_name]
end
Tenga en cuenta que el validador se inicializará solo una vez durante todo el ciclo de vida de la aplicación, y no en cada ejecución de validación, así que tenga cuidado al usar variables de instancia dentro de él.
Si su validador es lo suficientemente complejo como para que desee variables de instancia, puede usar fácilmente un objeto Ruby normal en su lugar:
class Person < ApplicationRecord
validate do |person|
GoodnessValidator.new(person).validate
end
end
class GoodnessValidator
def initialize(person)
@person = person
end
def validate
if some_complex_condition_involving_ivars_and_private_methods?
@person.errors.add :base, "Esta persona es malvada"
end
end
# ...
end
Cubriremos validaciones personalizadas más adelante.
3 Opciones comunes de validación
Hay varias opciones comunes admitidas por los validadores que acabamos de ver, ¡veamos algunas de ellas ahora!
NOTA: No todas estas opciones son compatibles con todos los validadores, consulte la documentación de la API de ActiveModel::Validations
.
Al utilizar cualquiera de los métodos de validación que acabamos de mencionar, también hay una lista de opciones comunes compartidas junto con los validadores. ¡Vamos a cubrirlos ahora!
:allow_nil
: Omitir la validación si el atributo esnil
.:allow_blank
: Omitir la validación si el atributo está en blanco.:message
: Especificar un mensaje de error personalizado.:on
: Especificar los contextos en los que esta validación está activa.:strict
: Lanzar una excepción cuando la validación falla.:if
y:unless
: Especificar cuándo debe o no debe ocurrir la validación.
3.1 :allow_nil
La opción :allow_nil
omite la validación cuando el valor que se está validando es nil
.
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} no es un tamaño válido" }, allow_nil: true
end
irb> Coffee.create(size: nil).valid?
=> true
irb> Coffee.create(size: "mega").valid?
=> false
Para obtener opciones completas para el argumento de mensaje, consulte la documentación de mensajes.
3.2 :allow_blank
La opción :allow_blank
es similar a la opción :allow_nil
. Esta opción permite que la validación pase si el valor del atributo está en blanco, como nil
o una cadena vacía, por ejemplo.
class Topic < ApplicationRecord
validates :title, length: { is: 5 }, allow_blank: true
end
irb> Topic.create(title: "").valid?
=> true
irb> Topic.create(title: nil).valid?
=> true
3.3 :message
Como ya has visto, la opción :message
te permite especificar el mensaje que se agregará a la colección de errors
cuando la validación falla. Cuando no se utiliza esta opción, Active Record utilizará el mensaje de error predeterminado correspondiente para cada ayudante de validación.
La opción :message
acepta tanto un String
como un Proc
como valor.
Un valor String
para :message
puede contener opcionalmente cualquier combinación de %{value}
, %{attribute}
y %{model}
, que se reemplazarán dinámicamente cuando la validación falle. Esta sustitución se realiza utilizando la gema i18n, y los marcadores de posición deben coincidir exactamente, no se permiten espacios.
class Person < ApplicationRecord
# Mensaje codificado
validates :name, presence: { message: "debe ser proporcionado por favor" }
# Mensaje con valor de atributo dinámico. %{value} será reemplazado
# con el valor real del atributo. %{attribute} y %{model}
# también están disponibles.
validates :age, numericality: { message: "%{value} parece incorrecto" }
end
Un valor Proc
para :message
recibe dos argumentos: el objeto que se está validando y un hash con pares clave-valor :model
, :attribute
y :value
.
class Person < ApplicationRecord
validates :username,
uniqueness: {
# object = objeto de persona que se está validando
# data = { model: "Person", attribute: "Username", value: <username> }
message: ->(object, data) do
"Hola #{object.name}, #{data[:value]} ya está en uso."
end
}
end
3.4 :on
La opción :on
te permite especificar cuándo debe ocurrir la validación. El comportamiento predeterminado para todos los ayudantes de validación integrados es ejecutarse al guardar (tanto al crear un nuevo registro como al actualizarlo). Si quieres cambiarlo, puedes usar on: :create
para ejecutar la validación solo cuando se crea un nuevo registro o on: :update
para ejecutar la validación solo cuando se actualiza un registro.
class Person < ApplicationRecord
# será posible actualizar el correo electrónico con un valor duplicado
validates :email, uniqueness: true, on: :create
# será posible crear el registro con una edad no numérica
validates :age, numericality: true, on: :update
# el predeterminado (valida tanto en la creación como en la actualización)
validates :name, presence: true
end
También puedes usar on:
para definir contextos personalizados. Los contextos personalizados deben activarse explícitamente pasando el nombre del contexto a valid?
, invalid?
o save
.
class Person < ApplicationRecord
validates :email, uniqueness: true, on: :account_setup
validates :age, numericality: true, on: :account_setup
end
irb> person = Person.new(age: 'treinta y tres')
irb> person.valid?
=> true
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["ya ha sido tomado"], :age=>["no es un número"]}
person.valid?(:account_setup)
ejecuta ambas validaciones sin guardar el modelo. person.save(context: :account_setup)
valida person
en el contexto account_setup
antes de guardar.
También se acepta pasar una matriz de símbolos.
class Book
include ActiveModel::Validations
validates :title, presence: true, on: [:update, :ensure_title]
end
irb> book = Book.new(title: nil)
irb> book.valid?
=> true
irb> book.valid?(:ensure_title)
=> false
irb> book.errors.messages
=> {:title=>["no puede estar en blanco"]}
Cuando se activa mediante un contexto explícito, las validaciones se ejecutan para ese contexto, así como para cualquier validación sin contexto.
class Person < ApplicationRecord
validates :email, uniqueness: true, on: :account_setup
validates :age, numericality: true, on: :account_setup
validates :name, presence: true
end
irb> person = Person.new
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["ya ha sido tomado"], :age=>["no es un número"], :name=>["no puede estar en blanco"]}
Abordaremos más casos de uso para on:
en la guía de callbacks.
4 Validaciones Estrictas
También puedes especificar validaciones estrictas que generen una excepción ActiveModel::StrictValidationFailed
cuando el objeto no sea válido.
class Person < ApplicationRecord
validates :name, presence: { strict: true }
end
irb> Person.new.valid?
ActiveModel::StrictValidationFailed: El nombre no puede estar en blanco
También se puede pasar una excepción personalizada a la opción :strict
.
class Person < ApplicationRecord
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
irb> Person.new.valid?
TokenGenerationException: El token no puede estar en blanco
5 Validación Condicional
A veces tiene sentido validar un objeto solo cuando se cumple un predicado determinado. Puedes hacerlo utilizando las opciones :if
y :unless
, que pueden tomar un símbolo, un Proc
o una matriz. Puedes usar la opción :if
cuando quieras especificar cuándo debe ocurrir la validación. Alternativamente, si quieres especificar cuándo no debe ocurrir la validación, puedes usar la opción :unless
.
5.1 Uso de un símbolo con :if
y :unless
Puede asociar las opciones :if
y :unless
con un símbolo correspondiente al nombre de un método que se llamará justo antes de que ocurra la validación. Esta es la opción más comúnmente utilizada.
class Order < ApplicationRecord
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
5.2 Uso de un Proc con :if
y :unless
Es posible asociar :if
y :unless
con un objeto Proc
que se llamará. El uso de un objeto Proc
le brinda la capacidad de escribir una condición en línea en lugar de un método separado. Esta opción es más adecuada para una sola línea.
class Account < ApplicationRecord
validates :password, confirmation: true,
unless: Proc.new { |a| a.password.blank? }
end
Como lambda
es un tipo de Proc
, también se puede usar para escribir condiciones en línea aprovechando la sintaxis abreviada.
validates :password, confirmation: true, unless: -> { password.blank? }
5.3 Agrupación de validaciones condicionales
A veces es útil que varias validaciones utilicen una misma condición. Esto se puede lograr fácilmente utilizando with_options
.
class User < ApplicationRecord
with_options if: :is_admin? do |admin|
admin.validates :password, length: { minimum: 10 }
admin.validates :email, presence: true
end
end
Todas las validaciones dentro del bloque with_options
habrán pasado automáticamente la condición if: :is_admin?
5.4 Combinación de condiciones de validación
Por otro lado, cuando varias condiciones definen si una validación debe ocurrir o no, se puede usar un Array
. Además, se pueden aplicar tanto :if
como :unless
a la misma validación.
class Computer < ApplicationRecord
validates :mouse, presence: true,
if: [Proc.new { |c| c.market.retail? }, :desktop?],
unless: Proc.new { |c| c.trackpad.present? }
end
La validación solo se ejecuta cuando todas las condiciones :if
y ninguna de las condiciones :unless
se evalúan como true
.
6 Realización de validaciones personalizadas
Cuando los ayudantes de validación incorporados no son suficientes para sus necesidades, puede escribir sus propios validadores o métodos de validación según lo prefiera.
6.1 Validadores personalizados
Los validadores personalizados son clases que heredan de ActiveModel::Validator
. Estas clases deben implementar el método validate
que toma un registro como argumento y realiza la validación en él. El validador personalizado se llama utilizando el método validates_with
.
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.start_with? 'X'
record.errors.add :name, "¡Proporcione un nombre que comience con X, por favor!"
end
end
end
class Person < ApplicationRecord
validates_with MyValidator
end
La forma más sencilla de agregar validadores personalizados para validar atributos individuales es con el conveniente ActiveModel::EachValidator
. En este caso, la clase de validador personalizado debe implementar un método validate_each
que toma tres argumentos: registro, atributo y valor. Estos corresponden a la instancia, el atributo que se va a validar y el valor del atributo en la instancia pasada.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless URI::MailTo::EMAIL_REGEXP.match?(value)
record.errors.add attribute, (options[:message] || "no es un correo electrónico")
end
end
end
class Person < ApplicationRecord
validates :email, presence: true, email: true
end
Como se muestra en el ejemplo, también puede combinar validaciones estándar con sus propios validadores personalizados.
6.2 Métodos personalizados
También puede crear métodos que verifiquen el estado de sus modelos y agreguen errores a la colección de errors
cuando no sean válidos. Luego debe registrar estos métodos utilizando el método de clase validate
, pasando los símbolos de los nombres de los métodos de validación.
Puede pasar más de un símbolo para cada método de clase y las respectivas validaciones se ejecutarán en el mismo orden en que se registraron.
El método valid?
verificará que la colección de errors
esté vacía, por lo que sus métodos de validación personalizados deben agregar errores a ella cuando desee que la validación falle:
class Invoice < ApplicationRecord
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
if expiration_date.present? && expiration_date < Date.today
errors.add(:expiration_date, "no puede estar en el pasado")
end
end
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "no puede ser mayor que el valor total")
end
end
end
De forma predeterminada, estas validaciones se ejecutarán cada vez que llame a valid?
o guarde el objeto. Pero también es posible controlar cuándo ejecutar estas validaciones personalizadas mediante la opción :on
del método validate
, con :create
o :update
.
class Invoice < ApplicationRecord
validate :active_customer, on: :create
def active_customer
errors.add(:customer_id, "no está activo") unless customer.active?
end
end
Consulte la sección anterior para obtener más detalles sobre :on
.
6.3 Listado de validadores
Si desea conocer todos los validadores para un objeto dado, no busque más allá de validators
.
Por ejemplo, si tenemos el siguiente modelo que utiliza un validador personalizado y un validador incorporado:
class Person < ApplicationRecord
validates :name, presence: true, on: :create
validates :email, format: URI::MailTo::EMAIL_REGEXP
validates_with MyOtherValidator, strict: true
end
Ahora podemos usar validators
en el modelo "Person" para listar todos los validadores, o incluso verificar un campo específico usando validators_on
.
irb> Person.validators
#=> [#<ActiveRecord::Validations::PresenceValidator:0x10b2f2158
@attributes=[:name], @options={:on=>:create}>,
#<MyOtherValidatorValidator:0x10b2f17d0
@attributes=[:name], @options={:strict=>true}>,
#<ActiveModel::Validations::FormatValidator:0x10b2f0f10
@attributes=[:email],
@options={:with=>URI::MailTo::EMAIL_REGEXP}>]
#<MyOtherValidator:0x10b2f0948 @options={:strict=>true}>]
irb> Person.validators_on(:name)
#=> [#<ActiveModel::Validations::PresenceValidator:0x10b2f2158
@attributes=[:name], @options={on: :create}>]
7 Trabajando con errores de validación
Los métodos valid?
e invalid?
solo proporcionan un resumen del estado de validez. Sin embargo, puede profundizar en cada error individual utilizando varios métodos de la colección errors
.
A continuación se muestra una lista de los métodos más utilizados. Consulte la documentación de ActiveModel::Errors
para obtener una lista de todos los métodos disponibles.
7.1 errors
La puerta de entrada a través de la cual puede profundizar en varios detalles de cada error.
Esto devuelve una instancia de la clase ActiveModel::Errors
que contiene todos los errores, cada error está representado por un objeto ActiveModel::Error
.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.full_messages
=> ["El nombre no puede estar en blanco", "El nombre es demasiado corto (mínimo 3 caracteres)"]
irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors.full_messages
=> []
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.first.details
=> {:error=>:too_short, :count=>3}
7.2 errors[]
errors[]
se utiliza cuando se desea verificar los mensajes de error para un atributo específico. Devuelve una matriz de cadenas con todos los mensajes de error para el atributo dado, cada cadena con un mensaje de error. Si no hay errores relacionados con el atributo, devuelve una matriz vacía.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors[:name]
=> []
irb> person = Person.new(name: "JD")
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["es demasiado corto (mínimo 3 caracteres)"]
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["no puede estar en blanco", "es demasiado corto (mínimo 3 caracteres)"]
7.3 errors.where
y objeto de error
A veces, es posible que necesitemos más información sobre cada error además de su mensaje. Cada error se encapsula como un objeto ActiveModel::Error
, y el método where
es la forma más común de acceder.
where
devuelve una matriz de objetos de error filtrados por diversos grados de condiciones.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
Podemos filtrar solo por el atributo
pasándolo como el primer parámetro a errors.where(:attr)
. El segundo parámetro se utiliza para filtrar el tipo
de error que queremos llamando a errors.where(:attr, :type)
.
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.where(:name)
=> [ ... ] # todos los errores para el atributo :name
irb> person.errors.where(:name, :too_short)
=> [ ... ] # errores :too_short para el atributo :name
Por último, podemos filtrar por cualquier opción
que pueda existir en el tipo de objeto de error dado.
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.where(:name, :too_short, minimum: 3)
=> [ ... ] # todos los errores de nombre que son demasiado cortos y el mínimo es 2
Puede leer varias informaciones de estos objetos de error:
irb> error = person.errors.where(:name).last
irb> error.attribute
=> :name
irb> error.type
=> :too_short
irb> error.options[:count]
=> 3
También puede generar el mensaje de error:
irb> error.message
=> "es demasiado corto (mínimo 3 caracteres)"
irb> error.full_message
=> "El nombre es demasiado corto (mínimo 3 caracteres)"
El método full_message
genera un mensaje más amigable para el usuario, con el nombre del atributo en mayúscula antepuesto. (Para personalizar el formato que utiliza full_message
, consulte la guía de I18n.)
7.4 errors.add
El método add
crea el objeto de error tomando el atributo
, el tipo
de error y un hash adicional de opciones. Esto es útil cuando se escribe su propio validador, ya que le permite definir situaciones de error muy específicas.
class Person < ApplicationRecord
validate do |person|
errors.add :name, :too_plain, message: "no es lo suficientemente genial"
end
end
irb> person = Person.create
irb> person.errors.where(:name).first.type
=> :too_plain
irb> person.errors.where(:name).first.full_message
=> "El nombre no es lo suficientemente cool"
7.5 errors[:base]
Puedes agregar errores que estén relacionados con el estado del objeto en su totalidad, en lugar de estar relacionados con un atributo específico. Para hacer esto, debes usar :base
como el atributo al agregar un nuevo error.
class Person < ApplicationRecord
validate do |person|
errors.add :base, :invalid, message: "Esta persona es inválida porque ..."
end
end
irb> person = Person.create
irb> person.errors.where(:base).first.full_message
=> "Esta persona es inválida porque ..."
7.6 errors.size
El método size
devuelve el número total de errores para el objeto.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.size
=> 2
irb> person = Person.new(name: "Andrea", email: "[email protected]")
irb> person.valid?
=> true
irb> person.errors.size
=> 0
7.7 errors.clear
El método clear
se utiliza cuando quieres borrar intencionalmente la colección de errors
. Por supuesto, llamar a errors.clear
en un objeto inválido no lo hará válido: la colección de errors
ahora estará vacía, pero la próxima vez que llames a valid?
o cualquier método que intente guardar este objeto en la base de datos, las validaciones se ejecutarán nuevamente. Si alguna de las validaciones falla, la colección de errors
se llenará nuevamente.
class Person < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.empty?
=> false
irb> person.errors.clear
irb> person.errors.empty?
=> true
irb> person.save
=> false
irb> person.errors.empty?
=> false
8 Mostrar errores de validación en las vistas
Una vez que hayas creado un modelo y agregado validaciones, si ese modelo se crea a través de un formulario web, probablemente quieras mostrar un mensaje de error cuando una de las validaciones falle.
Debido a que cada aplicación maneja este tipo de cosas de manera diferente, Rails no incluye ningún helper de vista para ayudarte a generar estos mensajes directamente. Sin embargo, debido a la gran cantidad de métodos que Rails te proporciona para interactuar con las validaciones en general, puedes construir los tuyos propios. Además, al generar un scaffold, Rails colocará algo de ERB en el _form.html.erb
que genera y que muestra la lista completa de errores en ese modelo.
Suponiendo que tenemos un modelo que se ha guardado en una variable de instancia llamada @article
, se vería así:
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %> impidieron que se guardara este artículo:</h2>
<ul>
<% @article.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
Además, si utilizas los helpers de formulario de Rails para generar tus formularios, cuando ocurre un error de validación en un campo, generará un <div>
adicional alrededor de la entrada.
<div class="field_with_errors">
<input id="article_title" name="article[title]" size="30" type="text" value="">
</div>
Luego puedes dar estilo a este div como desees. El scaffold predeterminado que genera Rails, por ejemplo, agrega esta regla CSS:
.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}
Esto significa que cualquier campo con un error termina con un borde rojo de 2 píxeles.
Comentarios
Se te anima a ayudar a mejorar la calidad de esta guía.
Por favor, contribuye si encuentras algún error tipográfico o factual. Para empezar, puedes leer nuestra contribución a la documentación sección.
También puedes encontrar contenido incompleto o desactualizado. Por favor, añade cualquier documentación faltante para main. Asegúrate de revisar Edge Guides primero para verificar si los problemas ya están resueltos o no en la rama principal. Consulta las Directrices de las Guías de Ruby on Rails para el estilo y las convenciones.
Si por alguna razón encuentras algo que corregir pero no puedes solucionarlo tú mismo, por favor abre un problema.
Y por último, cualquier tipo de discusión sobre la documentación de Ruby on Rails es muy bienvenida en el Foro oficial de Ruby on Rails.