Skip to content

Instantly share code, notes, and snippets.

@dportalesr
Created September 7, 2017 23:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dportalesr/9cae422b9e2442f89e1466b74449109a to your computer and use it in GitHub Desktop.
Save dportalesr/9cae422b9e2442f89e1466b74449109a to your computer and use it in GitHub Desktop.
ruby-on-rails.md

Preparación

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

Base de Datos

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

Tests

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.

Command Line

  • 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.

Rake

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 como db:create, db:up, db:down, db:redo, db:reset, db:version.

  • rake notes permite encontrar comentarios especiales en el código, tales que empiecen con TODO, OPTIMIZE, FIXME. Puede especificarse un tipo de comentario con rake notes:fixme, o incluso personalizados como en rake notes:custom ANNOTATION=MYBUG.

  • rake routes listará todas las rutas definidas de la aplicación.

  • rake tmp permite realizar acciones sobre la carpeta tmp. tmp:cache:clear, tmp:sessions:clear, tmp:sockets:clear limpian carpetas específicas dentro de tmp. tmp:clear limpia todo. tmp:create crea los directorios dentro de tmp.

  • rake stats muestra estadíticas del código.

  • rake secret genera una llave pseudo-aleatoria usada generalmente para la clave secreta de sesión.

Generadores

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

Controller

Genera controlador + test, vistas.

rails g controller <ControllerNamePlural> [<action>]* [<options>]

Model

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

Scaffold

Genera modelo, migración, controlador, vistas, la ruta como recurso y todos los tests.

rails g scaffold <ModelName> [<field>[:<type>][:<index>]]* [<options>]

Scaffold Controller

Genera controlador + test, vistas.

rails g scaffold_controller <ModelName> [field:type field:type] [options]

HAML

Actualizar el gemfile con gem "haml-rails" para luego convertir las vistas generadas a HAML usando:

rake haml:erb2haml

RSpec

Para crear los archivos base para nuestra suite de tests

rails g rspec:install

Migraciones

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

Rutas

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

Formularios

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.

Strong Parameters

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)

Validaciones

Definición

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.

Helpers

  • 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 clave accept 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 es nil.

  • exclusion ({in: <enumerator>}) usando la clave in para indicar con un objeto enumerable un conjunto de valores no permitidos. También acepta la clave message para poder hacer uso del valor inválido, usando interpolación: {.., message: "%{value} is reserved."} .

  • inclusion ({in: <enumerator>}) como contraparte de exclusion.

  • format ({with|without: <regex>}) usa una expresión regular para validar formato del valor. Tiene una versión negativa usando la clave without. Acepta la clave message.

  • 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 y too_long así como el token %{count} con la longitud recuperada. La clave message 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 es nil, 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 de presence, 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étodo validate desde la variable options.

  • 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 y value 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
    

Opciones

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 es nil.

  • :allow_blank pasa si se cumple blank? para el valor a probar (nil, "", por ejemplo).

  • :strict (bool | Class ) al fallar la validación arroja una excepción ActiveModel::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? }
    

Agrupando

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

Validaciones personalizadas

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.

Atributos específicos

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

Métodos personalizados

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.

Manejo de Errores

  • errors devuelve una instancia de ActiveModel::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 usa errors[: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.

Triggering

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

Mostrando errores

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment