1 Como Carregar Core Extensions
1.1 Active Support Stand-Alone
Para ter o menor espaço padrão possível, o Active Support carrega as dependências mínimas por padrão. Ele é quebrado em pequenos pedaços para que apenas as extensões desejadas possam ser carregadas. Ele também possui alguns pontos de entrada convenientes para carregar extensões relacionadas de uma só vez, até mesmo tudo.
Portanto, é possível inicializar após o uso de um simples require como:
require "active_support"
apenas as extensões exigidas pela estrutura do Active Support são carregadas.
1.1.1 Escolhendo a Definição
Este exemplo mostra como carregar Hash#with_indifferent_access
. Esta extensão permite a conversão de um Hash
em um ActiveSupport::HashWithIndifferentAccess
que permite o acesso às chaves como strings ou symbols.
{a: 1}.with_indifferent_access["a"] # => 1
Para cada método definido como core extension esse guia possui uma nota que diz onde tal método é definido. No caso de with_indifferent_access
a nota diz:
Definido em active_support/core_ext/hash/indifferent_access.rb
.
Isso significa que você pode fazer requires assim:
require "active_support"
require "active_support/core_ext/hash/indifferent_access"
O Active Support foi cuidadosamente projetado para que as seleções de arquivos carreguem somente as dependências extremamente necessárias, caso existam.
1.1.2 Carregando Core Extensions Agrupadas
O próximo passo é simplesmente carregar todas as extensões de Hash
. Como regra geral, extensões para SomeClass
estão disponíveis em um rápido carregamento de active_support/core_ext/some_class
.
Portanto, para carregar todas as extensões de Hash
(incluindo with_indifferent_access
):
require "active_support"
require "active_support/core_ext/hash"
1.1.3 Carregando Todas Core Extensions
Você pode escolher por carregar todas as extensões principais, há um arquivo para isso:
require "active_support"
require "active_support/core_ext"
1.1.4 Carregando Active Support Completamente
E finalmente, se você quer ter tudo que o Active Support fornece, basta apenas:
require "active_support/all"
Isso não vai inserir todo o Active Support na memória antes do necessário, algumas funcionalidades são configuradas via ʻautoload`, então só são carregadas se usadas.
1.2 Active Support Em Uma Aplicação Ruby on Rails
Uma aplicação Ruby on Rails carrega todo o Active Support a não ser que config.active_support.bare
esteja definida como true
. Neste caso, a aplicação vai carregar apenas o que o próprio framework escolhe como suas próprias necessidades, e ainda pode selecionar a si mesmo em qualquer nível de granularidade, conforme explicado na seção anterior.
2 Extensões para todos os objetos
2.1 blank?
e present?
Os seguintes valores são considerados blank em uma aplicação Rails:
nil
efalse
,strings compostas apenas por espaços em branco (veja a nota abaixo),
arrays e hashes vazios, e
qualquer outro objeto que responde a
empty?
comotrue
.
A condicional é que as strings usem a classe de caractere [:space:]
do Unicode-aware, como por exemplo U+2029 (separador de parágrafo) é considerado um espaço em branco.
Note que números não são mencionados. Em particular, 0 e 0.0 não são blank.
Por exemplo, este método de ActionController::HttpAuthentication::Token::ControllerMethods
usa blank?
pra checar se o 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
O método present?
é equivalente ao !blank?
. Este exemplo disponível em ActionDispatch::Http::Cache::Response
:
def set_conditional_cache_control!
return if self["Cache-Control"].present?
# ...
end
Definido em active_support/core_ext/object/blank.rb
.
2.2 presence
O método presence
retorna seu valor se present?
for true
, e nil
caso não seja. Isso é muito útil para expressões como esta:
host = config[:host].presence || 'localhost'
Definido em active_support/core_ext/object/blank.rb
.
2.3 duplicable?
A partir do Ruby 2.5, a maioria dos objetos podem ser duplicados com dup
ou clone
:
"foo".dup # => "foo"
"".dup # => ""
Rational(1).dup # => (1/1)
Complex(0).dup # => (0+0i)
1.method(:+).dup # => TypeError (allocator undefined for Method)
O Active Support fornece o duplicable?
para consultar se o objeto pode ser duplicado:
"foo".duplicable? # => true
"".duplicable? # => true
Rational(1).duplicable? # => true
Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
Qualquer classe pode ter a duplicação desabilitada a partir da remoção de dup
e clone
ou definindo exceções. Neste caso apenas rescue
pode informar se determinado objeto arbitrável é duplicável. duplicable?
depende da existência de uma lista de elementos a serem analisados, como no exemplo porém é muito mais veloz que rescue
. Use apenas se você souber que a lista é suficiente em seu caso.
Defined in active_support/core_ext/object/duplicable.rb
.
2.4 deep_dup
O método deep_dup
retorna uma cópia profunda de um objeto. Normalmente, quando você dup
um objeto que contêm outros objetos, Ruby não executa o dup
, então é criada uma cópia superficial do objeto. Caso você possua um array com uma string, por exemplo, terá algo parecido com:
array = ['string']
duplicate = array.dup
duplicate.push 'another-string'
# the object was duplicated, so the element was added only to the duplicate
array # => ['string']
duplicate # => ['string', 'another-string']
duplicate.first.gsub!('string', 'foo')
# first element was not duplicated, it will be changed in both arrays
array # => ['foo']
duplicate # => ['foo', 'another-string']
Como podemos ver, depois de duplicar a instância de Array
, possuímos agora outro objeto, portanto podemos modificá-lo sem alterar informações do objeto original. Isso não funciona para elementos de um array, entretanto. Desde que dup
não faça a cópia profunda, a string dentro do array se manterá como o mesmo objeto.
Se você precisa de uma cópia profunda de um objeto, pode então usar o deep_dup
. Confira um exemplo:
array = ['string']
duplicate = array.deep_dup
duplicate.first.gsub!('string', 'foo')
array # => ['string']
duplicate # => ['foo']
Se o objeto não é duplicável, deep_dup
apenas o retornará:
number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id # => true
Definido em active_support/core_ext/object/deep_dup.rb
.
2.5 try
Quando você quer chamar um método em um objeto somente se ele não for nil
, a forma mais simples de conseguir isso é através de uma estrutura condicional, adicionando uma desnecessária desordem. A alternativa é usar try
. try
é como Object#public_send
exceto que o retorno seja nil
se enviado para nil
.
Eis um exemplo:
# sem try
unless @number.nil?
@number.next
end
# com try
@number.try(:next)
Outro exemplo é o código em ActiveRecord::ConnectionAdapters::AbstractAdapter
onde @logger
não pode ser nil
. Você pode ver que o código usa try
e evita uma verificação desnecessária.
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
pode também ser chamada sem argumentos, porém em um bloco, no qual só será executado se o objeto não for nil
:
@person.try { |p| "#{p.first_name} #{p.last_name}" }
Perceba que try
não exibirá as mensagens de erro caso elas ocorram, retornando nil
em vez disso. Se você quiser se proteger de possíveis erros de digitação, use try!
:
@number.try(:nest) # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
Definido em active_support/core_ext/object/try.rb
.
2.6 class_eval(*args, &block)
Você pode evoluir o código no contexto de um singleton de qualquer 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
Definido em active_support/core_ext/kernel/singleton_class.rb
.
2.7 acts_like?(duck)
O método acts_like?
fornece um meio para conferir se alguma classe age como alguma outra classe baseada em uma simples convenção: a classe que fornece a mesma interface é definida como String
def acts_like_string?
end
que é apenas um marcador, seu corpo ou valor de retorno são irrelevantes. Então, o código do cliente pode consultar a tipagem desta forma:
some_klass.acts_like?(:string)
Rails possui classes que agem como Date
ou Time
e seguem essa linha.
Definido em active_support/core_ext/object/acts_like.rb
.
2.8 to_param
Todos objetos em Rails respondem ao método to_param
, o qual é usado para retornar representações de valores em strings, no qual podem ser usadas em consultas, ou fragmentos de URL.
Por padrão, to_param
apenas chama o método to_s
:
7.to_param # => "7"
O retorno de valores em to_param
não deve ser ignorado:
"Tom & Jerry".to_param # => "Tom & Jerry"
Várias classes em Rails sobrescrevem este método.
Por exemplo nil
, true
, e false
retornam a si mesmo. Array#to_param
chama to_param
para cada elemento, exibindo o resultado separando os elementos com "/":
[0, true, String].to_param # => "0/true/String"
Notavelmente, as rotas de sistemas Rails chamam to_param
em models para obter o valor do campo :id
. ActiveRecord::Base#to_param
retorna o id
do model, mas você pode redefinir esse método em seus models. Por exemplo, dado
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
nós temos:
user_path(@user) # => "/users/357-john-smith"
Controllers precisam estar alinhados a qualquer redefinição de to_param
porque quando uma requisição como essa chega em "357-john-smith" este é o valor de params[:id]
.
Definido em active_support/core_ext/object/to_param.rb
.
2.9 to_query
O método to_query
controi uma query em string que associam a key
com o retorno de to_param
. Por exemplo, dado a seguinte definição de to_param
:
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
Temos:
current_user.to_query('user') # => "user=357-john-smith"
Este método traz o que é necessário, tanto para chave, como para o valor:
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
então esse resultado esta pronto para ser usado em uma string de busca.
Arrays retornam o resultado da aplicação to_query
para cada elemento com key[]
como chave, e junta o resultado com "&":
[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
Hashes tambem respondem a to_query
mas com uma diferença. Se não passar um argumento a chamada gera uma série ordenada de chaves/valores atribuídas chamando to_query(key)
em seus valores. Em seguida, o resultado é mesclado com "&":
{c: 3, b: 2, a: 1}.to_query # => "a=1&b=2&c=3"
O método Hash#to_query
aceita um espaço para nomear as chaves:
{id: 89, name: "John Smith"}.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
Definido em active_support/core_ext/object/to_query.rb
.
2.10 with_options
O método with_options
fornece um meio de agrupar opções comuns em uma série de chamada de métodos.
Dado as opções default de uma hash, with_options
faz um objeto de "ponte" em um bloco. Dentro do bloco, métodos são chamados no objeto e são encaminhados ao receptor com suas opções mescladas. Por exemplo, você se livra da duplicação em:
class Account < ApplicationRecord
has_many :customers, dependent: :destroy
has_many :products, dependent: :destroy
has_many :invoices, dependent: :destroy
has_many :expenses, dependent: :destroy
end
desta forma:
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
Essa expressão pode transmitir um agrupamento para o leitor também. Por exemplo, digamos que você queira enviar um boletim informativo cujo idioma depende do usuário. Em algum lugar na mailer você poderá agrupar os receptores por localidade como no exemplo:
I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
subject i18n.t :subject
body i18n.t :body, user_name: user.name
end
Desde que with_options
envie chamadas para seus receptores eles podem ser aninhados. Cada nível de aninhamento mesclará os padrões herdados com os seus próprios.
Definido em active_support/core_ext/object/with_options.rb
.
2.11 Suporte ao JSON
Active Support fornece uma melhor implementação para to_json
do que a gem json
normalmente fornece para objetos em Ruby. Isso é porque algumas classes, como Hash
, e Process::Status
precisam de manipulações especiais a fim de fornecer uma representação de JSON adequada.
Definido em active_support/core_ext/object/json.rb
.
2.12 Variáveis de Instância
Active Support fornece vários métodos para facilitar o acesso a variáveis de instância.
2.12.1 instance_values
O método instance_values
retorna uma hash que mapeia variáveis de instância de nomes sem "@" para seus
valores correspondentes. As chaves são strings:
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
Definido em active_support/core_ext/object/instance_variables.rb
.
2.12.2 instance_variable_names
O método instance_variable_names
retorna um array. Cada nome inclui o sinal "@".
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_variable_names # => ["@x", "@y"]
Definido em active_support/core_ext/object/instance_variables.rb
.
2.13 Silenciando Warnings e Exceções
Os métodos silence_warnings
e enable_warnings
trocam o valor de $VERBOSE
de acordo com a duração do seu bloco, e o reiniciam depois:
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
Silenciar exceções também é possível com suppress
. Este método recebe um número arbitrário de classes de exceção. Se uma exceção é acionada durante a execução de um bloco e é kind_of?
qualquer um dos argumentos, suppress
captura e retorna silenciosamente. Caso contrário, a exceção não é capturada:
# If the user is locked, the increment is lost, no big deal.
suppress(ActiveRecord::StaleObjectError) do
current_user.increment! :visits
end
Definido in active_support/core_ext/kernel/reporting.rb
.
2.14 in?
A expressão in?
testa se um objeto é incluído em outro objeto. Uma exceção ArgumentError
será acionada se o argumento passado não responder a include?
.
Exemplos de in?
:
1.in?([1,2]) # => true
"lo".in?("hello") # => true
25.in?(30..50) # => false
1.in?(1) # => ArgumentError
Definido em active_support/core_ext/object/inclusion.rb
.
3 Extensões de Module
3.1 Atributos
3.1.1 alias_attribute
Atributos de models podem ser lidos, escritos e condicionados. Você pode criar um alias para um atributo de model correspondendo todos os três métodos definidos por você usando alias_attribute
. Em outro métodos de alias, o novo nome é o primeiro argumento, e o antigo nome é o segundo (uma forma de memorizar é pensar que eles se apresentam na mesma ordem como se você fizesse uma atribuição):
class User < ApplicationRecord
# Você pode referenciar a coluna email como "login".
# Isso pode ser importante para o código de autenticação.
alias_attribute :login, :email
end
Definido em active_support/core_ext/module/aliasing.rb
.
3.1.2 Atributos Internos
Quando você esta definindo um atributo em uma classe que pode ser uma subclasse, os conflitos de nomes são um risco. Isso é extremamente importante para as bibliotecas.
Active Support define as macros attr_internal_reader
, attr_internal_writer
, e attr_internal_accessor
. Elas comportam-se como seu próprio Ruby attr_*
embutido, exceto pelos nomes de variáveis de instância que faz com que os conflitos sejam menos comuns.
A macro attr_internal
é um sinônimo para attr_internal_accessor
:
# biblioteca
class ThirdPartyLibrary::Crawler
attr_internal :log_level
end
# código do cliente
class MyCrawler < ThirdPartyLibrary::Crawler
attr_accessor :log_level
end
No exemplo anterior, poderia ser que no caso :log_level
não pertença a interface pública da biblioteca e só seria usada em desenvolvimento. O código do cliente, não sabe do potencial conflito, subclasses e definições de seus próprios :log_level
. Graças ao attr_internal
não há conflito.
Por padrão, a variável de instancia interna é nomeada com uma underscore na frente, @_log_level
no exemplo acima. Isso é configurável via Module.attr_internal_naming_format
apesar disso, você pode passar qualquer tipo de sprintf
no formato string com a inicial @
e um %s
em algum lugar, no qual é onde o nome será colocado. O padrão é "@_%s"
.
Rails usa atributos internos em alguns pontos, para views como por exemplo:
module ActionView
class Base
attr_internal :captures
attr_internal :request, :layout
attr_internal :controller, :template
end
end
Definido em active_support/core_ext/module/attr_internal.rb
.
3.1.3 Atributos de Módulo
As macros mattr_reader
, mattr_writer
, e mattr_accessor
São iguais a cattr_*
macros definidas na classe. De fato, cattr_*
macros são apenas aliases para as mattr_*
macros. Confira a seção Atributos de Classe.
Por exemplo, a API para o registrador do Active Storage é gerada com mattr_accessor
:
module ActiveStorage
mattr_accessor :logger
end
Definido em active_support/core_ext/module/attribute_accessors.rb
.
3.2 Parents
3.2.1 module_parent
O método module_parent
em um módulo nomeado aninhado que retorna o módulo que contém uma constante correspondente:
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
Se o módulo é anônimo ou pertence a um nível superior, module_parent
retorna Object
.
Note que neste caso module_parent_name
retorna nil
.
Definido em active_support/core_ext/module/introspection.rb
.
3.2.2 module_parent_name
O método module_parent_name
em um modulo nomeado aninhado retorna o nome completamente qualificado do módulo que contém sua constante correspondente:
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 nível superior ou anônimos module_parent_name
retorna nil
.
Note que nesse caso module_parent
retorna Object
.
Definido em active_support/core_ext/module/introspection.rb
.
3.2.3 module_parents
O método module_parents
chama module_parent
no receptor e sobe até Object
ser alcançado. A cadeia é retornada em uma matriz, de baixo para cima:
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]
Definido em active_support/core_ext/module/introspection.rb
.
3.3 Anônimo
Um módulo pode ou não ter um nome:
module M
end
M.name # => "M"
N = Module.new
N.name # => "N"
Module.new.name # => nil
Você pode verificar se um módulo possui um nome com a condicional anonymous?
:
module M
end
M.anonymous? # => false
Module.new.anonymous? # => true
Observe que estar inacessível não significa ser anônimo:
module M
end
m = Object.send(:remove_const, :M)
m.anonymous? # => false
Embora um módulo anônimo seja inacessível por definição.
Definido em active_support/core_ext/module/anonymous.rb
.
3.4 Delegação de Método
3.4.1 delegate
A macro delegate
oferece uma maneira fácil de encaminhar métodos.
Vamos imaginar que os usuários de alguma aplicação possuem informações de login no model User
além de nome e outro dado em um model Profile
separado:
class User < ApplicationRecord
has_one :profile
end
Com essa configuração você consegue o nome dos usuários partir da classe perfil, user.profile.name
, mas isso poderia ser conveniente para habilitar o acesso ao atributo diretamente:
class User < ApplicationRecord
has_one :profile
def name
profile.name
end
end
Isso é o que o delegate
faz por você:
class User < ApplicationRecord
has_one :profile
delegate :name, to: :profile
end
É mais curto e a intenção mais óbvia.
O método deve ser público.
A macro delegate
aceita vários métodos:
delegate :name, :age, :address, :twitter, to: :profile
Quando interpolado em uma string, a opção :to
deve se tornar uma expressão que avalia o objeto ao qual o método é delegado. Normalmente uma string ou um symbol. Tal expressão é avaliada no contexto do receptor:
# delega para as constantes Rails
delegate :logger, to: :Rails
# delega para as classes receptoras
delegate :table_name, to: :class
Se a opção :prefix
for true
é menos genérica, veja abaixo.
Por padrão, se a delegação resulta em NoMethodError
e o objeto é nil
a exceção se propaga. Você pode perguntar se nil
é retornado ao invés com a opção :allow_nil
:
delegate :name, to: :profile, allow_nil: true
Com :allow_nil
a chamada user.name
retorna nil
se o usuário não tiver um perfil.
A opção :prefix
adiciona um prefixo ao nome do método gerado. Isso pode ser útil, por exemplo, para obter um nome melhor:
delegate :street, to: :address, prefix: true
Os exemplos prévios geram address_street
ao invés de street
.
Já que neste caso o nome do método gerado é composto pelos nomes do objeto alvo e do método alvo, a opção :to
deve ser um nome de método.
Um prefixo customizado pode também ser configurado:
delegate :size, to: :attachment, prefix: :avatar
Os macro exemplos prévios geram avatar_size
ao invés de size
.
A opção :private
mudam o escopo do método:
delegate :date_of_birth, to: :profile, private: true
Os métodos delegados são públicos por padrão. Passe private: true
para mudar isso.
Definido em active_support/core_ext/module/delegation.rb
3.4.2 delegate_missing_to
Imagine que você gostaria de delegar tudo o que está faltando no objeto User
,
para um Profile
. A macro delegate_missing_to
permite que você implemente isso
de forma facilitada:
class User < ApplicationRecord
has_one :profile
delegate_missing_to :profile
end
O destino pode ser qualquer coisa que possa ser chamada dentro do objeto, por exemplo: instância de variáveis, métodos, constantes etc. Somente métodos públicos do alvo são delegados.
Definido em active_support/core_ext/module/delegation.rb
.
3.5 Redefinindo Métodos
Existem casos onde você precisa definir um método com define_method
, mas não sei se já existe um método com esse nome. Caso sim, um warning é exibido se estiverem habilitados. Não é muito perigoso, mas não é uma boa prática.
O método redefine_method
previne um potencial warning, removendo um método existente, se necessário.
Você pode também usar silence_redefinition_of_method
se você precisa definir
o método de substituição (porque você está usando delegate
, por
exemplo).
Definido em active_support/core_ext/module/redefine_method.rb
.
4 Extensões para Class
4.1 Atributos de classe
4.1.1 class_attribute
O método class_attribute
declara um ou mais atributos de classe herdáveis que podem ser substituídos em qualquer nível abaixo da hierarquia.
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 exemplo 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
Eles também podem ser acessados e substituídos no nível de instância.
A.x = 1
a1 = A.new
a2 = A.new
a2.x = 2
a1.x # => 1, vem de A
a2.x # => 2, substituído em a2
A criação de um método de instância de escrita pode ser prevenido configurando a opção :instance_writer
para false
.
module ActiveRecord
class Base
class_attribute :table_name_prefix, instance_writer: false, default: "my"
end
end
Essa opção pode ser útil para prevenir atribuições em massa ao definir o atributo.
A criação de um método de instância de leitura pode ser prevenido configurando a opção :instance_reader
para false
.
class A
class_attribute :x, instance_reader: false
end
A.new.x = 1
A.new.x # NoMethodError
Por conveniência class_attribute
também define um predicado de instância que é uma negação dupla do que o leitor de instância retorna. No exemplo acima podemos usar x?
.
Quando :instance_reader
é false
, o predicado de instância retorna NoMethodError
assim como o método de leitura.
Se você não quiser o predicado de instância, passe instance_predicate: false
e ele não será definido.
Definido em active_support/core_ext/class/attribute.rb
.
4.1.2 cattr_reader
, cattr_writer
, e cattr_accessor
As macros cattr_reader
, cattr_writer
, e cattr_accessor
são análogas às suas attr_*
homólogas porém para classes. Eles inicializam a variável de classe com nil
a menos que ela já exista, e gera os métodos de classe correspondentes para acessá-la:
class MysqlAdapter < AbstractAdapter
# Gera métodos de classe para acessar @@emulate_booleans.
cattr_accessor :emulate_booleans
end
Além disso, você pode passar um bloco para cattr_*
para configurar o atributo com um valor padrão.
class MysqlAdapter < AbstractAdapter
# Gera métodos de classe para acessar @@emulate_booleans com true como valor padrão.
cattr_accessor :emulate_booleans, default: true
end
Métodos de instância são criados também por conveniência, eles são apenas uma forma de acesso ao atributo de classe. Logo, instâncias podem alterar o atributo de classe, porém não podem substituí-lo do mesmo modo que ocorre com class_attribute
(veja acima). Por exemplo, dado
module ActionView
class Base
cattr_accessor :field_error_proc, default: Proc.new { ... }
end
end
podemos acessar field_error_proc
nas views.
A geração do método de leitura de instância pode ser prevenido configurando :instance_reader
para false
e a geração dos métodos de escrita de instância podem ser prevenidos configurando :instance_writer
para false
. A geração de ambos os métodos podem ser prevenidos configurando :instance_accessor
para false
. Em todos os casos, o valor deve ser exatamente false
e não qualquer outro valor falso.
module A
class B
# Nenhuma leitura de instância first_name é gerada.
cattr_accessor :first_name, instance_reader: false
# Nenhuma escrita de instância last_name= é gerada.
cattr_accessor :last_name, instance_writer: false
# Nenhuma leitura surname ou escritor surname= de instância é gerada.
cattr_accessor :surname, instance_accessor: false
end
end
Pode ser útil configurar :instance_accessor
para false
no model como uma maneira de prevenir atribuições em massa ao definir o atributo.
Definido em active_support/core_ext/module/attribute_accessors.rb
.
4.2 Subclasses e Descendentes
4.2.1 subclasses
O método subclasses
retorna as subclasses do recebedor:
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]
A ordem em que essas classes são retornadas não é especificada.
Definido em active_support/core_ext/class/subclasses.rb
.
4.2.2 descendants
O método descendants
retorna todas as classes que são <
pelo recebedor:
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]
A ordem em que essas classes são retornadas não é especificada.
Definido em active_support/core_ext/class/subclasses.rb
.
5 Extensões para String
5.1 Segurança de saída
5.1.1 Motivação
Inserir dados em templates HTML, necessita de cuidados extras. Por exemplo, você não pode apenas literalmente juntar @review.title
em uma página HTML. Por outro lado, se o título do comentário é "Flanagan & Matz rules!" o retorno não será bem formada porque um 'e comercial' precisa ser usado como "&". Além do mais, dependendo da aplicação, isso pode ser uma grande falha de segurança porque os usuários podem injetar uma configuração HTML maliciosa em um título de revisão feito à mão. Confira a seção sobre cross-site scripting em Guia de Segurança para maiores informações sobre os riscos.
5.1.2 Strings Seguras
Active Support possui o conceito de (html) strings seguras. Uma string segura é aquela que é marcada como sendo inserível no HTML como é definida. Ela é confiável, não importando sua origem.
Strings são consideradas como inseguras por padrão:
"".html_safe? # => false
Pode-se obter uma string segura de um dado com o método html_safe
:
s = "".html_safe
s.html_safe? # => true
É importante entender que html_safe
não executa nenhuma operação, é apenas uma afirmação:
s = "<script>...</script>".html_safe
s.html_safe? # => true
s # => "<script>...</script>"
É sua responsabilidade garantir a chamada html_safe
em cada string particular.
Se você anexar em uma string segura, com concat
/<<
, ou com +
, o resultado é uma string segura. Argumentos inseguros são ignorados:
"".html_safe + "<" # => "<"
Argumentos seguros são anexados diretamente:
"".html_safe + "<".html_safe # => "<"
Esses métodos não devem ser usados em views comuns. Valores inseguros são ignorados automaticamente:
<%= @review.title %> <%# correto, ignora se necessário %>
Para inserir algo literal, use o helper raw
ao invés de chamar html_safe
:
<%= raw @cms.current_template %> <%# insere @cms.current_template como é %>
ou, equivalentemente, use <%==
:
<%== @cms.current_template %> <%# insere @cms.current_template como é %>
O helper raw
chama html_safe
pra você:
def raw(stringish)
stringish.to_s.html_safe
end
Definido em active_support/core_ext/string/output_safety.rb
.
5.1.3 Transformação
De modo geral, exceto talvez para concatenação conforme explicado acima, qualquer método que possa alterar uma string fornece uma string insegura. Estes são downcase
, gsub
, strip
, chomp
, underscore
, etc.
No caso de transformações locais, como com gsub!
o próprio receptor se torna inseguro.
O bit de segurança é perdido sempre, não importa se a transformação realmente mudou algo.
5.1.4 Conversão e Coerção
Chamando to_s
em uma string segura retorna uma string segura, mas a coerção com to_str
retorna uma string insegura.
5.1.5 Copiando
Chamando dup
ou clone
em strings seguras produz outras strings seguras.
5.2 remove
O método remove
vai remover todas ocorrências com o padrão:
"Hello World".remove(/Hello /) # => "World"
Há também a versão destrutiva String#remove!
.
Definido em active_support/core_ext/string/filters.rb
.
5.3 squish
O método squish
remove os espaços em branco à esquerda e à direita e substitui os espaços em branco por um único espaço cada:
" \n foo\n\r \t bar \n".squish # => "foo bar"
Há também a versão destrutiva String#squish!
.
Observe que se lida com espaços em branco ASCII e Unicode.
Definido em active_support/core_ext/string/filters.rb
.
5.4 truncate
O método truncate
retorna uma cópia de seu receptor truncado após um determinado length
:
"Oh dear! Oh dear! I shall be late!".truncate(20)
# => "Oh dear! Oh dear!..."
As reticências podem ser personalizadas com a opção :omission
:
"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '…')
# => "Oh dear! Oh …"
Observe em particular que o truncamento leva em consideração o comprimento da string de omissão.
Passe o método :separator
para truncar a string em uma pausa 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..."
A opção :separator
pode ser uma regexp:
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => "Oh dear! Oh..."
Nos exemplos acima "dear" é cortado primeiro, mas depois :separator
impede isso.
Definido em active_support/core_ext/string/filters.rb
.
5.5 truncate_bytes
O método truncate_bytes
retorna uma cópia de seu receptor truncado para no máximo bytesize
bytes:
"👍👍👍👍".truncate_bytes(15)
# => "👍👍👍…"
As reticências podem ser personalizadas com a opção :omission
:
"👍👍👍👍".truncate_bytes(15, omission: "🖖")
# => "👍👍🖖"
Definido em active_support/core_ext/string/filters.rb
.
5.6 truncate_words
O método truncate_words
retorna uma cópia da frase original truncada depois de receber um determinado número de palavras:
"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => "Oh dear! Oh dear!..."
Reticências podem ser customizadas com a opção :omission
:
"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '…')
# => "Oh dear! Oh dear!…"
Chame :separator
para truncar a string na pausa natural:
"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
# => "Oh dear! Oh dear! I shall be late..."
A opção :separator
pode ser uma regexp:
"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => "Oh dear! Oh dear!..."
Definido em active_support/core_ext/string/filters.rb
.
5.7 inquiry
O método inquiry
converte uma string em um objeto StringInquirer
fazendo verificações de igualdade mais elegantes.
"production".inquiry.production? # => true
"active".inquiry.inactive? # => false
Definido em active_support/core_ext/string/inquiry.rb
.
5.8 starts_with?
e ends_with?
Active Support define conjugação verbal para 3ª pessoa em String#start_with?
e String#end_with?
:
"foo".starts_with?("f") # => true
"foo".ends_with?("o") # => true
Definido em active_support/core_ext/string/starts_ends_with.rb
.
5.9 strip_heredoc
O método strip_heredoc
tira o recuo em heredocs.
Por exemplo em
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
o usuário veria a mensagem de uso alinhada à margem esquerda.
Tecnicamente, se procura a linha menos indentada em toda a string e remove essa quantidade de espaço em branco à esquerda.
Defined in active_support/core_ext/string/strip.rb
.
5.10 indent
O método indent
indenta as linhas no receptor:
<<EOS.indent(2)
def some_method
some_code
end
EOS
# =>
def some_method
some_code
end
O segundo argumento, indent_string
, especifica qual string de indentação usar. O padrão é nil
, que diz ao método para fazer uma suposição conferindo a primeira linha indentada, e recuando para um espaço se não houver nenhuma.
" foo".indent(2) # => " foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t") # => "\t\tfoo"
Enquanto indent_string
é normalmente um espaço ou tabulação, essa pode ser qualquer string.
O terceiro argumento, indent_empty_lines
, é uma sinalização que diz se as linhas vazias devem ser indentadas. O padrão é false.
"foo\n\nbar".indent(2) # => " foo\n\n bar"
"foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
O método indent!
realiza recuo no local.
Definido em active_support/core_ext/string/indent.rb
.
5.11 Acesso
5.11.1 at(position)
O método at
retorna o caractere da string na posição position
:
"hello".at(0) # => "h"
"hello".at(4) # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil
Definido em active_support/core_ext/string/access.rb
.
5.11.2 from(position)
O método from
retorna a substring da string iniciada na posição position
:
"hello".from(0) # => "hello"
"hello".from(2) # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil
Definido em active_support/core_ext/string/access.rb
.
5.11.3 to(position)
O método to
retorna a substring da string até a posição position
:
"hello".to(0) # => "h"
"hello".to(2) # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"
Definido em active_support/core_ext/string/access.rb
.
5.11.4 first(limit = 1)
O método first
retorna a substring contendo os primeiros limit
caracteres da string.
A chamada str.first(n)
é equivalente a str.to(n-1)
se n
> 0, e retorna uma string vazia para n
== 0.
Definido em active_support/core_ext/string/access.rb
.
5.11.5 last(limit = 1)
O método last
retorna a substring contendo os últimos limit
caracteres da string.
A chamada str.last(n)
é equivalente a str.from(-n)
se n
> 0, e retorna uma string vazia para n
== 0.
Definido em active_support/core_ext/string/access.rb
.
5.12 Inflexões
5.12.1 pluralize
O método pluralize
retorna o plural do receptor:
"table".pluralize # => "tables"
"ruby".pluralize # => "rubies"
"equipment".pluralize # => "equipment"
Como mostra o exemplo anterior, Active Support conhece alguns plurais irregulares e substantivos incontáveis. As regras integradas podem ser estendidas em config/initializers/inflections.rb
. Este arquivo é gerado por padrão, pelo comando rails new
e tem instruções nos comentários.
pluralize
também pode fazer um parâmetro count
opcional. Se count == 1
a forma singular será retornada. Para qualquer outro valor de count
a forma plural será retornada:
"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"
Active Record usa esse método pra computar a o nome da tabela padrão correspondente ao model:
# 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
Definido active_support/core_ext/string/inflections.rb
.
5.12.2 singularize
O método singularize
é o inverso do pluralize
:
"tables".singularize # => "table"
"rubies".singularize # => "ruby"
"equipment".singularize # => "equipment"
As associações calculam o nome padrão da classe associada correspondente usando 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
Definido em active_support/core_ext/string/inflections.rb
.
5.12.3 camelize
O método camelize
retorna o receptor em camel case:
"product".camelize # => "Product"
"admin_user".camelize # => "AdminUser"
Como regra geral, você pode pensar neste método como aquele que transforma pastas em nomes de classes ou módulos Ruby, em que barras separam os subarquivos:
"backoffice/session".camelize # => "Backoffice::Session"
Por exemplo, o Action Pack usa este método para carregar a classe que fornece um determinado armazenamento de sessão:
# 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
aceita um argumento opcional, que pode ser :upper
(padrão), ou :lower
. Com o último, a primeira letra torna-se minúscula:
"visual_effect".camelize(:lower) # => "visualEffect"
Isso pode ser útil para calcular nomes de métodos em uma linguagem que segue essa convenção, por exemplo, JavaScript.
Como regra geral, você pode pensar em camelize
como o inverso do underscore
, embora haja casos em que isso não se aplica: "SSLError".underscore.camelize
devolve "SslError"
. Para apoiar casos como este, o Active Support permite que você especifique acrônimos em config/initializers/inflections.rb
:
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'SSL'
end
"SSLError".underscore.camelize # => "SSLError"
camelize
é o mesmo que usar camelcase
.
Definido em active_support/core_ext/string/inflections.rb
.
5.12.4 underscore
O método underscore
vai ao contrário, de camel case para pastas:
"Product".underscore # => "product"
"AdminUser".underscore # => "admin_user"
Também converte "::" back para "/":
"Backoffice::Session".underscore # => "backoffice/session"
e entende strings que começam com start letra minúscula:
"visualEffect".underscore # => "visual_effect"
underscore
não aceita nenhum argumento.
O Rails usa de underscore
para inferir o nome de um controller ou class:
# actionpack/lib/abstract_controller/base.rb
def controller_path
@controller_path ||= name.delete_suffix("Controller").underscore
end
Por exemplo, esse valor é aquele que você obtém em params[:controller]
.
Como regra geral, pode-se pensar em underscore
como o inverso de camelize
, embora haja casos em que isso não se aplica. Por exemplo, "SSLError".underscore.camelize
devolve "SslError"
.
Definido em active_support/core_ext/string/inflections.rb
.
5.12.5 titleize
O método titleize
coloca cada palavra com letra maiúscula:
"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize # => "Fermat's Enigma"
titleize
é o mesmo que usar titlecase
.
Definido em active_support/core_ext/string/inflections.rb
.
5.12.6 dasherize
O método dasherize
troca underscores no receptor por traços:
"name".dasherize # => "name"
"contact_data".dasherize # => "contact-data"
O serializer XML de models usa este método para colocar traços nos nomes de seus nós:
# active_model/serializers/xml.rb
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
Definido em active_support/core_ext/string/inflections.rb
.
5.12.7 demodulize
Dado uma string com um nome de constante em módulo, demodulize
retorna o real nome da constante, ou seja, a parte mais à direita dela:
"Product".demodulize # => "Product"
"Backoffice::UsersController".demodulize # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize # => "Inflections"
"".demodulize # => ""
Active Record por exemplo, usa este método para calcular o nome de uma coluna de cache do 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
Definido em active_support/core_ext/string/inflections.rb
.
5.12.8 deconstantize
Dada uma string com uma expressão de referência a uma constante, deconstantize
remove o segmento mais à direita, geralmente deixando o nome do contêiner da constante:
"Product".deconstantize # => ""
"Backoffice::UsersController".deconstantize # => "Backoffice"
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"
Definido em active_support/core_ext/string/inflections.rb
.
5.12.9 parameterize
O método parameterize
normaliza seu receptor de uma forma que pode ser usada em URLs de forma mais elegante.
"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"
Para preservar a caixa da string, defina o argumento preserve_case
para true. Por padrão, preserve_case
será configurado como false
.
"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"
Para usar um separador customizado, sobrescreva o argumento separator
.
"John Smith".parameterize(separator: "_") # => "john_smith"
"Kurt Gödel".parameterize(separator: "_") # => "kurt_godel"
Definido em active_support/core_ext/string/inflections.rb
.
5.12.10 tableize
O método tableize
é underscore
seguido por pluralize
.
"Person".tableize # => "people"
"Invoice".tableize # => "invoices"
"InvoiceLine".tableize # => "invoice_lines"
Como um princípio básico, tableize
retorna o nome da tabela que corresponde a um determinado modelo para casos simples. A implementação real de tableize
no Active Record na verdade não é direta, porque também desmodulariza o nome da classe e verifica algumas opções que podem afetar a string retornada.
Definido em active_support/core_ext/string/inflections.rb
.
5.12.11 classify
O método classify
é o inverso de tableize
. Ele da o nome da classe correspondente ao nome da tabela:
"people".classify # => "Person"
"invoices".classify # => "Invoice"
"invoice_lines".classify # => "InvoiceLine"
O método compreende nomes de tabela associadas:
"highrise_production.companies".classify # => "Company"
Note que classify
retorna um nome de classe como uma string. Você pode obter o objeto de classe real invocando constantize
sobre isso, será explicado a seguir.
Definido em active_support/core_ext/string/inflections.rb
.
5.12.12 constantize
O método constantize
resolve a expressão de referência constante em seu receptor:
"Integer".constantize # => Integer
module M
X = 1
end
"M::X".constantize # => 1
Se a string não for avaliada como uma constante conhecida ou seu conteúdo nem mesmo for um nome de constante válido, constantize
executa NameError
.
Resolução de nome constante por constantize
inicia sempre no nível superior de Object
mesmo se não começar com "::".
X = :in_Object
module M
X = :in_M
X # => :in_M
"::X".constantize # => :in_Object
"X".constantize # => :in_Object (!)
end
So, it is in general not equivalent to what Ruby would do in the same spot, had a real constant be evaluated.
Mailer test cases obtain the mailer being tested from the name of the test class using constantize
:
# action_mailer/test_case.rb
def determine_default_mailer(name)
name.delete_suffix("Test").constantize
rescue NameError => e
raise NonInferrableMailerError.new(name)
end
Definido em active_support/core_ext/string/inflections.rb
.
5.12.13 humanize
O método humanize
ajusta um nome de atributo para exibir aos usuários.
Especificamente, ele realiza estas transformações:
- Aplica regras de inflexão humana ao argumento.
- Exclui os underlines iniciais, se houver.
- Remove um sufixo "_id", se houver.
- Substitui underlines por espaços, se houver.
- Reduz todas as palavras, exceto siglas.
- Coloca em maiúscula a primeira palavra.
A capitalização da primeira palavra pode ser desativada configurando o
:capitalize
opção para false
(o padrão é true
).
"name".humanize # => "Name"
"author_id".humanize # => "Author"
"author_id".humanize(capitalize: false) # => "author"
"comments_count".humanize # => "Comments count"
"_id".humanize # => "Id"
Se "SSL" for definido como uma sigla:
'ssl_error'.humanize # => "SSL error"
O método helper full_messages
usa humanize
como alternativa para incluir
nomes de atributos:
def full_messages
map { |attribute, message| full_message(attribute, message) }
end
def full_message
# ...
attr_name = attribute.to_s.tr('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
# ...
end
Definido em active_support/core_ext/string/inflections.rb
.
5.12.14 foreign_key
O método foreign_key
fornece um nome de coluna de chave estrangeira a partir de um nome de classe. Para fazer isso, ele desmoduliza, separa com underline e adiciona "_id":
"User".foreign_key # => "user_id"
"InvoiceLine".foreign_key # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"
Passe false
como argumento se você não quiser o underline em "_id":
"User".foreign_key(false) # => "userid"
As associações usam este método para inferir chaves estrangeiras, por exemplo has_one
e has_many
fazem isto:
# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
Definido em active_support/core_ext/string/inflections.rb
.
5.13 Conversões
5.13.1 to_date
, to_time
, to_datetime
Os métodos to_date
, to_time
, e to_datetime
são basicamente variações convenientes de Date._parse
:
"2010-07-27".to_date # => Tue, 27 Jul 2010
"2010-07-27 23:37:00".to_time # => 2010-07-27 23:37:00 +0200
"2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000
to_time
recebe an um argumento opcional :utc
ou :local
, para indicar qual fuso horário you quer se basear:
"2010-07-27 23:42:00".to_time(:utc) # => 2010-07-27 23:42:00 UTC
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200
O padrão é :local
.
Por favor, consulte a documentação de Date._parse
para mais detalhes.
Os três exemplos retornam nil
caso não recebam argumentos.
Definido em active_support/core_ext/string/conversions.rb
.
6 Extensões para Symbol
6.1 starts_with?
e ends_with?
O Active Support define aliases (nomes simbólicos) de terceira pessoa de Symbol#start_with?
e Symbol#end_with?
:
:foo.starts_with?("f") # => true
:foo.ends_with?("o") # => true
Definido em active_support/core_ext/symbol/starts_ends_with.rb
.
7 Extensões para Numeric
7.1 Bytes
Todos os números respondem a estes métodos:
Eles retornam a quantidade correspondente de bytes, usando um fator de conversão de 1024:
2.kilobytes # => 2048
3.megabytes # => 3145728
3.5.gigabytes # => 3758096384
-4.exabytes # => -4611686018427387904
As formas singulares têm um alias para que você possa dizer:
1.megabyte # => 1048576
Definido em active_support/core_ext/numeric/bytes.rb
.
7.2 Time
Os seguintes métodos:
habilitar declarações e cálculos de tempo, como 45.minutes + 2.hours + 4.weeks
. Seus valores de retorno também podem ser adicionados ou subtraídos dos objetos Time.
Esses métodos podem ser combinados com from_now
, ago
, etc, para cálculos de datas precisos. Por exemplo:
# equivalent to Time.current.advance(days: 1)
1.day.from_now
# equivalent to Time.current.advance(weeks: 2)
2.weeks.from_now
# equivalent to Time.current.advance(days: 4, weeks: 5)
(4.days + 5.weeks).from_now
Para outras durações, consulte as extensões de tempo para Integer
.
Definido em active_support/core_ext/numeric/time.rb
.
7.3 Formatação
Permite a formatação de números de várias maneiras.
Produz uma representação de string de um número como um número de telefone:
5551234.to_fs(:phone)
# => 555-1234
1235551234.to_fs(:phone)
# => 123-555-1234
1235551234.to_fs(:phone, area_code: true)
# => (123) 555-1234
1235551234.to_fs(:phone, delimiter: " ")
# => 123 555 1234
1235551234.to_fs(:phone, area_code: true, extension: 555)
# => (123) 555-1234 x 555
1235551234.to_fs(:phone, country_code: 1)
# => +1-123-555-1234
Produz uma representação de string de um número como moeda:
1234567890.50.to_fs(:currency) # => $1,234,567,890.50
1234567890.506.to_fs(:currency) # => $1,234,567,890.51
1234567890.506.to_fs(:currency, precision: 3) # => $1,234,567,890.506
Produz uma representação de string de um número como uma porcentagem:
100.to_fs(:percentage)
# => 100.000%
100.to_fs(:percentage, precision: 0)
# => 100%
1000.to_fs(:percentage, delimiter: '.', separator: ',')
# => 1.000,000%
302.24398923423.to_fs(:percentage, precision: 5)
# => 302.24399%
Produz uma representação de string de um número na forma delimitada:
12345678.to_fs(:delimited) # => 12,345,678
12345678.05.to_fs(:delimited) # => 12,345,678.05
12345678.to_fs(:delimited, delimiter: ".") # => 12.345.678
12345678.to_fs(:delimited, delimiter: ",") # => 12,345,678
12345678.05.to_fs(:delimited, separator: " ") # => 12,345,678 05
Produz uma representação de string de um número arredondado para uma precisão:
111.2345.to_fs(:rounded) # => 111.235
111.2345.to_fs(:rounded, precision: 2) # => 111.23
13.to_fs(:rounded, precision: 5) # => 13.00000
389.32314.to_fs(:rounded, precision: 0) # => 389
111.2345.to_fs(:rounded, significant: true) # => 111
Produz uma representação de string de um número como um número de bytes legível para humanos:
123.to_fs(:human_size) # => 123 Bytes
1234.to_fs(:human_size) # => 1.21 KB
12345.to_fs(:human_size) # => 12.1 KB
1234567.to_fs(:human_size) # => 1.18 MB
1234567890.to_fs(:human_size) # => 1.15 GB
1234567890123.to_fs(:human_size) # => 1.12 TB
1234567890123456.to_fs(:human_size) # => 1.1 PB
1234567890123456789.to_fs(:human_size) # => 1.07 EB
Produz uma representação de string de um número em palavras legíveis para humanos:
123.to_fs(:human) # => "123"
1234.to_fs(:human) # => "1.23 Thousand"
12345.to_fs(:human) # => "12.3 Thousand"
1234567.to_fs(:human) # => "1.23 Million"
1234567890.to_fs(:human) # => "1.23 Billion"
1234567890123.to_fs(:human) # => "1.23 Trillion"
1234567890123456.to_fs(:human) # => "1.23 Quadrillion"
Definido em active_support/core_ext/numeric/conversions.rb
.
8 Extensões para Integer
8.1 multiple_of?
O método multiple_of?
testa se um inteiro é múltiplo do argumento:
2.multiple_of?(1) # => true
1.multiple_of?(2) # => false
Definido em active_support/core_ext/integer/multiple.rb
.
8.2 ordinal
O método ordinal
retorna a string de sufixo ordinal correspondente ao inteiro receptor:
1.ordinal # => "st"
2.ordinal # => "nd"
53.ordinal # => "rd"
2009.ordinal # => "th"
-21.ordinal # => "st"
-134.ordinal # => "th"
Definido em active_support/core_ext/integer/inflections.rb
.
8.3 ordinalize
O método ordinalize
retorna a string ordinal correspondente ao inteiro receptor. Em comparação, observe que o método ordinal
retorna apenas a string de sufixo.
1.ordinalize # => "1st"
2.ordinalize # => "2nd"
53.ordinalize # => "53rd"
2009.ordinalize # => "2009th"
-21.ordinalize # => "-21st"
-134.ordinalize # => "-134th"
Definido em active_support/core_ext/integer/inflections.rb
.
8.4 Time
Os seguintes métodos:
habilitar declarações de tempo e cálculos, como 4.months + 5.years
. Seus valores de retorno também podem ser adicionados ou subtraídos dos objetos Time.
Esses métodos podem ser combinados com from_now
, ago
, etc, para cálculos de data precisos. Por exemplo:
# equivalente ao Time.current.advance(months: 1)
1.month.from_now
# eequivalente ao Time.current.advance(years: 2)
2.years.from_now
# equivalente ao Time.current.advance(months: 4, years: 5)
(4.months + 5.years).from_now
Para outras durações, consulte as extensões de tempo para Numeric
.
Definido em active_support/core_ext/integer/time.rb
.
9 Extensões para BigDecimal
9.1 to_s
O método to_s
fornece um especificador padrão de "F". Isso significa que uma simples chamada para to_s
resultará em representação de ponto flutuante em vez de notação de engenharia:
BigDecimal(5.00, 6).to_s # => "5.0"
e que especificadores usando symbols também são suportados:
BigDecimal(5.00, 6).to_s(:db) # => "5.0"
A notação de engenharia ainda é suportada:
BigDecimal(5.00, 6).to_s("e") # => "0.5E1"
10 Extensões para Enumerable
10.1 sum
O método sum
adiciona os elementos de um enumerable:
[1, 2, 3].sum # => 6
(1..100).sum # => 5050
A adição apenas assume que os elementos respondem 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]
A soma de uma coleção vazia é zero por padrão, mas isto é customizável:
[].sum # => 0
[].sum(1) # => 1
Se um bloco for fornecido, sum
se torna um iterador que fornece os elementos da coleção e soma os valores retornados:
(1..5).sum {|n| n * 2 } # => 30
[2, 4, 6, 8, 10].sum # => 30
A soma de um recipiente vazio pode ser customizado nessa forma também:
[].sum(1) {|n| n**3} # => 1
Definido em active_support/core_ext/enumerable.rb
.
10.2 index_by
O método index_by
gera um hash com os elementos de um enumerable indexados por alguma chave.
Ele itera pela coleção e passa cada elemento para um bloco. O elemento será chaveado pelo valor retornado pelo bloco:
invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}
As chaves normalmente devem ser exclusivas. Se o bloco retornar o mesmo valor para elementos diferentes, nenhuma coleção será construida para essa chave. O último item irá ganhar.
Definido em active_support/core_ext/enumerable.rb
.
10.3 index_with
O método index_with
gera um hash com os elementos de um enumerable
como chaves. O valor será o que foi passado como padrão ou o retornado em um bloco.
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 ], … }
Definido em active_support/core_ext/enumerable.rb
.
10.4 many?
O método many?
é um atalho para collection.size > 1
:
<% if pages.many? %>
<%= pagination_links %>
<% end %>
Se for fornecido um bloco opcional, many?
leva em consideração apenas aqueles elementos que retornam true:
@see_more = videos.many? {|video| video.category == params[:category]}
Definido em active_support/core_ext/enumerable.rb
.
10.5 exclude?
O predicado exclude?
testa se um dado objeto não pertence à coleção. É a negação do método embutido include?
:
to_visit << node if visited.exclude?(node)
Definido em active_support/core_ext/enumerable.rb
.
10.6 including
O método including
retorna um novo enumerable
que inclui os elementos passados:
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
["David", "Rafael"].including %w[ Aaron Todd ] # => ["David", "Rafael", "Aaron", "Todd"]
Definido em active_support/core_ext/enumerable.rb
.
10.7 excluding
O método excluding
retorna uma cópia de um enumerable
com os elementos especificados removidos:
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
excluding
é um alias para without
.
Definido em active_support/core_ext/enumerable.rb
.
10.8 pluck
O método pluck
extrai a chave fornecida 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"]]
Definido em active_support/core_ext/enumerable.rb
.
10.9 pick
O método pick
extrai a chave fornecida do primeiro elemento:
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) # => "David"
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) # => [1, "David"]
Definido em active_support/core_ext/enumerable.rb
.
11 Extensões para Array
11.1 Acessando
O Active Support aumenta a API de arrays para facilitar certas maneiras de acessá-los. Por exemplo, to
retorna o subarray de elementos até aquele no índice passado:
%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
Da mesma forma, from
retorna os elementos a partir do elemento no índice passado até o final. Se o índice for maior que o comprimento do array, será retornado um array vazio.
%w(a b c d).from(2) # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0) # => []
O método including
retorna um novo array que inclui os elementos passados:
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
O método excluding
retorna uma cópia do array excluindo os elementos especificados.
Esta é uma otimização de Enumerable#excluding
que usa Array#-
ao invés de Array#reject
por questão de desempenho.
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
Os métodos second
, third
, fourth
, e fifth
retornam o elemento correspondente, assim como second_to_last
e third_to_last
(first
e last
são embutidos). Graças à sabedoria social e construtividade positiva ao redor, forty_two
também está disponível.
%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil
Definido em active_support/core_ext/array/access.rb
.
11.2 Extraindo
O método extract!
remove e retorna os elementos para os quais o bloco retorna um valor true.
Se nenhum bloco for fornecido, um Enumerator será retornado.
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]
Definido em active_support/core_ext/array/extract.rb
.
11.3 Extração de opções
Quando o último argumento em uma chamada de método é um hash, exceto talvez por um argumento do tipo &block
, o Ruby permite que você omita os colchetes:
User.exists?(email: params[:email])
Esse syntactic sugar é muito usado no Rails para evitar argumentos posicionais onde haveria muitos, oferecendo interfaces que emulam os parâmetros nomeados. Em particular, é muito idiomático usar um hash no final para opções.
Se um método espera um número variável de argumentos e usa *
em sua declaração, entretanto, tal hash de opções acaba sendo um item do array de argumentos, onde perde seu papel.
Nesses casos, você pode dar um tratamento diferenciado ao hash de opções com extract_options!
. Este método verifica o tipo do último item de um array. Se for um hash, ele o exibe e o retorna, caso contrário, retorna um hash vazio.
Vejamos, por exemplo, a definição da macro do controlador caches_action
:
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
# ...
end
Este método recebe um número arbitrário de nomes de ações e um hash opcional de opções como último argumento. Com a chamada a extract_options!
você obtém o hash de opções e o remove de actions
de forma simples e explícita.
Definido em active_support/core_ext/array/extract_options.rb
.
11.4 Conversões
11.4.1 to_sentence
O método to_sentence
transforma um array em uma string contendo uma frase que enumera seus itens:
%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 aceita três opções:
-
:two_words_connector
: O que é usado para arrays de comprimento 2. O padrão é " e ". -
:words_connector
: O que é usado para unir os elementos de arrays com 3 ou mais elementos, exceto os dois últimos. O padrão é ", ". -
:last_word_connector
: O que é usado para unir os últimos itens de um array com 3 ou mais elementos. O padrão é ", e ".
Os padrões para essas opções podem ser localizados, suas chaves são:
Opção | Chave I18n |
---|---|
:two_words_connector |
support.array.two_words_connector |
:words_connector |
support.array.words_connector |
:last_word_connector |
support.array.last_word_connector |
Definido em active_support/core_ext/array/conversions.rb
.
11.4.2 to_fs
O método [to_fs
][Array#to_formatted_s] age como to_s
por padrão.
No entanto, se o array contém itens que respondem a id
, o símbolo
:db
pode ser passado como argumento. Isso é normalmente usado em coleções de objetos do Active Record. As strings retornadas são:
[].to_fs(:db) # => "null"
[user].to_fs(:db) # => "8456"
invoice.lines.to_fs(:db) # => "23,567,556,12"
Os inteiros no exemplo acima devem vir das respectivas chamadas para id
.
Definido em active_support/core_ext/array/conversions.rb
.
11.4.3 to_xml
O método to_xml
retorna uma string contendo uma representação em XML do seu recipiente:
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 fazer isso, ele envia to_xml
para cada item e coleta os resultados em um nó raiz. Todos os itens devem responder a to_xml
, caso contrário, uma exceção é provocada.
Por padrão, o nome do elemento raiz é o plural sublinhado e tracejado do nome da classe do primeiro item, desde que os demais elementos pertençam a esse tipo (verificado com is_a?
) e não sejam hashes. No exemplo acima são "contributors".
Se houver algum elemento que não pertença ao tipo do primeiro, o nó raiz se torna "objects":
[Contributor.first, Commit.first].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
# <object>
# <id type="integer">4583</id>
# <name>Aaron Batalion</name>
# <rank type="integer">53</rank>
# <url-id>aaron-batalion</url-id>
# </object>
# <object>
# <author>Joshua Peek</author>
# <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
# <branch>origin/master</branch>
# <committed-timestamp type="datetime">2009-09-02T16:44:36Z</committed-timestamp>
# <committer>Joshua Peek</committer>
# <git-show nil="true"></git-show>
# <id type="integer">190316</id>
# <imported-from-svn type="boolean">false</imported-from-svn>
# <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message>
# <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
# </object>
# </objects>
Se o recipiente é um array de hashes, o elemento raiz também é, por padrão, "objects":
[{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>
Se a coleção estiver vazia, o elemento raiz é, por padrão, "nil-classes". If the collection is empty the root element is by default "nil-classes". Isso é uma pegadinha, por exemplo, o elemento raiz da lista de contributors acima não seria "contributors" se a coleção estivesse vazia, e sim "nil-classes". Você pode usar a opção :root
para garantir um elemento raiz consistente.
O nome dos nós filhos é, por padrão, o nome do nó raiz singularizado. Nos exemplos acima vimos "contributor" e "object". A opção :children
permite que você defina esses nomes de nós.
O construtor XML padrão é uma nova instância de Builder::XmlMarkup
. Você pode configurar seu próprio construtor através da opção :builder
. O método também aceita opções como :dasherize
e afins, elas são encaminhadas para o construtor:
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>
Definido em active_support/core_ext/array/conversions.rb
.
11.5 Invólucro
O método Array.wrap
envolve seu argumento em um array, a menos que já seja um array (ou semelhante a um array).
Especificamente:
- Se o argumento for
nil
um array vazio é retornado. - Caso contrário, se o argumento responde a
to_ary
ele será invocado, e se o valor deto_ary
não fornil
, ele será retornado. - Caso contrário, um array com o argumento como seu único elemento é retornado.
Array.wrap(nil) # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0) # => [0]
Este método é semelhante em propósito ao Kernel#Array
, mas há algumas diferenças:
- Se o argumento responde a
to_ary
o método é invocado.Kernel#Array
segue em frente para tentar chamarto_a
se o valor retornado fornil
, masArray.wrap
retorna um array com o argumento como seu único elemento imediatamente. - Se o valor retornado de
to_ary
não fornil
nem um objetoArray
,Kernel#Array
gera uma exceção, enquantoArray.wrap
não, apenas retorna o valor. - Ele não chama
to_a
no argumento, se o argumento não responde ato_ary
ele retorna um array com o argumento como seu único elemento.
O último ponto é particularmente digno de comparação para alguns enumerables:
Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar) # => [[:foo, :bar]]
Há também um idioma relacionado que usa o operador splat:
[*object]
Definido em active_support/core_ext/array/wrap.rb
.
11.6 Duplicando
O método Array#deep_dup
duplica a si mesmo e todos os objetos dentro
recursivamente com o método do Active Support Object#deep_dup
. Funciona como um Array#map
, enviando o método deep_dup
para cada objeto dentro.
array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil # => true
Definido em active_support/core_ext/object/deep_dup.rb
.
11.7 Agrupamento
11.7.1 in_groups_of(number, fill_with = nil)
O método in_groups_of
divide um array em grupos consecutivos de um determinado tamanho. Ele retorna um array com os grupos:
[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
ou os fornece por sua vez se um bloco for passado:
<% sample.in_groups_of(3) do |a, b, c| %>
<tr>
<td><%= a %></td>
<td><%= b %></td>
<td><%= c %></td>
</tr>
<% end %>
O primeiro elemento mostra como in_groups_of
preenche o último grupo com quantos elementos nil
forem necessários para ter o tamanho solicitado. Você pode alterar esse valor de preenchimento usando o segundo argumento opcional:
[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
E você pode dizer ao método para não preencher o último grupo passando false
:
[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
Como consequência, false
não pode ser usado como valor de preenchimento.
Definido em active_support/core_ext/array/grouping.rb
.
11.7.2 in_groups(number, fill_with = nil)
O método in_groups
divide um array em um certo número de grupos. O método retorna um array com os grupos:
%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
ou os fornece por sua vez se um bloco for passado:
%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]
Os exemplos acima mostram que in_groups
preenche alguns grupos com um elemento nil
à direita conforme necessário. Um grupo pode obter no máximo um desses elementos extras, o mais à direita, se houver. E os grupos que os possuem são sempre os últimos.
Você pode alterar esse valor de preenchimento usando o segundo argumento opcional:
%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
E você pode dizer ao método para não preencher os grupos menores passando false
:
%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
Como consequência, false
não pode ser usado como valor de preenchimento.
Definido em active_support/core_ext/array/grouping.rb
.
11.7.3 split(value = nil)
O método split
divide um array por um separador e retorna os pedaços resultantes.
Se um bloco é passado, os separadores são os elementos do array para os quais o bloco retorna true:
(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
Caso contrário, o valor recebido como argumento, cujo padrão é nil
, é o separador:
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]
Observe no exemplo anterior que separadores consecutivos resultam em arrays vazios.
Definido em active_support/core_ext/array/grouping.rb
.
12 Extensions to Hash
12.1 Conversions
12.1.1 to_xml
The method to_xml
returns a string containing an XML representation of its receiver:
{"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>
To do so, the method loops over the pairs and builds nodes that depend on the values. Given a pair key
, value
:
If
value
is a hash there's a recursive call withkey
as:root
.If
value
is an array there's a recursive call withkey
as:root
, andkey
singularized as:children
.If
value
is a callable object it must expect one or two arguments. Depending on the arity, the callable is invoked with theoptions
hash as first argument withkey
as:root
, andkey
singularized as second argument. Its return value becomes a new node.If
value
responds toto_xml
the method is invoked withkey
as:root
.Otherwise, a node with
key
as tag is created with a string representation ofvalue
as text node. Ifvalue
isnil
an attribute "nil" set to "true" is added. Unless the option:skip_types
exists and is true, an attribute "type" is added as well according to the following mapping:
XML_TYPE_NAMES = {
"Symbol" => "symbol",
"Integer" => "integer",
"BigDecimal" => "decimal",
"Float" => "float",
"TrueClass" => "boolean",
"FalseClass" => "boolean",
"Date" => "date",
"DateTime" => "datetime",
"Time" => "datetime"
}
By default the root node is "hash", but that's configurable via the :root
option.
The default XML builder is a fresh instance of Builder::XmlMarkup
. You can configure your own builder with the :builder
option. The method also accepts options like :dasherize
and friends, they are forwarded to the builder.
Defined in active_support/core_ext/hash/conversions.rb
.
12.2 Merging
Ruby has a built-in method Hash#merge
that merges two hashes:
{a: 1, b: 1}.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}
Active Support defines a few more ways of merging hashes that may be convenient.
12.2.1 reverse_merge
and reverse_merge!
In case of collision the key in the hash of the argument wins in merge
. You can support option hashes with default values in a compact way with this idiom:
options = {length: 30, omission: "..."}.merge(options)
Active Support defines reverse_merge
in case you prefer this alternative notation:
options = options.reverse_merge(length: 30, omission: "...")
And a bang version reverse_merge!
that performs the merge in place:
options.reverse_merge!(length: 30, omission: "...")
Take into account that reverse_merge!
may change the hash in the caller, which may or may not be a good idea.
Defined in active_support/core_ext/hash/reverse_merge.rb
.
12.2.2 reverse_update
The method reverse_update
is an alias for reverse_merge!
, explained above.
Note that reverse_update
has no bang.
Defined in active_support/core_ext/hash/reverse_merge.rb
.
12.2.3 deep_merge
and deep_merge!
As you can see in the previous example if a key is found in both hashes the value in the one in the argument wins.
Active Support defines Hash#deep_merge
. In a deep merge, if a key is found in both hashes and their values are hashes in turn, then their merge becomes the value in the resulting hash:
{a: {b: 1}}.deep_merge(a: {c: 2})
# => {:a=>{:b=>1, :c=>2}}
The method deep_merge!
performs a deep merge in place.
Defined in active_support/core_ext/hash/deep_merge.rb
.
12.3 Deep Duplicating
The method Hash#deep_dup
duplicates itself and all keys and values
inside recursively with Active Support method Object#deep_dup
. It works like Enumerator#each_with_object
with sending deep_dup
method to each pair inside.
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
Defined in active_support/core_ext/object/deep_dup.rb
.
12.4 Working with Keys
12.4.1 except
and except!
The method except
returns a hash with the keys in the argument list removed, if present:
{a: 1, b: 2}.except(:a) # => {:b=>2}
If the receiver responds to convert_key
, the method is called on each of the arguments. This allows except
to play nice with hashes with indifferent access for instance:
{a: 1}.with_indifferent_access.except(:a) # => {}
{a: 1}.with_indifferent_access.except("a") # => {}
There's also the bang variant except!
that removes keys in place.
Defined in active_support/core_ext/hash/except.rb
.
12.4.2 stringify_keys
and stringify_keys!
The method stringify_keys
returns a hash that has a stringified version of the keys in the receiver. It does so by sending to_s
to them:
{nil => nil, 1 => 1, a: :a}.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}
In case of key collision, the value will be the one most recently inserted into the hash:
{"a" => 1, a: 2}.stringify_keys
# The result will be
# => {"a"=>2}
This method may be useful for example to easily accept both symbols and strings as options. For instance ActionView::Helpers::FormHelper
defines:
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
# ...
end
The second line can safely access the "type" key, and let the user to pass either :type
or "type".
There's also the bang variant stringify_keys!
that stringifies keys in place.
Besides that, one can use deep_stringify_keys
and deep_stringify_keys!
to stringify all the keys in the given hash and all the hashes nested in it. An example of the result is:
{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}
Defined in active_support/core_ext/hash/keys.rb
.
12.4.3 symbolize_keys
and symbolize_keys!
The method symbolize_keys
returns a hash that has a symbolized version of the keys in the receiver, where possible. It does so by sending to_sym
to them:
{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}
Note in the previous example only one key was symbolized.
In case of key collision, the value will be the one most recently inserted into the hash:
{"a" => 1, a: 2}.symbolize_keys
# => {:a=>2}
This method may be useful for example to easily accept both symbols and strings as options. For instance ActionText::TagHelper
defines
def rich_text_area_tag(name, value = nil, options = {})
options = options.symbolize_keys
options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
# ...
end
The third line can safely access the :input
key, and let the user to pass either :input
or "input".
There's also the bang variant symbolize_keys!
that symbolizes keys in place.
Besides that, one can use deep_symbolize_keys
and deep_symbolize_keys!
to symbolize all the keys in the given hash and all the hashes nested in it. An example of the result is:
{nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
Defined in active_support/core_ext/hash/keys.rb
.
12.4.4 to_options
and to_options!
The methods to_options
and to_options!
are aliases of symbolize_keys
and symbolize_keys!
, respectively.
Defined in active_support/core_ext/hash/keys.rb
.
12.4.5 assert_valid_keys
The method assert_valid_keys
receives an arbitrary number of arguments, and checks whether the receiver has any key outside that list. If it does ArgumentError
is raised.
{a: 1}.assert_valid_keys(:a) # passes
{a: 1}.assert_valid_keys("a") # ArgumentError
Active Record does not accept unknown options when building associations, for example. It implements that control via assert_valid_keys
.
Defined in active_support/core_ext/hash/keys.rb
.
12.5 Working with Values
12.5.1 deep_transform_values
and deep_transform_values!
The method deep_transform_values
returns a new hash with all values converted by the block operation. This includes the values from the root hash and from all nested hashes and arrays.
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_transform_values{ |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}
There's also the bang variant deep_transform_values!
that destructively converts all values by using the block operation.
12.6 Slicing
The method slice!
replaces the hash with only the given keys and returns a hash containing the removed key/value pairs.
hash = {a: 1, b: 2}
rest = hash.slice!(:a) # => {:b=>2}
hash # => {:a=>1}
Defined in active_support/core_ext/hash/slice.rb
.
12.7 Extracting
The method extract!
removes and returns the key/value pairs matching the given keys.
hash = {a: 1, b: 2}
rest = hash.extract!(:a) # => {:a=>1}
hash # => {:b=>2}
The method extract!
returns the same subclass of Hash that the receiver is.
hash = {a: 1, b: 2}.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess
Defined in active_support/core_ext/hash/slice.rb
.
12.8 Indifferent Access
The method with_indifferent_access
returns an ActiveSupport::HashWithIndifferentAccess
out of its receiver:
{a: 1}.with_indifferent_access["a"] # => 1
13 Extensões para Regexp
13.1 multiline?
O método multiline?
diz se uma regexp tem o sinalizador /m
definido, ou seja, se o ponto corresponde a novas linhas.
%r{.}.multiline? # => false
%r{.}m.multiline? # => true
Regexp.new('.').multiline? # => false
Regexp.new('.', Regexp::MULTILINE).multiline? # => true
O Rails usa esse método em um único lugar, também no código de roteamento. Regexps de várias linhas não são permitidas para requisitos de rota e esse sinalizador facilita a aplicação dessa restrição.
def verify_regexp_requirements(requirements)
# ...
if requirement.multiline?
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
end
# ...
end
Definido em active_support/core_ext/regexp.rb
.
14 Extensions to Range
14.1 to_s
Active Support extends the method Range#to_s
so that it understands an optional format argument. As of this writing the only supported non-default format is :db
:
(Date.today..Date.tomorrow).to_s
# => "2009-10-25..2009-10-26"
(Date.today..Date.tomorrow).to_s(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"
As the example depicts, the :db
format generates a BETWEEN
SQL clause. That is used by Active Record in its support for range values in conditions.
Defined in active_support/core_ext/range/conversions.rb
.
14.2 ===
and include?
The methods Range#===
and Range#include?
say whether some value falls between the ends of a given instance:
(2..3).include?(Math::E) # => true
Active Support extends these methods so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves:
(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
Defined in active_support/core_ext/range/compare_range.rb
.
14.3 overlaps?
The method Range#overlaps?
says whether any two given ranges have non-void intersection:
(1..10).overlaps?(7..11) # => true
(1..10).overlaps?(0..7) # => true
(1..10).overlaps?(11..27) # => false
Defined in active_support/core_ext/range/overlaps.rb
.
15 Extensions to Date
15.1 Calculations
The following calculation methods have edge cases in October 1582, since days 5..14 just do not exist. This guide does not document their behavior around those days for brevity, but it is enough to say that they do what you would expect. That is, Date.new(1582, 10, 4).tomorrow
returns Date.new(1582, 10, 15)
and so on. Please check test/core_ext/date_ext_test.rb
in the Active Support test suite for expected behavior.
15.1.1 Date.current
Active Support defines Date.current
to be today in the current time zone. That's like Date.today
, except that it honors the user time zone, if defined. It also defines Date.yesterday
and Date.tomorrow
, and the instance predicates past?
, today?
, tomorrow?
, next_day?
, yesterday?
, prev_day?
, future?
, on_weekday?
and on_weekend?
, all of them relative to Date.current
.
When making Date comparisons using methods which honor the user time zone, make sure to use Date.current
and not Date.today
. There are cases where the user time zone might be in the future compared to the system time zone, which Date.today
uses by default. This means Date.today
may equal Date.yesterday
.
Defined in active_support/core_ext/date/calculations.rb
.
15.1.2 Named Dates
15.1.2.1 beginning_of_week
, end_of_week
The methods beginning_of_week
and end_of_week
return the dates for the
beginning and end of the week, respectively. Weeks are assumed to start on
Monday, but that can be changed passing an argument, setting thread local
Date.beginning_of_week
or 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
is aliased to at_beginning_of_week
and end_of_week
is aliased to at_end_of_week
.
15.1.2.2 monday
, sunday
The methods monday
and sunday
return the dates for the previous Monday and
next Sunday, respectively.
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
15.1.2.3 prev_week
, next_week
The method next_week
receives a symbol with a day name in English (default is the thread local Date.beginning_of_week
, or config.beginning_of_week
, or :monday
) and it returns the date corresponding to that day.
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
The method prev_week
is analogous:
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
is aliased to last_week
.
Both next_week
and prev_week
work as expected when Date.beginning_of_week
or config.beginning_of_week
are set.
15.1.2.4 beginning_of_month
, end_of_month
The methods beginning_of_month
and end_of_month
return the dates for the beginning and end of the month:
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
is aliased to at_beginning_of_month
, and end_of_month
is aliased to at_end_of_month
.
15.1.2.5 beginning_of_quarter
, end_of_quarter
The methods beginning_of_quarter
and end_of_quarter
return the dates for the beginning and end of the quarter of the receiver's calendar year:
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
is aliased to at_beginning_of_quarter
, and end_of_quarter
is aliased to at_end_of_quarter
.
15.1.2.6 beginning_of_year
, end_of_year
The methods beginning_of_year
and end_of_year
return the dates for the beginning and end of the year:
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
is aliased to at_beginning_of_year
, and end_of_year
is aliased to at_end_of_year
.
15.1.3 Other Date Computations
15.1.3.1 years_ago
, years_since
The method years_ago
receives a number of years and returns the same date those many years ago:
date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000
years_since
moves forward in time:
date = Date.new(2010, 6, 7)
date.years_since(10) # => Sun, 07 Jun 2020
If such a day does not exist, the last day of the corresponding month is returned:
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
is short-hand for #years_ago(1)
.
15.1.3.2 months_ago
, months_since
The methods months_ago
and months_since
work analogously for months:
Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010
If such a day does not exist, the last day of the corresponding month is returned:
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
is short-hand for #months_ago(1)
.
15.1.3.3 weeks_ago
The method weeks_ago
works analogously for weeks:
Date.new(2010, 5, 24).weeks_ago(1) # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_ago(2) # => Mon, 10 May 2010
15.1.3.4 advance
The most generic way to jump to other days is advance
. This method receives a hash with keys :years
, :months
, :weeks
, :days
, and returns a date advanced as much as the present keys indicate:
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
Note in the previous example that increments may be negative.
Defined in active_support/core_ext/date/calculations.rb
.
15.1.4 Changing Components
The method change
allows you to get a new date which is the same as the receiver except for the given year, month, or day:
Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011
This method is not tolerant to non-existing dates, if the change is invalid ArgumentError
is raised:
Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date
Defined in active_support/core_ext/date/calculations.rb
.
15.1.5 Durations
Duration
objects can be added to and subtracted from dates:
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
They translate to calls to since
or advance
. For example here we get the correct jump in the calendar reform:
Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582
15.1.6 Timestamps
The following methods return a Time
object if possible, otherwise a DateTime
. If set, they honor the user time zone.
15.1.6.1 beginning_of_day
, end_of_day
The method beginning_of_day
returns a timestamp at the beginning of the day (00:00:00):
date = Date.new(2010, 6, 7)
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010
The method end_of_day
returns a timestamp at the end of the day (23:59:59):
date = Date.new(2010, 6, 7)
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010
beginning_of_day
is aliased to at_beginning_of_day
, midnight
, at_midnight
.
Defined in active_support/core_ext/date/calculations.rb
.
15.1.6.2 beginning_of_hour
, end_of_hour
The method beginning_of_hour
returns a timestamp at the beginning of the hour (hh:00:00):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010
The method end_of_hour
returns a timestamp at the end of the hour (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
is aliased to at_beginning_of_hour
.
15.1.6.3 beginning_of_minute
, end_of_minute
The method beginning_of_minute
returns a timestamp at the beginning of the minute (hh:mm:00):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010
The method end_of_minute
returns a timestamp at the end of the minute (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
is aliased to at_beginning_of_minute
.
beginning_of_hour
, end_of_hour
, beginning_of_minute
, and end_of_minute
are implemented for Time
and DateTime
but not Date
as it does not make sense to request the beginning or end of an hour or minute on a Date
instance.
15.1.6.4 ago
, since
The method ago
receives a number of seconds as argument and returns a timestamp those many seconds ago from midnight:
date = Date.current # => Fri, 11 Jun 2010
date.ago(1) # => Thu, 10 Jun 2010 23:59:59 EDT -04:00
Similarly, since
moves forward:
date = Date.current # => Fri, 11 Jun 2010
date.since(1) # => Fri, 11 Jun 2010 00:00:01 EDT -04:00
Defined in active_support/core_ext/date/calculations.rb
.
16 Extensions to DateTime
DateTime
is not aware of DST rules and so some of these methods have edge cases when a DST change is going on. For example seconds_since_midnight
might not return the real amount in such a day.
16.1 Calculations
The class DateTime
is a subclass of Date
so by loading active_support/core_ext/date/calculations.rb
you inherit these methods and their aliases, except that they will always return datetimes.
The following methods are reimplemented so you do not need to load active_support/core_ext/date/calculations.rb
for these ones:
On the other hand, advance
and change
are also defined and support more options, they are documented below.
The following methods are only implemented in active_support/core_ext/date_time/calculations.rb
as they only make sense when used with a DateTime
instance:
16.1.1 Named Datetimes
16.1.1.1 DateTime.current
Active Support defines DateTime.current
to be like Time.now.to_datetime
, except that it honors the user time zone, if defined. The instance predicates past?
and future?
are defined relative to DateTime.current
.
16.1.2 Other Extensions
16.1.2.1 seconds_since_midnight
The method seconds_since_midnight
returns the number of seconds since midnight:
now = DateTime.current # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
16.1.2.2 utc
The method utc
gives you the same datetime in the receiver expressed in UTC.
now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc # => Mon, 07 Jun 2010 23:27:52 +0000
This method is also aliased as getutc
.
16.1.2.3 utc?
The predicate utc?
says whether the receiver has UTC as its time zone:
now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc? # => false
now.utc.utc? # => true
16.1.2.4 advance
The most generic way to jump to another datetime is advance
. This method receives a hash with keys :years
, :months
, :weeks
, :days
, :hours
, :minutes
, and :seconds
, and returns a datetime advanced as much as the present keys indicate.
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
This method first computes the destination date passing :years
, :months
, :weeks
, and :days
to Date#advance
documented above. After that, it adjusts the time calling since
with the number of seconds to advance. This order is relevant, a different ordering would give different datetimes in some edge-cases. The example in Date#advance
applies, and we can extend it to show order relevance related to the time bits.
If we first move the date bits (that have also a relative order of processing, as documented before), and then the time bits we get for example the following computation:
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
but if we computed them the other way around, the result would be different:
d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000
Since DateTime
is not DST-aware you can end up in a non-existing point in time with no warning or error telling you so.
16.1.3 Changing Components
The method change
allows you to get a new datetime which is the same as the receiver except for the given options, which may include :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
If hours are zeroed, then minutes and seconds are too (unless they have given values):
now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000
Similarly, if minutes are zeroed, then seconds are too (unless it has given a value):
now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000
This method is not tolerant to non-existing dates, if the change is invalid ArgumentError
is raised:
DateTime.current.change(month: 2, day: 30)
# => ArgumentError: invalid date
16.1.4 Durations
Duration
objects can be added to and subtracted from 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
They translate to calls to since
or advance
. For example here we get the correct jump in the calendar reform:
DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000
17 Extensions to Time
17.1 Calculations
They are analogous. Please refer to their documentation above and take into account the following differences:
-
change
accepts an additional:usec
option. -
Time
understands DST, so you get correct DST calculations as in
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
# In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to 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
- If
since
orago
jumps to a time that can't be expressed withTime
aDateTime
object is returned instead.
17.1.1 Time.current
Active Support defines Time.current
to be today in the current time zone. That's like Time.now
, except that it honors the user time zone, if defined. It also defines the instance predicates past?
, today?
, tomorrow?
, next_day?
, yesterday?
, prev_day?
and future?
, all of them relative to Time.current
.
When making Time comparisons using methods which honor the user time zone, make sure to use Time.current
instead of Time.now
. There are cases where the user time zone might be in the future compared to the system time zone, which Time.now
uses by default. This means Time.now.to_date
may equal Date.yesterday
.
Defined in active_support/core_ext/time/calculations.rb
.
17.1.2 all_day
, all_week
, all_month
, all_quarter
, and all_year
The method all_day
returns a range representing the whole day of the current time.
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
Analogously, all_week
, all_month
, all_quarter
and all_year
all serve the purpose of generating time ranges.
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
17.1.3 prev_day
, next_day
prev_day
and next_day
return the time in the last or next day:
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
Defined in active_support/core_ext/time/calculations.rb
.
17.1.4 prev_month
, next_month
prev_month
and next_month
return the time with the same day in the last or next month:
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
If such a day does not exist, the last day of the corresponding month is returned:
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
Defined in active_support/core_ext/time/calculations.rb
.
17.1.5 prev_year
, next_year
prev_year
and next_year
return a time with the same day/month in the last or next year:
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
If date is the 29th of February of a leap year, you obtain the 28th:
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
Defined in active_support/core_ext/time/calculations.rb
.
17.1.6 prev_quarter
, next_quarter
prev_quarter
and next_quarter
return the date with the same day in the previous or next quarter:
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
If such a day does not exist, the last day of the corresponding month is returned:
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
is aliased to last_quarter
.
17.2 Time Constructors
Active Support defines Time.current
to be Time.zone.now
if there's a user time zone defined, with fallback to 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
Analogously to DateTime
, the predicates past?
, and future?
are relative to Time.current
.
If the time to be constructed lies beyond the range supported by Time
in the runtime platform, usecs are discarded and a DateTime
object is returned instead.
17.2.1 Durations
Duration
objects can be added to and subtracted from time objects:
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
They translate to calls to since
or advance
. For example here we get the correct jump in the calendar reform:
Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
18 Extensions to File
18.1 atomic_write
With the class method File.atomic_write
you can write to a file in a way that will prevent any reader from seeing half-written content.
The name of the file is passed as an argument, and the method yields a file handle opened for writing. Once the block is done atomic_write
closes the file handle and completes its job.
For example, Action Pack uses this method to write asset cache files like all.css
:
File.atomic_write(joined_asset_path) do |cache|
cache.write(join_asset_file_contents(asset_paths))
end
To accomplish this atomic_write
creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists atomic_write
overwrites it and keeps owners and permissions. However there are a few cases where atomic_write
cannot change the file ownership or permissions, this error is caught and skipped over trusting in the user/filesystem to ensure the file is accessible to the processes that need it.
Due to the chmod operation atomic_write
performs, if the target file has an ACL set on it this ACL will be recalculated/modified.
Note you can't append with atomic_write
.
The auxiliary file is written in a standard directory for temporary files, but you can pass a directory of your choice as second argument.
Defined in active_support/core_ext/file/atomic.rb
.
19 Extensions to NameError
Active Support adds missing_name?
to NameError
, which tests whether the exception was raised because of the name passed as argument.
The name may be given as a symbol or string. A symbol is tested against the bare constant name, a string is against the fully qualified constant name.
A symbol can represent a fully qualified constant name as in :"ActiveRecord::Base"
, so the behavior for symbols is defined for convenience, not because it has to be that way technically.
For example, when an action of ArticlesController
is called Rails tries optimistically to use ArticlesHelper
. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that articles_helper.rb
raises a NameError
due to an actual unknown constant. That should be reraised. The method missing_name?
provides a way to distinguish both cases:
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
Defined in active_support/core_ext/name_error.rb
.
20 Extensions to LoadError
Active Support adds is_missing?
to LoadError
.
Given a path name is_missing?
tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).
For example, when an action of ArticlesController
is called Rails tries to load articles_helper.rb
, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method is_missing?
provides a way to distinguish both cases:
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
Defined in active_support/core_ext/load_error.rb
.
21 Extensions to Pathname
21.1 existência
O existence
retorna o receptor se o arquivo nomeado existir, caso contrário retorna nil
. É útil para expressões idiomáticas como esta:
content = Pathname.new("file").existence&.read
Definido em active_support/core_ext/pathname/existence.rb
.
Feedback
Você é incentivado a ajudar a melhorar a qualidade deste guia.
Por favor, contribua caso veja quaisquer erros, inclusive erros de digitação. Para começar, você pode ler nossa sessão de contribuindo com a documentação.
Você também pode encontrar conteúdo incompleto ou coisas que não estão atualizadas. Por favor, adicione qualquer documentação em falta na main do Rails. Certifique-se de checar o Edge Guides (en-US) primeiro para verificar se o problema já foi resolvido ou não no branch main. Verifique as Diretrizes do Guia Ruby on Rails para estilo e convenções.
Se, por qualquer motivo, você encontrar algo para consertar, mas não conseguir consertá-lo, por favor abra uma issue no nosso Guia.
E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem vinda na forum oficial do Ruby on Rails e nas issues do Guia em português.