1 Como Carregar Extensões Principais
1.1 Active Support Independente
Para ter a menor pegada padrão possível, o Active Support carrega as dependências mínimas por padrão. Ele é dividido em pequenas partes 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, ou até mesmo tudo.
Assim, após um simples require como:
require "active_support"
apenas as extensões necessárias pelo framework Active Support são carregadas.
1.1.1 Selecionando uma 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 tanto como strings quanto como símbolos.
{ a: 1 }.with_indifferent_access["a"] # => 1
Para cada método definido como uma extensão principal, este guia possui uma nota que indica onde tal método é definido. No caso de with_indifferent_access
, a nota diz:
NOTA: Definido em active_support/core_ext/hash/indifferent_access.rb
.
Isso significa que você pode requerê-lo assim:
require "active_support"
require "active_support/core_ext/hash/indifferent_access"
O Active Support foi cuidadosamente revisado para que a seleção de um arquivo carregue apenas as dependências estritamente necessárias, se houver.
1.1.2 Carregando Extensões Principais Agrupadas
O próximo nível é simplesmente carregar todas as extensões para Hash
. Como regra geral, as extensões para AlgumaClasse
estão disponíveis de uma só vez ao carregar active_support/core_ext/alguma_classe
.
Assim, para carregar todas as extensões para Hash
(incluindo with_indifferent_access
):
require "active_support"
require "active_support/core_ext/hash"
1.1.3 Carregando Todas as Extensões Principais
Você pode preferir apenas carregar todas as extensões principais, há um arquivo para isso:
require "active_support"
require "active_support/core_ext"
1.1.4 Carregando Todo o Active Support
E finalmente, se você quiser ter todo o Active Support disponível, basta executar:
require "active_support/all"
Isso nem mesmo coloca todo o Active Support na memória de uma vez, na verdade, algumas coisas são configuradas via autoload
, então são carregadas apenas quando são utilizadas.
1.2 Active Support Dentro de uma Aplicação Ruby on Rails
Uma aplicação Ruby on Rails carrega todo o Active Support, a menos que config.active_support.bare
seja verdadeiro. Nesse caso, a aplicação carregará apenas o que o próprio framework selecionar para suas próprias necessidades, e ainda poderá selecionar a si mesma em qualquer nível de granularidade, como explicado na seção anterior.
2 Extensões para Todos os Objetos
2.1 blank?
e present?
Os seguintes valores são considerados em branco 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 responda a
empty?
e esteja vazio.
O predicado para strings usa a classe de caracteres sensível ao Unicode [:space:]
, então, por exemplo, U+2029 (separador de parágrafo) é considerado como espaço em branco.
AVISO: Observe que os números não são mencionados. Em particular, 0 e 0.0 não são vazios.
Por exemplo, este método de ActionController::HttpAuthentication::Token::ControllerMethods
usa blank?
para verificar se um 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 a !blank?
. Este exemplo é retirado de ActionDispatch::Http::Cache::Response
:
def set_conditional_cache_control!
return if self["Cache-Control"].present?
# ...
end
NOTA: Definido em active_support/core_ext/object/blank.rb
.
2.2 presence
O método presence
retorna seu receptor se present?
, e nil
caso contrário. É útil para idiomatismos como este:
host = config[:host].presence || 'localhost'
NOTA: Definido em active_support/core_ext/object/blank.rb
.
2.3 duplicable?
A partir do Ruby 2.5, a maioria dos objetos pode ser duplicada via 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 duplicable?
para consultar um objeto sobre isso:
"foo".duplicable? # => true
"".duplicable? # => true
Rational(1).duplicable? # => true
Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
AVISO: Qualquer classe pode impedir a duplicação removendo dup
e clone
ou lançando exceções a partir deles. Portanto, apenas rescue
pode dizer se um determinado objeto arbitrário é duplicável. duplicable?
depende da lista codificada acima, mas é muito mais rápido que rescue
. Use-o apenas se souber que a lista codificada é suficiente para o seu caso de uso.
NOTA: Definido em active_support/core_ext/object/duplicable.rb
.
2.4 deep_dup
O método deep_dup
retorna uma cópia profunda de um determinado objeto. Normalmente, quando você dup
um objeto que contém outros objetos, o Ruby não os dup
, então ele cria uma cópia rasa do objeto. Se você tiver um array com uma string, por exemplo, ficará assim:
array = ['string']
duplicate = array.dup
duplicate.push 'another-string'
# o objeto foi duplicado, então o elemento foi adicionado apenas à duplicata
array # => ['string']
duplicate # => ['string', 'another-string']
duplicate.first.gsub!('string', 'foo')
# o primeiro elemento não foi duplicado, ele será alterado em ambos os arrays
array # => ['foo']
duplicate # => ['foo', 'another-string']
Como você pode ver, após duplicar a instância do Array
, obtemos outro objeto, portanto, podemos modificá-lo e o objeto original permanecerá inalterado. No entanto, isso não é verdade para os elementos do array. Como dup
não faz uma cópia profunda, a string dentro do array ainda é o mesmo objeto.
Se você precisa de uma cópia profunda de um objeto, você deve usar deep_dup
. Aqui está um exemplo:
array = ['string']
duplicate = array.deep_dup
duplicate.first.gsub!('string', 'foo')
array # => ['string']
duplicate # => ['foo']
Se o objeto não for duplicável, deep_dup
simplesmente o retornará:
number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id # => true
NOTA: Definido em active_support/core_ext/object/deep_dup.rb
.
2.5 try
Quando você deseja chamar um método em um objeto apenas se ele não for nil
, a maneira mais simples de fazer isso é com declarações condicionais, adicionando desordem desnecessária. A alternativa é usar try
. try
é como Object#public_send
, exceto que retorna nil
se enviado para nil
.
Aqui está um exemplo:
# sem try
unless @number.nil?
@number.next
end
# com try
@number.try(:next)
Outro exemplo é este código de ActiveRecord::ConnectionAdapters::AbstractAdapter
onde @logger
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
também pode ser chamado sem argumentos, mas com um bloco, que só será executado se o objeto não for nulo:
@person.try { |p| "#{p.first_name} #{p.last_name}" }
Observe que try
irá ignorar erros de método inexistente, retornando nil em vez disso. Se você quiser se proteger contra erros de digitação, use try!
em vez disso:
@number.try(:nest) # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
NOTA: Definido em active_support/core_ext/object/try.rb
.
2.6 class_eval(*args, &block)
Você pode avaliar código no contexto da classe 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
NOTA: Definido em active_support/core_ext/kernel/singleton_class.rb
.
2.7 acts_like?(duck)
O método acts_like?
fornece uma maneira de verificar se uma classe age como outra classe com base em uma convenção simples: uma classe que fornece a mesma interface que String
define
def acts_like_string?
end
que é apenas um marcador, seu corpo ou valor de retorno são irrelevantes. Em seguida, o código do cliente pode consultar se é seguro usar o duck-type dessa maneira:
some_klass.acts_like?(:string)
O Rails possui classes que agem como Date
ou Time
e seguem esse contrato.
NOTA: Definido em active_support/core_ext/object/acts_like.rb
.
2.8 to_param
Todos os objetos no Rails respondem ao método to_param
, que deve retornar algo que os represente como valores em uma string de consulta ou como fragmentos de URL.
Por padrão, to_param
apenas chama to_s
:
7.to_param # => "7"
O valor de retorno de to_param
não deve ser escapado:
"Tom & Jerry".to_param # => "Tom & Jerry"
Várias classes no Rails sobrescrevem esse método.
Por exemplo, nil
, true
e false
retornam eles mesmos. Array#to_param
chama to_param
nos elementos e junta o resultado com "/":
[0, true, String].to_param # => "0/true/String"
É importante observar que o sistema de roteamento do Rails chama to_param
nos modelos para obter um valor para o espaço reservado :id
. ActiveRecord::Base#to_param
retorna o id
de um modelo, mas você pode redefinir esse método em seus modelos. Por exemplo, dado
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
obtemos:
user_path(@user) # => "/users/357-john-smith"
CUIDADO. Os controladores precisam estar cientes de qualquer redefinição de to_param
, porque quando uma solicitação como essa chega, "357-john-smith" é o valor de params[:id]
.
NOTA: Definido em active_support/core_ext/object/to_param.rb
.
2.9 to_query
O método to_query
constrói uma string de consulta que associa uma determinada key
com o valor de retorno de to_param
. Por exemplo, com a seguinte definição de to_param
:
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
obtemos:
current_user.to_query('user') # => "user=357-john-smith"
Este método escapa o que for necessário, tanto para a chave quanto para o valor:
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
portanto, sua saída está pronta para ser usada em uma string de consulta.
Arrays retornam o resultado da aplicação de to_query
a 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 também respondem a to_query
, mas com uma assinatura diferente. Se nenhum argumento for passado, uma chamada gera uma série ordenada de atribuições chave/valor chamando to_query(key)
em seus valores. Em seguida, junta o resultado com "&":
{ c: 3, b: 2, a: 1 }.to_query # => "a=1&b=2&c=3"
O método Hash#to_query
aceita um namespace opcional para as chaves:
{ id: 89, name: "John Smith" }.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
NOTA: Definido em active_support/core_ext/object/to_query.rb
.
2.10 with_options
O método with_options
fornece uma maneira de agrupar opções comuns em uma série de chamadas de método.
Dado um hash de opções padrão, with_options
gera um objeto proxy para um bloco. Dentro do bloco, os métodos chamados no proxy são encaminhados para o receptor com suas opções mescladas. Por exemplo, você pode se livrar 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
Esse idiom pode transmitir agrupamento também para o leitor. Por exemplo, digamos que você queira enviar um boletim informativo cujo idioma depende do usuário. Em algum lugar do mailer, você pode agrupar trechos dependentes do idioma da seguinte forma:
I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
subject i18n.t :subject
body i18n.t :body, user_name: user.name
end
DICA: Como with_options
encaminha chamadas para seu receptor, elas podem ser aninhadas. Cada nível de aninhamento mesclará os padrões herdados além dos próprios.
NOTA: Definido em active_support/core_ext/object/with_options.rb
.
2.11 Suporte a JSON
Active Support fornece uma implementação melhor de to_json
do que a gem json
normalmente fornece para objetos Ruby. Isso ocorre porque algumas classes, como Hash
e Process::Status
, precisam de tratamento especial para fornecer uma representação JSON adequada.
NOTA: 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 às variáveis de instância.
2.12.1 instance_values
O método instance_values
retorna um hash que mapeia nomes de variáveis de instância 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}
NOTA: 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"]
NOTA: Definido em active_support/core_ext/object/instance_variables.rb
.
2.13 Silenciando Avisos e Exceções
Os métodos silence_warnings
e enable_warnings
alteram o valor de $VERBOSE
de acordo com a duração de seu bloco e o redefinem posteriormente:
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
Também é possível silenciar exceções com suppress
. Este método recebe um número arbitrário de classes de exceção. Se uma exceção for lançada durante a execução do bloco e for kind_of?
qualquer um dos argumentos, suppress
a captura e retorna silenciosamente. Caso contrário, a exceção não é capturada:
```ruby
Se o usuário estiver bloqueado, o incremento é perdido, não é um grande problema.
suppress(ActiveRecord::StaleObjectError) do current_user.increment! :visits end ```
NOTA: Definido em active_support/core_ext/kernel/reporting.rb
.
2.14 in?
O predicado in?
testa se um objeto está incluído em outro objeto. Uma exceção ArgumentError
será lançada 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
NOTA: Definido em active_support/core_ext/object/inclusion.rb
.
3 Extensões para Module
3.1 Atributos
3.1.1 alias_attribute
Os atributos do modelo têm um leitor, um escritor e um predicado. Você pode criar um alias para um atributo do modelo tendo os três métodos correspondentes todos definidos para você usando alias_attribute
. Como em outros métodos de aliasing, o novo nome é o primeiro argumento e o antigo nome é o segundo (uma mnemônica é que eles seguem a mesma ordem que em uma atribuição):
class User < ApplicationRecord
# Você pode se referir à coluna de email como "login".
# Isso pode ser significativo para o código de autenticação.
alias_attribute :login, :email
end
NOTA: Definido em active_support/core_ext/module/aliasing.rb
.
3.1.2 Atributos Internos
Quando você está definindo um atributo em uma classe que é destinada a ser subclassificada, colisões de nomes são um risco. Isso é notavelmente importante para bibliotecas.
Active Support define as macros attr_internal_reader
, attr_internal_writer
e attr_internal_accessor
. Elas se comportam como suas contrapartes Ruby attr_*
embutidas, exceto que nomeiam a variável de instância subjacente de uma maneira que torna as colisões menos prováveis.
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, pode ser o caso de que :log_level
não pertença à interface pública da biblioteca e seja usado apenas para desenvolvimento. O código do cliente, desconhecendo o conflito potencial, faz uma subclasse e define seu próprio :log_level
. Graças ao attr_internal
, não há colisão.
Por padrão, a variável de instância interna é nomeada com um sublinhado inicial, @_log_level
no exemplo acima. Isso é configurável através de Module.attr_internal_naming_format
, você pode passar qualquer string de formato sprintf
-like com um @
inicial e um %s
em algum lugar, onde o nome será colocado. O padrão é "@_%s"
.
O Rails usa atributos internos em alguns pontos, por exemplo, para views:
module ActionView
class Base
attr_internal :captures
attr_internal :request, :layout
attr_internal :controller, :template
end
end
NOTA: Definido em active_support/core_ext/module/attr_internal.rb
.
3.1.3 Atributos do Módulo
As macros mattr_reader
, mattr_writer
e mattr_accessor
são as mesmas macros cattr_*
definidas para classes. Na verdade, as macros cattr_*
são apenas aliases para as macros mattr_*
. Veja Atributos de Classe.
Por exemplo, a API para o logger do Active Storage é gerada com mattr_accessor
:
module ActiveStorage
mattr_accessor :logger
end
NOTA: Definido em active_support/core_ext/module/attribute_accessors.rb
.
3.2 Pais
3.2.1 module_parent
O método module_parent
em um módulo nomeado aninhado retorna o 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 # => X::Y
M.module_parent # => X::Y
Se o módulo for anônimo ou pertencer ao nível superior, module_parent
retorna Object
.
AVISO: Observe que, nesse caso, module_parent_name
retorna nil
.
NOTA: Definido em active_support/core_ext/module/introspection.rb
.
3.2.2 module_parent_name
O método module_parent_name
em um módulo nomeado aninhado retorna o nome totalmente 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
.
AVISO: Observe que, nesse caso, module_parent
retorna Object
.
NOTA: 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 nos módulos superiores até que Object
seja alcançado. A cadeia é retornada em um array, 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]
NOTA: 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 tem um nome com o predicado anonymous?
:
module M
end
M.anonymous? # => false
Module.new.anonymous? # => true
Observe que ser inacessível não implica 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.
NOTA: 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 em um aplicativo têm informações de login no modelo User
, mas nome e outros dados em um modelo separado Profile
:
class User < ApplicationRecord
has_one :profile
end
Com essa configuração, você obtém o nome do usuário por meio do perfil, user.profile.name
, mas pode ser útil ainda poder acessar esse atributo diretamente:
class User < ApplicationRecord
has_one :profile
def name
profile.name
end
end
Isso é o que 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 no alvo.
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 para o objeto ao qual o método é delegado. Normalmente uma string ou símbolo. Essa expressão é avaliada no contexto do receptor:
# delega para a constante Rails
delegate :logger, to: :Rails
# delega para a classe do receptor
delegate :table_name, to: :class
AVISO: Se a opção :prefix
for true
, isso é menos genérico, veja abaixo.
Por padrão, se a delegação levantar NoMethodError
e o alvo for nil
, a exceção é propagada. Você pode solicitar que nil
seja retornado em vez disso 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:
ruby
delegate :street, to: :address, prefix: true
O exemplo anterior gera address_street
em vez de street
.
AVISO: Neste caso, como o nome do método gerado é composto pelo objeto alvo e pelos nomes dos métodos alvo, a opção :to
deve ser um nome de método.
Um prefixo personalizado também pode ser configurado:
delegate :size, to: :attachment, prefix: :avatar
No exemplo anterior, a macro gera avatar_size
em vez de size
.
A opção :private
altera o escopo dos métodos:
delegate :date_of_birth, to: :profile, private: true
Os métodos delegados são públicos por padrão. Passe private: true
para alterar isso.
NOTA: Definido em active_support/core_ext/module/delegation.rb
3.4.2 delegate_missing_to
Imagine que você gostaria de delegar tudo que está faltando no objeto User
para o objeto Profile
. A macro delegate_missing_to
permite que você implemente isso facilmente:
class User < ApplicationRecord
has_one :profile
delegate_missing_to :profile
end
O alvo pode ser qualquer coisa chamável dentro do objeto, como variáveis de instância, métodos, constantes, etc. Apenas os métodos públicos do alvo são delegados.
NOTA: Definido em active_support/core_ext/module/delegation.rb
.
3.5 Redefinindo Métodos
Existem casos em que você precisa definir um método com define_method
, mas não sabe se um método com esse nome já existe. Se existir, um aviso é emitido se eles estiverem habilitados. Não é um grande problema, mas também não é limpo.
O método redefine_method
evita esse aviso potencial, removendo o método existente antes, se necessário.
Você também pode usar silence_redefinition_of_method
se precisar definir o método de substituição você mesmo (porque está usando delegate
, por exemplo).
NOTA: 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 hereditários que podem ser substituídos em qualquer nível 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 da 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 geração do método de escrita da instância pode ser impedida definindo a opção :instance_writer
como false
.
module ActiveRecord
class Base
class_attribute :table_name_prefix, instance_writer: false, default: "my"
end
end
Um modelo pode achar essa opção útil como uma forma de impedir a atribuição em massa de definir o atributo.
A geração do método de leitura da instância pode ser impedida definindo a opção :instance_reader
como false
.
class A
class_attribute :x, instance_reader: false
end
A.new.x = 1
A.new.x # NoMethodError
Para conveniência, class_attribute
também define um predicado de instância que é a dupla negação do que o leitor de instância retorna. Nos exemplos acima, seria chamado de x?
.
Quando :instance_reader
é false
, o predicado da instância retorna um NoMethodError
, assim como o método leitor.
Se você não deseja o predicado da instância, passe instance_predicate: false
e ele não será definido.
NOTA: 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 contrapartes attr_*
, mas para classes. Elas inicializam uma variável de classe como nil
, a menos que ela já exista, e geram 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 valor padrão true.
cattr_accessor :emulate_booleans, default: true
end
Métodos de instância também são criados para conveniência, eles são apenas proxies para o atributo de classe. Portanto, as instâncias podem alterar o atributo de classe, mas não podem substituí-lo como acontece 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 leitor de instância pode ser impedida definindo :instance_reader
como false
e a geração do método escritor de instância pode ser impedida definindo :instance_writer
como false
. A geração de ambos os métodos pode ser impedida definindo :instance_accessor
como false
. Em todos os casos, o valor deve ser exatamente false
e não qualquer valor falso.
module A
class B
# Nenhum leitor de instância first_name é gerado.
cattr_accessor :first_name, instance_reader: false
# Nenhum escritor de instância last_name= é gerado.
cattr_accessor :last_name, instance_writer: false
# Nenhum leitor de instância surname ou escritor surname= é gerado.
cattr_accessor :surname, instance_accessor: false
end
end
Um modelo pode achar útil definir :instance_accessor
como false
como uma forma de impedir a atribuição em massa de definir o atributo.
NOTA: 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 receptor:
class C; end
C.subclasses # => []
class B < C; end
C.subclasses # => [B]
class A < B; end
C.subclasses # => [B]
class D < C; end
C.subclasses # => [B, D]
A ordem em que essas classes são retornadas não é especificada.
NOTA: Definido em active_support/core_ext/class/subclasses.rb
.
4.2.2 descendants
O método descendants
retorna todas as classes que são <
que o receptor:
class C; end
C.descendants # => []
class B < C; end
C.descendants # => [B]
class A < B; end
C.descendants # => [B, A]
class D < C; end
C.descendants # => [B, A, D]
A ordem em que essas classes são retornadas não é especificada.
NOTA: Definido em active_support/core_ext/class/subclasses.rb
.
5 Extensões para String
5.1 Segurança na Saída
5.1.1 Motivação
Inserir dados em modelos HTML requer cuidado extra. Por exemplo, você não pode simplesmente interpolar @review.title
literalmente em uma página HTML. Por um lado, se o título da revisão for "Flanagan & Matz rules!", a saída não será bem formada porque um ampersand deve ser escapado como "&". Além disso, dependendo da aplicação, isso pode ser uma grande vulnerabilidade de segurança, pois os usuários podem injetar HTML malicioso definindo um título de revisão feito sob medida. Consulte a seção sobre cross-site scripting no guia de segurança para obter mais informações sobre os riscos.
5.1.2 Strings Seguras
Active Support possui o conceito de strings (html) seguras. Uma string segura é aquela que é marcada como sendo inserível em HTML como está. Ela é confiável, independentemente de ter sido escapada ou não.
Por padrão, as strings são consideradas inseguras:
"".html_safe? # => false
Você pode obter uma string segura a partir de uma dada string usando o método html_safe
:
s = "".html_safe
s.html_safe? # => true
É importante entender que html_safe
não realiza nenhum tipo de escape, é apenas uma afirmação:
s = "<script>...</script>".html_safe
s.html_safe? # => true
s # => "<script>...</script>"
É sua responsabilidade garantir que chamar html_safe
em uma determinada string seja seguro.
Se você adicionar uma string segura, seja no local com concat
/<<
, ou com +
, o resultado será uma string segura. Argumentos inseguros são escapados:
"".html_safe + "<" # => "<"
Argumentos seguros são adicionados diretamente:
"".html_safe + "<".html_safe # => "<"
Esses métodos não devem ser usados em visualizações comuns. Valores inseguros são automaticamente escapados:
<%= @review.title %> <%# tudo bem, escapado se necessário %>
Para inserir algo literalmente, use o auxiliar raw
em vez de chamar html_safe
:
<%= raw @cms.current_template %> <%# insere @cms.current_template como está %>
ou, de forma equivalente, use <%==
:
<%== @cms.current_template %> <%# insere @cms.current_template como está %>
O auxiliar raw
chama html_safe
para você:
def raw(stringish)
stringish.to_s.html_safe
end
NOTA: Definido em active_support/core_ext/string/output_safety.rb
.
5.1.3 Transformação
Como regra geral, exceto talvez para concatenação, como explicado acima, qualquer método que possa alterar uma string retorna uma string insegura. Esses métodos são downcase
, gsub
, strip
, chomp
, underscore
, etc.
No caso de transformações no local, como gsub!
, o próprio receptor se torna inseguro.
O bit de segurança é sempre perdido, independentemente de a transformação ter realmente alterado algo.
5.1.4 Conversão e Coerção
Chamar to_s
em uma string segura retorna uma string segura, mas a coerção com to_str
retorna uma string insegura.
5.1.5 Cópia
Chamar dup
ou clone
em strings seguras produz strings seguras.
5.2 remove
O método remove
irá remover todas as ocorrências do padrão:
"Hello World".remove(/Hello /) # => "World"
Também existe a versão destrutiva String#remove!
.
NOTA: Definido em active_support/core_ext/string/filters.rb
.
5.3 squish
O método squish
remove espaços em branco no início e no final, e substitui sequências de espaços em branco por um único espaço:
" \n foo\n\r \t bar \n".squish # => "foo bar"
Também existe a versão destrutiva String#squish!
.
Observe que ele lida tanto com espaços em branco ASCII quanto Unicode.
NOTA: Definido em active_support/core_ext/string/filters.rb
.
5.4 truncate
O método truncate
retorna uma cópia da string truncada após um determinado length
:
"Oh dear! Oh dear! I shall be late!".truncate(20)
# => "Oh dear! Oh dear!..."
O ponto de reticências pode ser personalizado com a opção :omission
:
"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '…')
# => "Oh dear! Oh …"
Observe em particular que a truncagem leva em conta o comprimento da string de omissão.
Passe um :separator
para truncar a string em uma quebra natural:
```ruby
"Oh querido! Oh querido! Eu vou me atrasar!".truncate(18)
=> "Oh querido! Oh qu..."
"Oh querido! Oh querido! Eu vou me atrasar!".truncate(18, separator: ' ')
=> "Oh querido! Oh..."
A opção `:separator` pode ser uma expressão regular:
```ruby
"Oh querido! Oh querido! Eu vou me atrasar!".truncate(18, separator: /\s/)
# => "Oh querido! Oh..."
Nos exemplos acima, "querido" é cortado primeiro, mas depois :separator
impede isso.
NOTA: Definido em active_support/core_ext/string/filters.rb
.
5.5 truncate_bytes
O método truncate_bytes
retorna uma cópia da string truncada para no máximo bytesize
bytes:
"👍👍👍👍".truncate_bytes(15)
# => "👍👍👍…"
O ponto de reticências pode ser personalizado com a opção :omission
:
"👍👍👍👍".truncate_bytes(15, omission: "🖖")
# => "👍👍🖖"
NOTA: Definido em active_support/core_ext/string/filters.rb
.
5.6 truncate_words
O método truncate_words
retorna uma cópia da string truncada após um determinado número de palavras:
"Oh querido! Oh querido! Eu vou me atrasar!".truncate_words(4)
# => "Oh querido! Oh querido!..."
O ponto de reticências pode ser personalizado com a opção :omission
:
"Oh querido! Oh querido! Eu vou me atrasar!".truncate_words(4, omission: '…')
# => "Oh querido! Oh querido!…"
Passe um :separator
para truncar a string em uma quebra natural:
"Oh querido! Oh querido! Eu vou me atrasar!".truncate_words(3, separator: '!')
# => "Oh querido! Oh querido! Eu vou me atrasar..."
A opção :separator
pode ser uma expressão regular:
"Oh querido! Oh querido! Eu vou me atrasar!".truncate_words(4, separator: /\s/)
# => "Oh querido! Oh querido!..."
NOTA: Definido em active_support/core_ext/string/filters.rb
.
5.7 inquiry
O método inquiry
converte uma string em um objeto StringInquirer
, tornando as verificações de igualdade mais legíveis.
"produção".inquiry.production? # => true
"ativo".inquiry.inactive? # => false
NOTA: Definido em active_support/core_ext/string/inquiry.rb
.
5.8 starts_with?
e ends_with?
O Active Support define aliases de terceira pessoa para String#start_with?
e String#end_with?
:
"foo".starts_with?("f") # => true
"foo".ends_with?("o") # => true
NOTA: Definido em active_support/core_ext/string/starts_ends_with.rb
.
5.9 strip_heredoc
O método strip_heredoc
remove a indentação em heredocs.
Por exemplo, em
if options[:usage]
puts <<-USAGE.strip_heredoc
Este comando faz tal e tal coisa.
As opções suportadas são:
-h Esta mensagem
...
USAGE
end
o usuário veria a mensagem de uso alinhada à margem esquerda.
Tecnicamente, ele procura pela linha com a menor indentação em toda a string e remove essa quantidade de espaços em branco no início.
NOTA: Definido em active_support/core_ext/string/strip.rb
.
5.10 indent
O método indent
recua as linhas da string:
<<EOS.indent(2)
def algum_metodo
algum_codigo
end
EOS
# =>
def algum_metodo
algum_codigo
end
O segundo argumento, indent_string
, especifica qual string de recuo usar. O padrão é nil
, o que faz com que o método faça uma suposição educada olhando para a primeira linha recuada e usando um espaço se não houver.
" foo".indent(2) # => " foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t") # => "\t\tfoo"
Embora indent_string
seja tipicamente um espaço ou uma tabulação, ele pode ser qualquer string.
O terceiro argumento, indent_empty_lines
, é uma flag que indica se as linhas vazias devem ser recuadas. O padrão é falso.
"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 a recuo no local.
NOTA: 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
NOTA: Definido em active_support/core_ext/string/access.rb
.
5.11.2 from(position)
O método from
retorna a substring da string a partir da posição position
:
"hello".from(0) # => "hello"
"hello".from(2) # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil
NOTA: 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"
NOTA: Definido em active_support/core_ext/string/access.rb
.
5.11.4 first(limit = 1)
O método first
retorna uma 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.
NOTA: Definido em active_support/core_ext/string/access.rb
.
5.11.5 last(limit = 1)
O método last
retorna uma 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.
NOTA: Definido em active_support/core_ext/string/access.rb
.
5.12 Inflections
5.12.1 pluralize
O método pluralize
retorna o plural de sua string de entrada:
"table".pluralize # => "tables"
"ruby".pluralize # => "rubies"
"equipment".pluralize # => "equipment"
Como o exemplo anterior mostra, o Active Support conhece alguns plurais irregulares e substantivos incontáveis. Regras embutidas podem ser estendidas em config/initializers/inflections.rb
. Este arquivo é gerado por padrão pelo comando rails new
e possui instruções em comentários.
pluralize
também pode receber um parâmetro opcional count
. 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"
O Active Record usa esse método para calcular o nome padrão da tabela que corresponde a um modelo:
# active_record/model_schema.rb
def undecorated_table_name(model_name)
table_name = model_name.to_s.demodulize.underscore
pluralize_table_names ? table_name.pluralize : table_name
end
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.2 singularize
O método singularize
é o inverso de pluralize
:
"tables".singularize # => "table"
"rubies".singularize # => "ruby"
"equipment".singularize # => "equipment"
As associações calculam o nome da classe associada padrão correspondente usando esse método:
# active_record/reflection.rb
def derive_class_name
class_name = name.to_s.camelize
class_name = class_name.singularize if collection?
class_name
end
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.3 camelize
O método camelize
retorna sua string de entrada em camel case:
"product".camelize # => "Product"
"admin_user".camelize # => "AdminUser"
Como uma regra geral, você pode pensar neste método como aquele que transforma caminhos em nomes de classes ou módulos Ruby, onde as barras separa os namespaces:
"backoffice/session".camelize # => "Backoffice::Session"
Por exemplo, o Action Pack usa esse 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 se torna minúscula:
ruby
"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 de underscore
, embora haja casos em que isso não se aplica: "SSLError".underscore.camelize
retorna "SslError"
. Para suportar casos como esse, o Active Support permite que você especifique siglas em config/initializers/inflections.rb
:
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'SSL'
end
"SSLError".underscore.camelize # => "SSLError"
camelize
é um alias para camelcase
.
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.4 underscore
O método underscore
faz o contrário, de camel case para paths:
"Product".underscore # => "product"
"AdminUser".underscore # => "admin_user"
Também converte "::" para "/":
"Backoffice::Session".underscore # => "backoffice/session"
E entende strings que começam com letra minúscula:
"visualEffect".underscore # => "visual_effect"
underscore
não aceita argumentos.
O Rails usa underscore
para obter um nome em minúsculas para classes de controladores:
# actionpack/lib/abstract_controller/base.rb
def controller_path
@controller_path ||= name.delete_suffix("Controller").underscore
end
Por exemplo, esse valor é o que você obtém em params[:controller]
.
Como regra geral, você pode pensar em underscore
como o inverso de camelize
, embora haja casos em que isso não se aplica. Por exemplo, "SSLError".underscore.camelize
retorna "SslError"
.
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.5 titleize
O método titleize
capitaliza as palavras na string:
"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize # => "Fermat's Enigma"
titleize
é um alias para titlecase
.
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.6 dasherize
O método dasherize
substitui os underscores na string por hífens:
"name".dasherize # => "name"
"contact_data".dasherize # => "contact-data"
O serializador XML de modelos usa esse método para transformar os nomes dos nós em formato de hífens:
# active_model/serializers/xml.rb
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.7 demodulize
Dada uma string com um nome de constante qualificado, demodulize
retorna o próprio nome da constante, ou seja, a parte mais à direita:
"Product".demodulize # => "Product"
"Backoffice::UsersController".demodulize # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize # => "Inflections"
"".demodulize # => ""
O Active Record, por exemplo, usa esse método para calcular o nome de uma coluna de cache de contagem:
# active_record/reflection.rb
def counter_cache_column
if options[:counter_cache] == true
"#{active_record.name.demodulize.underscore.pluralize}_count"
elsif options[:counter_cache]
options[:counter_cache]
end
end
NOTA: Definido 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 qualificada, 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"
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.9 parameterize
O método parameterize
normaliza a string de forma que possa ser usada em URLs amigáveis.
"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"
Para preservar a caixa da string, defina o argumento preserve_case
como true. Por padrão, preserve_case
é definido como false.
"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"
Para usar um separador personalizado, substitua o argumento separator
.
"John Smith".parameterize(separator: "_") # => "john_smith"
"Kurt Gödel".parameterize(separator: "_") # => "kurt_godel"
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.10 tableize
O método tableize
é underscore
seguido de pluralize
.
"Person".tableize # => "people"
"Invoice".tableize # => "invoices"
"InvoiceLine".tableize # => "invoice_lines"
Como regra geral, tableize
retorna o nome da tabela que corresponde a um determinado modelo para casos simples. A implementação real no Active Record não é apenas tableize
, pois também desmodulariza o nome da classe e verifica algumas opções que podem afetar a string retornada.
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.11 classify
O método classify
é o inverso de tableize
. Ele retorna o nome da classe correspondente a um nome de tabela:
"people".classify # => "Person"
"invoices".classify # => "Invoice"
"invoice_lines".classify # => "InvoiceLine"
O método entende nomes de tabela qualificados:
"highrise_production.companies".classify # => "Company"
Observe que classify
retorna o nome da classe como uma string. Você pode obter o objeto de classe real invocando constantize
nele, explicado a seguir.
NOTA: 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 se refere a uma constante conhecida, ou seu conteúdo não é um nome de constante válido, constantize
gera uma exceção NameError
.
A resolução de nome de constante por constantize
sempre começa no nível superior do Object
, mesmo se não houver "::" no início.
X = :in_Object
module M
X = :in_M
X # => :in_M
"::X".constantize # => :in_Object
"X".constantize # => :in_Object (!)
end
Portanto, em geral, não é equivalente ao que o Ruby faria no mesmo local se uma constante real fosse avaliada.
Os casos de teste do Mailer obtêm o mailer sendo testado a partir do nome da classe de teste usando constantize
:
# action_mailer/test_case.rb
def determine_default_mailer(name)
name.delete_suffix("Test").constantize
rescue NameError => e
raise NonInferrableMailerError.new(name)
end
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.13 humanize
O método humanize
ajusta um nome de atributo para exibição aos usuários finais.
Especificamente, ele realiza as seguintes transformações:
- Aplica regras de inflexão humana ao argumento.
- Remove sublinhados iniciais, se houver.
- Remove o sufixo "_id", se presente.
- Substitui sublinhados por espaços, se houver.
- Coloca todas as palavras em minúsculas, exceto acrônimos.
- Capitaliza a primeira palavra.
A capitalização da primeira palavra pode ser desativada definindo a opção :capitalize
como 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 um acrônimo:
'ssl_error'.humanize # => "SSL error"
O método auxiliar full_messages
usa humanize
como fallback 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
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.14 foreign_key
O método foreign_key
retorna o nome da coluna de chave estrangeira a partir de um nome de classe. Para fazer isso, ele desmodulariza, adiciona sublinhados e adiciona "_id":
"User".foreign_key # => "user_id"
"InvoiceLine".foreign_key # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"
Passe um argumento falso se você não quiser o sublinhado em "_id":
"User".foreign_key(false) # => "userid"
As associações usam esse método para inferir chaves estrangeiras, por exemplo, has_one
e has_many
fazem isso:
# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.15 upcase_first
O método upcase_first
capitaliza a primeira letra do receptor:
"employee salary".upcase_first # => "Employee salary"
"".upcase_first # => ""
NOTA: Definido em active_support/core_ext/string/inflections.rb
.
5.12.16 downcase_first
O método downcase_first
converte a primeira letra do receptor para minúscula:
"If I had read Alice in Wonderland".downcase_first # => "if I had read Alice in Wonderland"
"".downcase_first # => ""
NOTA: 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 invólucros de conveniência em torno 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 um argumento opcional :utc
ou :local
, para indicar em qual fuso horário você deseja o horário:
"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
.
Consulte a documentação de Date._parse
para mais detalhes.
Os três retornam nil
para receptores em branco.
NOTA: 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 de terceira pessoa para Symbol#start_with?
e Symbol#end_with?
:
:foo.starts_with?("f") # => true
:foo.ends_with?("o") # => true
NOTA: 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 esses 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.0
-4.exabytes # => -4611686018427387904
As formas singulares são aliadas para que você possa dizer:
1.megabyte # => 1048576
NOTA: Definido em active_support/core_ext/numeric/bytes.rb
.
7.2 Time
Os seguintes métodos:
permitem 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 de objetos Time.
Esses métodos podem ser combinados com from_now
, ago
, etc, para cálculos precisos de datas. Por exemplo:
# equivalente a Time.current.advance(days: 1)
1.day.from_now
# equivalente a Time.current.advance(weeks: 2)
2.weeks.from_now
# equivalente a Time.current.advance(days: 4, weeks: 5)
(4.days + 5.weeks).from_now
ATENÇÃO. Para outras durações, consulte as extensões de tempo para Integer
.
NOTA: Definido em active_support/core_ext/numeric/time.rb
.
7.3 Formatação
Permite a formatação de números de várias maneiras.
Produza uma representação em 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
Produza uma representação em 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
Produza uma representação em string de um número como uma porcentagem:
100.to_fs(:porcentagem)
# => 100.000%
100.to_fs(:porcentagem, precisão: 0)
# => 100%
1000.to_fs(:porcentagem, delimitador: '.', separador: ',')
# => 1.000,000%
302.24398923423.to_fs(:porcentagem, precisão: 5)
# => 302.24399%
Produza uma representação em string de um número em forma delimitada:
12345678.to_fs(:delimitado) # => 12,345,678
12345678.05.to_fs(:delimitado) # => 12,345,678.05
12345678.to_fs(:delimitado, delimitador: ".") # => 12.345.678
12345678.to_fs(:delimitado, delimitador: ",") # => 12,345,678
12345678.05.to_fs(:delimitado, separador: " ") # => 12,345,678 05
Produza uma representação em string de um número arredondado para uma precisão:
111.2345.to_fs(:arredondado) # => 111.235
111.2345.to_fs(:arredondado, precisão: 2) # => 111.23
13.to_fs(:arredondado, precisão: 5) # => 13.00000
389.32314.to_fs(:arredondado, precisão: 0) # => 389
111.2345.to_fs(:arredondado, significativo: true) # => 111
Produza uma representação em string de um número como um número de bytes legível pelo ser humano:
123.to_fs(:tamanho_humano) # => 123 Bytes
1234.to_fs(:tamanho_humano) # => 1.21 KB
12345.to_fs(:tamanho_humano) # => 12.1 KB
1234567.to_fs(:tamanho_humano) # => 1.18 MB
1234567890.to_fs(:tamanho_humano) # => 1.15 GB
1234567890123.to_fs(:tamanho_humano) # => 1.12 TB
1234567890123456.to_fs(:tamanho_humano) # => 1.1 PB
1234567890123456789.to_fs(:tamanho_humano) # => 1.07 EB
Produza uma representação em string de um número em palavras legíveis pelo ser humano:
123.to_fs(:humano) # => "123"
1234.to_fs(:humano) # => "1.23 Mil"
12345.to_fs(:humano) # => "12.3 Mil"
1234567.to_fs(:humano) # => "1.23 Milhão"
1234567890.to_fs(:humano) # => "1.23 Bilhão"
1234567890123.to_fs(:humano) # => "1.23 Trilhão"
1234567890123456.to_fs(:humano) # => "1.23 Quadrilhão"
NOTA: Definido em active_support/core_ext/numeric/conversions.rb
.
8 Extensões para Integer
8.1 múltiplo_de?
O método [múltiplo_de?
][Integer#múltiplo_de?] testa se um número inteiro é múltiplo do argumento:
2.múltiplo_de?(1) # => true
1.múltiplo_de?(2) # => false
NOTA: Definido em active_support/core_ext/integer/multiple.rb
.
8.2 ordinal
O método ordinal
retorna a string de sufixo ordinal correspondente ao número inteiro:
1.ordinal # => "º"
2.ordinal # => "º"
53.ordinal # => "º"
2009.ordinal # => "º"
-21.ordinal # => "º"
-134.ordinal # => "º"
NOTA: Definido em active_support/core_ext/integer/inflections.rb
.
8.3 ordinalize
O método ordinalize
retorna a string ordinal correspondente ao número inteiro. Em comparação, observe que o método ordinal
retorna apenas a string de sufixo.
1.ordinalize # => "1º"
2.ordinalize # => "2º"
53.ordinalize # => "53º"
2009.ordinalize # => "2009º"
-21.ordinalize # => "-21º"
-134.ordinalize # => "-134º"
NOTA: Definido em active_support/core_ext/integer/inflections.rb
.
8.4 Tempo
Os seguintes métodos:
- [
meses
][Integer#meses] - [
anos
][Integer#anos]
permitem declarações e cálculos de tempo, como 4.meses + 5.anos
. Seus valores de retorno também podem ser adicionados ou subtraídos de objetos Time.
Esses métodos podem ser combinados com from_now
, ago
, etc, para cálculos precisos de datas. Por exemplo:
# equivalente a Time.current.advance(months: 1)
1.mês.from_now
# equivalente a Time.current.advance(years: 2)
2.anos.from_now
# equivalente a Time.current.advance(months: 4, years: 5)
(4.meses + 5.anos).from_now
ATENÇÃO. Para outras durações, consulte as extensões de tempo para Numeric
.
NOTA: 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 chamada simples para to_s
resultará em uma representação de ponto flutuante em vez de notação científica:
BigDecimal(5.00, 6).to_s # => "5.0"
A notação científica 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 enumerável:
ruby
[1, 2, 3].sum # => 6
(1..100).sum # => 5050
A adição assume apenas 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 isso pode ser personalizado:
[].sum # => 0
[].sum(1) # => 1
Se um bloco for fornecido, sum
se torna um iterador que retorna 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 receptor vazio também pode ser personalizada nessa forma:
[].sum(1) { |n| n**3 } # => 1
NOTA: 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 iterável indexados por alguma chave.
Ele itera pela coleção e passa cada elemento para um bloco. O elemento será indexado pelo valor retornado pelo bloco:
invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}
ATENÇÃO. As chaves normalmente devem ser únicas. Se o bloco retornar o mesmo valor para diferentes elementos, nenhuma coleção será construída para essa chave. O último item vencerá.
NOTA: 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 iterável como chaves. O valor
é ou um valor padrão passado ou 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 ], … }
NOTA: Definido em active_support/core_ext/enumerable.rb
.
10.4 many?
O método many?
é uma forma abreviada de collection.size > 1
:
<% if pages.many? %>
<%= pagination_links %>
<% end %>
Se um bloco opcional for fornecido, many?
leva em consideração apenas os elementos que retornam true:
@see_more = videos.many? { |video| video.category == params[:category] }
NOTA: Definido em active_support/core_ext/enumerable.rb
.
10.5 exclude?
O predicado exclude?
testa se um determinado objeto não pertence à coleção. É a negação do include?
embutido:
to_visit << node if visited.exclude?(node)
NOTA: Definido em active_support/core_ext/enumerable.rb
.
10.6 including
O método including
retorna um novo iterável 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"]
NOTA: Definido em active_support/core_ext/enumerable.rb
.
10.7 excluding
O método excluding
retorna uma cópia de um iterável com os elementos especificados
removidos:
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
excluding
é um alias para without
.
NOTA: 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"]]
NOTA: 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"]
NOTA: Definido em active_support/core_ext/enumerable.rb
.
11 Extensões para Array
11.1 Acesso
O Active Support aprimora a API de arrays para facilitar certas formas de acesso. Por exemplo, to
retorna o subarray de elementos até o índice passado:
%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
Da mesma forma, from
retorna a cauda a partir do elemento no índice passado até o final. Se o índice for maior que o comprimento do array, ele retorna 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#-
em vez de Array#reject
por motivos 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 integrados). Graças à sabedoria social e à construtividade positiva em todos os lugares, forty_two
também está disponível.
%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil
NOTA: Definido em active_support/core_ext/array/access.rb
.
11.2 Extração
O método extract!
remove e retorna os elementos para os quais o bloco retorna um valor verdadeiro.
Se nenhum bloco for fornecido, um Enumerator é 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]
NOTA: 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 &block
, o Ruby permite omitir os colchetes:
User.exists?(email: params[:email])
Açúcar sintático é usado muito no Rails para evitar argumentos posicionais onde haveria muitos, oferecendo em vez disso interfaces que emulam parâmetros nomeados. Em particular, é muito idiomático usar um hash final para opções.
Se um método espera um número variável de argumentos e usa *
em sua declaração, no entanto, esse hash de opções acaba sendo um item do array de argumentos, onde ele perde seu papel.
Nesses casos, você pode dar a um hash de opções um tratamento distinto com extract_options!
. Este método verifica o tipo do último item de um array. Se for um hash, ele o remove e o retorna, caso contrário, retorna um hash vazio.
Vamos ver, 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ção e um hash opcional de opções como último argumento. Com a chamada para extract_options!
, você obtém o hash de opções e o remove de actions
de uma maneira simples e explícita.
NOTA: 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 que contém uma frase enumerando seus itens:
%w().to_sentence # => ""
%w(Terra).to_sentence # => "Terra"
%w(Terra Vento).to_sentence # => "Terra e Vento"
%w(Terra Vento Fogo).to_sentence # => "Terra, Vento e Fogo"
Este método aceita três opções:
:two_words_connector
: O que é usado para arrays de tamanho 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 valores padrão 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 |
NOTA: Definido em active_support/core_ext/array/conversions.rb
.
11.4.2 to_fs
O método to_fs
age como to_s
por padrão.
No entanto, se o array contiver itens que respondem a id
, o símbolo :db
pode ser passado como argumento. Isso é tipicamente usado com coleções de objetos 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 são supostos vir das respectivas chamadas para id
.
NOTA: 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 XML de seu receptor:
Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
# <contributor>
# <id type="integer">4356</id>
# <name>Jeremy Kemper</name>
# <rank type="integer">1</rank>
# <url-id>jeremy-kemper</url-id>
# </contributor>
# <contributor>
# <id type="integer">4404</id>
# <name>David Heinemeier Hansson</name>
# <rank type="integer">2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </contributor>
# </contributors>
Para fazer isso, ele envia to_xml
para cada item, coletando os resultados em um nó raiz. Todos os itens devem responder a to_xml
, caso contrário, uma exceção é lançada.
Por padrão, o nome do elemento raiz é o plural do nome da classe do primeiro item, com underscores e traços, desde que o restante dos elementos pertença a esse tipo (verificado com is_a?
) e eles não sejam hashes. No exemplo acima, isso é "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 receptor for uma matriz de hashes, o elemento raiz é, por padrão, também "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>
ATENÇÃO. Se a coleção estiver vazia, o elemento raiz será, por padrão, "nil-classes". Isso é uma pegadinha, por exemplo, o elemento raiz da lista de contribuidores acima não seria "contribuidores" se a coleção estivesse vazia, mas 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 "contribuidor" e "objeto". A opção :children
permite definir esses nomes de nó.
O construtor XML padrão é uma nova instância de Builder::XmlMarkup
. Você pode configurar seu próprio construtor por meio da opção :builder
. O método também aceita opções como :dasherize
e outros, que são repassados ao construtor:
Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contribuidores>
# <contribuidor>
# <id>4356</id>
# <nome>Jeremy Kemper</nome>
# <classificação>1</classificação>
# <id-url>jeremy-kemper</id-url>
# </contribuidor>
# <contribuidor>
# <id>4404</id>
# <nome>David Heinemeier Hansson</nome>
# <classificação>2</classificação>
# <id-url>david-heinemeier-hansson</id-url>
# </contribuidor>
# </contribuidores>
NOTA: Definido em active_support/core_ext/array/conversions.rb
.
11.5 Encapsulamento
O método Array.wrap
encapsula seu argumento em uma matriz, a menos que ele já seja uma matriz (ou semelhante a uma matriz).
Especificamente:
- Se o argumento for
nil
, uma matriz vazia é retornada. - Caso contrário, se o argumento responder a
to_ary
, ele é invocado e, se o valor deto_ary
não fornil
, ele é retornado. - Caso contrário, uma matriz com o argumento como seu único elemento é retornada.
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 existem algumas diferenças:
- Se o argumento responder a
to_ary
, o método é invocado.Kernel#Array
continua tentandoto_a
se o valor retornado fornil
, masArray.wrap
retorna imediatamente uma matriz com o argumento como seu único elemento. - Se o valor retornado de
to_ary
não for nemnil
nem um objetoArray
,Kernel#Array
gera uma exceção, enquantoArray.wrap
não, ele apenas retorna o valor. - Ele não chama
to_a
no argumento, se o argumento não responder ato_ary
, ele retorna uma matriz com o argumento como seu único elemento.
O último ponto é particularmente digno de comparação para algumas enumeráveis:
Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar) # => [[:foo, :bar]]
Também existe um idioma relacionado que usa o operador splat:
[*object]
NOTA: Definido em active_support/core_ext/array/wrap.rb
.
11.6 Duplicação
O método Array#deep_dup
duplica a si mesmo e todos os objetos internos de forma recursiva com o método Object#deep_dup
do Active Support. Ele funciona como Array#map
, enviando o método deep_dup
para cada objeto interno.
array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil # => true
NOTA: Definido em active_support/core_ext/object/deep_dup.rb
.
11.7 Agrupamento
11.7.1 in_groups_of(número, preencher_com = 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 retorna em sequência 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 exemplo 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.
NOTA: Definido em active_support/core_ext/array/grouping.rb
.
11.7.2 in_groups(número, preencher_com = 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 retorna em sequência 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
adicional, se necessário. Um grupo pode ter no máximo um desses elementos extras, o mais à direita, se houver. E os grupos que os têm 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.
NOTA: Definido em active_support/core_ext/array/grouping.rb
.
11.7.3 split(valor = nil)
O método split
divide um array por um separador e retorna os pedaços resultantes.
Se um bloco for passado, os separadores são aqueles elementos do array para os quais o bloco retorna verdadeiro:
(-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, que é opcional e tem o valor padrão nil
, é o separador:
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]
DICA: Observe no exemplo anterior que separadores consecutivos resultam em arrays vazios.
NOTA: Definido em active_support/core_ext/array/grouping.rb
.
12 Extensões para Hash
12.1 Conversões
12.1.1 to_xml
O método to_xml
retorna uma string contendo uma representação XML de seu receptor:
{ foo: 1, bar: 2 }.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <foo type="integer">1</foo>
# <bar type="integer">2</bar>
# </hash>
Para fazer isso, o método percorre os pares e constrói nós que dependem dos valores. Dado um par chave
, valor
:
Se
valor
for um hash, há uma chamada recursiva comchave
como:root
.Se
valor
for um array, há uma chamada recursiva comchave
como:root
echave
singularizada como:children
.Se
valor
for um objeto chamável, ele deve esperar um ou dois argumentos. Dependendo da aridade, o objeto chamável é invocado com o hashoptions
como primeiro argumento comchave
como:root
echave
singularizada como segundo argumento. O valor de retorno se torna um novo nó.Se
valor
responder ato_xml
, o método é invocado comchave
como:root
.Caso contrário, um nó com
chave
como tag é criado com uma representação em string devalor
como nó de texto. Sevalor
fornil
, um atributo "nil" definido como "true" é adicionado. A menos que a opção:skip_types
exista e seja verdadeira, um atributo "type" também é adicionado de acordo com o seguinte mapeamento:
XML_TYPE_NAMES = {
"Symbol" => "symbol",
"Integer" => "integer",
"BigDecimal" => "decimal",
"Float" => "float",
"TrueClass" => "boolean",
"FalseClass" => "boolean",
"Date" => "date",
"DateTime" => "datetime",
"Time" => "datetime"
}
Por padrão, o nó raiz é "hash", mas isso pode ser configurado através da opção :root
.
O construtor XML padrão é uma nova instância de Builder::XmlMarkup
. Você pode configurar seu próprio construtor com a opção :builder
. O método também aceita opções como :dasherize
e amigos, que são encaminhados para o construtor.
NOTA: Definido em active_support/core_ext/hash/conversions.rb
.
12.2 Mesclando
Ruby possui um método embutido Hash#merge
que mescla dois hashes:
{ a: 1, b: 1 }.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}
O Active Support define algumas maneiras adicionais de mesclar hashes que podem ser convenientes.
12.2.1 reverse_merge
e reverse_merge!
Em caso de colisão, a chave no hash do argumento vence em merge
. Você pode suportar hashes de opções com valores padrão de forma compacta com esse idiom:
options = { length: 30, omission: "..." }.merge(options)
O Active Support define reverse_merge
caso você prefira essa notação alternativa:
options = options.reverse_merge(length: 30, omission: "...")
E uma versão com bang reverse_merge!
que realiza a mesclagem no local:
options.reverse_merge!(length: 30, omission: "...")
ATENÇÃO. Leve em consideração que reverse_merge!
pode alterar o hash no chamador, o que pode ou não ser uma boa ideia.
NOTA: Definido em active_support/core_ext/hash/reverse_merge.rb
.
12.2.2 reverse_update
O método reverse_update
é um alias para reverse_merge!
, explicado acima.
ATENÇÃO. Observe que reverse_update
não possui bang.
NOTA: Definido em active_support/core_ext/hash/reverse_merge.rb
.
12.2.3 deep_merge
e deep_merge!
Como você pode ver no exemplo anterior, se uma chave for encontrada em ambos os hashes, o valor no hash do argumento vence.
O Active Support define Hash#deep_merge
. Em uma mesclagem profunda, se uma chave for encontrada em ambos os hashes e seus valores forem hashes por sua vez, então a mesclagem deles se torna o valor no hash resultante:
{ a: { b: 1 } }.deep_merge(a: { c: 2 })
# => {:a=>{:b=>1, :c=>2}}
O método deep_merge!
realiza uma mesclagem profunda no local.
NOTA: Definido em active_support/core_ext/hash/deep_merge.rb
.
12.3 Duplicação Profunda
O método Hash#deep_dup
duplica a si mesmo e todas as chaves e valores
internamente de forma recursiva com o método Object#deep_dup
do Active Support. Funciona como Enumerator#each_with_object
enviando o método deep_dup
para cada par dentro.
hash = { a: 1, b: { c: 2, d: [3, 4] } }
dup = hash.deep_dup
dup[:b][:e] = 5
dup[:b][:d] << 5
hash[:b][:e] == nil # => true
hash[:b][:d] == [3, 4] # => true
NOTA: Definido em active_support/core_ext/object/deep_dup.rb
.
12.4 Trabalhando com Chaves
12.4.1 except
e except!
O método except
retorna um hash com as chaves na lista de argumentos removidas, se presentes:
{ a: 1, b: 2 }.except(:a) # => {:b=>2}
Se o receptor responder a convert_key
, o método é chamado em cada um dos argumentos. Isso permite que except
funcione bem com hashes com acesso indiferente, por exemplo:
{ a: 1 }.with_indifferent_access.except(:a) # => {}
{ a: 1 }.with_indifferent_access.except("a") # => {}
Existe também a variante com exclamação except!
que remove as chaves no local.
NOTA: Definido em active_support/core_ext/hash/except.rb
.
12.4.2 stringify_keys
e stringify_keys!
O método stringify_keys
retorna um hash que tem uma versão em string das chaves no receptor. Ele faz isso enviando to_s
para elas:
{ nil => nil, 1 => 1, a: :a }.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}
Em caso de colisão de chaves, o valor será o mais recentemente inserido no hash:
{ "a" => 1, a: 2 }.stringify_keys
# O resultado será
# => {"a"=>2}
Este método pode ser útil, por exemplo, para aceitar facilmente símbolos e strings como opções. Por exemplo, ActionView::Helpers::FormHelper
define:
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
# ...
end
A segunda linha pode acessar com segurança a chave "type" e permitir que o usuário passe tanto :type
quanto "type".
Existe também a variante com exclamação stringify_keys!
que converte as chaves em string no local.
Além disso, pode-se usar deep_stringify_keys
e deep_stringify_keys!
para converter todas as chaves no hash fornecido e todos os hashes aninhados nele em string. Um exemplo do resultado é:
{ nil => nil, 1 => 1, nested: { a: 3, 5 => 5 } }.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}
NOTA: Definido em active_support/core_ext/hash/keys.rb
.
12.4.3 symbolize_keys
e symbolize_keys!
O método symbolize_keys
retorna um hash que tem uma versão simbolizada das chaves no receptor, quando possível. Ele faz isso enviando to_sym
para elas:
{ nil => nil, 1 => 1, "a" => "a" }.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}
ATENÇÃO. Note que no exemplo anterior apenas uma chave foi simbolizada.
Em caso de colisão de chaves, o valor será o mais recentemente inserido no hash:
{ "a" => 1, a: 2 }.symbolize_keys
# => {:a=>2}
Este método pode ser útil, por exemplo, para aceitar facilmente símbolos e strings como opções. Por exemplo, ActionText::TagHelper
define
```ruby
def rich_text_area_tag(name, value = nil, options = {})
options = options.symbolize_keys
options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}" # ... end ```
A terceira linha pode acessar com segurança a chave :input
e permite que o usuário passe tanto :input
quanto "input".
Também existe a variante com exclamação symbolize_keys!
que simboliza as chaves no local.
Além disso, pode-se usar deep_symbolize_keys
e deep_symbolize_keys!
para simbolizar todas as chaves no hash fornecido e todos os hashes aninhados nele. Um exemplo do resultado é:
{ nil => nil, 1 => 1, "nested" => { "a" => 3, 5 => 5 } }.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
NOTA: Definido em active_support/core_ext/hash/keys.rb
.
12.4.4 to_options
e to_options!
Os métodos to_options
e to_options!
são aliases de symbolize_keys
e symbolize_keys!
, respectivamente.
NOTA: Definido em active_support/core_ext/hash/keys.rb
.
12.4.5 assert_valid_keys
O método assert_valid_keys
recebe um número arbitrário de argumentos e verifica se o receptor tem alguma chave fora dessa lista. Se tiver, ArgumentError
é lançado.
{ a: 1 }.assert_valid_keys(:a) # passa
{ a: 1 }.assert_valid_keys("a") # ArgumentError
O Active Record não aceita opções desconhecidas ao criar associações, por exemplo. Ele implementa esse controle por meio de assert_valid_keys
.
NOTA: Definido em active_support/core_ext/hash/keys.rb
.
12.5 Trabalhando com Valores
12.5.1 deep_transform_values
e deep_transform_values!
O método deep_transform_values
retorna um novo hash com todos os valores convertidos pela operação do bloco. Isso inclui os valores do hash raiz e de todos os hashes e arrays aninhados.
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_transform_values { |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}
Também existe a variante com exclamação deep_transform_values!
que converte destrutivamente todos os valores usando a operação do bloco.
NOTA: Definido em active_support/core_ext/hash/deep_transform_values.rb
.
12.6 Slicing
O método slice!
substitui o hash apenas pelas chaves fornecidas e retorna um hash contendo os pares chave/valor removidos.
hash = { a: 1, b: 2 }
rest = hash.slice!(:a) # => {:b=>2}
hash # => {:a=>1}
NOTA: Definido em active_support/core_ext/hash/slice.rb
.
12.7 Extrair
O método extract!
remove e retorna os pares chave/valor correspondentes às chaves fornecidas.
hash = { a: 1, b: 2 }
rest = hash.extract!(:a) # => {:a=>1}
hash # => {:b=>2}
O método extract!
retorna a mesma subclasse de Hash que o receptor é.
hash = { a: 1, b: 2 }.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess
NOTA: Definido em active_support/core_ext/hash/slice.rb
.
12.8 Acesso Indiferente
O método with_indifferent_access
retorna um ActiveSupport::HashWithIndifferentAccess
a partir de seu receptor:
{ a: 1 }.with_indifferent_access["a"] # => 1
NOTA: Definido em active_support/core_ext/hash/indifferent_access.rb
.
13 Extensões para Regexp
13.1 multiline?
O método multiline?
indica se uma expressão regular tem a flag /m
definida, ou seja, se o ponto corresponde a quebras de linha.
%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. Expressões regulares multilinhas não são permitidas para requisitos de rota e essa flag 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
NOTA: Definido em active_support/core_ext/regexp.rb
.
14 Extensões para Range
14.1 to_fs
O Active Support define Range#to_fs
como uma alternativa para to_s
que entende um argumento de formato opcional. No momento em que este documento foi escrito, o único formato não padrão suportado é :db
:
(Date.today..Date.tomorrow).to_fs
# => "2009-10-25..2009-10-26"
(Date.today..Date.tomorrow).to_fs(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"
Como o exemplo mostra, o formato :db
gera uma cláusula SQL BETWEEN
. Isso é usado pelo Active Record em seu suporte a valores de intervalo em condições.
NOTA: Definido em active_support/core_ext/range/conversions.rb
.
14.2 ===
e include?
Os métodos Range#===
e Range#include?
indicam se um determinado valor está entre os extremos de uma instância dada:
(2..3).include?(Math::E) # => true
O Active Support estende esses métodos para que o argumento possa ser outro intervalo. Nesse caso, testamos se os extremos do intervalo do argumento pertencem ao próprio intervalo receptor:
(1..10) === (3..7) # => true
(1..10) === (0..7) # => false
(1..10) === (3..11) # => false
(1...9) === (3..9) # => false
(1..10).include?(3..7) # => true
(1..10).include?(0..7) # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9) # => false
NOTA: Definido em active_support/core_ext/range/compare_range.rb
.
14.3 overlap?
O método Range#overlap?
indica se dois intervalos dados têm uma interseção não vazia:
(1..10).overlap?(7..11) # => true
(1..10).overlap?(0..7) # => true
(1..10).overlap?(11..27) # => false
NOTA: Definido em active_support/core_ext/range/overlap.rb
.
15 Extensões para Date
15.1 Cálculos
Os seguintes métodos de cálculo têm casos especiais em outubro de 1582, pois os dias 5 a 14 simplesmente não existem. Este guia não documenta seu comportamento em torno desses dias por brevidade, mas é suficiente dizer que eles fazem o que você espera. Ou seja, Date.new(1582, 10, 4).tomorrow
retorna Date.new(1582, 10, 15)
e assim por diante. Verifique test/core_ext/date_ext_test.rb
no conjunto de testes do Active Support para o comportamento esperado.
15.1.1 Date.current
O Active Support define Date.current
como sendo hoje no fuso horário atual. Isso é semelhante a Date.today
, exceto que ele respeita o fuso horário do usuário, se definido. Ele também define Date.yesterday
e Date.tomorrow
, e os predicados de instância past?
, today?
, tomorrow?
, next_day?
, yesterday?
, prev_day?
, future?
, on_weekday?
e on_weekend?
, todos eles relativos a Date.current
.
Ao fazer comparações de datas usando métodos que respeitam o fuso horário do usuário, certifique-se de usar Date.current
e não Date.today
. Existem casos em que o fuso horário do usuário pode estar no futuro em comparação com o fuso horário do sistema, que é usado por padrão pelo Date.today
. Isso significa que Date.today
pode ser igual a Date.yesterday
.
NOTA: Definido em active_support/core_ext/date/calculations.rb
.
15.1.2 Datas Nomeadas
15.1.2.1 beginning_of_week
, end_of_week
Os métodos beginning_of_week
e end_of_week
retornam as datas para o início e o fim da semana, respectivamente. Assume-se que as semanas começam na segunda-feira, mas isso pode ser alterado passando um argumento, definindo Date.beginning_of_week
localmente ou 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
é um alias para at_beginning_of_week
e end_of_week
é um alias para at_end_of_week
.
NOTA: Definido em active_support/core_ext/date_and_time/calculations.rb
.
15.1.2.2 monday
, sunday
Os métodos monday
e sunday
retornam as datas para a segunda-feira anterior e o próximo domingo, respectivamente.
ruby
date = Date.new(2010, 6, 7)
date.months_ago(3) # => Mon, 07 Mar 2010
date.months_since(3) # => Thu, 07 Sep 2010
If such a day does not exist, the last day of the corresponding month is returned:
Date.new(2012, 3, 31).months_ago(1) # => Thu, 29 Feb 2012
Date.new(2012, 1, 31).months_since(1) # => Thu, 29 Feb 2012
last_month
is short-hand for #months_ago(1)
.
15.1.2.3 weeks_ago
, weeks_since
The methods weeks_ago
and [weeks_since
][DateAndTime::Calculations#weeks_since] work analogously for weeks:
date = Date.new(2010, 6, 7)
date.weeks_ago(2) # => Mon, 24 May 2010
date.weeks_since(2) # => Mon, 21 Jun 2010
If such a day does not exist, the last day of the corresponding month is returned:
Date.new(2012, 2, 29).weeks_ago(1) # => Wed, 22 Feb 2012
Date.new(2012, 2, 29).weeks_since(1) # => Wed, 07 Mar 2012
last_week
is short-hand for #weeks_ago(1)
.
15.1.2.4 days_ago
, days_since
The methods [days_ago
][DateAndTime::Calculations#days_ago] and [days_since
][DateAndTime::Calculations#days_since] work analogously for days:
date = Date.new(2010, 6, 7)
date.days_ago(5) # => Wed, 02 Jun 2010
date.days_since(5) # => Sat, 12 Jun 2010
[yesterday
][DateAndTime::Calculations#yesterday] is short-hand for #days_ago(1)
, and [tomorrow
][DateAndTime::Calculations#tomorrow] is short-hand for #days_since(1)
.
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
.
15.1.2.5 change
The method change
allows you to get a new timestamp which is the same as the receiver except for the given year, month, day, hour, minute, or second:
date = DateTime.new(2010, 12, 23, 12, 30, 45)
date.change(year: 2011, month: 11, day: 15)
# => Tue, 15 Nov 2011 12:30:45 +0200
This method is not tolerant to non-existing dates, if the change is invalid ArgumentError
is raised:
date = DateTime.new(2010, 1, 31, 12, 30, 45)
date.change(month: 2)
# => ArgumentError: invalid date
15.1.3 Durations
Duration
objects can be added to and subtracted from timestamps:
t = Time.current
# => Mon, 09 Aug 2010 12:34:56 UTC +00:00
t + 1.year
# => Tue, 09 Aug 2011 12:34:56 UTC +00:00
t - 3.hours
# => Mon, 09 Aug 2010 09:34:56 UTC +00:00
They translate to calls to since
or advance
. For example here we get the correct jump in the calendar reform:
Time.new(1582, 10, 4, 12, 0, 0) + 1.day
# => Fri, 15 Oct 1582 12:00:00 UTC +00:00
15.1.4 Time Zones
The following methods return a Time
object if possible, otherwise a DateTime
. If set, they honor the user time zone.
15.1.4.1 in_time_zone
The method [in_time_zone
][Time#in_time_zone] returns a new Time
or DateTime
object in the specified time zone:
time = Time.utc(2010, 6, 7, 19, 55, 25)
time.in_time_zone('Eastern Time (US & Canada)') # => Mon, 07 Jun 2010 15:55:25 EDT -04:00
Defined in active_support/core_ext/time/zones.rb
.
15.1.4.2 utc
The method [utc
][Time#utc] returns a new Time
object in UTC time zone:
time = Time.new(2010, 6, 7, 19, 55, 25, '-04:00')
time.utc # => Tue, 08 Jun 2010 03:55:25 UTC +00:00
Defined in active_support/core_ext/time/calculations.rb
.
15.1.4.3 local
The method [local
][Time#local] returns a new Time
or DateTime
object in the local time zone:
time = Time.utc(2010, 6, 7, 19, 55, 25)
time.local # => Mon, 07 Jun 2010 15:55:25 EDT -04:00
Defined in active_support/core_ext/time/calculations.rb
.
15.1.4.4 to_time
The method [to_time
][Time#to_time] returns a new Time
object representing the same time as the receiver:
date = Date.new(2010, 6, 7)
date.to_time # => Mon, 07 Jun 2010 00:00:00 UTC +00:00
Defined in active_support/core_ext/date/conversions.rb
.
15.1.4.5 to_datetime
The method [to_datetime
][Time#to_datetime] returns a new DateTime
object representing the same time as the receiver:
time = Time.new(2010, 6, 7, 19, 55, 25)
time.to_datetime # => Mon, 07 Jun 2010 19:55:25 +0000
Defined in active_support/core_ext/time/conversions.rb
.
O método end_of_minute
retorna um timestamp no final do minuto (hh:mm:59):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_minute # => Segunda-feira, 07 de Junho de 2010 19:55:59 +0200
beginning_of_minute
é um alias para at_beginning_of_minute
.
beginning_of_hour
, end_of_hour
, beginning_of_minute
e end_of_minute
são implementados para Time
e DateTime
, mas não para Date
, pois não faz sentido solicitar o início ou o fim de uma hora ou minuto em uma instância de Date
.
Definido em active_support/core_ext/date_time/calculations.rb
.
15.1.4.6 ago
, since
O método ago
recebe um número de segundos como argumento e retorna um timestamp correspondente a essa quantidade de segundos atrás da meia-noite:
date = Date.current # => Sexta-feira, 11 de Junho de 2010
date.ago(1) # => Quinta-feira, 10 de Junho de 2010 23:59:59 EDT -04:00
Da mesma forma, since
move para frente:
date = Date.current # => Sexta-feira, 11 de Junho de 2010
date.since(1) # => Sexta-feira, 11 de Junho de 2010 00:00:01 EDT -04:00
Definido em active_support/core_ext/date/calculations.rb
.
16 Extensões para DateTime
DateTime
não está ciente das regras de DST (Horário de Verão) e, portanto, alguns desses métodos têm casos especiais quando ocorre uma mudança de DST. Por exemplo, seconds_since_midnight
pode não retornar a quantidade real em um dia assim.
16.1 Cálculos
A classe DateTime
é uma subclasse de Date
, então ao carregar active_support/core_ext/date/calculations.rb
, você herda esses métodos e seus aliases, exceto que eles sempre retornarão datetimes.
Os seguintes métodos são reimplementados para que você não precise carregar active_support/core_ext/date/calculations.rb
para esses:
Por outro lado, advance
e change
também são definidos e suportam mais opções, eles estão documentados abaixo.
Os seguintes métodos são implementados apenas em active_support/core_ext/date_time/calculations.rb
, pois só fazem sentido quando usados com uma instância de DateTime
:
16.1.1 Datetimes Nomeados
16.1.1.1 DateTime.current
Active Support define DateTime.current
para ser como Time.now.to_datetime
, exceto que ele respeita o fuso horário do usuário, se definido. Os predicados de instância past?
e future?
são definidos em relação a DateTime.current
.
Definido em active_support/core_ext/date_time/calculations.rb
.
16.1.2 Outras Extensões
16.1.2.1 seconds_since_midnight
O método seconds_since_midnight
retorna o número de segundos desde a meia-noite:
now = DateTime.current # => Segunda-feira, 07 de Junho de 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
Definido em active_support/core_ext/date_time/calculations.rb
.
16.1.2.2 utc
O método utc
retorna o mesmo datetime no receptor expresso em UTC.
now = DateTime.current # => Segunda-feira, 07 de Junho de 2010 19:27:52 -0400
now.utc # => Segunda-feira, 07 de Junho de 2010 23:27:52 +0000
Esse método também é um alias para getutc
.
Definido em active_support/core_ext/date_time/calculations.rb
.
16.1.2.3 utc?
O predicado utc?
indica se o receptor tem UTC como seu fuso horário:
now = DateTime.now # => Segunda-feira, 07 de Junho de 2010 19:30:47 -0400
now.utc? # => false
now.utc.utc? # => true
Definido em active_support/core_ext/date_time/calculations.rb
.
16.1.2.4 advance
A maneira mais genérica de pular para outro datetime é advance
. Este método recebe um hash com as chaves :years
, :months
, :weeks
, :days
, :hours
, :minutes
e :seconds
, e retorna um datetime avançado conforme as chaves presentes indicam.
```ruby
d = DateTime.current
=> Qui, 05 Ago 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
=> Ter, 06 Set 2011 12:34:32 +0000
Este método primeiro calcula a data de destino passando `:years`, `:months`, `:weeks` e `:days` para `Date#advance` documentado acima. Depois disso, ajusta o horário chamando [`since`][DateTime#since] com o número de segundos para avançar. Esta ordem é relevante, uma ordem diferente resultaria em datas e horas diferentes em alguns casos extremos. O exemplo em `Date#advance` se aplica e podemos estendê-lo para mostrar a relevância da ordem relacionada aos bits de tempo.
Se primeiro movermos os bits de data (que também têm uma ordem relativa de processamento, como documentado anteriormente) e depois os bits de tempo, obtemos, por exemplo, o seguinte cálculo:
```ruby
d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Dom, 28 Fev 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Seg, 29 Mar 2010 00:00:00 +0000
mas se os calculássemos na ordem inversa, o resultado seria diferente:
d.advance(seconds: 1).advance(months: 1)
# => Qui, 01 Abr 2010 00:00:00 +0000
AVISO: Como DateTime
não é DST-aware, você pode acabar em um ponto no tempo que não existe sem nenhum aviso ou erro informando isso.
NOTA: Definido em active_support/core_ext/date_time/calculations.rb
.
16.1.3 Alterando Componentes
O método change
permite obter um novo datetime que é o mesmo que o receptor, exceto pelas opções fornecidas, que podem incluir :year
, :month
, :day
, :hour
, :min
, :sec
, :offset
, :start
:
now = DateTime.current
# => Ter, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Qua, 08 Jun 2011 01:56:22 -0600
Se as horas forem zeradas, então os minutos e segundos também serão (a menos que tenham valores fornecidos):
now.change(hour: 0)
# => Ter, 08 Jun 2010 00:00:00 +0000
Da mesma forma, se os minutos forem zerados, então os segundos também serão (a menos que tenha um valor fornecido):
now.change(min: 0)
# => Ter, 08 Jun 2010 01:00:00 +0000
Este método não tolera datas que não existem, se a alteração for inválida, ArgumentError
é lançado:
DateTime.current.change(month: 2, day: 30)
# => ArgumentError: data inválida
NOTA: Definido em active_support/core_ext/date_time/calculations.rb
.
16.1.4 Durações
Objetos Duration
podem ser adicionados e subtraídos de datetimes:
now = DateTime.current
# => Seg, 09 Ago 2010 23:15:17 +0000
now + 1.year
# => Ter, 09 Ago 2011 23:15:17 +0000
now - 1.week
# => Seg, 02 Ago 2010 23:15:17 +0000
Eles se traduzem em chamadas para since
ou advance
. Por exemplo, aqui obtemos o salto correto na reforma do calendário:
DateTime.new(1582, 10, 4, 23) + 1.hour
# => Sex, 15 Out 1582 00:00:00 +0000
17 Extensões para Time
17.1 Cálculos
São análogos. Consulte a documentação acima e leve em consideração as seguintes diferenças:
change
aceita uma opção adicional:usec
.Time
entende DST, então você obtém cálculos corretos de DST como em
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
# Em Barcelona, 2010/03/28 02:00 +0100 se torna 2010/03/28 03:00 +0200 devido ao DST.
t = Time.local(2010, 3, 28, 1, 59, 59)
# => Dom Mar 28 01:59:59 +0100 2010
t.advance(seconds: 1)
# => Dom Mar 28 03:00:00 +0200 2010
- Se
since
ouago
saltarem para um tempo que não pode ser expresso comTime
, um objetoDateTime
é retornado.
17.1.1 Time.current
O Active Support define Time.current
como a data de hoje no fuso horário atual. É como Time.now
, mas respeita o fuso horário do usuário, se definido. Ele também define os predicados de instância past?
, today?
, tomorrow?
, next_day?
, yesterday?
, prev_day?
e future?
, todos relativos a Time.current
.
Ao fazer comparações de tempo usando métodos que respeitam o fuso horário do usuário, certifique-se de usar Time.current
em vez de Time.now
. Existem casos em que o fuso horário do usuário pode estar no futuro em comparação com o fuso horário do sistema, que é usado por padrão pelo Time.now
. Isso significa que Time.now.to_date
pode ser igual a Date.yesterday
.
NOTA: Definido em active_support/core_ext/time/calculations.rb
.
17.1.2 all_day
, all_week
, all_month
, all_quarter
e all_year
O método all_day
retorna um intervalo representando o dia inteiro do tempo atual.
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
Analogamente, all_week
, all_month
, all_quarter
e all_year
servem para gerar intervalos de tempo.
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00
NOTA: Definido em active_support/core_ext/date_and_time/calculations.rb
.
17.1.3 prev_day
, next_day
prev_day
e next_day
retornam o tempo no dia anterior ou no próximo dia:
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_day # => 2010-05-07 00:00:00 +0900
t.next_day # => 2010-05-09 00:00:00 +0900
NOTA: Definido em active_support/core_ext/time/calculations.rb
.
17.1.4 prev_month
, next_month
prev_month
e next_month
retornam o tempo com o mesmo dia no mês anterior ou no próximo mês:
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
Se um dia assim não existir, o último dia do mês correspondente é retornado:
Time.new(2000, 5, 31).prev_month # => 2000-04-30 00:00:00 +0900
Time.new(2000, 3, 31).prev_month # => 2000-02-29 00:00:00 +0900
Time.new(2000, 5, 31).next_month # => 2000-06-30 00:00:00 +0900
Time.new(2000, 1, 31).next_month # => 2000-02-29 00:00:00 +0900
NOTA: Definido em active_support/core_ext/time/calculations.rb
.
17.1.5 prev_year
, next_year
prev_year
e next_year
retornam um tempo com o mesmo dia/mês no ano anterior ou no próximo ano:
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
Se a data for o dia 29 de fevereiro de um ano bissexto, você obtém o dia 28:
t = Time.new(2000, 2, 29) # => 2000-02-29 00:00:00 +0900
t.prev_year # => 1999-02-28 00:00:00 +0900
t.next_year # => 2001-02-28 00:00:00 +0900
NOTA: Definido em active_support/core_ext/time/calculations.rb
.
17.1.6 prev_quarter
, next_quarter
prev_quarter
e next_quarter
retornam a data com o mesmo dia no trimestre anterior ou seguinte:
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
Se esse dia não existir, o último dia do mês correspondente é retornado:
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
é um alias para last_quarter
.
NOTA: Definido em active_support/core_ext/date_and_time/calculations.rb
.
17.2 Construtores de Tempo
Active Support define Time.current
como Time.zone.now
se houver um fuso horário do usuário definido, com fallback para 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
Analogamente ao DateTime
, os predicados past?
e future?
são relativos ao Time.current
.
Se o tempo a ser construído estiver além do intervalo suportado por Time
na plataforma em execução, os microssegundos são descartados e um objeto DateTime
é retornado.
17.2.1 Durações
Objetos Duration
podem ser adicionados e subtraídos de objetos de tempo:
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
Eles se traduzem em chamadas para since
ou advance
. Por exemplo, aqui obtemos o salto correto na reforma do calendário:
Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
18 Extensões para File
18.1 atomic_write
Com o método de classe File.atomic_write
, você pode escrever em um arquivo de forma que nenhum leitor veja o conteúdo meio escrito.
O nome do arquivo é passado como argumento, e o método gera um identificador de arquivo aberto para escrita. Uma vez que o bloco é concluído, atomic_write
fecha o identificador de arquivo e conclui seu trabalho.
Por exemplo, o Action Pack usa esse método para escrever arquivos de cache de ativos como all.css
:
File.atomic_write(joined_asset_path) do |cache|
cache.write(join_asset_file_contents(asset_paths))
end
Para realizar isso, atomic_write
cria um arquivo temporário. Esse é o arquivo em que o código no bloco realmente escreve. Ao concluir, o arquivo temporário é renomeado, o que é uma operação atômica em sistemas POSIX. Se o arquivo de destino existir, atomic_write
o sobrescreve e mantém proprietários e permissões. No entanto, existem alguns casos em que atomic_write
não pode alterar a propriedade ou permissões do arquivo, esse erro é capturado e ignorado, confiando no usuário/sistema de arquivos para garantir que o arquivo seja acessível aos processos que o necessitam.
NOTA. Devido à operação chmod que atomic_write
executa, se o arquivo de destino tiver um ACL definido nele, esse ACL será recalculado/modificado.
ATENÇÃO. Observe que você não pode anexar com atomic_write
.
O arquivo auxiliar é gravado em um diretório padrão para arquivos temporários, mas você pode passar um diretório de sua escolha como segundo argumento.
NOTA: Definido em active_support/core_ext/file/atomic.rb
.
19 Extensões para NameError
O Active Support adiciona missing_name?
ao NameError
, que testa se a exceção foi lançada por causa do nome passado como argumento.
O nome pode ser fornecido como um símbolo ou uma string. Um símbolo é testado em relação ao nome da constante simples, uma string é testada em relação ao nome da constante totalmente qualificado.
DICA: Um símbolo pode representar um nome de constante totalmente qualificado como em :"ActiveRecord::Base"
, então o comportamento para símbolos é definido por conveniência, não porque precisa ser assim tecnicamente.
Por exemplo, quando uma ação de ArticlesController
é chamada, o Rails tenta otimisticamente usar ArticlesHelper
. Está tudo bem se o módulo helper não existir, então se uma exceção for lançada para esse nome de constante, ela deve ser silenciada. Mas pode ser o caso de articles_helper.rb
lançar um NameError
devido a uma constante desconhecida real. Isso deve ser relançado. O método missing_name?
fornece uma maneira de distinguir ambos os casos:
def default_helper_module!
module_name = name.delete_suffix("Controller")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
NOTA: Definido em active_support/core_ext/name_error.rb
.
20 Extensões para LoadError
O Active Support adiciona is_missing?
ao LoadError
.
Dado um nome de caminho, is_missing?
testa se a exceção foi lançada devido a esse arquivo específico (exceto talvez pela extensão ".rb").
Por exemplo, quando uma ação de ArticlesController
é chamada, o Rails tenta carregar articles_helper.rb
, mas esse arquivo pode não existir. Isso é normal, o módulo helper não é obrigatório, então o Rails silencia um erro de carregamento. Mas pode ser o caso de o módulo helper existir e, por sua vez, requerer outra biblioteca que está faltando. Nesse caso, o Rails deve relançar a exceção. O método is_missing?
fornece uma maneira de distinguir ambos os casos:
def default_helper_module!
module_name = name.delete_suffix("Controller")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
NOTA: Definido em active_support/core_ext/load_error.rb
.
21 Extensões para Pathname
21.1 existence
O método existence
retorna o próprio objeto se o arquivo com o nome especificado existir, caso contrário, retorna nil
. É útil para idiomatismos como este:
content = Pathname.new("file").existence&.read
NOTA: Definido em active_support/core_ext/pathname/existence.rb
.
Feedback
Você é incentivado a ajudar a melhorar a qualidade deste guia.
Por favor, contribua se encontrar algum erro de digitação ou factual. Para começar, você pode ler nossa contribuição à documentação seção.
Você também pode encontrar conteúdo incompleto ou desatualizado. Por favor, adicione qualquer documentação ausente para o principal. Certifique-se de verificar Guias Edge primeiro para verificar se os problemas já foram corrigidos ou não no branch principal. Verifique as Diretrizes dos Guias do Ruby on Rails para estilo e convenções.
Se por algum motivo você encontrar algo para corrigir, mas não puder corrigi-lo você mesmo, por favor abra uma issue.
E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem-vinda no Fórum oficial do Ruby on Rails.