1 Cómo cargar las Extensiones Principales
1.1 Active Support independiente
Con el fin de tener la menor huella predeterminada posible, Active Support carga las dependencias mínimas de forma predeterminada. Está dividido en pequeñas piezas para que solo se carguen las extensiones deseadas. También tiene algunos puntos de entrada convenientes para cargar extensiones relacionadas de una sola vez, incluso todo.
Así, después de un simple require como este:
require "active_support"
solo se cargan las extensiones requeridas por el framework Active Support.
1.1.1 Seleccionar una definición
Este ejemplo muestra cómo cargar Hash#with_indifferent_access
. Esta extensión permite la conversión de un Hash
en un ActiveSupport::HashWithIndifferentAccess
que permite acceder a las claves tanto como cadenas o símbolos.
{ a: 1 }.with_indifferent_access["a"] # => 1
Para cada método definido como una extensión principal, esta guía tiene una nota que indica dónde se define dicho método. En el caso de with_indifferent_access
, la nota dice:
NOTA: Definido en active_support/core_ext/hash/indifferent_access.rb
.
Eso significa que puedes requerirlo de esta manera:
require "active_support"
require "active_support/core_ext/hash/indifferent_access"
Active Support ha sido cuidadosamente revisado para que al seleccionar un archivo solo se carguen las dependencias estrictamente necesarias, si las hay.
1.1.2 Cargar extensiones principales agrupadas
El siguiente nivel es simplemente cargar todas las extensiones de Hash
. Como regla general, las extensiones de SomeClass
están disponibles de una sola vez cargando active_support/core_ext/some_class
.
Así, para cargar todas las extensiones de Hash
(incluyendo with_indifferent_access
):
require "active_support"
require "active_support/core_ext/hash"
1.1.3 Cargar todas las extensiones principales
Tal vez prefieras simplemente cargar todas las extensiones principales, hay un archivo para eso:
require "active_support"
require "active_support/core_ext"
1.1.4 Cargar todo Active Support
Y finalmente, si quieres tener todo Active Support disponible, simplemente ejecuta:
require "active_support/all"
Esto ni siquiera carga todo Active Support en memoria de antemano, de hecho, algunas cosas están configuradas a través de autoload
, por lo que solo se cargan si se utilizan.
1.2 Active Support dentro de una aplicación Ruby on Rails
Una aplicación Ruby on Rails carga todo Active Support a menos que config.active_support.bare
sea verdadero. En ese caso, la aplicación solo cargará lo que el propio framework selecciona para sus propias necesidades, y aún puede seleccionar a cualquier nivel de granularidad, como se explica en la sección anterior.
2 Extensiones para todos los objetos
2.1 blank?
y present?
Los siguientes valores se consideran en blanco en una aplicación Rails:
nil
yfalse
,cadenas compuestas solo de espacios en blanco (ver nota a continuación),
matrices y hashes vacíos, y
cualquier otro objeto que responda a
empty?
y esté vacío.
El predicado para las cadenas utiliza la clase de caracteres con conocimiento de Unicode [:space:]
, por lo que, por ejemplo, U+2029 (separador de párrafo) se considera espacio en blanco.
ADVERTENCIA: Ten en cuenta que los números no se mencionan. En particular, 0 y 0.0 no están en blanco.
Por ejemplo, este método de ActionController::HttpAuthentication::Token::ControllerMethods
utiliza blank?
para verificar si un token está presente:
def authenticate(controller, &login_procedure)
token, options = token_and_options(controller.request)
unless token.blank?
login_procedure.call(token, options)
end
end
El método present?
es equivalente a !blank?
. Este ejemplo se toma de ActionDispatch::Http::Cache::Response
:
def set_conditional_cache_control!
return if self["Cache-Control"].present?
# ...
end
NOTA: Definido en active_support/core_ext/object/blank.rb
.
2.2 presence
El método presence
devuelve su receptor si present?
, y nil
en caso contrario. Es útil para idiomatismos como este:
ruby
host = config[:host].presence || 'localhost'
NOTA: Definido en active_support/core_ext/object/blank.rb
.
2.3 duplicable?
A partir de Ruby 2.5, la mayoría de los objetos se pueden duplicar mediante dup
o clone
:
"foo".dup # => "foo"
"".dup # => ""
Rational(1).dup # => (1/1)
Complex(0).dup # => (0+0i)
1.method(:+).dup # => TypeError (allocator undefined for Method)
Active Support proporciona duplicable?
para consultar a un objeto sobre esto:
"foo".duplicable? # => true
"".duplicable? # => true
Rational(1).duplicable? # => true
Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
ADVERTENCIA: Cualquier clase puede evitar la duplicación eliminando dup
y clone
o lanzando excepciones desde ellos. Por lo tanto, solo rescue
puede decir si un objeto arbitrario dado es duplicable. duplicable?
depende de la lista codificada anteriormente, pero es mucho más rápido que rescue
. Úsalo solo si sabes que la lista codificada es suficiente en tu caso de uso.
NOTA: Definido en active_support/core_ext/object/duplicable.rb
.
2.4 deep_dup
El método deep_dup
devuelve una copia profunda de un objeto dado. Normalmente, cuando haces dup
de un objeto que contiene otros objetos, Ruby no los duplica, por lo que crea una copia superficial del objeto. Si tienes un array con un string, por ejemplo, se verá así:
array = ['string']
duplicate = array.dup
duplicate.push 'another-string'
# el objeto fue duplicado, por lo que el elemento se agregó solo a la copia
array # => ['string']
duplicate # => ['string', 'another-string']
duplicate.first.gsub!('string', 'foo')
# el primer elemento no fue duplicado, se cambiará en ambos arrays
array # => ['foo']
duplicate # => ['foo', 'another-string']
Como puedes ver, después de duplicar la instancia de Array
, obtuvimos otro objeto, por lo tanto, podemos modificarlo y el objeto original permanecerá sin cambios. Sin embargo, esto no es cierto para los elementos del array. Dado que dup
no hace una copia profunda, el string dentro del array sigue siendo el mismo objeto.
Si necesitas una copia profunda de un objeto, debes usar deep_dup
. Aquí tienes un ejemplo:
array = ['string']
duplicate = array.deep_dup
duplicate.first.gsub!('string', 'foo')
array # => ['string']
duplicate # => ['foo']
Si el objeto no se puede duplicar, deep_dup
simplemente lo devuelve:
number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id # => true
NOTA: Definido en active_support/core_ext/object/deep_dup.rb
.
2.5 try
Cuando quieres llamar a un método en un objeto solo si no es nil
, la forma más sencilla de lograrlo es con declaraciones condicionales, agregando un desorden innecesario. La alternativa es usar try
. try
es como Object#public_send
, excepto que devuelve nil
si se envía a nil
.
Aquí tienes un ejemplo:
# sin try
unless @number.nil?
@number.next
end
# con try
@number.try(:next)
Otro ejemplo es este código de ActiveRecord::ConnectionAdapters::AbstractAdapter
donde @logger
podría ser nil
. Puedes ver que el código usa try
y evita una verificación innecesaria.
def log_info(sql, name, ms)
if @logger.try(:debug?)
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
try
también se puede llamar sin argumentos pero con un bloque, que solo se ejecutará si el objeto no es nulo:
@person.try { |p| "#{p.first_name} #{p.last_name}" }
Ten en cuenta que try
ignorará los errores de no método, devolviendo nil en su lugar. Si quieres protegerte contra errores tipográficos, usa try!
en su lugar:
@number.try(:nest) # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
NOTA: Definido en active_support/core_ext/object/try.rb
.
2.6 class_eval(*args, &block)
Puedes evaluar código en el contexto de la clase singleton de cualquier objeto usando class_eval
:
class Proc
def bind(object)
block, time = self, Time.current
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end
NOTA: Definido en active_support/core_ext/kernel/singleton_class.rb
.
2.7 acts_like?(duck)
El método acts_like?
proporciona una forma de verificar si una clase se comporta como otra clase según una convención simple: una clase que proporciona la misma interfaz que String
define
ruby
def acts_like_string?
end
que solo es un marcador, su cuerpo o valor de retorno son irrelevantes. Luego, el código del cliente puede consultar si se comporta como un pato de tipo de cadena de esta manera:
some_klass.acts_like?(:string)
Rails tiene clases que se comportan como Date
o Time
y siguen este contrato.
NOTA: Definido en active_support/core_ext/object/acts_like.rb
.
2.8 to_param
Todos los objetos en Rails responden al método to_param
, que se supone que devuelve algo que los representa como valores en una cadena de consulta o como fragmentos de URL.
Por defecto, to_param
simplemente llama a to_s
:
7.to_param # => "7"
El valor de retorno de to_param
no debe ser escapado:
"Tom & Jerry".to_param # => "Tom & Jerry"
Varias clases en Rails sobrescriben este método.
Por ejemplo, nil
, true
y false
devuelven ellos mismos. Array#to_param
llama a to_param
en los elementos y une el resultado con "/":
[0, true, String].to_param # => "0/true/String"
Es importante destacar que el sistema de enrutamiento de Rails llama a to_param
en los modelos para obtener un valor para el marcador :id
. ActiveRecord::Base#to_param
devuelve el id
de un modelo, pero puedes redefinir ese método en tus modelos. Por ejemplo, dado
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
obtenemos:
user_path(@user) # => "/users/357-john-smith"
ADVERTENCIA. Los controladores deben ser conscientes de cualquier redefinición de to_param
porque cuando llega una solicitud como esa, "357-john-smith" es el valor de params[:id]
.
NOTA: Definido en active_support/core_ext/object/to_param.rb
.
2.9 to_query
El método to_query
construye una cadena de consulta que asocia una clave dada con el valor de retorno de to_param
. Por ejemplo, con la siguiente definición de to_param
:
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
obtenemos:
current_user.to_query('user') # => "user=357-john-smith"
Este método escapa lo que sea necesario, tanto para la clave como para el valor:
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
por lo que su salida está lista para ser utilizada en una cadena de consulta.
Los arrays devuelven el resultado de aplicar to_query
a cada elemento con key[]
como clave, y unen el resultado con "&":
[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
Los hashes también responden a to_query
pero con una firma diferente. Si no se pasa ningún argumento, una llamada genera una serie ordenada de asignaciones clave/valor llamando a to_query(key)
en sus valores. Luego une el resultado con "&":
{ c: 3, b: 2, a: 1 }.to_query # => "a=1&b=2&c=3"
El método Hash#to_query
acepta un espacio de nombres opcional para las claves:
{ id: 89, name: "John Smith" }.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
NOTA: Definido en active_support/core_ext/object/to_query.rb
.
2.10 with_options
El método with_options
proporciona una forma de factorizar opciones comunes en una serie de llamadas a métodos.
Dado un hash de opciones predeterminado, with_options
cede un objeto proxy a un bloque. Dentro del bloque, los métodos llamados en el proxy se reenvían al receptor con sus opciones fusionadas. Por ejemplo, te deshaces de la duplicación en:
class Account < ApplicationRecord
has_many :customers, dependent: :destroy
has_many :products, dependent: :destroy
has_many :invoices, dependent: :destroy
has_many :expenses, dependent: :destroy
end
de esta manera:
class Account < ApplicationRecord
with_options dependent: :destroy do |assoc|
assoc.has_many :customers
assoc.has_many :products
assoc.has_many :invoices
assoc.has_many :expenses
end
end
Ese idioma también puede transmitir agrupamiento al lector. Por ejemplo, digamos que quieres enviar un boletín cuyo idioma depende del usuario. En algún lugar del mailer podrías agrupar las partes dependientes del idioma de esta manera:
I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
subject i18n.t :subject
body i18n.t :body, user_name: user.name
end
CONSEJO: Dado que with_options
reenvía las llamadas a su receptor, se pueden anidar. Cada nivel de anidamiento fusionará los valores predeterminados heredados además de los propios.
NOTA: Definido en active_support/core_ext/object/with_options.rb
.
2.11 Soporte JSON
Active Support proporciona una mejor implementación de to_json
que la que el gem json
proporciona normalmente para los objetos de Ruby. Esto se debe a que algunas clases, como Hash
y Process::Status
, necesitan un manejo especial para proporcionar una representación JSON adecuada.
NOTA: Definido en active_support/core_ext/object/json.rb
.
2.12 Variables de instancia
Active Support proporciona varios métodos para facilitar el acceso a las variables de instancia.
2.12.1 instance_values
El método instance_values
devuelve un hash que mapea los nombres de las variables de instancia sin "@" a sus valores correspondientes. Las claves son cadenas de texto:
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
NOTA: Definido en active_support/core_ext/object/instance_variables.rb
.
2.12.2 instance_variable_names
El método instance_variable_names
devuelve un array. Cada nombre incluye el signo "@".
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_variable_names # => ["@x", "@y"]
NOTA: Definido en active_support/core_ext/object/instance_variables.rb
.
2.13 Silenciar advertencias y excepciones
Los métodos silence_warnings
y enable_warnings
cambian el valor de $VERBOSE
de acuerdo con la duración de su bloque, y lo restablecen después:
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
También es posible silenciar excepciones con suppress
. Este método recibe un número arbitrario de clases de excepción. Si se produce una excepción durante la ejecución del bloque y es kind_of?
cualquiera de los argumentos, suppress
la captura y la devuelve en silencio. De lo contrario, la excepción no se captura:
# Si el usuario está bloqueado, el incremento se pierde, no es gran cosa.
suppress(ActiveRecord::StaleObjectError) do
current_user.increment! :visits
end
NOTA: Definido en active_support/core_ext/kernel/reporting.rb
.
2.14 in?
El predicado in?
comprueba si un objeto está incluido en otro objeto. Se generará una excepción ArgumentError
si el argumento pasado no responde a include?
.
Ejemplos de in?
:
1.in?([1, 2]) # => true
"lo".in?("hello") # => true
25.in?(30..50) # => false
1.in?(1) # => ArgumentError
NOTA: Definido en active_support/core_ext/object/inclusion.rb
.
3 Extensiones a Module
3.1 Atributos
3.1.1 alias_attribute
Los atributos del modelo tienen un lector, un escritor y un predicado. Puedes crear un alias para un atributo del modelo teniendo los tres métodos correspondientes definidos para ti utilizando alias_attribute
. Como en otros métodos de aliasing, el nuevo nombre es el primer argumento y el antiguo nombre es el segundo (una mnemotecnia es que van en el mismo orden que si hicieras una asignación):
class User < ApplicationRecord
# Puedes referirte a la columna de correo electrónico como "login".
# Esto puede ser significativo para el código de autenticación.
alias_attribute :login, :email
end
NOTA: Definido en active_support/core_ext/module/aliasing.rb
.
3.1.2 Atributos internos
Cuando defines un atributo en una clase que está destinada a ser subclaseada, existe el riesgo de colisiones de nombres. Esto es especialmente importante para las bibliotecas.
Active Support define las macros attr_internal_reader
, attr_internal_writer
y attr_internal_accessor
. Se comportan como sus contrapartes attr_*
integradas en Ruby, excepto que nombran la variable de instancia subyacente de una manera que reduce las posibilidades de colisión.
La macro attr_internal
es un sinónimo de attr_internal_accessor
:
# biblioteca
class ThirdPartyLibrary::Crawler
attr_internal :log_level
end
# código del cliente
class MyCrawler < ThirdPartyLibrary::Crawler
attr_accessor :log_level
end
En el ejemplo anterior, podría suceder que :log_level
no pertenezca a la interfaz pública de la biblioteca y solo se use para el desarrollo. El código del cliente, sin conocer el posible conflicto, subclases y define su propio :log_level
. Gracias a attr_internal
, no hay colisión.
Por defecto, la variable de instancia interna se nombra con un guión bajo inicial, @_log_level
en el ejemplo anterior. Sin embargo, esto es configurable a través de Module.attr_internal_naming_format
, puedes pasar cualquier cadena de formato similar a sprintf
con un @
inicial y un %s
en algún lugar, donde se colocará el nombre. El valor predeterminado es "@_%s"
.
Rails utiliza atributos internos en algunos lugares, por ejemplo, para las vistas:
module ActionView
class Base
attr_internal :captures
attr_internal :request, :layout
attr_internal :controller, :template
end
end
NOTA: Definido en active_support/core_ext/module/attr_internal.rb
.
3.1.3 Atributos de módulo
Las macros mattr_reader
, mattr_writer
y mattr_accessor
son iguales que las macros cattr_*
definidas para las clases. De hecho, las macros cattr_*
son simplemente alias de las macros mattr_*
. Consulta Atributos de clase.
Por ejemplo, la API para el registro de Active Storage se genera con mattr_accessor
:
module ActiveStorage
mattr_accessor :logger
end
NOTA: Definido en active_support/core_ext/module/attribute_accessors.rb
.
3.2 Padres
3.2.1 module_parent
El método module_parent
en un módulo con nombre anidado devuelve el módulo que contiene su constante correspondiente:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.module_parent # => X::Y
M.module_parent # => X::Y
Si el módulo es anónimo o pertenece al nivel superior, module_parent
devuelve Object
.
ADVERTENCIA: Ten en cuenta que en ese caso module_parent_name
devuelve nil
.
NOTA: Definido en active_support/core_ext/module/introspection.rb
.
3.2.2 module_parent_name
El método module_parent_name
en un módulo con nombre anidado devuelve el nombre completamente cualificado del módulo que contiene su constante correspondiente:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.module_parent_name # => "X::Y"
M.module_parent_name # => "X::Y"
Para módulos de nivel superior o anónimos, module_parent_name
devuelve nil
.
ADVERTENCIA: Ten en cuenta que en ese caso module_parent
devuelve Object
.
NOTA: Definido en active_support/core_ext/module/introspection.rb
.
3.2.3 module_parents
El método module_parents
llama a module_parent
en el receptor y hacia arriba hasta llegar a Object
. La cadena se devuelve en un array, de abajo hacia arriba:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.module_parents # => [X::Y, X, Object]
M.module_parents # => [X::Y, X, Object]
NOTA: Definido en active_support/core_ext/module/introspection.rb
.
3.3 Anónimo
Un módulo puede tener o no un nombre:
module M
end
M.name # => "M"
N = Module.new
N.name # => "N"
Module.new.name # => nil
Puedes verificar si un módulo tiene un nombre con el predicado anonymous?
:
module M
end
M.anonymous? # => false
Module.new.anonymous? # => true
Ten en cuenta que ser inaccesible no implica ser anónimo:
module M
end
m = Object.send(:remove_const, :M)
m.anonymous? # => false
aunque un módulo anónimo es inaccesible por definición.
NOTA: Definido en active_support/core_ext/module/anonymous.rb
.
3.4 Delegación de Métodos
3.4.1 delegate
La macro delegate
ofrece una forma sencilla de reenviar métodos.
Imaginemos que los usuarios en una aplicación tienen información de inicio de sesión en el modelo User
, pero el nombre y otros datos en un modelo separado Profile
:
class User < ApplicationRecord
has_one :profile
end
Con esa configuración, se obtiene el nombre de un usuario a través de su perfil, user.profile.name
, pero podría ser útil poder acceder directamente a dicho atributo:
class User < ApplicationRecord
has_one :profile
def name
profile.name
end
end
Esto es lo que hace delegate
por ti:
class User < ApplicationRecord
has_one :profile
delegate :name, to: :profile
end
Es más corto y la intención es más clara.
El método debe ser público en el objetivo.
La macro delegate
acepta varios métodos:
delegate :name, :age, :address, :twitter, to: :profile
Cuando se interpola en una cadena, la opción :to
debe convertirse en una expresión que se evalúa en el objeto al que se delega el método. Normalmente una cadena o símbolo. Dicha expresión se evalúa en el contexto del receptor:
# delega a la constante Rails
delegate :logger, to: :Rails
# delega a la clase del receptor
delegate :table_name, to: :class
ADVERTENCIA: Si la opción :prefix
es true
, esto es menos genérico, ver más abajo.
Por defecto, si la delegación genera un NoMethodError
y el objetivo es nil
, se propaga la excepción. Puedes pedir que en su lugar se devuelva nil
con la opción :allow_nil
:
delegate :name, to: :profile, allow_nil: true
Con :allow_nil
, la llamada user.name
devuelve nil
si el usuario no tiene perfil.
La opción :prefix
agrega un prefijo al nombre del método generado. Esto puede ser útil, por ejemplo, para obtener un mejor nombre:
delegate :street, to: :address, prefix: true
El ejemplo anterior genera address_street
en lugar de street
.
ADVERTENCIA: En este caso, el nombre del método generado está compuesto por el objeto de destino y los nombres de método de destino, por lo que la opción :to
debe ser un nombre de método.
También se puede configurar un prefijo personalizado:
delegate :size, to: :attachment, prefix: :avatar
En el ejemplo anterior, la macro genera avatar_size
en lugar de size
.
La opción :private
cambia el alcance de los métodos:
delegate :date_of_birth, to: :profile, private: true
Los métodos delegados son públicos de forma predeterminada. Pase private: true
para cambiar eso.
NOTA: Definido en active_support/core_ext/module/delegation.rb
3.4.2 delegate_missing_to
Imagina que quieres delegar todo lo que falta en el objeto User
al objeto Profile
. La macro delegate_missing_to
te permite implementar esto fácilmente:
class User < ApplicationRecord
has_one :profile
delegate_missing_to :profile
end
El objetivo puede ser cualquier cosa llamable dentro del objeto, como variables de instancia, métodos, constantes, etc. Solo se delegan los métodos públicos del objetivo.
NOTA: Definido en active_support/core_ext/module/delegation.rb
.
3.5 Redefinición de métodos
Hay casos en los que necesitas definir un método con define_method
, pero no sabes si ya existe un método con ese nombre. Si existe, se emite una advertencia si están habilitadas. No es gran cosa, pero tampoco es limpio.
El método redefine_method
evita esa advertencia potencial, eliminando el método existente antes si es necesario.
También puedes usar silence_redefinition_of_method
si necesitas definir el método de reemplazo tú mismo (porque estás usando delegate
, por ejemplo).
NOTA: Definido en active_support/core_ext/module/redefine_method.rb
.
4 Extensiones a Class
4.1 Atributos de clase
4.1.1 class_attribute
El método class_attribute
declara uno o más atributos de clase heredables que se pueden anular en cualquier nivel de la jerarquía.
class A
class_attribute :x
end
class B < A; end
class C < B; end
A.x = :a
B.x # => :a
C.x # => :a
B.x = :b
A.x # => :a
C.x # => :b
C.x = :c
A.x # => :a
B.x # => :b
Por ejemplo, ActionMailer::Base
define:
class_attribute :default_params
self.default_params = {
mime_version: "1.0",
charset: "UTF-8",
content_type: "text/plain",
parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze
También se pueden acceder y anular en el nivel de instancia.
A.x = 1
a1 = A.new
a2 = A.new
a2.x = 2
a1.x # => 1, proviene de A
a2.x # => 2, anulado en a2
La generación del método de instancia de escritura se puede evitar configurando la opción :instance_writer
en false
.
module ActiveRecord
class Base
class_attribute :table_name_prefix, instance_writer: false, default: "my"
end
end
Un modelo puede encontrar útil esa opción como una forma de evitar la asignación masiva de establecer el atributo.
La generación del método de instancia de lectura se puede evitar configurando la opción :instance_reader
en false
.
class A
class_attribute :x, instance_reader: false
end
A.new.x = 1
A.new.x # NoMethodError
Para mayor comodidad, class_attribute
también define un predicado de instancia que es la doble negación de lo que devuelve el lector de instancia. En los ejemplos anteriores se llamaría x?
.
Cuando :instance_reader
es false
, el predicado de instancia devuelve un NoMethodError
al igual que el método lector.
Si no deseas el predicado de instancia, pasa instance_predicate: false
y no se definirá.
NOTA: Definido en active_support/core_ext/class/attribute.rb
.
4.1.2 cattr_reader
, cattr_writer
y cattr_accessor
Las macros cattr_reader
, cattr_writer
y cattr_accessor
son análogas a sus contrapartes attr_*
pero para clases. Inicializan una variable de clase a nil
a menos que ya exista y generan los métodos de clase correspondientes para acceder a ella:
class MysqlAdapter < AbstractAdapter
# Genera métodos de clase para acceder a @@emulate_booleans.
cattr_accessor :emulate_booleans
end
Además, puedes pasar un bloque a cattr_*
para configurar el atributo con un valor predeterminado:
class MysqlAdapter < AbstractAdapter
# Genera métodos de clase para acceder a @@emulate_booleans con un valor predeterminado de true.
cattr_accessor :emulate_booleans, default: true
end
Los métodos de instancia también se crean por conveniencia, son solo proxies para el atributo de clase. Por lo tanto, las instancias pueden cambiar el atributo de clase, pero no pueden anularlo como sucede con class_attribute
(ver arriba). Por ejemplo, dado
module ActionView
class Base
cattr_accessor :field_error_proc, default: Proc.new { ... }
end
end
podemos acceder a field_error_proc
en las vistas.
La generación del método de instancia lector se puede evitar configurando :instance_reader
en false
y la generación del método de instancia escritor se puede evitar configurando :instance_writer
en false
. La generación de ambos métodos se puede evitar configurando :instance_accessor
en false
. En todos los casos, el valor debe ser exactamente false
y no cualquier valor falso.
module A
class B
# No se genera el lector de instancia first_name.
cattr_accessor :first_name, instance_reader: false
# No se genera el escritor de instancia last_name=.
cattr_accessor :last_name, instance_writer: false
# No se genera el lector de instancia surname ni el escritor surname=.
cattr_accessor :surname, instance_accessor: false
end
end
Un modelo puede encontrar útil configurar :instance_accessor
en false
como una forma de evitar la asignación masiva de atributos.
NOTA: Definido en active_support/core_ext/module/attribute_accessors.rb
.
4.2 Subclases y descendientes
4.2.1 subclasses
El método subclasses
devuelve las subclases del receptor:
class C; end
C.subclasses # => []
class B < C; end
C.subclasses # => [B]
class A < B; end
C.subclasses # => [B]
class D < C; end
C.subclasses # => [B, D]
El orden en el que se devuelven estas clases no está especificado.
NOTA: Definido en active_support/core_ext/class/subclasses.rb
.
4.2.2 descendants
El método descendants
devuelve todas las clases que son <
que su receptor:
class C; end
C.descendants # => []
class B < C; end
C.descendants # => [B]
class A < B; end
C.descendants # => [B, A]
class D < C; end
C.descendants # => [B, A, D]
El orden en el que se devuelven estas clases no está especificado.
NOTA: Definido en active_support/core_ext/class/subclasses.rb
.
5 Extensiones a String
5.1 Seguridad de la salida
5.1.1 Motivación
Insertar datos en plantillas HTML requiere cuidado adicional. Por ejemplo, no se puede simplemente interpolar @review.title
literalmente en una página HTML. Por un lado, si el título de la reseña es "¡Flanagan & Matz rules!", la salida no será válida porque un ampersand debe escaparse como "&". Además, dependiendo de la aplicación, esto puede ser un gran agujero de seguridad porque los usuarios pueden inyectar HTML malicioso estableciendo un título de reseña personalizado. Consulta la sección sobre scripting entre sitios en la guía de seguridad para obtener más información sobre los riesgos.
5.1.2 Cadenas seguras
Active Support tiene el concepto de cadenas (html) seguras. Una cadena segura es aquella que se marca como insertable en HTML tal cual. Se considera confiable, sin importar si se ha escapado o no.
Las cadenas se consideran inseguras de forma predeterminada:
"".html_safe? # => false
Puedes obtener una cadena segura a partir de una cadena dada con el método html_safe
:
s = "".html_safe
s.html_safe? # => true
Es importante entender que html_safe
no realiza ninguna escapada en absoluto, es solo una afirmación:
s = "<script>...</script>".html_safe
s.html_safe? # => true
s # => "<script>...</script>"
Es tu responsabilidad asegurarte de que llamar a html_safe
en una cadena en particular sea seguro.
Si agregas contenido a una cadena segura, ya sea directamente con concat
/<<
, o con +
, el resultado es una cadena segura. Los argumentos inseguros se escapan:
"".html_safe + "<" # => "<"
Los argumentos seguros se agregan directamente:
"".html_safe + "<".html_safe # => "<"
Estos métodos no deben usarse en vistas normales. Los valores inseguros se escapan automáticamente:
<%= @review.title %> <%# bien, escapado si es necesario %>
Para insertar algo textualmente, utiliza el ayudante raw
en lugar de llamar a html_safe
:
<%= raw @cms.current_template %> <%# inserta @cms.current_template tal cual %>
o, de manera equivalente, utiliza <%==
:
<%== @cms.current_template %> <%# inserta @cms.current_template tal cual %>
El ayudante raw
llama a html_safe
por ti:
def raw(stringish)
stringish.to_s.html_safe
end
NOTA: Definido en active_support/core_ext/string/output_safety.rb
.
5.1.3 Transformación
Como regla general, excepto quizás para la concatenación como se explicó anteriormente, cualquier método que pueda cambiar una cadena te devuelve una cadena insegura. Estos son downcase
, gsub
, strip
, chomp
, underscore
, etc.
En el caso de las transformaciones en su lugar, como gsub!
, el receptor en sí mismo se vuelve inseguro.
La parte de seguridad se pierde siempre, sin importar si la transformación realmente cambió algo.
5.1.4 Conversión y coerción
Llamar a to_s
en una cadena segura devuelve una cadena segura, pero la coerción con to_str
devuelve una cadena insegura.
5.1.5 Copia
Llamar a dup
o clone
en cadenas seguras produce cadenas seguras.
5.2 remove
El método remove
eliminará todas las ocurrencias del patrón:
"Hello World".remove(/Hello /) # => "World"
También existe la versión destructiva String#remove!
.
NOTA: Definido en active_support/core_ext/string/filters.rb
.
5.3 squish
El método squish
elimina los espacios en blanco al principio y al final, y sustituye las secuencias de espacios en blanco por un solo espacio:
" \n foo\n\r \t bar \n".squish # => "foo bar"
También existe la versión destructiva String#squish!
.
Ten en cuenta que maneja tanto espacios en blanco ASCII como Unicode.
NOTA: Definido en active_support/core_ext/string/filters.rb
.
5.4 truncate
El método truncate
devuelve una copia de su receptor truncado después de una longitud dada:
"Oh dear! Oh dear! I shall be late!".truncate(20)
# => "Oh dear! Oh dear!..."
Elipsis se puede personalizar con la opción :omission
:
"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '…')
# => "Oh dear! Oh …"
Ten en cuenta en particular que la truncación tiene en cuenta la longitud de la cadena de omisión.
Pasa un :separator
para truncar la cadena en una ruptura natural:
"Oh dear! Oh dear! I shall be late!".truncate(18)
# => "Oh dear! Oh dea..."
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: ' ')
# => "Oh dear! Oh..."
La opción :separator
puede ser una expresión regular:
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => "Oh dear! Oh..."
En los ejemplos anteriores, "dear" se corta primero, pero luego :separator
lo evita.
NOTA: Definido en active_support/core_ext/string/filters.rb
.
5.5 truncate_bytes
El método truncate_bytes
devuelve una copia de su receptor truncado a un máximo de bytesize
bytes:
"👍👍👍👍".truncate_bytes(15)
# => "👍👍👍…"
Elipsis se puede personalizar con la opción :omission
:
"👍👍👍👍".truncate_bytes(15, omission: "🖖")
# => "👍👍🖖"
NOTA: Definido en active_support/core_ext/string/filters.rb
.
5.6 truncate_words
El método truncate_words
devuelve una copia de su receptor truncado después de un número dado de palabras:
"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => "Oh dear! Oh dear!..."
Elipsis se puede personalizar con la opción :omission
:
"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '…')
# => "Oh dear! Oh dear!…"
Pasa un :separator
para truncar la cadena en una ruptura natural:
"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
# => "Oh dear! Oh dear! I shall be late..."
La opción :separator
puede ser una expresión regular:
"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => "Oh dear! Oh dear!..."
NOTA: Definido en active_support/core_ext/string/filters.rb
.
5.7 inquiry
El método inquiry
convierte una cadena en un objeto StringInquirer
, lo que hace que las comprobaciones de igualdad sean más bonitas.
"production".inquiry.production? # => true
"active".inquiry.inactive? # => false
NOTA: Definido en active_support/core_ext/string/inquiry.rb
.
5.8 starts_with?
y ends_with?
Active Support define alias de tercera persona de String#start_with?
y String#end_with?
:
"foo".starts_with?("f") # => true
"foo".ends_with?("o") # => true
NOTA: Definido en active_support/core_ext/string/starts_ends_with.rb
.
5.9 strip_heredoc
El método strip_heredoc
elimina la sangría en los heredocs.
Por ejemplo, en
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
el usuario vería el mensaje de uso alineado con el margen izquierdo.
Técnicamente, busca la línea con menos sangría en toda la cadena y elimina esa cantidad de espacios en blanco al principio.
NOTA: Definido en active_support/core_ext/string/strip.rb
.
5.10 indent
El método indent
sangra las líneas en el receptor:
<<EOS.indent(2)
def some_method
some_code
end
EOS
# =>
def some_method
some_code
end
El segundo argumento, indent_string
, especifica qué cadena de sangría usar. El valor predeterminado es nil
, lo que indica al método que haga una suposición educada mirando la primera línea sangrada y que use un espacio si no hay ninguna.
" foo".indent(2) # => " foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t") # => "\t\tfoo"
Si bien indent_string
suele ser un espacio o una tabulación, puede ser cualquier cadena.
El tercer argumento, indent_empty_lines
, es una bandera que indica si las líneas vacías deben sangrarse. El valor predeterminado es falso.
"foo\n\nbar".indent(2) # => " foo\n\n bar"
"foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
El método indent!
realiza la sangría en su lugar.
NOTA: Definido en active_support/core_ext/string/indent.rb
.
5.11 Acceso
5.11.1 at(position)
El método at
devuelve el carácter de la cadena en la posición position
:
"hello".at(0) # => "h"
"hello".at(4) # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil
NOTA: Definido en active_support/core_ext/string/access.rb
.
5.11.2 from(position)
El método from
devuelve la subcadena de la cadena que comienza en la posición position
:
"hello".from(0) # => "hello"
"hello".from(2) # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil
NOTA: Definido en active_support/core_ext/string/access.rb
.
5.11.3 to(position)
El método to
devuelve la subcadena de la cadena hasta la posición position
:
"hello".to(0) # => "h"
"hello".to(2) # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"
NOTA: Definido en active_support/core_ext/string/access.rb
.
5.11.4 first(limit = 1)
El método first
devuelve una subcadena que contiene los primeros limit
caracteres de la cadena.
La llamada str.first(n)
es equivalente a str.to(n-1)
si n
> 0, y devuelve una cadena vacía para n
== 0.
NOTA: Definido en active_support/core_ext/string/access.rb
.
5.11.5 last(limit = 1)
El método last
devuelve una subcadena que contiene los últimos limit
caracteres de la cadena.
La llamada str.last(n)
es equivalente a str.from(-n)
si n
> 0, y devuelve una cadena vacía para n
== 0.
NOTA: Definido en active_support/core_ext/string/access.rb
.
5.12 Inflections
5.12.1 pluralize
El método pluralize
devuelve el plural de su receptor:
"table".pluralize # => "tables"
"ruby".pluralize # => "rubies"
"equipment".pluralize # => "equipment"
Como muestra el ejemplo anterior, Active Support conoce algunos plurales irregulares y sustantivos incontables. Las reglas incorporadas se pueden ampliar en config/initializers/inflections.rb
. Este archivo se genera de forma predeterminada con el comando rails new
y tiene instrucciones en comentarios.
pluralize
también puede tomar un parámetro opcional count
. Si count == 1
, se devolverá la forma singular. Para cualquier otro valor de count
, se devolverá la forma plural:
"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"
Active Record utiliza este método para calcular el nombre de tabla predeterminado que corresponde a un modelo:
# active_record/model_schema.rb
def undecorated_table_name(model_name)
table_name = model_name.to_s.demodulize.underscore
pluralize_table_names ? table_name.pluralize : table_name
end
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.2 singularize
El método singularize
es el inverso de pluralize
:
"tables".singularize # => "table"
"rubies".singularize # => "ruby"
"equipment".singularize # => "equipment"
Las asociaciones calculan el nombre de la clase asociada predeterminada correspondiente utilizando este método:
# active_record/reflection.rb
def derive_class_name
class_name = name.to_s.camelize
class_name = class_name.singularize if collection?
class_name
end
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.3 camelize
El método camelize
devuelve su receptor en formato camel case:
"product".camelize # => "Product"
"admin_user".camelize # => "AdminUser"
Como regla general, puedes pensar en este método como aquel que transforma rutas en nombres de clases o módulos de Ruby, donde las barras diagonales separan los espacios de nombres:
"backoffice/session".camelize # => "Backoffice::Session"
Por ejemplo, Action Pack utiliza este método para cargar la clase que proporciona un determinado almacenamiento de sesión:
# action_controller/metal/session_management.rb
def session_store=(store)
@@session_store = store.is_a?(Symbol) ?
ActionDispatch::Session.const_get(store.to_s.camelize) :
store
end
camelize
acepta un argumento opcional, que puede ser :upper
(predeterminado) o :lower
. Con este último, la primera letra se convierte en minúscula:
"visual_effect".camelize(:lower) # => "visualEffect"
Esto puede ser útil para calcular nombres de métodos en un lenguaje que sigue esa convención, como JavaScript.
Como regla general, puedes pensar en camelize
como el inverso de underscore
, aunque hay casos en los que esto no se cumple: "SSLError".underscore.camelize
devuelve "SslError"
. Para admitir casos como este, Active Support te permite especificar acrónimos en config/initializers/inflections.rb
:
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'SSL'
end
"SSLError".underscore.camelize # => "SSLError"
camelize
está aliasado como camelcase
.
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.4 underscore
El método underscore
hace lo contrario, convierte de camel case a rutas:
"Product".underscore # => "product"
"AdminUser".underscore # => "admin_user"
También convierte "::" en "/":
"Backoffice::Session".underscore # => "backoffice/session"
y entiende cadenas que comienzan con minúscula:
"visualEffect".underscore # => "visual_effect"
underscore
no acepta argumentos.
Rails utiliza underscore
para obtener un nombre en minúscula para las clases de controladores:
# actionpack/lib/abstract_controller/base.rb
def controller_path
@controller_path ||= name.delete_suffix("Controller").underscore
end
Por ejemplo, ese valor es el que obtienes en params[:controller]
.
Como regla general, puedes pensar en underscore
como el inverso de camelize
, aunque hay casos en los que esto no se cumple. Por ejemplo, "SSLError".underscore.camelize
devuelve "SslError"
.
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.5 titleize
El método titleize
capitaliza las palabras en el receptor:
"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize # => "Fermat's Enigma"
titleize
está aliasado como titlecase
.
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.6 dasherize
El método dasherize
reemplaza los guiones bajos en el receptor por guiones:
"name".dasherize # => "name"
"contact_data".dasherize # => "contact-data"
El serializador XML de los modelos utiliza este método para convertir los nombres de los nodos en formato guionizado:
# active_model/serializers/xml.rb
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.7 demodulize
Dada una cadena con un nombre de constante calificado, demodulize
devuelve el nombre de la constante en sí, es decir, la parte más a la derecha:
"Product".demodulize # => "Product"
"Backoffice::UsersController".demodulize # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize # => "Inflections"
"".demodulize # => ""
Active Record, por ejemplo, utiliza este método para calcular el nombre de una columna de caché de contador:
# active_record/reflection.rb
def counter_cache_column
if options[:counter_cache] == true
"#{active_record.name.demodulize.underscore.pluralize}_count"
elsif options[:counter_cache]
options[:counter_cache]
end
end
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.8 deconstantize
Dada una cadena con una expresión de referencia a una constante calificada, deconstantize
elimina el segmento más a la derecha, dejando generalmente el nombre del contenedor de la constante:
"Product".deconstantize # => ""
"Backoffice::UsersController".deconstantize # => "Backoffice"
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"
NOTA: Definido en active_support/core_ext/string/inflections.rb
.
5.12.9 parameterize
El método parameterize
normaliza su receptor de una manera que se puede utilizar en URLs legibles.
"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"
Para preservar el caso de la cadena, establece el argumento preserve_case
en true. Por defecto, preserve_case
se establece en false.
"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"
Para utilizar un separador personalizado, sobrescribe el argumento separator
.
ruby
"Employee Salary".downcase_first # => "employee Salary"
"".downcase_first # => ""
Defined in active_support/core_ext/string/inflections.rb
.
Defined in active_support/core_ext/numeric/conversions.rb
.
NOTA: Definido en active_support/core_ext/numeric/conversions.rb
.
6 Extensiones a Integer
6.1 multiple_of?
El método multiple_of?
verifica si un entero es múltiplo del argumento:
2.multiple_of?(1) # => true
1.multiple_of?(2) # => false
NOTA: Definido en active_support/core_ext/integer/multiple.rb
.
6.2 ordinal
El método ordinal
devuelve el sufijo ordinal correspondiente al entero receptor:
1.ordinal # => "º"
2.ordinal # => "º"
53.ordinal # => "º"
2009.ordinal # => "º"
-21.ordinal # => "º"
-134.ordinal # => "º"
NOTA: Definido en active_support/core_ext/integer/inflections.rb
.
6.3 ordinalize
El método ordinalize
devuelve el string ordinal correspondiente al entero receptor. En comparación, el método ordinal
devuelve solo el sufijo.
1.ordinalize # => "1º"
2.ordinalize # => "2º"
53.ordinalize # => "53º"
2009.ordinalize # => "2009º"
-21.ordinalize # => "-21º"
-134.ordinalize # => "-134º"
NOTA: Definido en active_support/core_ext/integer/inflections.rb
.
6.4 Time
Los siguientes métodos:
permiten declaraciones y cálculos de tiempo, como 4.months + 5.years
. Sus valores de retorno también se pueden sumar o restar a objetos de tipo Time.
Estos métodos se pueden combinar con from_now
, ago
, etc., para cálculos precisos de fechas. Por ejemplo:
# equivalente a Time.current.advance(months: 1)
1.month.from_now
# equivalente a Time.current.advance(years: 2)
2.years.from_now
# equivalente a Time.current.advance(months: 4, years: 5)
(4.months + 5.years).from_now
ADVERTENCIA. Para otras duraciones, consulte las extensiones de tiempo a Numeric
.
NOTA: Definido en active_support/core_ext/integer/time.rb
.
7 Extensiones a BigDecimal
7.1 to_s
El método to_s
proporciona un especificador predeterminado de "F". Esto significa que una llamada simple a to_s
dará como resultado una representación de punto flotante en lugar de notación científica:
BigDecimal(5.00, 6).to_s # => "5.0"
También se admite la notación científica:
BigDecimal(5.00, 6).to_s("e") # => "0.5E1"
8 Extensiones a Enumerable
8.1 sum
El método sum
suma los elementos de un enumerable:
[1, 2, 3].sum # => 6
(1..100).sum # => 5050
La suma asume que los elementos responden a +
:
[[1, 2], [2, 3], [3, 4]].sum # => [1, 2, 2, 3, 3, 4]
%w(foo bar baz).sum # => "foobarbaz"
{ a: 1, b: 2, c: 3 }.sum # => [:a, 1, :b, 2, :c, 3]
La suma de una colección vacía es cero por defecto, pero esto se puede personalizar:
[].sum # => 0
[].sum(1) # => 1
Si se proporciona un bloque, sum
se convierte en un iterador que devuelve los elementos de la colección y suma los valores devueltos:
(1..5).sum { |n| n * 2 } # => 30
[2, 4, 6, 8, 10].sum # => 30
La suma de un receptor vacío también se puede personalizar en esta forma:
[].sum(1) { |n| n**3 } # => 1
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.2 index_by
El método index_by
genera un hash con los elementos de un enumerable indexados por alguna clave.
Itera a través de la colección y pasa cada elemento a un bloque. El elemento se indexará por el valor devuelto por el bloque:
invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}
ADVERTENCIA. Las claves normalmente deben ser únicas. Si el bloque devuelve el mismo valor para diferentes elementos, no se construirá una colección para esa clave. El último elemento ganará.
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.3 index_with
El método index_with
genera un hash con los elementos de un enumerable como claves. El valor
es un valor predeterminado pasado o devuelto en un bloque.
post = Post.new(title: "hey there", body: "what's up?")
%i( title body ).index_with { |attr_name| post.public_send(attr_name) }
# => { title: "hey there", body: "what's up?" }
WEEKDAYS.index_with(Interval.all_day)
# => { monday: [ 0, 1440 ], … }
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.4 many?
El método many?
es una forma abreviada de collection.size > 1
:
<% if pages.many? %>
<%= pagination_links %>
<% end %>
Si se proporciona un bloque opcional, many?
solo tiene en cuenta aquellos elementos que devuelven verdadero:
@see_more = videos.many? { |video| video.category == params[:category] }
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.5 exclude?
El predicado exclude?
prueba si un objeto dado no pertenece a la colección. Es la negación de include?
incorporado:
to_visit << node if visited.exclude?(node)
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.6 including
El método including
devuelve un nuevo enumerable que incluye los elementos pasados:
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
["David", "Rafael"].including %w[ Aaron Todd ] # => ["David", "Rafael", "Aaron", "Todd"]
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.7 excluding
El método excluding
devuelve una copia de un enumerable con los elementos especificados eliminados:
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
excluding
es un alias de without
.
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.8 pluck
El método pluck
extrae la clave dada de cada elemento:
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name) # => [[1, "David"], [2, "Rafael"]]
NOTA: Definido en active_support/core_ext/enumerable.rb
.
8.9 pick
El método pick
extrae la clave dada del primer elemento:
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) # => "David"
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) # => [1, "David"]
NOTA: Definido en active_support/core_ext/enumerable.rb
.
9 Extensiones para Array
9.1 Acceso
Active Support amplía la API de los arrays para facilitar ciertas formas de acceso. Por ejemplo, to
devuelve el subarray de elementos hasta el índice pasado:
%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
De manera similar, from
devuelve la cola desde el elemento en el índice pasado hasta el final. Si el índice es mayor que la longitud del array, devuelve un array vacío.
%w(a b c d).from(2) # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0) # => []
El método including
devuelve un nuevo array que incluye los elementos pasados:
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
El método excluding
devuelve una copia del Array excluyendo los elementos especificados.
Esta es una optimización de Enumerable#excluding
que utiliza Array#-
en lugar de Array#reject
por razones de rendimiento.
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
Los métodos second
, third
, fourth
y fifth
devuelven el elemento correspondiente, al igual que second_to_last
y third_to_last
(first
y last
son incorporados). Gracias a la sabiduría social y la constructividad positiva en todas partes, también está disponible forty_two
.
%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil
NOTA: Definido en active_support/core_ext/array/access.rb
.
9.2 Extracción
El método extract!
elimina y devuelve los elementos para los cuales el bloque devuelve un valor verdadero.
Si no se proporciona un bloque, en su lugar se devuelve un Enumerator.
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
numbers # => [0, 2, 4, 6, 8]
NOTA: Definido en active_support/core_ext/array/extract.rb
.
9.3 Extracción de opciones
Cuando el último argumento en una llamada a un método es un hash, excepto tal vez por un argumento &block
, Ruby permite omitir los corchetes:
User.exists?(email: params[:email])
Este azúcar sintáctico se utiliza mucho en Rails para evitar argumentos posicionales cuando habría demasiados, ofreciendo en su lugar interfaces que emulan parámetros nombrados. En particular, es muy idiomático usar un hash al final para las opciones.
Sin embargo, si un método espera un número variable de argumentos y utiliza *
en su declaración, ese hash de opciones termina siendo un elemento del array de argumentos, donde pierde su función.
En esos casos, puedes darle un tratamiento distinguido a un hash de opciones con extract_options!
. Este método verifica el tipo del último elemento de un array. Si es un hash, lo extrae y lo devuelve, de lo contrario devuelve un hash vacío.
Veamos por ejemplo la definición de la macro del controlador caches_action
:
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
# ...
end
Este método recibe un número arbitrario de nombres de acciones y un hash opcional de opciones como último argumento. Con la llamada a extract_options!
obtienes el hash de opciones y lo eliminas de actions
de una manera simple y explícita.
NOTA: Definido en active_support/core_ext/array/extract_options.rb
.
9.4 Conversiones
9.4.1 to_sentence
El método to_sentence
convierte un array en una cadena que enumera sus elementos:
%w().to_sentence # => ""
%w(Earth).to_sentence # => "Earth"
%w(Earth Wind).to_sentence # => "Earth and Wind"
%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"
Este método acepta tres opciones:
:two_words_connector
: Lo que se utiliza para arrays de longitud 2. El valor predeterminado es " and ".:words_connector
: Lo que se utiliza para unir los elementos de arrays con 3 o más elementos, excepto los dos últimos. El valor predeterminado es ", ".:last_word_connector
: Lo que se utiliza para unir los últimos elementos de un array con 3 o más elementos. El valor predeterminado es ", and ".
Los valores predeterminados para estas opciones se pueden localizar, sus claves son:
Opción | Clave I18n |
---|---|
:two_words_connector |
support.array.two_words_connector |
:words_connector |
support.array.words_connector |
:last_word_connector |
support.array.last_word_connector |
NOTA: Definido en active_support/core_ext/array/conversions.rb
.
9.4.2 to_fs
El método to_fs
actúa como to_s
de forma predeterminada.
Sin embargo, si el array contiene elementos que responden a id
, se puede pasar el símbolo :db
como argumento. Esto se utiliza típicamente con colecciones de objetos Active Record. Las cadenas devueltas son:
[].to_fs(:db) # => "null"
[user].to_fs(:db) # => "8456"
invoice.lines.to_fs(:db) # => "23,567,556,12"
Se supone que los enteros en el ejemplo anterior provienen de las respectivas llamadas a id
.
NOTA: Definido en active_support/core_ext/array/conversions.rb
.
9.4.3 to_xml
El método to_xml
devuelve una cadena que contiene una representación XML de su receptor:
Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
# <contributor>
# <id type="integer">4356</id>
# <name>Jeremy Kemper</name>
# <rank type="integer">1</rank>
# <url-id>jeremy-kemper</url-id>
# </contributor>
# <contributor>
# <id type="integer">4404</id>
# <name>David Heinemeier Hansson</name>
# <rank type="integer">2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </contributor>
# </contributors>
Para hacer esto, envía to_xml
a cada elemento por turno y recopila los resultados bajo un nodo raíz. Todos los elementos deben responder a to_xml
, de lo contrario se genera una excepción.
De forma predeterminada, el nombre del elemento raíz es el plural en minúsculas y con guiones del nombre de la clase del primer elemento, siempre que el resto de los elementos pertenezcan a ese tipo (comprobado con is_a?
) y no sean hashes. En el ejemplo anterior, eso es "contributors".
Si hay algún elemento que no pertenece al tipo del primero, el nodo raíz se convierte en "objects": ```ruby [Contributor.first, Commit.first].to_xml
=>
<?xml version="1.0" encoding="UTF-8"?>
Si el receptor es un array de hashes, el elemento raíz por defecto también es "objects":
```ruby
[{ a: 1, b: 2 }, { c: 3 }].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
# <object>
# <b type="integer">2</b>
# <a type="integer">1</a>
# </object>
# <object>
# <c type="integer">3</c>
# </object>
# </objects>
ADVERTENCIA. Si la colección está vacía, el elemento raíz por defecto es "nil-classes". Esto puede ser confuso, por ejemplo, el elemento raíz de la lista de contribuyentes anterior no sería "contributors" si la colección estuviera vacía, sino "nil-classes". Puede utilizar la opción :root
para asegurar un elemento raíz consistente.
El nombre de los nodos hijos es, por defecto, el nombre del nodo raíz en singular. En los ejemplos anteriores hemos visto "contributor" y "object". La opción :children
te permite establecer estos nombres de nodos.
El constructor XML por defecto es una nueva instancia de Builder::XmlMarkup
. Puedes configurar tu propio constructor a través de la opción :builder
. El método también acepta opciones como :dasherize
y otros, que se envían al constructor:
Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors>
# <contributor>
# <id>4356</id>
# <name>Jeremy Kemper</name>
# <rank>1</rank>
# <url-id>jeremy-kemper</url-id>
# </contributor>
# <contributor>
# <id>4404</id>
# <name>David Heinemeier Hansson</name>
# <rank>2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </contributor>
# </contributors>
NOTA: Definido en active_support/core_ext/array/conversions.rb
.
9.5 Envoltura
El método Array.wrap
envuelve su argumento en un array a menos que ya sea un array (o similar a un array).
Específicamente:
- Si el argumento es
nil
, se devuelve un array vacío. - De lo contrario, si el argumento responde a
to_ary
, se invoca, y si el valor deto_ary
no esnil
, se devuelve. - De lo contrario, se devuelve un array con el argumento como su único elemento.
Array.wrap(nil) # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0) # => [0]
Este método es similar en propósito a Kernel#Array
, pero hay algunas diferencias:
- Si el argumento responde a
to_ary
, se invoca el método.Kernel#Array
pasa a intentarto_a
si el valor devuelto esnil
, peroArray.wrap
devuelve de inmediato un array con el argumento como su único elemento. - Si el valor devuelto de
to_ary
no esnil
ni un objetoArray
,Kernel#Array
lanza una excepción, mientras queArray.wrap
no lo hace, simplemente devuelve el valor. - No llama a
to_a
en el argumento, si el argumento no responde ato_ary
, devuelve un array con el argumento como su único elemento.
El último punto es particularmente importante al comparar algunos enumerables:
Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar) # => [[:foo, :bar]]
También hay un idioma relacionado que utiliza el operador splat:
[*object]
NOTA: Definido en active_support/core_ext/array/wrap.rb
.
9.6 Duplicación
El método Array#deep_dup
duplica el array y todos los objetos en su interior de forma recursiva con el método Object#deep_dup
de Active Support. Funciona como Array#map
, enviando el método deep_dup
a cada objeto en su interior.
array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil # => true
NOTA: Definido en active_support/core_ext/object/deep_dup.rb
.
9.7 Agrupamiento
9.7.1 in_groups_of(number, fill_with = nil)
El método in_groups_of
divide un array en grupos consecutivos de un tamaño determinado. Devuelve un array con los grupos:
[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
o los devuelve a su vez si se pasa un bloque:
<% sample.in_groups_of(3) do |a, b, c| %>
<tr>
<td><%= a %></td>
<td><%= b %></td>
<td><%= c %></td>
</tr>
<% end %>
El primer ejemplo muestra cómo in_groups_of
llena el último grupo con tantos elementos nil
como sea necesario para tener el tamaño solicitado. Puede cambiar este valor de relleno utilizando el segundo argumento opcional:
[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
Y puede indicarle al método que no llene el último grupo pasando false
:
[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
Como consecuencia, false
no se puede utilizar como valor de relleno.
NOTA: Definido en active_support/core_ext/array/grouping.rb
.
9.7.2 in_groups(number, fill_with = nil)
El método in_groups
divide una matriz en un cierto número de grupos. El método devuelve una matriz con los grupos:
%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
o los devuelve a su vez si se pasa un bloque:
%w(1 2 3 4 5 6 7).in_groups(3) { |group| p group }
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]
Los ejemplos anteriores muestran que in_groups
llena algunos grupos con un elemento nil
adicional según sea necesario. Un grupo puede tener como máximo uno de estos elementos adicionales, el más a la derecha si lo hay. Y los grupos que los tienen siempre son los últimos.
Puede cambiar este valor de relleno utilizando el segundo argumento opcional:
%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
Y puede indicarle al método que no llene los grupos más pequeños pasando false
:
%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
Como consecuencia, false
no se puede utilizar como valor de relleno.
NOTA: Definido en active_support/core_ext/array/grouping.rb
.
9.7.3 split(value = nil)
El método split
divide una matriz por un separador y devuelve los fragmentos resultantes.
Si se pasa un bloque, los separadores son aquellos elementos de la matriz para los cuales el bloque devuelve verdadero:
(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
De lo contrario, el valor recibido como argumento, que por defecto es nil
, es el separador:
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]
CONSEJO: Observa en el ejemplo anterior que los separadores consecutivos resultan en matrices vacías.
NOTA: Definido en active_support/core_ext/array/grouping.rb
.
10 Extensiones a Hash
10.1 Conversiones
10.1.1 to_xml
El método to_xml
devuelve una cadena que contiene una representación XML de su receptor:
{ foo: 1, bar: 2 }.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <foo type="integer">1</foo>
# <bar type="integer">2</bar>
# </hash>
Para hacer esto, el método recorre los pares y construye nodos que dependen de los valores. Dado un par clave
, valor
:
Si
valor
es un hash, hay una llamada recursiva conclave
como:root
.Si
valor
es una matriz, hay una llamada recursiva conclave
como:root
, yclave
singularizado como:children
.Si
valor
es un objeto invocable, debe esperar uno o dos argumentos. Dependiendo de la aridad, se invoca al objeto invocable con el hash deopciones
como primer argumento conclave
como:root
, yclave
singularizado como segundo argumento. Su valor de retorno se convierte en un nuevo nodo.Si
valor
responde ato_xml
, se invoca al método conclave
como:root
.De lo contrario, se crea un nodo con
clave
como etiqueta y una representación en cadena devalor
como nodo de texto. Sivalor
esnil
, se agrega un atributo "nil" establecido en "true". A menos que exista la opción:skip_types
y sea verdadera, también se agrega un atributo "type" según la siguiente asignación:ruby XML_TYPE_NAMES = { "Symbol" => "símbolo", "Integer" => "entero", "BigDecimal" => "decimal", "Float" => "flotante", "TrueClass" => "booleano", "FalseClass" => "booleano", "Date" => "fecha", "DateTime" => "fecha_hora", "Time" => "fecha_hora" }
Por defecto, el nodo raíz es "hash", pero esto se puede configurar mediante la opción :root
.
El constructor XML predeterminado es una nueva instancia de Builder::XmlMarkup
. Puede configurar su propio constructor con la opción :builder
. El método también acepta opciones como :dasherize
y otras, que se envían al constructor.
NOTA: Definido en active_support/core_ext/hash/conversions.rb
.
10.2 Combinación
Ruby tiene un método incorporado Hash#merge
que combina dos hashes:
{ a: 1, b: 1 }.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}
Active Support define algunas formas más de combinar hashes que pueden ser convenientes.
10.2.1 reverse_merge
y reverse_merge!
En caso de colisión, la clave en el hash del argumento gana en merge
. Puede admitir hashes de opciones con valores predeterminados de manera compacta con esta expresión:
options = { length: 30, omission: "..." }.merge(options)
Active Support define reverse_merge
en caso de que prefiera esta notación alternativa:
options = options.reverse_merge(length: 30, omission: "...")
Y una versión con exclamación reverse_merge!
que realiza la combinación en el lugar:
options.reverse_merge!(length: 30, omission: "...")
ADVERTENCIA. Tenga en cuenta que reverse_merge!
puede cambiar el hash en el llamador, lo cual puede ser o no una buena idea.
NOTA: Definido en active_support/core_ext/hash/reverse_merge.rb
.
10.2.2 reverse_update
El método reverse_update
es un alias de reverse_merge!
, explicado anteriormente.
ADVERTENCIA. Tenga en cuenta que reverse_update
no tiene exclamación.
NOTA: Definido en active_support/core_ext/hash/reverse_merge.rb
.
10.2.3 deep_merge
y deep_merge!
Como se puede ver en el ejemplo anterior, si una clave se encuentra en ambos hashes, el valor en el hash del argumento gana.
Active Support define Hash#deep_merge
. En una combinación profunda, si una clave se encuentra en ambos hashes y sus valores son hashes a su vez, entonces su combinación se convierte en el valor en el hash resultante:
{ a: { b: 1 } }.deep_merge(a: { c: 2 })
# => {:a=>{:b=>1, :c=>2}}
El método deep_merge!
realiza una combinación profunda en el lugar.
NOTA: Definido en active_support/core_ext/hash/deep_merge.rb
.
10.3 Duplicación Profunda
El método Hash#deep_dup
duplica a sí mismo y a todas las claves y valores
dentro de forma recursiva con el método Object#deep_dup
de Active Support. Funciona como Enumerator#each_with_object
enviando el método deep_dup
a cada par dentro.
hash = { a: 1, b: { c: 2, d: [3, 4] } }
dup = hash.deep_dup
dup[:b][:e] = 5
dup[:b][:d] << 5
hash[:b][:e] == nil # => true
hash[:b][:d] == [3, 4] # => true
NOTA: Definido en active_support/core_ext/object/deep_dup.rb
.
10.4 Trabajando con Claves
10.4.1 except
y except!
El método except
devuelve un hash con las claves de la lista de argumentos eliminadas, si están presentes:
{ a: 1, b: 2 }.except(:a) # => {:b=>2}
Si el receptor responde a convert_key
, se llama al método en cada uno de los argumentos. Esto permite que except
funcione correctamente con hashes de acceso indiferente, por ejemplo:
{ a: 1 }.with_indifferent_access.except(:a) # => {}
{ a: 1 }.with_indifferent_access.except("a") # => {}
También existe la variante con exclamación except!
que elimina las claves en su lugar.
NOTA: Definido en active_support/core_ext/hash/except.rb
.
10.4.2 stringify_keys
y stringify_keys!
El método stringify_keys
devuelve un hash que tiene una versión en forma de cadena de las claves en el receptor. Lo hace enviando to_s
a ellas:
{ nil => nil, 1 => 1, a: :a }.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}
En caso de colisión de claves, el valor será el más recientemente insertado en el hash:
{ "a" => 1, a: 2 }.stringify_keys
# El resultado será
# => {"a"=>2}
Este método puede ser útil, por ejemplo, para aceptar fácilmente tanto símbolos como cadenas como opciones. Por ejemplo, ActionView::Helpers::FormHelper
define:
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
# ...
end
La segunda línea puede acceder de forma segura a la clave "type" y permitir al usuario pasar tanto :type
como "type".
También existe la variante con exclamación stringify_keys!
que convierte las claves en cadenas en su lugar.
Además, se puede utilizar deep_stringify_keys
y deep_stringify_keys!
para convertir en cadenas todas las claves del hash dado y todos los hashes anidados en él. Un ejemplo del resultado es:
{ nil => nil, 1 => 1, nested: { a: 3, 5 => 5 } }.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}
NOTA: Definido en active_support/core_ext/hash/keys.rb
.
10.4.3 symbolize_keys
y symbolize_keys!
El método symbolize_keys
devuelve un hash que tiene una versión simbolizada de las claves en el receptor, cuando es posible. Lo hace enviando to_sym
a ellas:
{ nil => nil, 1 => 1, "a" => "a" }.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}
ADVERTENCIA. Observa que en el ejemplo anterior solo se simbolizó una clave.
En caso de colisión de claves, el valor será el más recientemente insertado en el hash:
{ "a" => 1, a: 2 }.symbolize_keys
# => {:a=>2}
Este método puede ser útil, por ejemplo, para aceptar fácilmente tanto símbolos como cadenas como opciones. Por ejemplo, ActionText::TagHelper
define:
def rich_text_area_tag(name, value = nil, options = {})
options = options.symbolize_keys
options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
# ...
end
La tercera línea puede acceder de forma segura a la clave :input
y permitir al usuario pasar tanto :input
como "input".
También existe la variante con exclamación symbolize_keys!
que simboliza las claves en su lugar.
Además, se puede utilizar deep_symbolize_keys
y deep_symbolize_keys!
para simbolizar todas las claves del hash dado y todos los hashes anidados en él. Un ejemplo del resultado es:
{ nil => nil, 1 => 1, "nested" => { "a" => 3, 5 => 5 } }.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
NOTA: Definido en active_support/core_ext/hash/keys.rb
.
10.4.4 to_options
y to_options!
Los métodos to_options
y to_options!
son alias de symbolize_keys
y symbolize_keys!
, respectivamente.
NOTA: Definido en active_support/core_ext/hash/keys.rb
.
10.4.5 assert_valid_keys
El método assert_valid_keys
recibe un número arbitrario de argumentos y verifica si el receptor tiene alguna clave fuera de esa lista. Si lo hace, se lanza un ArgumentError
.
{ a: 1 }.assert_valid_keys(:a) # pasa
{ a: 1 }.assert_valid_keys("a") # ArgumentError
Active Record no acepta opciones desconocidas al construir asociaciones, por ejemplo. Implementa ese control a través de assert_valid_keys
.
NOTA: Definido en active_support/core_ext/hash/keys.rb
.
10.5 Trabajando con Valores
10.5.1 deep_transform_values
y deep_transform_values!
El método deep_transform_values
devuelve un nuevo hash con todos los valores convertidos por la operación del bloque. Esto incluye los valores del hash raíz y de todos los hashes y arrays anidados.
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_transform_values { |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}
También existe la variante con exclamación deep_transform_values!
que convierte destructivamente todos los valores utilizando la operación del bloque.
NOTA: Definido en active_support/core_ext/hash/deep_transform_values.rb
.
10.6 Slicing
El método slice!
reemplaza el hash con solo las claves dadas y devuelve un hash que contiene los pares clave/valor eliminados.
hash = { a: 1, b: 2 }
rest = hash.slice!(:a) # => {:b=>2}
hash # => {:a=>1}
NOTA: Definido en active_support/core_ext/hash/slice.rb
.
10.7 Extracción
El método extract!
elimina y devuelve los pares clave/valor que coinciden con las claves dadas.
hash = { a: 1, b: 2 }
rest = hash.extract!(:a) # => {:a=>1}
hash # => {:b=>2}
El método extract!
devuelve la misma subclase de Hash que el receptor.
```ruby
hash = { a: 1, b: 2 }.with_indifferent_access
rest = hash.extract!(:a).class
=> ActiveSupport::HashWithIndifferentAccess
NOTA: Definido en `active_support/core_ext/hash/slice.rb`.
### Acceso indiferente
El método [`with_indifferent_access`][Hash#with_indifferent_access] devuelve un [`ActiveSupport::HashWithIndifferentAccess`][ActiveSupport::HashWithIndifferentAccess] a partir de su receptor:
```ruby
{ a: 1 }.with_indifferent_access["a"] # => 1
NOTA: Definido en active_support/core_ext/hash/indifferent_access.rb
.
11 Extensiones a Regexp
11.1 multiline?
El método multiline?
indica si una expresión regular tiene la bandera /m
establecida, es decir, si el punto coincide con saltos de línea.
%r{.}.multiline? # => false
%r{.}m.multiline? # => true
Regexp.new('.').multiline? # => false
Regexp.new('.', Regexp::MULTILINE).multiline? # => true
Rails utiliza este método en un solo lugar, también en el código de enrutamiento. Las expresiones regulares de varias líneas no están permitidas para los requisitos de ruta y esta bandera facilita la aplicación de esa restricción.
def verify_regexp_requirements(requirements)
# ...
if requirement.multiline?
raise ArgumentError, "La opción multiline de la expresión regular no está permitida en los requisitos de enrutamiento: #{requirement.inspect}"
end
# ...
end
NOTA: Definido en active_support/core_ext/regexp.rb
.
12 Extensiones a Range
12.1 to_fs
Active Support define Range#to_fs
como una alternativa a to_s
que entiende un argumento de formato opcional. Hasta la fecha de esta escritura, el único formato no predeterminado compatible es :db
:
(Date.today..Date.tomorrow).to_fs
# => "2009-10-25..2009-10-26"
(Date.today..Date.tomorrow).to_fs(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"
Como muestra el ejemplo, el formato :db
genera una cláusula SQL BETWEEN
. Esto es utilizado por Active Record en su soporte para valores de rango en condiciones.
NOTA: Definido en active_support/core_ext/range/conversions.rb
.
12.2 ===
y include?
Los métodos Range#===
y Range#include?
indican si un valor se encuentra entre los extremos de una instancia dada:
(2..3).include?(Math::E) # => true
Active Support extiende estos métodos para que el argumento pueda ser otro rango a su vez. En ese caso, se prueba si los extremos del rango del argumento pertenecen al receptor en sí:
(1..10) === (3..7) # => true
(1..10) === (0..7) # => false
(1..10) === (3..11) # => false
(1...9) === (3..9) # => false
(1..10).include?(3..7) # => true
(1..10).include?(0..7) # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9) # => false
NOTA: Definido en active_support/core_ext/range/compare_range.rb
.
12.3 overlap?
El método Range#overlap?
indica si dos rangos dados tienen una intersección no vacía:
(1..10).overlap?(7..11) # => true
(1..10).overlap?(0..7) # => true
(1..10).overlap?(11..27) # => false
NOTA: Definido en active_support/core_ext/range/overlap.rb
.
13 Extensiones a Date
13.1 Cálculos
Los siguientes métodos de cálculo tienen casos especiales en octubre de 1582, ya que los días 5..14 simplemente no existen. Esta guía no documenta su comportamiento en torno a esos días por brevedad, pero es suficiente decir que hacen lo que esperarías. Es decir, Date.new(1582, 10, 4).tomorrow
devuelve Date.new(1582, 10, 15)
y así sucesivamente. Consulta test/core_ext/date_ext_test.rb
en el conjunto de pruebas de Active Support para conocer el comportamiento esperado.
13.1.1 Date.current
Active Support define Date.current
como la fecha de hoy en la zona horaria actual. Es similar a Date.today
, excepto que respeta la zona horaria del usuario, si está definida. También define Date.yesterday
y Date.tomorrow
, y los predicados de instancia past?
, today?
, tomorrow?
, next_day?
, yesterday?
, prev_day?
, future?
, on_weekday?
y on_weekend?
, todos ellos en relación a Date.current
.
Cuando se hacen comparaciones de fechas utilizando métodos que respetan la zona horaria del usuario, asegúrate de usar Date.current
y no Date.today
. Hay casos en los que la zona horaria del usuario puede estar en el futuro en comparación con la zona horaria del sistema, que es la que utiliza Date.today
de forma predeterminada. Esto significa que Date.today
puede ser igual a Date.yesterday
.
NOTA: Definido en active_support/core_ext/date/calculations.rb
.
13.1.2 Fechas nombradas
13.1.2.1 beginning_of_week
, end_of_week
Los métodos beginning_of_week
y end_of_week
devuelven las fechas del inicio y fin de la semana, respectivamente. Se asume que las semanas comienzan el lunes, pero eso se puede cambiar pasando un argumento, estableciendo Date.beginning_of_week
en el hilo local o config.beginning_of_week
.
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.beginning_of_week # => Mon, 03 May 2010
d.beginning_of_week(:sunday) # => Sun, 02 May 2010
d.end_of_week # => Sun, 09 May 2010
d.end_of_week(:sunday) # => Sat, 08 May 2010
beginning_of_week
se asigna a at_beginning_of_week
y end_of_week
se asigna a at_end_of_week
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.2.2 monday
, sunday
Los métodos monday
y sunday
devuelven las fechas del lunes anterior y del domingo siguiente, respectivamente.
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.monday # => Mon, 03 May 2010
d.sunday # => Sun, 09 May 2010
d = Date.new(2012, 9, 10) # => Mon, 10 Sep 2012
d.monday # => Mon, 10 Sep 2012
d = Date.new(2012, 9, 16) # => Sun, 16 Sep 2012
d.sunday # => Sun, 16 Sep 2012
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.2.3 prev_week
, next_week
El método next_week
recibe un símbolo con el nombre del día en inglés (por defecto es el Date.beginning_of_week
local del hilo, o config.beginning_of_week
, o :monday
) y devuelve la fecha correspondiente a ese día.
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.next_week # => Mon, 10 May 2010
d.next_week(:saturday) # => Sat, 15 May 2010
El método prev_week
es análogo:
d.prev_week # => Mon, 26 Apr 2010
d.prev_week(:saturday) # => Sat, 01 May 2010
d.prev_week(:friday) # => Fri, 30 Apr 2010
prev_week
se asigna a last_week
.
Tanto next_week
como prev_week
funcionan como se espera cuando Date.beginning_of_week
o config.beginning_of_week
están configurados.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.2.4 beginning_of_month
, end_of_month
Los métodos beginning_of_month
y end_of_month
devuelven las fechas del principio y fin del mes:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_month # => Sat, 01 May 2010
d.end_of_month # => Mon, 31 May 2010
beginning_of_month
se asigna a at_beginning_of_month
, y end_of_month
se asigna a at_end_of_month
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.2.5 quarter
, beginning_of_quarter
, end_of_quarter
El método quarter
devuelve el trimestre del año calendario del receptor:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.quarter # => 2
Los métodos beginning_of_quarter
y end_of_quarter
devuelven las fechas del principio y fin del trimestre del año calendario del receptor:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_quarter # => Thu, 01 Apr 2010
d.end_of_quarter # => Wed, 30 Jun 2010
beginning_of_quarter
se asigna a at_beginning_of_quarter
, y end_of_quarter
se asigna a at_end_of_quarter
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.2.6 beginning_of_year
, end_of_year
Los métodos beginning_of_year
y end_of_year
devuelven las fechas del principio y fin del año:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_year # => Fri, 01 Jan 2010
d.end_of_year # => Fri, 31 Dec 2010
beginning_of_year
se asigna a at_beginning_of_year
, y end_of_year
se asigna a at_end_of_year
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.3 Otras Computaciones de Fecha
13.1.3.1 years_ago
, years_since
El método years_ago
recibe un número de años y devuelve la misma fecha hace tantos años:
date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000
years_since
se mueve hacia adelante en el tiempo:
date = Date.new(2010, 6, 7)
date.years_since(10) # => Sun, 07 Jun 2020
Si ese día no existe, se devuelve el último día del mes correspondiente:
Date.new(2012, 2, 29).years_ago(3) # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3) # => Sat, 28 Feb 2015
last_year
es una forma abreviada de #years_ago(1)
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.3.2 months_ago
, months_since
Los métodos months_ago
y months_since
funcionan de manera análoga para los meses:
Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010
Si ese día no existe, se devuelve el último día del mes correspondiente:
Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010
last_month
es una forma abreviada de #months_ago(1)
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.3.3 weeks_ago
El método weeks_ago
funciona de manera análoga para semanas:
Date.new(2010, 5, 24).weeks_ago(1) # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_ago(2) # => Mon, 10 May 2010
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
13.1.3.4 advance
La forma más genérica de saltar a otros días es advance
. Este método recibe un hash con las claves :years
, :months
, :weeks
, :days
, y devuelve una fecha avanzada tanto como indiquen las claves presentes:
date = Date.new(2010, 6, 6)
date.advance(years: 1, weeks: 2) # => Mon, 20 Jun 2011
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010
Observa en el ejemplo anterior que los incrementos pueden ser negativos.
NOTA: Definido en active_support/core_ext/date/calculations.rb
.
13.1.4 Cambiar Componentes
El método change
te permite obtener una nueva fecha que es igual a la original excepto por el año, mes o día especificado:
Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011
Este método no tolera fechas que no existen, si el cambio es inválido se lanzará un ArgumentError
:
Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date
NOTA: Definido en active_support/core_ext/date/calculations.rb
.
13.1.5 Duraciones
Los objetos Duration
se pueden sumar y restar a fechas:
d = Date.current
# => Mon, 09 Aug 2010
d + 1.year
# => Tue, 09 Aug 2011
d - 3.hours
# => Sun, 08 Aug 2010 21:00:00 UTC +00:00
Se traducen en llamadas a since
o advance
. Por ejemplo, aquí obtenemos el salto correcto en la reforma del calendario:
Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582
13.1.6 Marcas de tiempo
Los siguientes métodos devuelven un objeto Time
si es posible, de lo contrario un DateTime
. Si se establece, respetan la zona horaria del usuario.
13.1.6.1 beginning_of_day
, end_of_day
El método beginning_of_day
devuelve una marca de tiempo al comienzo del día (00:00:00):
date = Date.new(2010, 6, 7)
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010
El método end_of_day
devuelve una marca de tiempo al final del día (23:59:59):
date = Date.new(2010, 6, 7)
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010
beginning_of_day
se aliasa a at_beginning_of_day
, midnight
, at_midnight
.
NOTA: Definido en active_support/core_ext/date/calculations.rb
.
13.1.6.2 beginning_of_hour
, end_of_hour
El método beginning_of_hour
devuelve una marca de tiempo al comienzo de la hora (hh:00:00):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010
El método end_of_hour
devuelve una marca de tiempo al final de la hora (hh:59:59):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010
beginning_of_hour
se aliasa a at_beginning_of_hour
.
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
13.1.6.3 beginning_of_minute
, end_of_minute
El método beginning_of_minute
devuelve una marca de tiempo al comienzo del minuto (hh:mm:00):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010
El método end_of_minute
devuelve una marca de tiempo al final del minuto (hh:mm:59):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010
beginning_of_minute
se aliasa a at_beginning_of_minute
.
beginning_of_hour
, end_of_hour
, beginning_of_minute
y end_of_minute
están implementados para Time
y DateTime
pero no para Date
, ya que no tiene sentido solicitar el comienzo o el final de una hora o minuto en una instancia de Date
.
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
13.1.6.4 ago
, since
El método ago
recibe un número de segundos como argumento y devuelve una marca de tiempo que corresponde a esa cantidad de segundos antes de la medianoche:
date = Date.current # => Fri, 11 Jun 2010
date.ago(1) # => Thu, 10 Jun 2010 23:59:59 EDT -04:00
De manera similar, since
avanza en el tiempo:
date = Date.current # => Fri, 11 Jun 2010
date.since(1) # => Fri, 11 Jun 2010 00:00:01 EDT -04:00
NOTA: Definido en active_support/core_ext/date/calculations.rb
.
14 Extensiones a DateTime
ADVERTENCIA: DateTime
no es consciente de las reglas de DST, por lo que algunos de estos métodos tienen casos especiales cuando ocurre un cambio de DST. Por ejemplo, seconds_since_midnight
puede no devolver la cantidad real en ese día.
14.1 Cálculos
La clase DateTime
es una subclase de Date
, por lo que al cargar active_support/core_ext/date/calculations.rb
heredas estos métodos y sus alias, excepto que siempre devolverán datetimes.
Los siguientes métodos se vuelven a implementar para que no necesites cargar active_support/core_ext/date/calculations.rb
para estos:
Por otro lado, advance
y change
también están definidos y admiten más opciones, se documentan a continuación.
Los siguientes métodos solo se implementan en active_support/core_ext/date_time/calculations.rb
ya que solo tienen sentido cuando se usan con una instancia de DateTime
:
14.1.1 Datetimes Nombrados
14.1.1.1 DateTime.current
Active Support define DateTime.current
como Time.now.to_datetime
, excepto que respeta la zona horaria del usuario, si está definida. Los predicados de instancia past?
y future?
se definen en relación a DateTime.current
.
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
14.1.2 Otras Extensiones
14.1.2.1 seconds_since_midnight
El método seconds_since_midnight
devuelve el número de segundos desde la medianoche:
now = DateTime.current # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
14.1.2.2 utc
El método utc
te da el mismo datetime en el receptor expresado en UTC.
now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc # => Mon, 07 Jun 2010 23:27:52 +0000
Este método también se aliasa como getutc
.
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
14.1.2.3 utc?
El predicado utc?
indica si el receptor tiene UTC como su zona horaria:
now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc? # => false
now.utc.utc? # => true
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
14.1.2.4 advance
La forma más genérica de saltar a otro datetime es advance
. Este método recibe un hash con las claves :years
, :months
, :weeks
, :days
, :hours
, :minutes
y :seconds
, y devuelve un datetime avanzado tanto como indiquen las claves presentes.
d = DateTime.current
# => Thu, 05 Aug 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
# => Tue, 06 Sep 2011 12:34:32 +0000
Este método primero calcula la fecha de destino pasando :years
, :months
, :weeks
y :days
a Date#advance
documentado anteriormente. Después de eso, ajusta la hora llamando a since
con el número de segundos a avanzar. Este orden es relevante, un orden diferente daría diferentes datetimes en algunos casos especiales. Se aplica el ejemplo en Date#advance
, y podemos extenderlo para mostrar la relevancia del orden relacionado con los bits de tiempo.
Si primero movemos los bits de fecha (que también tienen un orden relativo de procesamiento, como se documentó anteriormente), y luego los bits de tiempo, obtenemos, por ejemplo, el siguiente cálculo:
d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Sun, 28 Feb 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Mon, 29 Mar 2010 00:00:00 +0000
pero si los calculamos al revés, el resultado sería diferente:
d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000
ADVERTENCIA: Como DateTime
no es consciente de DST, puedes terminar en un punto en el tiempo que no existe sin ninguna advertencia o error que te lo indique.
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
14.1.3 Cambiar Componentes
El método change
te permite obtener un nuevo datetime que es igual al receptor excepto por las opciones dadas, que pueden incluir :year
, :month
, :day
, :hour
, :min
, :sec
, :offset
, :start
:
now = DateTime.current
# => Tue, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Wed, 08 Jun 2011 01:56:22 -0600
Si las horas se establecen en cero, entonces los minutos y segundos también se establecen en cero (a menos que tengan valores dados):
now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000
De manera similar, si los minutos se establecen en cero, entonces los segundos también se establecen en cero (a menos que se haya dado un valor):
now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000
Este método no es tolerante a fechas que no existen, si el cambio es inválido se genera un ArgumentError
:
DateTime.current.change(month: 2, day: 30)
# => ArgumentError: fecha inválida
NOTA: Definido en active_support/core_ext/date_time/calculations.rb
.
14.1.4 Duraciones
Los objetos Duration
se pueden sumar y restar a datetimes:
now = DateTime.current
# => Mon, 09 Aug 2010 23:15:17 +0000
now + 1.year
# => Tue, 09 Aug 2011 23:15:17 +0000
now - 1.week
# => Mon, 02 Aug 2010 23:15:17 +0000
Se traducen en llamadas a since
o advance
. Por ejemplo, aquí obtenemos el salto correcto en la reforma del calendario:
DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000
15 Extensiones a Time
15.1 Cálculos
Son análogos. Por favor, consulte su documentación anterior y tenga en cuenta las siguientes diferencias:
change
acepta una opción adicional:usec
.Time
entiende DST, por lo que se obtienen cálculos DST correctos como en
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
# En Barcelona, el 28/03/2010 02:00 +0100 se convierte en 28/03/2010 03:00 +0200 debido a DST.
t = Time.local(2010, 3, 28, 1, 59, 59)
# => Sun Mar 28 01:59:59 +0100 2010
t.advance(seconds: 1)
# => Sun Mar 28 03:00:00 +0200 2010
- Si
since
oago
salta a un tiempo que no se puede expresar conTime
, se devuelve un objetoDateTime
en su lugar.
15.1.1 Time.current
Active Support define Time.current
como hoy en la zona horaria actual. Es como Time.now
, excepto que respeta la zona horaria del usuario, si está definida. También define los predicados de instancia past?
, today?
, tomorrow?
, next_day?
, yesterday?
, prev_day?
y future?
, todos ellos relativos a Time.current
.
Cuando se realizan comparaciones de tiempo utilizando métodos que respetan la zona horaria del usuario, asegúrese de usar Time.current
en lugar de Time.now
. Hay casos en los que la zona horaria del usuario puede estar en el futuro en comparación con la zona horaria del sistema, que Time.now
utiliza de forma predeterminada. Esto significa que Time.now.to_date
puede ser igual a Date.yesterday
.
NOTA: Definido en active_support/core_ext/time/calculations.rb
.
15.1.2 all_day
, all_week
, all_month
, all_quarter
y all_year
El método all_day
devuelve un rango que representa todo el día del tiempo actual.
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_day
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00
De manera análoga, all_week
, all_month
, all_quarter
y all_year
sirven para generar rangos de tiempo.
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
15.1.3 prev_day
, next_day
prev_day
y next_day
devuelven el tiempo en el día anterior o siguiente:
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_day # => 2010-05-07 00:00:00 +0900
t.next_day # => 2010-05-09 00:00:00 +0900
NOTA: Definido en active_support/core_ext/time/calculations.rb
.
15.1.4 prev_month
, next_month
prev_month
y next_month
devuelven el tiempo con el mismo día en el mes anterior o siguiente:
ruby
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_month # => 2010-04-08 00:00:00 +0900
t.next_month # => 2010-06-08 00:00:00 +0900
Si ese día no existe, se devuelve el último día del mes correspondiente:
Time.new(2000, 5, 31).prev_month # => 2000-04-30 00:00:00 +0900
Time.new(2000, 3, 31).prev_month # => 2000-02-29 00:00:00 +0900
Time.new(2000, 5, 31).next_month # => 2000-06-30 00:00:00 +0900
Time.new(2000, 1, 31).next_month # => 2000-02-29 00:00:00 +0900
NOTA: Definido en active_support/core_ext/time/calculations.rb
.
15.1.5 prev_year
, next_year
prev_year
y next_year
devuelven una fecha con el mismo día/mes en el año anterior o siguiente:
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_year # => 2009-05-08 00:00:00 +0900
t.next_year # => 2011-05-08 00:00:00 +0900
Si la fecha es el 29 de febrero de un año bisiesto, se obtiene el 28:
t = Time.new(2000, 2, 29) # => 2000-02-29 00:00:00 +0900
t.prev_year # => 1999-02-28 00:00:00 +0900
t.next_year # => 2001-02-28 00:00:00 +0900
NOTA: Definido en active_support/core_ext/time/calculations.rb
.
15.1.6 prev_quarter
, next_quarter
prev_quarter
y next_quarter
devuelven la fecha con el mismo día en el trimestre anterior o siguiente:
t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
t.prev_quarter # => 2010-02-08 00:00:00 +0200
t.next_quarter # => 2010-08-08 00:00:00 +0300
Si ese día no existe, se devuelve el último día del mes correspondiente:
Time.local(2000, 7, 31).prev_quarter # => 2000-04-30 00:00:00 +0300
Time.local(2000, 5, 31).prev_quarter # => 2000-02-29 00:00:00 +0200
Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200
prev_quarter
se define como un alias de last_quarter
.
NOTA: Definido en active_support/core_ext/date_and_time/calculations.rb
.
15.2 Constructores de Tiempo
Active Support define Time.current
como Time.zone.now
si hay una zona horaria de usuario definida, con fallback a Time.now
:
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
Time.current
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00
Análogamente a DateTime
, los predicados past?
y future?
son relativos a Time.current
.
Si el tiempo a construir está fuera del rango soportado por Time
en la plataforma de ejecución, los microsegundos se descartan y se devuelve un objeto DateTime
en su lugar.
15.2.1 Duraciones
Se pueden sumar y restar objetos Duration
a objetos de tiempo:
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now + 1.year
# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
now - 1.week
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00
Se traducen en llamadas a since
o advance
. Por ejemplo, aquí obtenemos el salto correcto en la reforma del calendario:
Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
16 Extensiones a File
16.1 atomic_write
Con el método de clase File.atomic_write
se puede escribir en un archivo de una manera que evite que cualquier lector vea contenido medio escrito.
El nombre del archivo se pasa como argumento y el método cede un manejador de archivo abierto para escribir. Una vez que el bloque se completa, atomic_write
cierra el manejador de archivo y completa su trabajo.
Por ejemplo, Action Pack utiliza este método para escribir archivos de caché de activos como all.css
:
File.atomic_write(joined_asset_path) do |cache|
cache.write(join_asset_file_contents(asset_paths))
end
Para lograr esto, atomic_write
crea un archivo temporal. Ese es el archivo al que el código en el bloque realmente escribe. Al completarse, el archivo temporal se renombra, lo cual es una operación atómica en sistemas POSIX. Si el archivo de destino existe, atomic_write
lo sobrescribe y mantiene los propietarios y permisos. Sin embargo, hay algunos casos en los que atomic_write
no puede cambiar la propiedad o los permisos del archivo, este error se captura y se salta, confiando en el usuario/sistema de archivos para asegurarse de que el archivo sea accesible para los procesos que lo necesiten.
NOTA. Debido a la operación chmod que realiza atomic_write
, si el archivo de destino tiene un ACL establecido en él, este ACL se recalculará/modificará.
``
ADVERTENCIA. Tenga en cuenta que no se puede agregar con
atomic_write`.
El archivo auxiliar se escribe en un directorio estándar para archivos temporales, pero puede pasar un directorio de su elección como segundo argumento.
NOTA: Definido en active_support/core_ext/file/atomic.rb
.
17 Extensiones a NameError
Active Support agrega missing_name?
a NameError
, que prueba si la excepción se produjo debido al nombre pasado como argumento.
El nombre puede ser dado como un símbolo o una cadena. Un símbolo se prueba contra el nombre constante sin formato, una cadena se prueba contra el nombre constante completamente calificado.
CONSEJO: Un símbolo puede representar un nombre constante completamente calificado como en :"ActiveRecord::Base"
, por lo que el comportamiento para los símbolos está definido por conveniencia, no porque tenga que ser así técnicamente.
Por ejemplo, cuando se llama a una acción de ArticlesController
, Rails intenta usar optimistamente ArticlesHelper
. Está bien que el módulo de ayuda no exista, por lo que si se genera una excepción para ese nombre constante, debe ser silenciada. Pero podría ser el caso de que articles_helper.rb
genere un NameError
debido a una constante desconocida real. Eso debería ser relanzado. El método missing_name?
proporciona una forma de distinguir ambos casos:
def default_helper_module!
module_name = name.delete_suffix("Controller")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
NOTA: Definido en active_support/core_ext/name_error.rb
.
18 Extensiones a LoadError
Active Support agrega is_missing?
a LoadError
.
Dado un nombre de ruta, is_missing?
prueba si la excepción se generó debido a ese archivo en particular (excepto tal vez por la extensión ".rb").
Por ejemplo, cuando se llama a una acción de ArticlesController
, Rails intenta cargar articles_helper.rb
, pero ese archivo puede no existir. Eso está bien, el módulo de ayuda no es obligatorio, por lo que Rails silencia un error de carga. Pero podría ser el caso de que el módulo de ayuda exista y, a su vez, requiera otra biblioteca que falte. En ese caso, Rails debe relanzar la excepción. El método is_missing?
proporciona una forma de distinguir ambos casos:
def default_helper_module!
module_name = name.delete_suffix("Controller")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
NOTA: Definido en active_support/core_ext/load_error.rb
.
19 Extensiones a Pathname
19.1 existence
El método existence
devuelve el receptor si el archivo con el nombre especificado existe, de lo contrario devuelve nil
. Es útil para idiomatismos como este:
content = Pathname.new("file").existence&.read
NOTA: Definido en active_support/core_ext/pathname/existence.rb
.
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.