01: CONFIG : Ambiente Rails y Configuración - LPC-Ltda/Ruby-on-Rails GitHub Wiki
[Rails] ganó mucho de su atención y atractivo porque yo no trato de agradar a quien no comparte mis problemas. La diferenciación entre producción y desarrollo fue un problema real para mí, así que lo resolví en la mejor forma que conozco. - David Heinemeier Hansson
Las aplicaciones Rails están pre-configuradas con tres modos estándar de operación: desarrollo, prueba y producción. Estos modos son básicamente ambientes de ejecución y tienen una colección de configuraciones asociadas que determinan cosas como: a que base de dato conectar y si las clases de su aplicación pueden ser vueltas a cargar con cada requerimiento. Es también simple crear su propio ambiente personalizado si es necesario.
El ambiente actual puede ser especificado vía la variable de ambiente RAILS_ENV, la cual da nombre al modo de operación deseado y corresponde al nombre de un archivo de definición de ambiente en la carpeta config/environments. Usted puede también configurar la variable RACK_ENV, o como último recurso, usted puede confiar en que el valor por defecto es development. Ya que esta configuración de ambiente gobierna algunos de los aspectos más fundamentales de Rails, como la carga de clases, para comprender la forma de trabajar de Rails usted debe comprender la configuración de su ambiente.
En este capítulo comenzaremos explicando el Bundler, una herramienta que administra las dependencias de las gemas para sus aplicaciones Ruby. Usa un archivo que declara las gemas y es capaz de obtener, descargar e instalar las gemas declaradas y todas sus dependencias hijas. Luego nos moveremos a cómo Rails inicia y maneja requerimientos, al examinar scripts como boot.rb y application.rb y las configuraciones que aplican a los tres configuraciones ambiente (modos).También cubriremos algo de las bases para que defina su propio ambiente y el porque usted puede elegir hacerlo.
Note que este libro no está escrito con los recién llegados a Rails en mente. Para hacer la mayoría de lo que este libro muestra, usted tiene que tener algo de familiaridad con Bootstrap y con el significado de MVC.
Bundler no es una tecnología específica de Rails 4 (http://bundler.io), pero es la forma preferida para manejar las dependencias de las gemas de la aplicación. Las aplicaciones generadas con Rails 4 usan Bundler automáticamente, y usted no necesita instalar la gema bundler separadamente ya que es una dependencia de Rails.
Ya que creemos que debe usar Bundler, encontrar la manera de no usar Bundler se deja como ejercicio para los lectores aventureros y/o inconformistas.
Una de las más importantes cosas que hace Bundler es la resolución de dependencias sobre la lista completa de gemas especificada en su configuración, todas de una vez. Esto difiere de la aproximación de resolución de dependencias una a la vez empleada por RubyGem y las versiones previas de Rails, las cuales pueden (y frecuentemente lo hacen) resultar en el siguiente problema difícil de resolver:
Asuma que su sistema tiene las siguientes versiones RubyGems instaladas.
activesupport 4.0.2
activesupport 3.2.11
activemerchant 1.29.3
rails 3.2.11
Y resulta que activemerchant 1.29.3 depende de activesuport 2.3.14; por lo tanto, cuando usted lo carga usando el comando gem
(de la librería RubyGems),
gem 'activemerchant', '1.29.3'
resulta en la carga de activemerchant, así como las últimas versiones compatibles de sus dependencias, incluida la gema activesupport 4.0.2, ya que es mayor o igual a la versión 2.3.14. Subsecuentemente, al tratar de cargar Rails con
gem 'rails', '3.2.11'
Resulta la siguiente excepción:
can't activate activesupport (=3.2.11, runtime)
for ["rails-3.2.11"], already activated
activesupport-4.0.2 for ["activemerchant-1.29.3"]
La excepción ocurre porque activemerchant tiene una dependencia más amplia que resulta en la activación de un Active Support que no satisface la dependencia más restringida de las versiones antiguas de Rails. Bundler resuelve este problema evaluando todas las dependencias de una vez y averiguando las versiones exactas de las gemas a cargar.
Para una interesante perspectiva acerca de la forma en que Bundler fue concebido, asegúrese de leer el post el el blog de Yehuda sobre el tema (http://yehudakatz.com/2010/04/21/named-gem-environments-and-bundler).
Ubicado en la raíz del directorio de su proyecto Rails está un archivo de declaración de sus gemas basadas en Ruby llamado Gemfile. El archivo Gemfile especifica todas las dependencias de su aplicación Rails, incluyendo la versión de Ruby que está siendo usada. La sintaxis del archivo Gemfile es simple:
gem 'kaminari'
gem 'nokogiri'
Para cargar una dependencia sólo en un ambiente específico, póngala en un grupo especificando uno o más nombres de ambientes:
group :development do
gem 'pry-rails'
end
group :test do
gem 'capybara'
gem 'database_cleaner'
end
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
end
Si usted está haciendo upgrade desde Rails 3, note que Rails 4 no usa más el grupo assets para las gemas relacionadas con el Asset Pipeline. Usted necesitará mover todas las gemas agrupadas en assets a las líneas.
La directiva gem
toma un segundo argumento opcional que describe la versión de la RubyGem deseada. Si no se especifica la versión significa que se desea la última disponible y estable, la cual puede no ser la última disponible. Incluir una versión candidata o una gema de preversión, usted necesita especificar la versión explícitamente.
El formato del argumento versión coincide con el esquema de versionamiento de RubyGem, al que usted ya debe estar acostumbrado.
gem 'nokogiri', '1.5.6'
gem 'pry-rails', '> 0.2.2'
gem 'decent_exposure', '~> 2.0.1'
gem 'draper', '1.0.0.beta6'
Usted puede encontrar instrucciones completas sobre como construir un string de versión en la documentación de RubyGems (http://docs.rubygems.org/read/chapter/16).
Ocasionalmente, el nombre de la gema que debe ser usada en una instrucción require (requerida) es diferente del nombre de la gema en el repositorio. En esos casos, la opción :require resuleve esto en el Gemfile.
gem 'webmock', require: 'webmock/rspec'
1.1.1.1 Cargando gemas directamente desde un repositorio Git
Hasta ahora hemos cargado nuestras gemas desde https://rubygems.org. Es posible especificar una gema por su repositorio de código siempre que tenga un archivo .gemspec en el directorio raíz. Solo agregue una opción :git
al llamado a gem
.
gem 'carrierwave', git: '[email protected]:carrierwaveuploader/carrierwave.git'
Si el repositorio de código de la gema esta hosteado en GitHub y es público, usted puede usar la opción :github.
gem 'cerrierwave', github: 'carrierwaveuploader/carrierwave'
Gemspecs con binarios o extesiones C también son soportados.
gem 'nokogiri', git: 'git://github.com/tenderlove/nokogiri.git'
Si no hay un archivo .gemspec en la raíz del directorio Git raíz de la gema, usted debe contarle a Bundler cual usar cuando resuelva sus dependencias.
gem 'deep_merge', '1.0', git: 'git://github.com/peritor/deep_merge.git'
Es también posible especificar que un repositorio Git contiene múltiples archivos .gemspec y deben ser tratados como una fuente gem. Los siguientes ejemplos hacen exactamente eso para el repositorio Git más común que cumple ese criterio - El código base de Rails mismo. (Note que usted nunca necesitará poner este código en un Gemfile para una de sus aplicaciones Rails!)
git 'git://github.com/rails/rails.git'
gem 'railties'
gem 'action_pack'
gem 'active_model'
Adicionalmente, usted puede especificar que un repositorio Git use una ref, branch, o tag particular como opción de la directiva git:
git 'git://github.com/rails/rails.git'
ref: '4aded'
git 'git://github.com/rails/rails.git'
branch: '3-2-stable'
git 'git://github.com/rails/rails.git'
tag: 'v3.2.11'
Especificando un ref, branch o tag para un repositorio Git especificado inline usa la misma sintaxis de una opción.
gem 'nokogiri', git: 'git://github.com/tenderlove/nokogiri.git', ref: '0eec4'
1.1.1.2 Cargando Gemas desde el sistema de archivos
Usted puede usar una gema que está desarrollando en su estación de trabajo local usando la opción :path.
gem 'nokogiri', path: '~/code/nokogiri'
###1.1.2 Instalando Gemas
El comando install actualiza todas las dependencias nombradas en su Gemfile a las últimas versiones que no tengan conflictos con otras dependencias.
Usted puede optar por instalar dependencias, con excepción de aquellos grupos específicos que usan la opción --without
.
$ bundle install --without development test
$ bundle install --without test
###1.1.3 Cierre (Locking) de Gemas
Cada vez que usted corre bundle install
o bundle update
. Bundle calcula el árbol de dependencias para su aplicación y almacena el resultado en un archivo llamado Gemfile.lock. A partir de este punto, Bundler sólo cargará versiones específicas de las gemas que usted está usando al momento en que el Gemfile fue cerrado -las versiones que usted sabe trabajarán bien con su aplicación.
Nota: El archivo Gemfile.lock. siempre deberá estar dentro del control de version para asegurarnos que cada máquina que corra la aplicación use siempre exactamente la misma versión de las gemas (http://yehudakatz.com/2010/12/16/clearifying-the-roles-of-the-gemspec-and-gemfile/). Para ilustrar la importancia de esto, imagine que el archivo Gemfile.lock desapareció y la aplicción está siendo desplegada en producción. Ya que el árbol de dependencias no existe, Bundler tiene que resolver todas las gemas del Gemfile desde esta máquina. Esto puede instalar versiones de las gemas más recientes que las que usted probó causando efecto indeseados.
###1.1.4 Empacando Gemas
Usted puede empacar todas sus gemas en el directorio vendor/cache dentro de su aplicación Rails.
$ bundle package
Al correr bundle install --local en una aplicación con gemas empaquetadas usará las gemas empaquetadas y evitará conectar a rubygem.org o cualquiera otra fuente de gemas. Usted puede usar esto para eliminar dependencias externas en tiempo de despliegue o si usted depende de gemas privadas que no están disponible en repositorios públicos.
1.1.4.1 Haciendo disponibles las dependencias de gemas para scripts no-Rails
Los scripts No-Rails deben ser ejecutados con bundle exec
con el propósito de obtener un ambiente RubyGem apropiadamente inicializado.
$ bundle exec guard
A partir de Rails 4, la generación de una nueva aplicación resultará en la creación de binstubs para los ejecutables de Rails, ubicados en el directorio bin. Un binstub es un script que contiene un ejecutable que corre en el contexto del bundle. Esto significa que uno no tiene que prefijar bundle exec
cada vez que un ejecutable específico de Rails es invocado. Los binstubs son también ciudadanos de primera clase en Rails 4 y debe ser agregado dentro de su sistema de control de versiones como cualquier otro archivo de código fuente.
Por defecto, los siguientes stubs están disponibles en todo nuevo proyecto Rails 4.
- bin/bundle
- bin/rails
- bin/rake
- bin/spring
1.1.4.2 Haciendo upgrade desde Rails 3
Si usted está haciendo upgrade desde Rails 3 y ha generado binstubs usando Bundler en el pasado, usted debe hacer el upgrade de sus binstubs corriendo los siguientes comandos:
bundle config --delete bin # Turn off Bundler's stub generator
rake rails:update:bin # Use the new rails 4 Executables
git add bin # Add bin/ to source control
Para agregar un binstub a un ejecutable usado comunmente en su bundle, invoque bundle binstubs some-gem-name
. Para ilustrar consideremos el siguiente ejemplo:
$ bundle binstubs guard
Este ejemplo crea un binstub para guard en el directorio bin.
# !/usr/bin/env ruby
#
# This file was generated by Blunder
#
# The application 'guard' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('guard', 'guard')
Usando binstub, el script puede ser ejecutado directamente desde el directorio bin.
$ bin/guard
##1.2 Configuración de inicio y aplicación
Donde quiera usted inicie un proceso para manejar requerimientos con Rails (como con un rails server), una de las primeras cosas que ocurre es que el archivo config/boot.rb es cargado.
Hay tres archivos envueltos en la configuración del stack de Rails completo:
config/boot.rb Setea el Bundler y carga las rutas.
config/application.rb Carga las gemas de Rails y las gemas para Rails-env específicos y configura la aplicación.
config/environment.rb Corre todos los inicializadores.
Los tres se ejecutan cuando usted necesita el ambiente Rails completo cargado. Es lo que hacen runner, console, server, y así...
###1.2.1 config/application.rb
El archivo config/application.rb es el hogar de la configuración de su aplicación Rails, y es el único archivo requerido en la cima de config/environment.rb.
Vamos paso a paso a través del seteo provisto por defecto en el archivo config/application.rb que usted encontrará en una aplicación Rails recién creada. A propósito, mientras lea las siguientes secciones, tome nota mental de qué cambios a estos archivos requieren un reinicio de servidor para surtir efecto
Las siguientes líneas del archivo config/application.rb es donde las ruedas comienzan a rodar, una vez que config/boot.rb es cargado:
require File.expand_path('../boot', __FILE__)
Note que el script boot es generado como parte de su aplicación Rails, pero usted no necesitará generalmente editarlo.
Volviendo a config/application.rb, encontramos la siguiente línea:
require 'rails/all'
Usted también tiene la habilidad de fácilmente elegir sólo las componentes necesarias para su aplicación.
# To pick the frameworks you want, remove 'require "rails/all"'.
# and list the framework railties that you want:
#
# require "active_model/railtie"
# require "active_record/railtie"
# require "action_controller/railtie"
# require "action_mailer/railtie"
# require "action_view/railtie"
# require "sprockets/railtie"
# require "rails/test_unit/railtie"
Sigue la configuración principal de nuestra aplicación, la cual en Rails 4 tiene su propio módulo y clase:
module TimeAndExpenses
class Application < Rails::Application
# Setting in config/environments/* take precedence over those
# specified here. Application configuration should go into files
# in config/initializers
# -- all .rb files in that directory are automatically loaded.
La creación de un módulo específico para su aplicación es parte del piso de trabajo para soportar la ejecución de múltiples aplicaciones Rails en el mismo proceso.
1.2.1.1 Zonas horarias
La zona horaria por defecto para las aplicaciones Rails 4 es UTC (Coordinated Universal Time). Si el dominio de negocios de su aplicación es sensible a saber exactamente en que zona horaria está el servidor, entonces usted puede usar el siguiente seteo para sobre-escribir el que ofrece por defecto.
# Set Time.zone default to the specified zone and make Active Record
# autoconvert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names.
config.time_zone = 'Central Time (US & Canada)'
rake time:zones:all
listará todas las zonas horarias que Rails reconoce
1.2.1.2 Localización (personalización de idiomas)
Rails soporta la característica de personalización de idiomas vía archivos locales y es cubierta en detalle en el Capítulo 11, "Todo acerca de los Helpers", en el TranslationHelper y en la sección API I18n.
El locale por defecto es :en y puede ser sobre-escrito en su configuración.
# The default locale is :en and all translations from
# config/locales/*.rb, yml are autoloaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales',
# '*.{rb,yml}')]
# config.i18n.default_locale = :de
1.2.1.3 Seteo por defecto del generador
Los scripts del generador de Rails hace ciertos supuestos acerca de la cadena de herramientas. Setear los valores correctos aquí sigifica tener que tipear menos parámetros en la línea de comandos. Por ejemplo, usar RSpec sin accesorios (fixtures) y Haml como el motor de templates, nuestro seteo debiera lucir como el siguiente:
# Configure generator's values. Many other options are available.
# Be sure to check the documentation
config.generators do |g|
g.template_engine :haml
g.test_framework :rspec, fixture: false
end
Note que RubyGems como rspec-rails
y factory_girl_rails
manejan esto por usted automáticamente.
###1.2.2 Inicializadores
Rails 2 introdujo el concepto de romper el seteo de la configuración dentro de sus propios pequeños archivos Ruby bajo el directorio config/initializers
, desde donde son automáticamente cargados al iniciar. Usted puede agregar seteos de configuración para sus aplicaciones agregando archivos Ruby en el directorio initializers. Los siguientes siete initializers son incluidos por defecto en todos las aplicaciones Rails.
1.2.2.1 Silenciadores de traza (Backtrace silencers)
A nadie le gustan las trazas extensas de excepciones, excepto quizá a los programadores de Java. Rails tiene un mecanismo para eliminar trazas eliminando líneas que no aportan para su revisión (debugging).
El inicializador backtrace_silencers.rb
le permite modificar la forma en que las trazas son acortadas. He encontrado útil remover las trazas de librería ruidosas, pero remover todos los silenciadores no es necesario generalmente durante el desarrollo normal de una aplicación.
# You can add backtrace silencers for libraries that you're using but
# don't wish to see in your backtraces.
Rail.backtrace_cleaner.remove_silencers!
1.2.2.2 Filtro de parametros para el log
Cuando un request es hecho para su aplicación, por defecto Rails hace un log de algunos detalles como la ruta del request, método HTTP, dirección IP, y parámetros. Si un atacante tiene acceso a sus logs puede ver información sensible, como passwords o números de tarjetas de crédito.
El inicializador filter_parameter_logging.rb
le permite especificar qué parámetros del requerimiento deben ser filtrados para sus archivos logs. Si Rails recibe un parámetro de requerimiento incluido en la colección filter_parameters
, será marcado como [FILTERED]
en su logs.
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]
1.2.2.3 Inflecciones
Rails tiene una clase llamada Inflector
cuya responsabilidad es transformar string (palabras) desde el singular al plural, nombres de clases a nombres de tablas, nombres de clases modularizadas a unas sin ellos, nombres de clases a llaves foráneas, y así. (algunas de sus operaciones tienen nombres divertidos como dasherize
).
Las inflecciones por defecto para la pluralización y singularización de incontables palabras son puestas en un archivo interesante dentro de la gema Active Support, llamado inflections.rb
.
La mayor parte del tiempo la clase Inflector
hace un trabajo decente al construir el nombre de la tabla para una determinada clase, pero ocasionalmente no. Esta es una de los primeros bloques de tropiezo para muchos usuarios Rails nuevos, pero no es necesario entrar en pánico. Con un poco de pruebas previas es fácil saber como Inflector
reaccionará a ciertas palabras. Sólo necesitamos usar la consola de Rails, la cual es una de las mejores cosas de trabajar con Rails.
Usted inicia la consola desde su terminal con el comando rails console
.
$ rails console
>> ActiveSupport::Inflector.pluralize "project"
=> "projects"
>> ActiveSupport::Inflector.pluralize "virus"
=> "viri"
>> "pensum".pluralize # Inflector features are mixed into String by default
=> "pensums"
Como usted puede apreciar en este ejemplo, El Inflector
trata de ser inteligente, al pluralizar virus como viri; pero si usted sabe Latin, habrá notado que el plural de pensum es pensa. El inflector no sabe Latin. La inflección de Rails de virus tampoco es correcta. (http://en:wikipedia.org/wiki/Plural_from_of_words_ending_in_-us#Virus)
Sin embargo usted le puede enseñar al inflector nuevos trucos para agregar nuevas reglas de patrones, apuntando una excepción, o declarando cierta palabra no pluralizable. El lugar preferido para hacer esto es dentro del archivo config/initializers/inflections.rb
, donde se provee un ejemplo comentado:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.plural /^(ox)$/i, '\1en'
inflect.singular /^(ox)en/i, '\1'
inflect.irregular 'person', 'people'
inflect.uncountable %w( fish sheep )
end
El archivo activesupport/test/inflector_test_cases.rb
tiene una larga lista de pluralizaciones correctamente manejadas por Inflector
. Yo encuentro algunas de ellas muy interesantes, como las siguientes:
"datum" => "data"
"medium" => "media"
"analysis" => "analyses"
(https://github.com/rails/rails/blob/master/activesupport/test/inflector_test_cases.rb)
1.2.2.4 Tipos MIME personalizados
Rails soporte un conjunto estándar de tipos MIME (/, text/html, text/plain, text/javascript, text/css, text/calendar, text/csv, application/xml, application/rss+xml, application/atom+xml, application/x-yaml, multipar/form-data, application/x-www-form-urlencoded, application/json).
Tabla 1.1 Tipos MIME personalizados
Nombre corto | Símbolo respond_to | Alias y explicaciones |
---|---|---|
text/html | :html, :xhtml | application/xhtml+xml |
text/plain | :text, :txt | |
text/javascript | :js | application/javascript, application/x-javascript |
text/css | :css | Cascading style sheets |
text/ics | :ics | formato iCalendar para compartir pedido de reuniones y tareas |
text/csv | :csv | Valores separados por coma |
application/xml | :xml | text/xml, application/x-xml |
application/rss+xml | :rss | Really Simple Syndication para feeds de la web |
application/atom+xml | :atom | Formato Atom Syndication para feeds de la web |
application/x-yaml | :yaml | text/yaml -legible para el humano |
application/x-www-form-urlencoded | :url_encoded_form | Contenido por defecto para HTML |
multipart/form-data | :multipart_form | Usado para formas HTML que contiene archivos con datos binarios o No-Data |
application/json | :json | text/x-json, application/jsonrequest -notación de objetos JavaScript |
Si su aplicación necesita responder a otro tipo MIME, usted puede registrarlo en el inicializador mime_types.rb.
# Add new MIME types for use in respond_to block:
# Mime::Type.register "text/richtext", :rtf
# Mime::Type.register_alias "text/html", :iphone
1.2.2.5 Almacenamiento de sesión
Como en Rails 4, las cookies de la sesión son encriptadas por defecto usando el nuevo almacén de cookies encriptado. El inicializador session_store.rb
configura el almacenamiento de cookies de la aplicación setando el tipo de almacenamiento y llave de su sesion.
Rails.application.config.session_store :cookie_store, key: '_example_session'
Las cookies de la sesión son firmadas usando el conjunto secret_key_base
en el archivo de configuración config/secrets.yml
. Si usted es realmente paranoico, puede cambiar la llave secreta en config/secrets.yml
o ejecutar rake secret
para generar una nueva automáticamente.
1.2.2.6 Parámetros de envoltura
Introducido en Rails 3.1, el inicializador wrap_parameters.rb
configura su aplicación para trabajar con frameworks JavaScript MVC como Backbone.js en alto nivel.
# Be sure to restart your server when you modify this file
# This file contains settings for ActionController::ParamsWrapper,
# which is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting
# :format to an empty array.
ActiveSupport.on_load(:active_record) do
wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end
# To enable root element in JSON for ActiveRecord objects.
# ActiveSupport.on_load(:active_record) do
# self.include_root_in_json = true
# end
Cuando se envía parámetros en la notación de objetos JavaScript (JSON) a un controlador, Rails envolverá los parámetros dentro de un hash anidado, con el nombre del controlador seteado como llave del hash. Para ilustrar considere el siguiente JSON:
{"title": "The Rails 4 Way"}
Si un cliente envía este JSON a un controlador llamado ArticlesController
, Rails anidará el hash params
bajo la llave "article". Esto asegura que el seteo de los atributos del modelo desde los parámetros del requerimiento es consistente con la convención usada cuando se envía desde los helpers de formulario de Rails.
{"title": "The Rails 4 Way", "article" => {"title": "The Rails 4 Way"}}
###1.2.3 Configuraciones adicionales
Para las cuales tenemos ejemplos en config/application.rb que viene por defecto y en los inicializadores estándar. Hay opciones adicionales que usted puede agregar en archivos de inicialización adicionales.
1.2.3.1 Modificación de las rutas de carga
Por defecto, Rails busca código en una cantidad de directorios estándar, incluidos todos los directorios anidados bajo app
, tales como app/models
. Esto es conocido colectivamente como ruta de carga. Usted puede agregar otros directorios a la ruta de carga usando el siguiente código.
# Custom directories with classes and modules you want to be autoloadable
# config.autoload_path += %W(#{config.root}/extras)
En caso que usted no lo sepa, %W funciona como un arreglo delimitado por espacios y se usa muy seguido en el código Rails.
1.2.3.2 Anulación a nivel de Log
El nivel log por defecto es :debug
, y usted puede anularlo si es necesario.
# Force all environments to use the same logger level
# (by default production uses :info, the others :debug).
config-log_level = :debug
El uso de los loggers de Rails es discutido en profundidad después en este capitulo.
1.2.3.3 Vuelco del schema
Cada vez que usted corre un test, Rails vuelca el schema de su base de datos de desarrollo y copia la base de datos de prueba usando un script schema.rb
autogenerado. Esto se ve muy similar a un script de migración de Active Record; de hecho, usa la misma API.
Usted puede encontrar necesario volver al estilo antiguo de vuelco de schema usando SQL si usted está haciendo cosas incompatibles con el código de vuelco de schema (ver el comentario).
# Use SQL instead of Active Record's schema dumper when creating the
# test database. This is necessary if your schema can't be completely
# dumped by the schema dumper-for example, if you have constraints
# or db-specific column types.
config.active_record.schema_format = :sql
Recuerde como dijimos que el valor de la variable de ambiente RAILS_ENV dictaba que seteo de ambiente adicional era cargado a continuación? Revisaremos el seteo por defecto para cada modo estándar de Rails.
1.2.3.4 Consola
Nueva en Rails 4 es la habilidad de entregar un block a console
, un método que es sólo evaluado cuando el ambiente Rails es cargado a través de la consola. Esto le permite setear configuraciones específicas de la consola, tales como usar Pry sobre IRB. Ponga esto en su archivo config/application.rb
:
console do
# This block is called only when running console.
# so we can safely require pry here
require "pry"
config.console = Pry
end
Note que la gema pry
debe ser incluida en su Gemfile
###1.2.4 Precargador de aplicaciones Spring
A partir de la versión 4.1 Rails viene con un precargador de aplicaciones llamado Spring (https://github.com/rails/spring). Al utilizarlo, durante el desarrollo, su aplicación permanecerá corriendo en el background. Esto acelera el desarrollo eliminando la necesidad de reiniciar Rails cada vez que usted ejecuta una prueba o corre una tarea rake
.
Mientras corre, Spring monitorea las carpetas config
e initializers
por posibles cambios. Si un archivo dentro de esas carpetas es cambiado, Spring automáticamente reinicia su aplicación. Spring también reiniciará si alguna dependencia de gema es cambiada durante el desarrollo.
Para demostrar el incremente de velocidad que Spring provee, corramos la misma tarea rake
en una aplicación Rails 4.0 y en una 4.1:
# Rails 4.0
$ time bin/rake about
...
bin/rake about 1.20s user 0.36s system 22% cpu 6.845 total
# Rails 4.1
$ time bin/rake about
...
bin/rake about 0.08s user 0.04 system 32% cpu 0.370 total
El ambiente precargado Rails usando Spring provee un ahorro de más de seis segundos.
##1.3 Modo Desarrollo
Desarrollo es el modo por defecto y en el que invertiremos más tiempo como desarrolladores. Esta sección contiene una explicación profunda de cada seteo.
# File: config/environments/development.rb
# Rails.application.configure do
# Settings specified here will take precedence over those in
# config/application.rb
###1.3.1 Recarga de clases automática
Una de los beneficios reconocidos de usar Rails es el rápido ciclo de feedback cuando usted está trabajando en el modo desarrollo. Hacer cambios en su código, hacer un Reload en el browser, y Shazam! Mágicamente, los cambios son reflejados en su aplicación. Este comportamiento es gobernado por el seteo de config.cache_classes
:
# In the development environment, your application's code is reloaded on
# every request. This slows down response time but is perfect for
# development since you don't have to restart the web server when you
# make code changes.
config.cache_classes = false
Sin entrar en demasiados detalles, cuando el seteo de config.cache_classes
es true
, Rails usará su instrucción require
para hacer su carga de clases, y cuando sea false
usará load
.
Cuando usted require un archivo Ruby, el intérprete lo ejecuta y lo pone en el cache. Si el archvo es require nuevamente (en un requerimiento subsecuente), el intérprete ignora la instrucción require y avanza. Cuando usted load un archivo Ruby, el intérprete ejecuta el archivo nuevamente, sin importar cuantas veces ha sido cargado antes.
Ahora es tiempo de examinar el comportamiento de la carga de clases de Rails un poco más profundamente, porque a veces usted no será capaz de obtener cierta cosa para recargar automáticamente y se volverá loco si no se entiende cómo funciona la carga de clases!
1.3.1.1 El cargador de clases de Rails
En el viejo y simple Ruby, un archivo script no necesita ser nombrado en una forma particular que coincida con su contenido. En Rails, sin embargo, usted notará que casi siempre hay una correlación directa entre el nombre de un archivo Ruby y la clase o módulo contenido dentro. Rails toma ventaja del hecho de que Ruby provee de un mecanismo de llamada de vuelta (callbacks) para constantes desaparecidas. Cuando Rails encuentra una constante indefinida en el código, usa una rutina cargadora de clases basado en una convención de nombramiento de archivos para encontrar y requerir el script Ruby requerido.
Cómo sabe el cargador de clases donde buscar? Ya cobrimos esto antes cuando discutimos el rol de initilizer.rb
en el proceso de partida de Rails. Rails tiene el concepto de rutas de carga, y las rutas de carga por defecto incluyen los directorios base de todos los lugares donde se le pueda ocurrir agregar código para su aplicación Rails.
Desea ver el contenido de las rutas de carga de su proyecto? Sólo abra la consola y tipee $LOAD_PATH
:
$ rails console
Loading development environment.
>> $LOAD_PATH
=> ["/usr/local/lib/ruby/... # about 20 lines output
Corté la salida de la consola para ahorrar espacio. Las rutas de carga de un proyecto Rails típico pueden tener 60 o más ítem en su rutas de cargas. Intente y mire.
1.3.1.2 Rails, Módulos y Código de autocargado
Normalmente en Ruby, cuando desea incluir código de otro archivo en su aplicación, ustede debe incluir una instrucción require
, Sin embargo, Rails extiende su comportamiento por defecto para establecer una convención simple que le permite a Rails cargar automáticamente su código en muchos casos. Si usted ha usado la consola de Rails ya ha visto este comportamiento en acción: usted nunca tienen que explicitar una instrucción require
en ninguna parte.
Así es cómo trabaja: si Rails encuentra una clase o módulo en su código que no ha sido definido aún, usa la siguiente convención para adivinar cual archivo debe cargar ese módulo o clase.
Si la clase o módulo no está anidado, inserta un underscore entre los nombres de la constante y requiere un archivo son ese nombre. Los siguientes son algunos ejemplos:
-
EstimationCalculator
se transforma enrequire "estimation_calculator"
. -
KittTurboBoost
se transforma enrequire
"kitt_turbo_boost"`.
Si la clase o módulo está anidado. Rails inserta un underscore entre cada uno de los módulos contenidos y requiere un archivo en el conjunto de subdirectorios correspondiente. Los siguientes son algunos ejemplos:
-
MacGyver::SwissArmyKnife
se transforma en"mac_gyver/swiss_army_knife"
. -
Example::ReallyRatherDeeply::nested_class
se transforma enrequire_ "example/really_rather_deeply/nested_class"
, y si no está cargado, Rails esperará encontrarlo en un archivo llamado"nested_class.rb"
en algun directorio llamadoreally_rather_deeply
, que está en un directorioexample
, los cuales pueden estar en alguna parte de las rutas de carga de Rails (por ejemplo, uno de los subdirectorios deapp
o en un directoriolib
de plugin).
El asunto es que rara vez necesitará explicitar código de carga Ruby en sus aplicaciones Rails (usando require
) si usted sigue la convención de nombres.
###1.3.2 Carga ansiosa (Eager Load)
Para acelerar el tiempo de booteo al iniciar el servidor Rails durante el desarrollo, el código no es cargado ansiosamente. Este comportamiento es gobernado por es seteo config.eager_load
:
# Do not eager load code on boot
config.eager_load = false
En su ambiente de producción, usted deseará setear esto a true
, como la mayoría de sus aplicaciones en la memoria. Esto permite un incremento en el desempeño en servidores web que copian en escritura, como Unicorn.
###1.3.3 Reportes de Error
Los request desde el localhost, como cuando usted está desarrollando, generan mensajes de error útiles que incluyen información de debuggin como el número de línea donde el error ocurrió y una traza (backtrace). Setear consider_all_request_local
a true
causa que Rails despliegue estas pantallas de errores amigables al desarrollador, incluso cuando la máquina que hace el requerimiento es remota.
config.consider_all_request_local = true
###1.3.4 Caching (poner en cache)
Usted normalmente no deseará poner en cache el comportamiento cuando está en modo desarrollo. La única vez que usted deseará hacerlo es cuando esté probando el uso del cache.
config.action_controller.perform_caching = true
# for testing in development mode
Recuerde volverlo a false
una vez haya realizado la prueba. El comportamiento inesperado del cache puede ser muy complejo de imaginar.
###1.3.5 Envío de errores de Raise
Rails asume que usted no desea que Action Mailer levante excepciones de entrega en modo desarrollo, así, basado en el seteo config.action_mailer.raise_delivery_errors
, se las traga. Las capacidades de envío de correo no necesariamente trabajan en una estación de desarrollo promedio, particularmente en Windows y otras plataformas que les falta sendmail
.
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
Si usted realmente desea enviar un mail mientras está en modo desarrollo como parte del debuggin o de una prueba ad hoc, entonces probablemente desee activar este seteo.
Me resulta útil
setear config.action_mailer.perform_deliveries = false
en modo desarrollo. Ningún intento de envío es realizado, pero usted aún puede ver el mail en el archivo log para verificar que se ve bien, copiar URL de activación de cuentas, etc.
###1.3.6 Avisos de obsolecencia (Deprecation Notices)
Los avisos de obsolescencia son muy útiles para hacerle saber cuando usted debe dejar de usar una pieza particular de funcionalidad. El seteo de la configuración config.active_support.deprecation
le permite setear el cómo recibirá sus avisos de obsolescencia. En el modo desarrollo, por defecto todos los avisos de obsolescencia aparecerán en el log de desarrollo.
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
###1.3.7 Página de error de Migraciones Pendientes
En las versiones previas de Rails, si se necesitaba correr migraciones pendientes, el servidor web podía fallar al partir. A partir de Rails 4, una nueva página de error es desplegada a cambio, indicando al desarrollador que deben correr rake db:migrate RAILS_ENV=development
para resolver el tema.
# Raise an error on page load if there are pending migrations
config.active_record.migration_error = :page_load
###1.3.8 Modo de Debug de Activos (assets)
Rails 3.1 nos presentó el Asset Pipeline, un framework para concatenar y minimizar los activos JavaScript y CSS. Por defecto en el modo desarrollo, los archivos JavaScript y CSS son servidos separadamente en el orden en que son declarados en sus respectivos archivos de declaración. Al setear config.assets.debug
a false
resultaría en la concatenación Sprockets y la ejecución de preprocesadores sobre todos los activos.
# Debug mode disables concatenation and preprocessing of assets.
config.assets.debug = true
El Asset Pipeline es cubierto en detalle en el capítulo 20 "Asset Pipeline"
##1.4 Modo Prueba (Test)
Donde quiera que usted ejecute Rails en modo prueba -esto es, el valor de ambiente de RAILS_ENV
es test
- entonces la siguiente configuración está en efecto (reproducido sólo para referencia):
# File: config/environment/test.rb
Rails.application.configure do
# Settings specified here will take precedence over those in
# config/application.rb.
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Do not eager load code on boot. This avoids loading your whole
# application just for the purpose of running a single test. If you are
# using a tool that preload Rails for running tests, you may have to set
# it to true
config.eager_load = false
# Configure static asset server for tests with Cache-Control for
# performance.
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
# Show full error reports and disable caching.
config.consider_all_request_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
# Disable request forgery protection intest environment.
config.action_controller.allow_forgery_protection = false
# Tell action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
end
La mayoría de las personas se las arreglan sin cambiar su configuración del ambiente de prueba.
Si es necesario, usted puede crear ambientes adicionales para que sus aplicaciones Rails corran, al clonar uno de los archivos de ambientes existentes en el directorio
config/environments
de su aplicación. El casó más común de uso de ambientes personalizados es en el seteo de configuraciones de producción adicionales, tales como desarrollos por etapas y QA. Tiene usted acceso a la base de datos de producción desde su estación de trabajo de desarrollo? Entonces un ambiente de este tipo puede tener sentido. Use la configuración de ambiente normal para el modo desarrollador, pero apunte su conexión de base de datos al servidor de base de datos de producción. Esta es una combinación que le salvará la vida cuando necesite un diagnóstico rápido de temas en producción.
##1.5 Modo Producción
Finalmente, el modo producción es lo que quiere que su aplicación ejecute cuando corra donde quiera que este desplegada en un ambiente de hosting y sirva requerimientos públicos. Hay un número de formas significativas en las cuales un modo de producción puede diferir de otros modos, uno no menos importante es el aumento de velocidad que se obtiene de no volver a cargar todas sus clases de la aplicación para cada requerimiento.
# File: config/environment/production.rb
Rails.application.configure do
# Settings specified here will take precedence over those in
# config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both thread web servers
# and those relying on copy on write to perform better.
# Rake task automtically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_request_local = false
config.action_controller.perform_caching = true
# Enable Rack::Cache to put a simple HTTP cache in front of your
# application.
# Add `rack-cache` to your Gemfile before enabling this.
# For large-scale production use, consider using a caching reverse proxy
# like nginx, varnish, or squid.
# config.action_dispatch.rack_cache = true
# Disable Rails's static asset server (Apache or nginx will
# already do this).
config.server_static_assets = false
# Compress JavaScript and CSS.
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :sass
# Whether to fallback to assets pipeline if a precompiled
# asset is missed.
config.assets.compile = false
# Generate digests for assets URLs.
config.assets.digests = true
# Versions of your assets, change this if you want to expire
# all your assets.
config.assets.version = '1.0'
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
# config.action_sidpatch.x_sendfile_header = 'X-Accel-Redirect'
# for nginx
# For all access to the app over SSL, use Strict-Transport-Security,
# and use secure cookies.
# config.force_ssl = true
# Set to :debug to see everithing in the log.
config.log_level = :info
# Prepend all log lines with the folowing tags.
# config.log_tags = [ :subdomain, :uuid]
# Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScript from an
# asset server.
# config.action_controller.asset_host = "http://assets.example.com"
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets
# folder are already added.
# config.assets.precompile += %w ( search.js )
# Ignore bad emails addresses and do not raise email delivery errors.
# Set this to true and configure the email server for inmediate delivery
# to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable locale fallback for I18n (makes lookups for any locale fall
# back to the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Disable automatic flushing of the log to improve performance.
# config.autoflush_log = false
# Use default logging formatter so that PID and timestamp
# are not suppressed.
config.log_formatter = ::Logger::Formatter.new
end
###1.5.1 Activos
En el modo producción, los activos son precompilados por defecto por el Asset Pipeline. Todos los archivos incluidos en los manifiestos de activos application.js
y application.css
son comprimidos y concatenados en sus respectivos archivos con el mismo nombre, ubicados en el directorio public/assets
.
Si un activo es solicitado que no exista en el directorio public/assets
, Rails lanzará una excepción. Para permitir el la compilación de activos en vivo, setee config.assets.compile
a true
.
Los archivos de declaraciones application.js
y application.css
son los único incluidos durante el paso de precompilado de Asset Pipeline. Para incluir activos adicionales, especifíquelos usando el seteo de configuración config.assets.precompile
.
config.assets.precompile += %w( administration.css )
Como muchas características en Rails, el uso del Asset Pipeline es completamente opcional. Para incluir activos en su proyecto como lo hacía en en Rails 3.0, setee config.assets.enabled
a false
.
###1.5.2 Host de Activos
Por defecto, Rails se vincula con los activos en el servidor actual en el directorio public, pero usted puede direccionar Rails para vincular activos desde un servidor de activos dedicado. El seteo de config.action_controller.asset_host
es cubierto en detalle en el Capitulo 11 "Todo acerca de los Helpers" en la sección "Usando un Host de Activos".
##1.6 Configurando una Base de Datos
El archivo database.yml
encontrado en el directorio config
especifica todos los seteos de configuración requeridos por Active Record parsa conectarse a una base de datos. Cuando una nueva aplicación es generada, Rails automáticamente genera secciones en el YAML para cada ambiente.
El siguiente es un ejemplo de archivo database.yml
configurado para trabajar con PostgreSQL.
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
# For details on connection pooling, see rails configuration guide:
# http://guides.rubyonrails.org/configuring.html#database-pooling
pool: 5
username: example
password:
development:
<<: *default
database: example_development
# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain socket, so uncomment these lines.
# host: localhost
# The TCP port the server listens on. Default to 5432.
# If your server runs on a different port number, change accordingly.
# port: 5432
# Schema search path. The server defaults to $user, public
#schema_search_path: myapp, sharedapp, public.
# Minimun log levels, in increasig order:
# debug5, debug4, debug3, debug2, debug1,
# log, notice, warning, error, fatal, and panic.
# Defaults to warning.
#min_messages: notice
# Warning: The database defined as "test" will be erased and
# re-generated form your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: example_test
production:
<<: *dafault
database: example_production
Una práctica común dentro de la comunidad Rails es NO almacenar el archivo config/database.yml
en el control de versiones. Primero y principalmente, si un hacker logra acceder al repositorio de su aplicación, tendrá todo el seteo de conexión a su base de datos de producción. Segundo, los desarrolladores del equipo podrían tener diferentes versiones de los seteos de las bases de datos de producción y prueba. Nuevo en Rails 4.1 es la habilidad de configurar Active Record con una variable de ambiente DATABASE_URL
. Esto permite que cada desarrollador trabaje sobre el proyecto que tiene su propia copia de config/database.yml
que no es guardada en el control de versión. El ambiente de producción de una aplicación Rails sólo necesita tener DATABASE_URL
seteado con un string de conexión válido para configurarlo correctamente.
##1.7 Configurando secretos de la aplicación
Se está introduciendo en Rails 4.1 el archivo secrets.yml
dentro del directorio config
. Este archivo se entiende que almacena los datos sensibles de su aplicación, como llaves de acceso, passwords que se requieren para APIs externas. Como mínimo, Rails requiere que secret_key_base
sea seteado para cada ambiente de su aplicación. En Rails 4.0 secret_key_base
se setea en el inicializador secret_token.rb
.
# config/secrets.yml
# Be sure to restart your server when you modify this file.
# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# nonregular words or you'll be exposed to dictionary attacks.
# you can use 'rake secret' to generate a secure secret key.
# Make sure the secrets in this file are kept private
# if you're sharing your code publicity.
development:
secret_key_base: 7aed4bcb28...
test:
secret_key_base: a4b717a2a8...
production:
secret_key_base: 39a63892bd...
Yo le recomiendo con energía que no almacene ninguna valor secreto de producción en el control de versiones. Como con
database.yml
si un hacker logra acceder al repositorio de su aplicación, puede usar esos valores para explotar su aplicación. A cambio, setee todos los valores secretos de producción en variables de ambiente. Las variables de ambiente sólo pueden ser seteadas en su máquina de producción.
# config/secrets.yml
...
production:
secret_key_base: <% ENV['SECRET_KEY_BASE'] %>
Un hash de todos los secretos definidos en config/secrets.yml
puede ser accesado vía Rails.application.secrets
.
>> Rails.application.secrets
=> {:secret_key_base=>"7aed4bcb28..."}
To access a specific secrets, pass X to
Un acceso (accessor) para cada llave secreta es provisto también. Por ejemplo, para accesar el secreto para secret_key_base
, invoque Rails.application.secrets.secret_key_base
. Esto retornará el valor de secret_key_base
para el ambiente actual.
>> Rails.env
=> "development"
>> Rails.application.secrets.secret_key_base
=> "7aed4bcb28..."
Secret Token: Cierto tipo de hackeo modifica el contenido de las cookies sin que el servidor se dé cuenta. A través de la firma digital de todas las cookies enviadas al browser, Rails puede detectar cuando han sido manipulados. Rails firma las cookies usando el valor de
secret_key_base
, encontrado enconfig/secrets.yml
, el cual es generado al azar junto con su aplicación.
##1.8 Logging
Muchos contextos de programación en Rails (modelos, controladores, templates de vistas) tienen un atributo logger
, el cual contiene una referencia a un logger conforme a interface de Log4r
o la clase por defecto para Ruby 1.8+: Logger
. No se puede obtener una referencia al Logger en cualquier lugar del código? El método Rails.logger
referencia un logger que usted puede usar en cualquier lugar.
Es muy fácil crear un nuevo Logger en Ruby, como se muestra en el siguiente ejemplo:
$ pry
> require 'logger'
=> true
> logger = Logger.new STDOUT
=> #<Logger:0x00000106c795f0 @progname=nil, @level=0, ...>
> logger.warn "do not want!!!"
W, [2013-11-02T18:34:30.281003 #54844] WARN -- : do not want!!!
=> true
> logger.info "in your logger, giving info"
I, [2013-11-02T18:34:57.186636 #54844] INFO -- : in your logger, giving info
=> true
Típicamente, usted agrega un mensaje al log usando el logger donde la necesidad llegue, usando un método correspondiente a la severidad del mensaje log. Las severidades estándar del logger son las siguientes (en orden creciente de severidad):
Debug Use el nivel debug para capturar data y estado de la aplicación útiles para hacer debug posterior de problemas. Este nivel no es usualmente capturado en logs de producción.
Info Use el nivel info para capturar mensajes informativos. Me gusta usar este nivel de log para seguir en el tiempo (timestamping) eventos no ordinarios que están aún dentro del comportamiento de una buena aplicación.
Warn Use el nivel warn para capturar cosas que están fuera de lo ordinario y que deben ser investigadas. Algunas veces lanzaré una advertencia loggeada en el momento en que las cláusulas de guardia en mi código pesquisan clientes haciendo algo que no se supone deben hacer. Mi objetivo es alertar a quienquiera sobre el uso malicioso de la aplicación o acerca de un error en la interface de usuario, como en el siguiente ejemplo:
def create
begin
group.add_member(current_user)
flash[:notice] = "Successfully joined #{scene.display_name}"
rescue ActiveRecord::RecordInvalid
flash[:error] = "You are already a member of #{group,name}"
logger.warn "A user tried to join a group twice. UI should not have allowed it."
end
redirect_to :back
end
Error Use el nivel error de log para capturar información acerca de condiciones de error que no requieran el reinicio del servidor.
Fatal El peor caso imaginable ha ocurrido -su aplicación esta ahora muerta y se requiere intervención manual para reiniciarla.
###1.8.1 Archivos Log de Rails
El directorio log
de su aplicación Rails contiene tres archivos log correspondientes a cada uno de los ambientes estándar. Los archivos log pueden crecer mucho en el tiempo. Se provee una tarea rake para fácilmente limpiar los archivos log:
$ rake log:clear # Truncates all *.log files in log/ to zero bytes
El contenido de log/development.log
es muy útil mientras usted está trabajando. Muchos codificadores de Rails mantienen una ventana terminal abierta con la punta continua del log de desarrollo mientras codifican:
$ tail -f log/development.log
Article Load (0.2ms) SELECT "articles".* FROM "articles" WHERE
"articles"."id" = $1 LIMIT 1 [["id", "1"]]
Todo tipo de información valiosa está disponible en el log de desarrollo. Por ejemplo, cada vez que usted hace un requerimiento, un pedazo de información útil acerca de él se muestra en el log. Aquí hay un ejemplo de uno de mis proyectos:
Started GET "/user_photos/1" for 127.0.0,1 at 2007-06-06 17:43:13
Processing by UserPhotosController#show as HTML
Parameters: {"/users/8-Obie-Fernandez/photos/406"=>nil,
"action"=>"show", "id"=>"406", "controller"=>"user_photos",
"user_id"=>"8-Obie-Fernandez"}
User Load (0.4ms) SELECT * FROM users WHERE (users.'id' = 8)
Photo Load (0.9ms) SELECT * FROM photos WHERE (photos.'id' = 406
AND (photos.resourse_id = 8 AND photos.resource_type = 'User'))
CACHE (0.0ms) SELECT * FROM users WHERE (users.'id' = 8)
Rendered adsense/_medium_rectangle (1.5ms)
User Load (0.5ms) SELECT * FROM users WHERE (users.'id' = 8)
LIMIT 1
SQL (0.4ms) SELECT count(*) AS count_all FROM messages WHERE
(messages.receiver_id = 8 AND (messages.'read' = 0))
Rendered layouts/_header (25.3ms)
Rendered adsense/_leaderboard (0.4ms)
Rendered layout/_footer (0.8ms)
Rendered photos/show.html.erb within layouts/application.html.erb (38.9ms)
Completed in 99ms (Views: 37.4ms | ActiveRecord: 12.3ms) with 200
Esta es una lista de todos los ítem de datos contenidos en este pedazo de salida de log:
- Controlador y acción que fue invocada
- Dirección IP remota del computador que hizo el requerimiento
- Timestamp indicando cuando ocurrió el requerimiento
- ID de la sesión asociado con el requerimiento
- Hash de parámetros asociados con el requerimiento
- Información del requerimiento a la base de datos, incluyendo la hora y la instrucción SQL ejecutada.
- Información de la activación del cache de consultas, incluyendo la hora y la instrucción SQL que gatilló resultados desde el cache en lugar de un viaje a la base de datos.
- Información de rendereo para cada template usado en en render de la vista de salida y el tiempo consumido por cada uno.
- Tiempo total usado en completar el requerimiento con el detalle adjunto.
- Análisis del tiempo utilizado en operaciones de bases de datos versus rendereo
- Código de estado HTTP y URL de la respuesta enviada de vuelta al cliente.
###1.8.2 Logging etiquetado (Tagged Logging)
Los archivos log pueden contener una extensa cantidad de información, haciendo difícil el seguimiento de un requerimiento particular. Para aliviar este tema, Rails 3.2 introdujo la habilidad de anteponer información a cada uno de sus mensajes de log.
Para agregar información "etiquetada" a sus logs, pase un arreglo de uno o muchos nombres de métodos que respondan al objeto request
en el seteo de configuración de config.logs_tags
.
Para ilustrar, asumamos que deseamos dar seguimiento al subdominio desde donde cada requerimiento es hecho, podemos lograr esto al setear config.log_tags
a [:subdomain]
. Cuando Rails escribe el log, prefijara la salida de request.subdomain
, resultando un mensaje log como el siguiente:
[some_subdomain] Started GET "/articles" for 127.0.0.1 at 2013-02-01 11:49:09 -0500
###1.8.3 Análisis de un archivo log
Un número de análisis informal pueden ser realizados usando sólo la salida del log de desarrollo y algo de sentido común.
Desempeño Uno de los análisis más obvios es el estudio del desempeño de su aplicación. Que tan rápido se ejecuta su requerimiento, la mayor cantidad de requerimientos que usted puede servir dado un proceso Rails. Esta es la razón por la cual los niveles de desempeño son frecuentemente expresados en términos de requerimientos por segundo. Encuentre las consultas y secciones de rendereo que están tomando mucho tiempo y descifre por que.
Es importante darse cuenta que el tiempo registrado por el logger no es exacto. De hecho está equivocado más frecuentemente que correcto, simplemente porque es muy difícil medir la duración de algunas cosas desde dentro de ellas mismas. Sume el procentage de rendereo y tiempo de bases de datos para un requerimiento dado y no siempre estará cerca del 100%.
Sin embargo, a pesar de no ser exacta en un sentido purista, el reporte de tiempo es perfecto para hacer comparaciones subjetivas dentro de una misma aplicación. Nos dan una forma de medir si una acción está tomando más tiempo del que solía tomar, o si es relativamente rápida o lenta en comparación con otra acción.
Consultas SQL Active Record no se comporta como esperamos? El hecho que el SQL generado por Active Record sea loggeado puede frecuentemente ayudar a resolver problemas causados por consultas complicadas.
Identificación de N+1 problemas seleccionados Cada vez que se está visualizando un registro asociado a una colección de registros, hay una posibilidad de que usted tenga un problema llamado select N+1. Usted reconocerá el problema por una serie de muchas instrucciones SELECT
, con la única diferencia del valor de la llave primaria.
Por ejemplo, aquí hay un francotirador de una salida de los de una aplicación Rails real mostrando un tema select N+1 en la forma en que instancias de FlickrPhoto
están siendo cargadas:
FlickrPhoto Load (1.3ms) SELECT * FROM flickr_photos WHERE
(flickr_photos.resource_id = 15749 AND flickr_photos.resource_type =
'Place' AND (flickr_photos.'profile' = 1)) ORDER BY updated_at desc
LIMIT 1
FlickrPhoto Load (1.7ms) SELECT * FROM flickr_photos WHERE
(flickr_photos.resource_id = 15785 AND flickr_photos.resource_type =
'Place' AND (flickr_photos.'profile' = 1)) ORDER BY updated_at desc
LIMIT 1
FlickrPhoto Load (1.4ms) SELECT * FROM flickr_photos WHERE
(flickr_photos.resource_id = 15831 AND flickr_photos.resource_type =
'Place' AND (flickr_photos.'profile' = 1)) ORDER BY updated_at desc
LIMIT 1
...y así, para páginas y páginas de salida de log. Le parece familiar?
Afortunadamente, cada una de estas consultas a bases de datos es ejecutada muy rápido (cerca de 0.0015 segundos cada una). Esto es porque (1) MySQL es extraordinariamente rápido para instrucciones SELECT pequeñas, y (2) mi proceso Rails está en la misma máquina física que la base de datos.
Pero, acumule suficientes de esas N consultas y consumirán su desempeño. En ausencia de los factores de mitigación que he mencionado, tendría un serio problema de desempeño que manejar. El problema puede ser especialmente severo si la base de datos está en una máquina separada, dándome una latencia de red para lidear en cada una de estas consultas.
El tema select N+1 no es el fin del mundo. Muchas veces sólo se necesita un uso apropiado del método includes
en una consulta particular para aliviar el problema.
Separación de preocupaciones: Una aplicación MVC bien diseñada sigue ciertos protocolos relacionados con el nivel lógico de las operaciones de bases de datos (esto sería el modelo) versus las tareas de rendereo (las vistas). Generalmente hablando, usted desea que su controlador cause la carga de toda la data que necesitará para renderear desde la base de datos. En Rails, esto es realizado por el código del controlador que consulta al modelo la data necesaria y hace esa data disponible para la vista.
El acceso a la base de datos durante el rendereo es considerado una mala práctica. Llamar métodos de la base de datos directamente desde el código del template viola la separación apropiada de preocupaciones y es una pesadilla del mantenimiento (prácticamente todas las aplicaciones PHP tienen este problema).
Sin embargo, hay muchas oportunidades para acceso implícito a bases de datos durante el rendereo de una vista para arrastrarse en su código base, encapsulado por el modelo y quizá gatillado por la carga lenta de asociaciones. Podemos llamarlo de forma concluyente una mala práctica? Es difícil decirlo en forma definitiva. Hay casos (tales como el uso de un fragmento de almacenamiento en caché) donde hace sentido tener operaciones de bases de datos ocurriendo durante el rendereo de la vista.
Usar esquemas de Loogeo alternados. Es fácil! Basta con asignar una clase compatible con el Logger de Ruby a una de las variables de clase
logger
, tal comoActiveRecord::Base.logger
. Un hachazo rápido basado en la habilidad de cambiar loggers fue demostrado por David en varios eventos, incluido su notas claves en Railsconf 2007. Durante una sesión de consola, asignó una nueva instancia deLogger
apuntandoSTDOUT
aActiveRecord::Base.logger
con el fin de ver el SQL que se está generando en su consola. Jamis tiene un escrito completo de la técnica y más en http://weblog.jamisbuck.org/2007/1/31more-on-watching-activerecord.
1.8.3.1 Rails::Subscriber.colorize_logging
Este código le cuenta a Rails cuando usar los códigos ANSI para colorear las instrucciones de logging. El color hace que sea más fácil leer los logs (excepto en Windows) y puede complicar las cosas si usted usa software com syslog. Por defecto es true
. Cámbielo a false
si usted ve sus logs con software que no comprende los códigos de color ANSI.
Aquí hay un fragmento de salida de código con códigos ANSI visibles:
^[[4;36;1mSQL (0.0ms)^[[0;1.Mysql::Error: Unknow table
'expense_reports': DROP TABLE expense_reports^[[0m
^[[4:35:1mSQL (3.2ms)^[[0m ^[[0mCREATE TABLE expense_reports ('id'
int(11) DEFAULT NULL auto_increment PRIMARY KEY, 'user:id' int(11))
Casi nadie parece saber cómo mostrar los registros de colorearse en un buscapersonas (pager). La opción
-R
le cuenta aless
que arroje caracteres de control "puros" a la pantalla.
Syslog: Los sistemas del tipo UNIX tienen un servicio de sistema llamado
syslog
. Por varias razones, este puede ser la mejor alternativa para loggear la producción de sus aplicaciones Rails.
- Ofrece un control más preciso sobre los niveles de registro y el contenido.
- Consolidación de salidas de logger para múltiples applicaciones Rails
- Si usted está usando las capacidades remotas syslog de muchos sistemas, es posible la consolidación de las salidas de logger me múltiples servidores de aplicaciones Rails. En contraste con el tener que manejar archivos log individuales para cada caja servidor de aplicación separadamente.
Usted puede usar SyslogLogger de Eric Holdel (http://docs.seattlerb.org/SyslogLogger/)