mkdir cool-app
cd cool-app
Usar RVM para crear un gemset para el proyecto con la versión de Ruby deseada.
rvm --create --ruby-version use ruby-2.2.3@cool-app
Al generar los archivos base de nuestra aplicación usando rails new
, hacemos uso de la opción --database=postgresql
. Eso agregará la gema automáticamente al gemfile.
Una vez modificado el archivo de configuración config/database.yml
con los valores adecuados, creamos la base de datos (test, development) con:
rake db:setup
Para usar RSpec por default en el proyecto, basta con agregar lo siguiente al gemfile:
group :development, :test do
gem 'rspec-rails'
end
Con su respectivo bundle install
. RSpec incluye un generador, como veremos más adelante.
-
rails new
inicializa una nueva aplicación. -
rails server
inicia el servidor de la aplicación. Se especifica el entorno de ejecución con el argumento-e
.rails server -e production
. -
rails generate
usa templates para generar múltiples archivos. -
rails console
permite interactuar con la aplicación usando IRB. -
rails dbconsole
ejecuta el CLI de la base de datos usada en la aplicación. -
rails runner
permite ejecutar código de ruby en segundo plano. -
rails destroy
remueve archivos creador por un generador.
R uby's M ake, es una utilidad que usa Rakefiles y archivos .rake
para ejecutar tareas.
-
rake --tasks
listará las tareas disponibles en el directorio actual. -
rake about
devuelve información acerca del entorno, como versión de Ruby, RubyGems, Rails, Middleware, base de datos, motor de JS, etc. -
rake assets
administra los assets de la aplicación. Algunos tasks disponibles son:assets:precompile
,assets:clean
,assets:clobber
. -
rake db
ejecuta tareas relacionadas con la base de datos, tales comodb:create
,db:up
,db:down
,db:redo
,db:reset
,db:version
. -
rake notes
permite encontrar comentarios especiales en el código, tales que empiecen conTODO
,OPTIMIZE
,FIXME
. Puede especificarse un tipo de comentario conrake notes:fixme
, o incluso personalizados como enrake notes:custom ANNOTATION=MYBUG
. -
rake routes
listará todas las rutas definidas de la aplicación. -
rake tmp
permite realizar acciones sobre la carpetatmp
.tmp:cache:clear
,tmp:sessions:clear
,tmp:sockets:clear
limpian carpetas específicas dentro detmp
.tmp:clear
limpia todo.tmp:create
crea los directorios dentro detmp
. -
rake stats
muestra estadíticas del código. -
rake secret
genera una llave pseudo-aleatoria usada generalmente para la clave secreta de sesión.
Opciones generales de comandos generadores
-h, [--help] # Descripción de uso
-p, [--pretend] # Simula la ejecución
-f, [--force] # Sobreescribe archivos existentes
-s, [--skip] # Omite crear archivos existentes
-q, [--quiet] # No output
Genera controlador + test, vistas.
rails g controller <ControllerNamePlural> [<action>]* [<options>]
Genera modelo + test y la migración correspondiente.
rails g model <ModelName> [<fieldname>[:<type>][:<index>]]* [<options>]
Field types:
string
text
integer
float
decimal
boolean
date
datetime
time
timestamp
primary_key
binary
Genera modelo, migración, controlador, vistas, la ruta como recurso y todos los tests.
rails g scaffold <ModelName> [<field>[:<type>][:<index>]]* [<options>]
Genera controlador + test, vistas.
rails g scaffold_controller <ModelName> [field:type field:type] [options]
Actualizar el gemfile con gem "haml-rails"
para luego convertir las vistas generadas a HAML usando:
rake haml:erb2haml
Para crear los archivos base para nuestra suite de tests
rails g rspec:install
Correr migraciones pendientes usando rake db:migrate
Se puede especificar el ENV
para seleccionar la base de datos donde se desean aplicar las migraciones.
RAILS_ENV=production rake db:migrate
Se usa rake routes
para mostrar las rutas existentes.
Una ruta de recurso genera todos los endpoints REST para una entidad.
resources :articles
genera las siguientes rutas:
get /articles => articles#index
get /articles/:id => articles#show
get /articles/new => articles#new
post /articles => articles#create
get /articles/:id/edit => articles#edit
put|patch /articles/:id => articles#update
delete /articles/:id => articles#destroy
Ejemplo básico de un formulario:
form_for @article do |f|
f.text_field :title, class: "form-control"
f.text_area :body, class: "form-control"
f.submit "Save", class: "btn"
end
No hay que definir la url destino puesto que al ser un recurso REST, el formulario puede deducirla dependiendo si el elemento que se envía como primer parámetro es persistente o no.
Por cuestiones de seguridad, debemos definir qué parámetros se pueden recibir en ciertas acciones en los controladores, esto, usando require
y permit
.
@article = Article.new(params.require(:article).permit(:title, :text))
Generalmente, se usa un método del controlador para manejar los strong parameteres.
private
def article_params
params.require(:article).permit(:title, :text)
end
Y la implementación quedaría como:
@article = Article.new(article_params)
Se definen en el modelo usando la palabra clave validates
y tienen la forma:
validates :fieldname, {rules}
Donde rules pueden ser uno o varios helpers u opciones.
-
acceptance
(bool |{accept: <value>}
) checa la presencia de un valor. Si el atributo no existe en el modelo, se virtualiza (no se salva). Puede tomar la forma de un objeto con la claveaccept
para especificar el valor que se debe recibir para considerarse aceptado. -
confirmation
(bool) valida que 2 campos tengan el mismo valor. Virtualiza el segundo campo que tiene la forma<fieldname>_confirmation
. La validación ocurre sólo si el valor del campo virtual no esnil
. -
exclusion
({in: <enumerator>}
) usando la clavein
para indicar con un objeto enumerable un conjunto de valores no permitidos. También acepta la clavemessage
para poder hacer uso del valor inválido, usando interpolación:{.., message: "%{value} is reserved."}
. -
inclusion
({in: <enumerator>}
) como contraparte deexclusion
. -
format
({with|without: <regex>}
) usa una expresión regular para validar formato del valor. Tiene una versión negativa usando la clavewithout
. Acepta la clavemessage
. -
length
({minimum|maximum|in|is: <int>|<range> }
) valida la longitud de un valor de diferentes formas. Permite la personalización del mensaje usando las claves:wrong_length
,:too_short
ytoo_long
así como el token%{count}
con la longitud recuperada. La clavemessage
también puede ser usada de manera general.tokenizer
permite definir qué elementos se cuentan (letras, por default) como por ejemplo, contar palabras:tokenizer: lambda { |str| str.split(/\s+/) }
. -
numericality
(bool | obj) espera recibir únicamente valores numéricos de cualquier tipo, incluyendo, de manera opcional, un signo (+,-). Algunas claves opcionales::only_integer
para aceptar sólo números enteros.:greater_than
mayor que.:greater_than_or_equal_to
mayor o igual que.:equal_to
valor exacto.:less_than
menor que.:less_than_or_equal_to
menor o igual que.:odd
valor impar.:even
valor par.
-
presence
(bool) verifica que el valor esté presente y no sea vacío. Fallará si el valor esnil
, una cadena vacía o llena de espacios. Puede usarse para validar que una asociación está presente.class LineItem < ActiveRecord::Base belongs_to :order validates :order, presence: true end
Para validar la presencia de valores booleanos se aconseja hacerlo de la siguiente manera:
validates :boolean_field_name, presence: true validates :boolean_field_name, inclusion: { in: [true, false] } validates :boolean_field_name, exclusion: { in: [nil] }
-
absence
contraparte depresence
, se asegura que el valor no esté presente. Igualmente puede usarse con asociaciones de modelo. Para validar la ausencia de un booleano se usa:validates :field_name, exclusion: { in: [true, false] }
-
uniqueness
se asegura que el valor sea único dentro de un conjunto determinado (toda la tabla, por default).:case_sensitive
define si la unicidad del valor distingue entre mayúsculas y minúsculas.:scope
permite redefinir el conjunto al que se aplica la regla, definiendo un atributo que sirve como condición.validates :name, uniqueness: { scope: :year, message: "should happen once per year" }
-
validates_with
permite especificar una clase validadora para realizar la validación. Ésta debe encargarse de asignar los mensajes de error, puesto que no existen mensajes por default en estos casos.class GoodnessValidator < ActiveModel::Validator def validate(record) if ... record.errors[:base] << "Error sin atributo relacionado" end end end class Person < ActiveRecord::Base validates_with GoodnessValidator end
Cualquier opción usada en esta regla, que sea diferente de
:on
,:if
o:unless
será pasada a la clase validadora donde será accesible en el métodovalidate
desde la variableoptions
. -
validates_each
usa un bloque para validar varios campos, el cual recibe 3 argumentos:record
, el registro completo;attr
, el nombre del atributo en turno yvalue
el valor del atributo a validar.class Person < ActiveRecord::Base validates_each :name, :surname do |record, attr, value| record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/ end end
-
validates_associated
permite extender la validación a los modelos relacionados.NOTA: Debe evitarse el uso en ambas partes de una asociación o provocará una validación recursiva infinita.
Ejemplo:
class Library < ActiveRecord::Base has_many :books validates_associated :books end
Algunas opciones que pueden aplicarse a los helpers de validación son:
-
:on
(:create
|:update
) define el escenario de aplicación de la validación. Si se omite la opción, la validación correrá para ambos casos. -
:message
(string) permite definir el mensaje de error a mostrar. -
:allow_nil
omite la validación si el valor esnil
. -
:allow_blank
pasa si se cumpleblank?
para el valor a probar (nil
,""
, por ejemplo). -
:strict
(bool | Class ) al fallar la validación arroja una excepciónActiveModel::StrictValidationFailed
, o una personalizada si se especifica. -
:if
and:unless
aplica la regla sólo si una condición se cumple. Puede recibir un símbolo (para llamar un método), cadena (que se evalúa como código Ruby), Proc (definir una condición inline) o Array (para especificar varias condiciones).validates :fieldname, unless: Proc.new { |record| record.fieldname.blank? }
Se puede hacer uso de with_options
para aplicar una o más opciones a un bloque de declaraciones de reglas de validación:
class User < ActiveRecord::Base
with_options if: :is_admin? do |admin|
admin.validates :password, length: { minimum: 10 }
admin.validates :email, presence: true
end
end
Consisten en clases basadas en ActiveModel::Validator
que implementan un método validate
que recibe el registro a validar y que se encargan de asignar los errores manualmente. La clase se invoca usando validates_with
.
Para realizar una validación personalizada sobre un atributo en específico, se usa la clase ActiveModel::EachValidator
como base y un método validate_each
que recibe 3 argumentos: record
, attribute
y value
.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
Entonces puede usarse en combinación con otros helpers.
class Person < ActiveRecord::Base
validates :email, presence: true, email: true
end
Una alternativa a usar clases validadoras, es usar métodos. Con validate
se pueden asignar, mediante símbolos, uno o varios métodos validadores, los cuales correrán en el orden en el que fueron asignados.
class Invoice < ActiveRecord::Base
validate :valid_expiration,
:valid_discount
def valid_expiration
if expiration_date.present? && expiration_date < Date.today
errors.add(:expiration_date, "can't be in the past")
end
end
def valid_discount
if discount > total_value
errors.add(:discount, "can't be greater than total value")
end
end
end
Estos métodos pueden hacer uso de algunas opciones como si fueran helpers.
-
errors
devuelve una instancia deActiveModel::Errors
con todos los errores. -
errors[:attribute]
se usa para consultar el array de los mensajes de error de un atributo específico. Éstos sólo contienen el enunciado del error (sin nombre del campo). -
errors.add(:attribute, "message")
permite agregar mensajes de error al atributo especificado. De igual forma se usaerrors[:attribute] = "message"
. -
errors[:base]
es un array donde se asignan errores del objeto que no se relacionan a algún atributo específico. -
errors.full_messages
contiene los mensajes formateados (que incluyen el nombre del campo) que se mostrarán en la vista. -
errors.clear
vacía el array de mensajes de error.
Algunos métodos que detonan las validaciones (a menos que se use validate: false
como argumento) son:
create
create!
save
save!
update
update!
valid?
invalid?
Aquellos métodos sin bang (!
) regresan false en caso de fallar la validación. Métodos con bang arrojarán una excepción en ese mismo caso.
Otros métodos permiten salvar datos sin realizar validaciones:
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
Rails no incluye helpers nativos para listar los contenidos de @model.errors
, pero podemos hacerlo a nuestro modo de la siguiente manera:
<% if @article.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Adicionalmente, si se usan los helpers de formulario, los inputs con errores serán mostrados dentro de un div
con la clase field_with_errors
.