v6.1.4
Veja mais em rubyonrails.org: Mais Ruby on Rails

Active Support Core Extensions

O Active Support é o componente em Ruby on Rails responsável por fornecer à linguagem Ruby extensões, utilidades e outras funcionalidades paralelas.

Ele oferece um riquíssimo ponto de partida no nível da linguagem, onde pode-se aproveitar tanto para o desenvolvimento de aplicações Rails, quanto no próprio desenvolvimento da tecnologia Ruby on Rails.

Depois de ler esse guia, você saberá:

Chapters

  1. Como Carregar Core Extensions
  2. Extensões para todos os objetos
  3. Extensões de Module
  4. Extensões para Class
  5. Extensões para String
  6. Extensões para Symbol
  7. Extensões para Numeric
  8. Extensões para Integer
  9. Extensões para BigDecimal
  10. Extensions to Enumerable
  11. Extensions to Array
  12. Extensions to Hash
  13. Extensions to Regexp
  14. Extensions to Range
  15. Extensions to Date
  16. Extensions to DateTime
  17. Extensions to Time
  18. Extensions to File
  19. Extensions to Marshal
  20. Extensions to NameError
  21. Extensions to LoadError

1 Como Carregar Core Extensions

1.1 Active Support Stand-Alone

A fim de ter uma configuração padrão mais básica, o Active Support não carrega nada por padrão. Com isso, ele é granularizado em pequenas unidades para que seja possível carregar apenas o que se é necessário no contexto, além de oferecer pontos de entrada que são interessantes para o carregamento de extensões convenientes de uma só vez, ou até mesmo não carregar nenhuma.

Portanto, é possível inicializar após o uso de um simples require como:

require "active_support"

objetos não respondem nem mesmo a um blank?. Vejamos como carregar essa definição.

1.1.1 Escolhendo a Definição

A forma mais limitada de conseguir respostas a um blank? é selecionando o arquivo que faz essa definição.

Para cada método definido como core extension esse guia possui uma nota que diz onde tal método é definido. No caso de blank? a nota diz:

Definido em active_support/core_ext/object/blank.rb.

Isso significa que você pode fazer requires assim:

require "active_support"
require "active_support/core_ext/object/blank"

O Active Support foi cuidadosamente projetado para que as seleções de arquivos carreguem somente as dependências extremamente necessárias, caso existam.

1.1.2 Carregando Core Extensions Agrupadas

O próximo passo é simplesmente carregar todas as extensões de Object. Como regra geral, extensões para SomeClass estão disponíveis em um rápido carregamento de active_support/core_ext/some_class.

Portanto, para carregar todas as extensões de Object (incluindo blank?):

require "active_support"
require "active_support/core_ext/object"
1.1.3 Carregando Todas Core Extensions

Você pode escolher por carregar todas as extensões principais, há um arquivo para isso:

require "active_support"
require "active_support/core_ext"
1.1.4 Carregando Active Support Completamente

E finalmente, se você quer ter tudo que o Active Support fornece, basta apenas:

require "active_support/all"

Isso não vai inserir todo o Active Support na memória antes do necessário, algumas funcionalidades são configuradas via ʻautoload`, então só são carregadas se usadas.

1.2 Active Support Em Uma Aplicação Ruby on Rails

Uma aplicação Ruby on Rails carrega todo o Active Support a não ser que config.active_support.bare esteja definida como true. Neste caso, a aplicação vai carregar apenas o que o próprio framework escolhe como suas próprias necessidades, e ainda pode selecionar a si mesmo em qualquer nível de granularidade, conforme explicado na seção anterior.

2 Extensões para todos os objetos

2.1 blank? e present?

Os seguintes valores são considerados blank em uma aplicação Rails:

  • nil e false,

  • strings compostas apenas por espaços em branco (veja a nota abaixo),

  • arrays e hashes vazios, e

  • qualquer outro objeto que responde a empty? como true.

A condicional é que as strings usem a classe de caractere [:space:] do Unicode-aware, como por exemplo U+2029 (separador de parágrafo) é considerado um espaço em branco.

Note que números não são mencionados. Em particular, 0 e 0.0 não são blank.

Por exemplo, este método de ActionController::HttpAuthentication::Token::ControllerMethods usa blank? pra checar se o token está presente:

def authenticate(controller, &login_procedure)
  token, options = token_and_options(controller.request)
  unless token.blank?
    login_procedure.call(token, options)
  end
end

O método present? é equivalente ao !blank?. Este exemplo disponível em ActionDispatch::Http::Cache::Response:

def set_conditional_cache_control!
  return if self["Cache-Control"].present?
  # ...
end

Definido em active_support/core_ext/object/blank.rb.

2.2 presence

O método presence retorna seu valor se present? for true, e nil caso não seja. Isso é muito útil para expressões como esta:

host = config[:host].presence || 'localhost'

Definido em active_support/core_ext/object/blank.rb.

2.3 duplicable?

A partir do Ruby 2.5, a maioria dos objetos podem ser duplicados com dup ou clone:

"foo".dup           # => "foo"
"".dup              # => ""
Rational(1).dup     # => (1/1)
Complex(0).dup      # => (0+0i)
1.method(:+).dup    # => TypeError (allocator undefined for Method)

O Active Support fornece o duplicable? para consultar se o objeto pode ser duplicado:

"foo".duplicable?           # => true
"".duplicable?              # => true
Rational(1).duplicable?     # => true
Complex(1).duplicable?      # => true
1.method(:+).duplicable?    # => false

Qualquer classe pode ter a duplicação desabilitada a partir da remoção de dup e clone ou definindo exceções. Neste caso apenas rescue pode informar se determinado objeto arbitrável é duplicável. duplicable? depende da existência de uma lista de elementos a serem analisados, como no exemplo porém é muito mais veloz que rescue. Use apenas se você souber que a lista é suficiente em seu caso.

2.4 deep_dup

O método deep_dup retorna uma cópia profunda de um objeto. Normalmente, quando você dup um objeto que contêm outros objetos, Ruby não executa o dup, então é criada uma cópia superficial do objeto. Caso você possua um array com uma string, por exemplo, terá algo parecido com:

array     = ['string']
duplicate = array.dup

duplicate.push 'another-string'

# the object was duplicated, so the element was added only to the duplicate
array     # => ['string']
duplicate # => ['string', 'another-string']

duplicate.first.gsub!('string', 'foo')

# first element was not duplicated, it will be changed in both arrays
array     # => ['foo']
duplicate # => ['foo', 'another-string']

Como podemos ver, depois de duplicar a instância de Array, possuímos agora outro objeto, portanto podemos modificá-lo sem alterar informações do objeto original. Isso não funciona para elementos de um array, entretanto. Desde que dup não faça a cópia profunda, a string dentro do array se manterá como o mesmo objeto.

Se você precisa de uma cópia profunda de um objeto, pode então usar o deep_dup. Confira um exemplo:

array     = ['string']
duplicate = array.deep_dup

duplicate.first.gsub!('string', 'foo')

array     # => ['string']
duplicate # => ['foo']

Se o objeto não é duplicável, deep_dup apenas o retornará:

number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id   # => true

Definido em active_support/core_ext/object/deep_dup.rb.

2.5 try

Quando você quer chamar um método em um objeto somente se ele não for nil, a forma mais simples de conseguir isso é através de uma estrutura condicional, adicionando uma desnecessária desordem. A alternativa é usar try. try é como Object#send exceto que o retorno seja nil se enviado para nil.

Eis um exemplo:

# sem try
unless @number.nil?
  @number.next
end

# com try
@number.try(:next)

Outro exemplo é o código em ActiveRecord::ConnectionAdapters::AbstractAdapter onde @logger não pode ser nil. Você pode ver que o código usa try e evita uma verificação desnecessária.

def log_info(sql, name, ms)
  if @logger.try(:debug?)
    name = '%s (%.1fms)' % [name || 'SQL', ms]
    @logger.debug(format_log_entry(name, sql.squeeze(' ')))
  end
end

try pode também ser chamada sem argumentos, porém em um bloco, no qual só será executado se o objeto não for nil:

@person.try { |p| "#{p.first_name} #{p.last_name}" }

Perceba que try não exibirá as mensagens de erro caso elas ocorram, retornando nil em vez disso. Se você quiser se proteger de possíveis erros de digitação, use try!:

@number.try(:nest)  # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer

Definido em active_support/core_ext/object/try.rb.

2.6 class_eval(*args, &block)

Você pode evoluir o código no contexto de um singleton de qualquer objeto usando class_eval:

class Proc
  def bind(object)
    block, time = self, Time.current
    object.class_eval do
      method_name = "__bind_#{time.to_i}_#{time.usec}"
      define_method(method_name, &block)
      method = instance_method(method_name)
      remove_method(method_name)
      method
    end.bind(object)
  end
end

Definido em active_support/core_ext/kernel/singleton_class.rb.

2.7 acts_like?(duck)

O método acts_like? fornece um meio para conferir se alguma classe age como alguma outra classe baseada em uma simples convenção: a classe que fornece a mesma interface é definida como String

def acts_like_string?
end

que é apenas um marcador, seu corpo ou valor de retorno são irrelevantes. Então, o código do cliente pode consultar a tipagem desta forma:

some_klass.acts_like?(:string)

Rails possui classes que agem como Date ou Time e seguem essa linha.

Definido em active_support/core_ext/object/acts_like.rb.

2.8 to_param

Todos objetos em Rails respondem ao método to_param, o qual é usado para retornar representações de valores em strings, no qual podem ser usadas em consultas, ou fragmentos de URL.

Por padrão, to_param apenas chama o método to_s:

7.to_param # => "7"

O retorno de valores em to_param não deve ser ignorado:

"Tom & Jerry".to_param # => "Tom & Jerry"

Várias classes em Rails sobrescrevem este método.

Por exemplo nil, true, e false retornam a si mesmo. Array#to_param chama to_param para cada elemento, exibindo o resultado separando os elementos com "/":

[0, true, String].to_param # => "0/true/String"

Notavelmente, as rotas de sistemas Rails chamam to_param em models para obter o valor do campo :id. ActiveRecord::Base#to_param retorna o id do model, mas você pode redefinir esse método em seus models. Por exemplo, dado

class User
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

nós temos:

user_path(@user) # => "/users/357-john-smith"

Controllers precisam estar alinhados a qualquer redefinição de to_param porque quando uma requisição como essa chega em "357-john-smith" este é o valor de params[:id].

Definido em active_support/core_ext/object/to_param.rb.

2.9 to_query

O método to_query controi uma query em string que associam a key com o retorno de to_param. Por exemplo, dado a seguinte definição de to_param:

class User
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

Temos:

current_user.to_query('user') # => "user=357-john-smith"

Este método traz o que é necessário, tanto para chave, como para o valor:

account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"

então esse resultado esta pronto para ser usado em uma string de busca.

Arrays retornam o resultado da aplicação to_query para cada elemento com key[] como chave, e junta o resultado com "&":

[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"

Hashes tambem respondem a to_query mas com uma diferença. Se não passar um argumento a chamada gera uma série ordenada de chaves/valores atribuídas chamando to_query(key) em seus valores. Em seguida, o resultado é mesclado com "&":

{c: 3, b: 2, a: 1}.to_query # => "a=1&b=2&c=3"

O método Hash#to_query aceita um espaço para nomear as chaves:

{id: 89, name: "John Smith"}.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"

Definido em active_support/core_ext/object/to_query.rb.

2.10 with_options

O método with_options fornece um meio de agrupar opções comuns em uma série de chamada de métodos.

Dado as opções default de uma hash, with_options faz um objeto de "ponte" em um bloco. Dentro do bloco, métodos são chamados no objeto e são encaminhados ao receptor com suas opções mescladas. Por exemplo, você se livra da duplicação em:

class Account < ApplicationRecord
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
  has_many :invoices,  dependent: :destroy
  has_many :expenses,  dependent: :destroy
end

desta forma:

class Account < ApplicationRecord
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

Essa expressão pode transmitir um agrupamento para o leitor também. Por exemplo, digamos que você queira enviar um boletim informativo cujo idioma depende do usuário. Em algum lugar na mailer você poderá agrupar os receptores por localidade como no exemplo:

I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
  subject i18n.t :subject
  body    i18n.t :body, user_name: user.name
end

Desde que with_options envie chamadas para seus receptores eles podem ser aninhados. Cada nível de aninhamento mesclará os padrões herdados com os seus próprios.

Definido em active_support/core_ext/object/with_options.rb.

2.11 Suporte ao JSON

Active Support fornece uma melhor implementação para to_json do que a gem json normalmente fornece para objetos em Ruby. Isso é porque algumas classes, como Hash, OrderedHash e Process::Status precisam de manipulações especiais a fim de fornecer uma representação de JSON adequada.

Definido em active_support/core_ext/object/json.rb.

2.12 Variáveis de Instância

Active Support fornece vários métodos para facilitar o acesso a variáveis de instância.

2.12.1 instance_values

O método instance_values retorna uma hash que mapeia variáveis de instância de nomes sem "@" para seus valores correspondentes. As chaves são strings:

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end

C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}

Definido em active_support/core_ext/object/instance_variables.rb.

2.12.2 instance_variable_names

O método instance_variable_names retorna um array. Cada nome inclui o sinal "@".

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end

C.new(0, 1).instance_variable_names # => ["@x", "@y"]

Definido em active_support/core_ext/object/instance_variables.rb.

2.13 Silenciando Warnings e Exceções

Os métodos silence_warnings e enable_warnings trocam o valor de $VERBOSE de acordo com a duração do seu bloco, e o reiniciam depois:

silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }

Silenciar exceções também é possível com suppress. Este método recebe um número arbitrário de classes de exceção. Se uma exceção é acionada durante a execução de um bloco e é kind_of? qualquer um dos argumentos, suppress captura e retorna silenciosamente. Caso contrário, a exceção não é capturada:

# If the user is locked, the increment is lost, no big deal.
suppress(ActiveRecord::StaleObjectError) do
  current_user.increment! :visits
end

Definido in active_support/core_ext/kernel/reporting.rb.

2.14 in?

A expressão in? testa se um objeto é incluído em outro objeto. Uma exceção ArgumentError será acionada se o argumento passado não responder a include?.

Exemplos de in?:

1.in?([1,2])        # => true
"lo".in?("hello")   # => true
25.in?(30..50)      # => false
1.in?(1)            # => ArgumentError

Definido em active_support/core_ext/object/inclusion.rb.

3 Extensões de Module

3.1 Atributos

3.1.1 alias_attribute

Atributos de models podem ser lidos, escritos e condicionados. Você pode criar um alias para um atributo de model correspondendo todos os três métodos definidos por você usando alias_attribute. Em outro métodos de alias, o novo nome é o primeiro argumento, e o antigo nome é o segundo (uma forma de memorizar é pensar que eles se apresentam na mesma ordem como se você fizesse uma atribuição):

class User < ApplicationRecord
  # Você pode referenciar a coluna email como "login".
  # Isso pode ser importante para o código de autenticação.
  alias_attribute :login, :email
end

Definido em active_support/core_ext/module/aliasing.rb.

3.1.2 Atributos Internos

Quando você esta definindo um atributo em uma classe que pode ser uma subclasse, os conflitos de nomes são um risco. Isso é extremamente importante para as bibliotecas.

Active Support define as macros attr_internal_reader, attr_internal_writer, e attr_internal_accessor. Elas comportam-se como seu próprio Ruby attr_* embutido, exceto pelos nomes de variáveis de instância que faz com que os conflitos sejam menos comuns.

A macro attr_internal é um sinônimo para attr_internal_accessor:

# biblioteca
class ThirdPartyLibrary::Crawler
  attr_internal :log_level
end

# código do cliente
class MyCrawler < ThirdPartyLibrary::Crawler
  attr_accessor :log_level
end

No exemplo anterior, poderia ser que no caso :log_level não pertença a interface pública da biblioteca e só seria usada em desenvolvimento. O código do cliente, não sabe do potencial conflito, subclasses e definições de seus próprios :log_level. Graças ao attr_internal não há conflito.

Por padrão, a variável de instancia interna é nomeada com uma underscore na frente, @_log_level no exemplo acima. Isso é configurável via Module.attr_internal_naming_format apesar disso, você pode passar qualquer tipo de sprintf no formato string com a inicial @ e um %s em algum lugar, no qual é onde o nome será colocado. O padrão é "@_%s".

Rails usa atributos internos em alguns pontos, para views como por exemplo:

module ActionView
  class Base
    attr_internal :captures
    attr_internal :request, :layout
    attr_internal :controller, :template
  end
end

Definido em active_support/core_ext/module/attr_internal.rb.

3.1.3 Atributos de Módulo

As macros mattr_reader, mattr_writer, e mattr_accessor São iguais a cattr_* macros definidas na classe. De fato, cattr_* macros são apenas aliases para as mattr_* macros. Confira a seção Atributos de Classe.

Por exemplo, os mecanismos de dependências usam:

module ActiveSupport
  module Dependencies
    mattr_accessor :warnings_on_first_load
    mattr_accessor :history
    mattr_accessor :loaded
    mattr_accessor :mechanism
    mattr_accessor :load_paths
    mattr_accessor :load_once_paths
    mattr_accessor :autoloaded_constants
    mattr_accessor :explicitly_unloadable_constants
    mattr_accessor :constant_watch_stack
    mattr_accessor :constant_watch_stack_mutex
  end
end

Definido em active_support/core_ext/module/attribute_accessors.rb.

3.2 Parents

3.2.1 module_parent

O método module_parent em um módulo nomeado aninhado que retorna o módulo que contém uma constante correspondente:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z

X::Y::Z.module_parent # => X::Y
M.module_parent       # => X::Y

Se o módulo é anônimo ou pertence a um nível superior, module_parent retorna Object.

Note que neste caso module_parent_name retorna nil.

Definido em active_support/core_ext/module/introspection.rb.

3.2.2 module_parent_name

O método module_parent_name em um modulo nomeado aninhado retorna o nome completamente qualificado do módulo que contém sua constante correspondente:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z

X::Y::Z.module_parent_name # => "X::Y"
M.module_parent_name       # => "X::Y"

Para módulos de nível superior ou anônimos module_parent_name retorna nil.

Note que nesse caso module_parent retorna Object.

Definido em active_support/core_ext/module/introspection.rb.

3.2.3 module_parents

O método module_parents chama module_parent no receptor e sobe até Object ser alcançado. A cadeia é retornada em uma matriz, de baixo para cima:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z

X::Y::Z.module_parents # => [X::Y, X, Object]
M.module_parents       # => [X::Y, X, Object]

Definido em active_support/core_ext/module/introspection.rb.

3.3 Anônimo

Um módulo pode ou não ter um nome:

module M
end
M.name # => "M"

N = Module.new
N.name # => "N"

Module.new.name # => nil

Você pode verificar se um módulo possui um nome com a condicional anonymous?:

module M
end
M.anonymous? # => false

Module.new.anonymous? # => true

Observe que estar inacessível não significa ser anônimo:

module M
end

m = Object.send(:remove_const, :M)

m.anonymous? # => false

Embora um módulo anônimo seja inacessível por definição.

Definido em active_support/core_ext/module/anonymous.rb.

3.4 Delegação de Método

3.4.1 delegate

A macro delegate oferece uma maneira fácil de encaminhar métodos.

Vamos imaginar que os usuários de alguma aplicação possuem informações de login no model User além de nome e outro dado em um model Profile separado:

class User < ApplicationRecord
  has_one :profile
end

Com essa configuração você consegue o nome dos usuários partir da classe perfil, user.profile.name, mas isso poderia ser conveniente para habilitar o acesso ao atributo diretamente:

class User < ApplicationRecord
  has_one :profile

  def name
    profile.name
  end
end

Isso é o que o delegate faz por você:

class User < ApplicationRecord
  has_one :profile

  delegate :name, to: :profile
end

É mais curto e a intenção mais óbvia.

O método deve ser público.

A macro delegate aceita vários métodos:

delegate :name, :age, :address, :twitter, to: :profile

Quando interpolado em uma string, a opção :to deve se tornar uma expressão que avalia o objeto ao qual o método é delegado. Normalmente uma string ou um symbol. Tal expressão é avaliada no contexto do receptor:

# delega para as constantes Rails
delegate :logger, to: :Rails

# delega para as classes receptoras
delegate :table_name, to: :class

Se a opção :prefix for true é menos genérica, veja abaixo.

Por padrão, se a delegação resulta em NoMethodError e o objeto é nil a exceção se propaga. Você pode perguntar se nil é retornado ao invés com a opção :allow_nil:

delegate :name, to: :profile, allow_nil: true

Com :allow_nil a chamada user.name retorna nil se o usuário não tiver um perfil.

A opção :prefix adiciona um prefixo ao nome do método gerado. Isso pode ser útil, por exemplo, para obter um nome melhor:

delegate :street, to: :address, prefix: true

Os exemplos prévios geram address_street ao invés de street.

Já que neste caso o nome do método gerado é composto pelos nomes do objeto alvo e do método alvo, a opção :to deve ser um nome de método.

Um prefixo customizado pode também ser configurado:

delegate :size, to: :attachment, prefix: :avatar

Os macro exemplos prévios geram avatar_size ao invés de size.

A opção :private mudam o escopo do método:

delegate :date_of_birth, to: :profile, private: true

Os métodos delegados são públicos por padrão. Passe private: true para mudar isso.

Definido em active_support/core_ext/module/delegation.rb

3.4.2 delegate_missing_to

Imagine que você gostaria de delegar tudo o que está faltando no objeto User, para um Profile. A macro delegate_missing_to permite que você implemente isso de forma facilitada:

class User < ApplicationRecord
  has_one :profile

  delegate_missing_to :profile
end

O destino pode ser qualquer coisa que possa ser chamada dentro do objeto, por exemplo: instância de variáveis, métodos, constantes etc. Somente métodos públicos do alvo são delegados.

Definido em active_support/core_ext/module/delegation.rb.

3.5 Redefinindo Métodos

Existem casos onde você precisa definir um método com define_method, mas não sei se já existe um método com esse nome. Caso sim, um warning é exibido se estiverem habilitados. Não é muito perigoso, mas não é uma boa prática.

O método redefine_method previne um potencial warning, removendo um método existente, se necessário.

Você pode também usar silence_redefinition_of_method se você precisa definir o método de substituição (porque você está usando delegate, por exemplo).

Definido em active_support/core_ext/module/redefine_method.rb.

4 Extensões para Class

4.1 Atributos de classe

4.1.1 class_attribute

O método class_attribute declara um ou mais atributos de classe herdáveis que podem ser substituídos em qualquer nível abaixo da hierarquia.

class A
  class_attribute :x
end

class B < A; end

class C < B; end

A.x = :a
B.x # => :a
C.x # => :a

B.x = :b
A.x # => :a
C.x # => :b

C.x = :c
A.x # => :a
B.x # => :b

Por exemplo ActionMailer::Base define:

class_attribute :default_params
self.default_params = {
  mime_version: "1.0",
  charset: "UTF-8",
  content_type: "text/plain",
  parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze

Eles também podem ser acessados e substituídos no nível de instância.

A.x = 1

a1 = A.new
a2 = A.new
a2.x = 2

a1.x # => 1, vem de A
a2.x # => 2, substituído em a2

A criação de um método de instância de escrita pode ser prevenido configurando a opção :instance_writer para false.

module ActiveRecord
  class Base
    class_attribute :table_name_prefix, instance_writer: false, default: "my"
  end
end

Essa opção pode ser útil para prevenir atribuições em massa ao definir o atributo.

A criação de um método de instância de leitura pode ser prevenido configurando a opção :instance_reader para false.

class A
  class_attribute :x, instance_reader: false
end

A.new.x = 1
A.new.x # NoMethodError

Por conveniência class_attribute também define um predicado de instância que é uma negação dupla do que o leitor de instância retorna. No exemplo acima podemos usar x?.

Quando :instance_reader é false, o predicado de instância retorna NoMethodError assim como o método de leitura.

Se você não quiser o predicado de instância, passe instance_predicate: false e ele não será definido.

Definido em active_support/core_ext/class/attribute.rb.

4.1.2 cattr_reader, cattr_writer, e cattr_accessor

As macros cattr_reader, cattr_writer, e cattr_accessor são análogas às suas attr_* homólogas porém para classes. Eles inicializam a variável de classe com nil a menos que ela já exista, e gera os métodos de classe correspondentes para acessá-la:

class MysqlAdapter < AbstractAdapter
  # Gera métodos de classe para acessar @@emulate_booleans.
  cattr_accessor :emulate_booleans
end

Além disso, você pode passar um bloco para cattr_* para configurar o atributo com um valor padrão.

class MysqlAdapter < AbstractAdapter
  # Gera métodos de classe para acessar @@emulate_booleans com true como valor padrão.
  cattr_accessor :emulate_booleans, default: true
end

Métodos de instância são criados também por conveniência, eles são apenas uma forma de acesso ao atributo de classe. Logo, instâncias podem alterar o atributo de classe, porém não podem substituí-lo do mesmo modo que ocorre com class_attribute (veja acima). Por exemplo, dado

module ActionView
  class Base
    cattr_accessor :field_error_proc, default: Proc.new { ... }
  end
end

podemos acessar field_error_proc nas views.

A geração do método de leitura de instância pode ser prevenido configurando :instance_reader para false e a geração dos métodos de escrita de instância podem ser prevenidos configurando :instance_writer para false. A geração de ambos os métodos podem ser prevenidos configurando :instance_accessor para false. Em todos os casos, o valor deve ser exatamente false e não qualquer outro valor falso.

module A
  class B
    # Nenhuma leitura de instância first_name é gerada.
    cattr_accessor :first_name, instance_reader: false
    # Nenhuma escrita de instância last_name= é gerada.
    cattr_accessor :last_name, instance_writer: false
    # Nenhuma leitura surname ou escritor surname= de instância é gerada.
    cattr_accessor :surname, instance_accessor: false
  end
end

Pode ser útil configurar :instance_accessor para false no model como uma maneira de prevenir atribuições em massa ao definir o atributo.

Definido em active_support/core_ext/module/attribute_accessors.rb.

4.2 Subclasses e Descendentes

4.2.1 subclasses

O método subclasses retorna as subclasses do recebedor:

class C; end
C.subclasses # => []

class B < C; end
C.subclasses # => [B]

class A < B; end
C.subclasses # => [B]

class D < C; end
C.subclasses # => [B, D]

A ordem em que essas classes são retornadas não é especificada.

Definido em active_support/core_ext/class/subclasses.rb.

4.2.2 descendants

O método descendants retorna todas as classes que são < pelo recebedor:

class C; end
C.descendants # => []

class B < C; end
C.descendants # => [B]

class A < B; end
C.descendants # => [B, A]

class D < C; end
C.descendants # => [B, A, D]

A ordem em que essas classes são retornadas não é especificada.

Definido em active_support/core_ext/class/subclasses.rb.

5 Extensões para String

5.1 Segurança de saída

5.1.1 Motivação

Inserir dados em templates HTML, necessita de cuidados extras. Por exemplo, você não pode apenas literalmente juntar @review.title em uma página HTML. Por outro lado, se o título do comentário é "Flanagan & Matz rules!" o retorno não será bem formada porque um 'e comercial' precisa ser usado como "&amp;". Além do mais, dependendo da aplicação, isso pode ser uma grande falha de segurança porque os usuários podem injetar uma configuração HTML maliciosa em um título de revisão feito à mão. Confira a seção sobre cross-site scripting em Guia de Segurança para maiores informações sobre os riscos.

5.1.2 Strings Seguras

Active Support possui o conceito de (html) strings seguras. Uma string segura é aquela que é marcada como sendo inserível no HTML como é definida. Ela é confiável, não importando sua origem.

Strings são consideradas como inseguras por padrão:

"".html_safe? # => false

Pode-se obter uma string segura de um dado com o método html_safe:

s = "".html_safe
s.html_safe? # => true

É importante entender que html_safe não executa nenhuma operação, é apenas uma afirmação:

s = "<script>...</script>".html_safe
s.html_safe? # => true
s            # => "<script>...</script>"

É sua responsabilidade garantir a chamada html_safe em cada string particular.

Se você anexar em uma string segura, com concat/<<, ou com +, o resultado é uma string segura. Argumentos inseguros são ignorados:

"".html_safe + "<" # => "&lt;"

Argumentos seguros são anexados diretamente:

"".html_safe + "<".html_safe # => "<"

Esses métodos não devem ser usados em views comuns. Valores inseguros são ignorados automaticamente:

<%= @review.title %> <%# correto, ignora se necessário %>

Para inserir algo literal, use o helper raw ao invés de chamar html_safe:

<%= raw @cms.current_template %> <%# insere @cms.current_template como é %>

ou, equivalentemente, use <%==:

<%== @cms.current_template %> <%# insere @cms.current_template como é %>

O helper raw chama html_safe pra você:

def raw(stringish)
  stringish.to_s.html_safe
end

Definido em active_support/core_ext/string/output_safety.rb.

5.1.3 Transformação

De modo geral, exceto talvez para concatenação conforme explicado acima, qualquer método que possa alterar uma string fornece uma string insegura. Estes são downcase, gsub, strip, chomp, underscore, etc.

No caso de transformações locais, como com gsub! o próprio receptor se torna inseguro.

O bit de segurança é perdido sempre, não importa se a transformação realmente mudou algo.

5.1.4 Conversão e Coerção

Chamando to_s em uma string segura retorna uma string segura, mas a coerção com to_str retorna uma string insegura.

5.1.5 Copiando

Chamando dup ou clone em strings seguras produz outras strings seguras.

5.2 remove

O método remove vai remover todas ocorrências com o padrão:

"Hello World".remove(/Hello /) # => "World"

Há também a versão destrutiva String#remove!.

Definido em active_support/core_ext/string/filters.rb.

5.3 squish

O método squish remove os espaços em branco à esquerda e à direita e substitui os espaços em branco por um único espaço cada:

" \n  foo\n\r \t bar \n".squish # => "foo bar"

Há também a versão destrutiva String#squish!.

Observe que se lida com espaços em branco ASCII e Unicode.

Definido em active_support/core_ext/string/filters.rb.

5.4 truncate

O método truncate retorna uma cópia de seu receptor truncado após um determinado length:

"Oh dear! Oh dear! I shall be late!".truncate(20)
# => "Oh dear! Oh dear!..."

As reticências podem ser personalizadas com a opção :omission:

"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '&hellip;')
# => "Oh dear! Oh &hellip;"

Observe em particular que o truncamento leva em consideração o comprimento da string de omissão.

Passe o método :separator para truncar a string em uma pausa natural:

"Oh dear! Oh dear! I shall be late!".truncate(18)
# => "Oh dear! Oh dea..."
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: ' ')
# => "Oh dear! Oh..."

A opção :separator pode ser uma regexp:

"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => "Oh dear! Oh..."

Nos exemplos acima "dear" é cortado primeiro, mas depois :separator impede isso.

Definido em active_support/core_ext/string/filters.rb.

5.5 truncate_bytes

O método truncate_bytes retorna uma cópia de seu receptor truncado para no máximo bytesize bytes:

"👍👍👍👍".truncate_bytes(15)
# => "👍👍👍…"

As reticências podem ser personalizadas com a opção :omission:

"👍👍👍👍".truncate_bytes(15, omission: "🖖")
# => "👍👍🖖"

Definido em active_support/core_ext/string/filters.rb.

5.6 truncate_words

O método truncate_words retorna uma cópia da frase original truncada depois de receber um determinado número de palavras:

"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => "Oh dear! Oh dear!..."

Reticências podem ser customizadas com a opção :omission:

"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '&hellip;')
# => "Oh dear! Oh dear!&hellip;"

Chame :separator para truncar a string na pausa natural:

"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
# => "Oh dear! Oh dear! I shall be late..."

A opção :separator pode ser uma regexp:

"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => "Oh dear! Oh dear!..."

Definido em active_support/core_ext/string/filters.rb.

5.7 inquiry

O método inquiry converte uma string em um objeto StringInquirer fazendo verificações de igualdade mais elegantes.

"production".inquiry.production? # => true
"active".inquiry.inactive?       # => false

Definido em active_support/core_ext/string/inquiry.rb.

5.8 starts_with? e ends_with?

Active Support define conjugação verbal para 3ª pessoa em String#start_with? e String#end_with?:

"foo".starts_with?("f") # => true
"foo".ends_with?("o")   # => true

Definido em active_support/core_ext/string/starts_ends_with.rb.

5.9 strip_heredoc

O método strip_heredoc tira o recuo em heredocs.

Por exemplo em

if options[:usage]
  puts <<-USAGE.strip_heredoc
    This command does such and such.

    Supported options are:
      -h         This message
      ...
  USAGE
end

o usuário veria a mensagem de uso alinhada à margem esquerda.

Tecnicamente, se procura a linha menos indentada em toda a string e remove essa quantidade de espaço em branco à esquerda.

5.10 indent

O método indent indenta as linhas no receptor:

<<EOS.indent(2)
def some_method
  some_code
end
EOS
# =>
  def some_method
    some_code
  end

O segundo argumento, indent_string, especifica qual string de indentação usar. O padrão é nil, que diz ao método para fazer uma suposição conferindo a primeira linha indentada, e recuando para um espaço se não houver nenhuma.

"  foo".indent(2)        # => "    foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t")    # => "\t\tfoo"

Enquanto indent_string é normalmente um espaço ou tabulação, essa pode ser qualquer string.

O terceiro argumento, indent_empty_lines, é uma sinalização que diz se as linhas vazias devem ser indentadas. O padrão é false.

"foo\n\nbar".indent(2)            # => "  foo\n\n  bar"
"foo\n\nbar".indent(2, nil, true) # => "  foo\n  \n  bar"

O método indent! realiza recuo no local.

Definido em active_support/core_ext/string/indent.rb.

5.11 Acesso

5.11.1 at(position)

O método at retorna o caractere da string na posição position:

"hello".at(0)  # => "h"
"hello".at(4)  # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil

Definido em active_support/core_ext/string/access.rb.

5.11.2 from(position)

O método from retorna a substring da string iniciada na posição position:

"hello".from(0)  # => "hello"
"hello".from(2)  # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil

Definido em active_support/core_ext/string/access.rb.

5.11.3 to(position)

O método to retorna a substring da string até a posição position:

"hello".to(0)  # => "h"
"hello".to(2)  # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"

Definido em active_support/core_ext/string/access.rb.

5.11.4 first(limit = 1)

O método first retorna a substring contendo os primeiros limit caracteres da string.

A chamada str.first(n) é equivalente a str.to(n-1) se n > 0, e retorna uma string vazia para n == 0.

Definido em active_support/core_ext/string/access.rb.

5.11.5 last(limit = 1)

O método last retorna a substring contendo os últimos limit caracteres da string.

A chamada str.last(n) é equivalente a str.from(-n) se n > 0, e retorna uma string vazia para n == 0.

Definido em active_support/core_ext/string/access.rb.

5.12 Inflexões

5.12.1 pluralize

O método pluralize retorna o plural do receptor:

"table".pluralize     # => "tables"
"ruby".pluralize      # => "rubies"
"equipment".pluralize # => "equipment"

Como mostra o exemplo anterior, Active Support conhece alguns plurais irregulares e substantivos incontáveis. As regras integradas podem ser estendidas em config/initializers/inflections.rb. Este arquivo é gerado por padrão, pelo comando rails new e tem instruções nos comentários.

pluralize também pode fazer um parâmetro count opcional. Se count == 1 a forma singular será retornada. Para qualquer outro valor de count a forma plural será retornada:

"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"

Active Record usa esse método pra computar a o nome da tabela padrão correspondente ao model:

# active_record/model_schema.rb
def undecorated_table_name(class_name = base_class.name)
  table_name = class_name.to_s.demodulize.underscore
  pluralize_table_names ? table_name.pluralize : table_name
end

Definido active_support/core_ext/string/inflections.rb.

5.12.2 singularize

O método singularize é o inverso do pluralize:

"tables".singularize    # => "table"
"rubies".singularize    # => "ruby"
"equipment".singularize # => "equipment"

As associações calculam o nome padrão da classe associada correspondente usando este método:

# active_record/reflection.rb
def derive_class_name
  class_name = name.to_s.camelize
  class_name = class_name.singularize if collection?
  class_name
end

Definido em active_support/core_ext/string/inflections.rb.

5.12.3 camelize

O método camelize retorna o receptor em camel case:

"product".camelize    # => "Product"
"admin_user".camelize # => "AdminUser"

Como regra geral, você pode pensar neste método como aquele que transforma pastas em nomes de classes ou módulos Ruby, em que barras separam os subarquivos:

"backoffice/session".camelize # => "Backoffice::Session"

Por exemplo, o Action Pack usa este método para carregar a classe que fornece um determinado armazenamento de sessão:

# action_controller/metal/session_management.rb
def session_store=(store)
  @@session_store = store.is_a?(Symbol) ?
    ActionDispatch::Session.const_get(store.to_s.camelize) :
    store
end

camelize aceita um argumento opcional, que pode ser :upper (padrão), ou :lower. Com o último, a primeira letra torna-se minúscula:

"visual_effect".camelize(:lower) # => "visualEffect"

Isso pode ser útil para calcular nomes de métodos em uma linguagem que segue essa convenção, por exemplo, JavaScript.

Como regra geral, você pode pensar em camelize como o inverso do underscore, embora haja casos em que isso não se aplica: "SSLError".underscore.camelize devolve "SslError". Para apoiar casos como este, o Active Support permite que você especifique acrônimos em config/initializers/inflections.rb:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym 'SSL'
end

"SSLError".underscore.camelize # => "SSLError"

camelize é o mesmo que usar camelcase.

Definido em active_support/core_ext/string/inflections.rb.

5.12.4 underscore

O método underscore vai ao contrário, de camel case para pastas:

"Product".underscore   # => "product"
"AdminUser".underscore # => "admin_user"

Também converte "::" back para "/":

"Backoffice::Session".underscore # => "backoffice/session"

e entende strings que começam com start letra minúscula:

"visualEffect".underscore # => "visual_effect"

underscore não aceita nenhum argumento.

Classes e módulos Rails carregam automaticamente o uso de underscore para inferir o endereço relativo sem extensão de um arquivo que definiria uma constante ausente:

# active_support/dependencies.rb
def load_missing_constant(from_mod, const_name)
  # ...
  qualified_name = qualified_name_for from_mod, const_name
  path_suffix = qualified_name.underscore
  # ...
end

Como regra geral, pode-se pensar em underscore como o inverso de camelize, embora haja casos em que isso não se aplica. Por exemplo, "SSLError".underscore.camelize devolve "SslError".

Definido em active_support/core_ext/string/inflections.rb.

5.12.5 titleize

O método titleize coloca cada palavra com letra maiúscula:

"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize     # => "Fermat's Enigma"

titleize é o mesmo que usar titlecase.

Definido em active_support/core_ext/string/inflections.rb.

5.12.6 dasherize

O método dasherize troca underscores no receptor por traços:

"name".dasherize         # => "name"
"contact_data".dasherize # => "contact-data"

O serializer XML de models usa este método para colocar traços nos nomes de seus nós:

# active_model/serializers/xml.rb
def reformat_name(name)
  name = name.camelize if camelize?
  dasherize? ? name.dasherize : name
end

Definido em active_support/core_ext/string/inflections.rb.

5.12.7 demodulize

Dado uma string com um nome de constante em módulo, demodulize retorna o real nome da constante, ou seja, a parte mais à direita dela:

"Product".demodulize                        # => "Product"
"Backoffice::UsersController".demodulize    # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize                  # => "Inflections"
"".demodulize                               # => ""

Active Record por exemplo, usa este método para calcular o nome de uma coluna de cache do contador:

# active_record/reflection.rb
def counter_cache_column
  if options[:counter_cache] == true
    "#{active_record.name.demodulize.underscore.pluralize}_count"
  elsif options[:counter_cache]
    options[:counter_cache]
  end
end

Definido em active_support/core_ext/string/inflections.rb.

5.12.8 deconstantize

Dada uma string com uma expressão de referência a uma constante, deconstantize remove o segmento mais à direita, geralmente deixando o nome do contêiner da constante:

"Product".deconstantize                        # => ""
"Backoffice::UsersController".deconstantize    # => "Backoffice"
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"

Definido em active_support/core_ext/string/inflections.rb.

5.12.9 parameterize

O método parameterize normaliza seu receptor de uma forma que pode ser usada em URLs de forma mais elegante.

"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"

Para preservar a caixa da string, defina o argumento preserve_case para true. Por padrão, preserve_case será configurado como false.

"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"

Para usar um separador customizado, sobrescreva o argumento separator.

"John Smith".parameterize(separator: "_") # => "john\_smith"
"Kurt Gödel".parameterize(separator: "_") # => "kurt\_godel"

Definido em active_support/core_ext/string/inflections.rb.

5.12.10 tableize

O método tableize é underscore seguido por pluralize.

"Person".tableize      # => "people"
"Invoice".tableize     # => "invoices"
"InvoiceLine".tableize # => "invoice_lines"

Como um princípio básico, tableize retorna o nome da tabela que corresponde a um determinado modelo para casos simples. A implementação real de tableize no Active Record na verdade não é direta, porque também desmodulariza o nome da classe e verifica algumas opções que podem afetar a string retornada.

Definido em active_support/core_ext/string/inflections.rb.

5.12.11 classify

O método classify é o inverso de tableize. Ele da o nome da classe correspondente ao nome da tabela:

"people".classify        # => "Person"
"invoices".classify      # => "Invoice"
"invoice_lines".classify # => "InvoiceLine"

O método compreende nomes de tabela associadas:

"highrise_production.companies".classify # => "Company"

Note que classify retorna um nome de classe como uma string. Você pode obter o objeto de classe real invocando constantize sobre isso, será explicado a seguir.

Definido em active_support/core_ext/string/inflections.rb.

5.12.12 constantize

O método constantize resolve a expressão de referência constante em seu receptor:

"Integer".constantize # => Integer

module M
  X = 1
end
"M::X".constantize # => 1

Se a string não for avaliada como uma constante conhecida ou seu conteúdo nem mesmo for um nome de constante válido, constantize executa NameError.

Resolução de nome constante por constantize inicia sempre no nível superior de Object mesmo se não começar com "::".

X = :in_Object
module M
  X = :in_M

  X                 # => :in_M
  "::X".constantize # => :in_Object
  "X".constantize   # => :in_Object (!)
end

So, it is in general not equivalent to what Ruby would do in the same spot, had a real constant be evaluated.

Mailer test cases obtain the mailer being tested from the name of the test class using constantize:

# action_mailer/test_case.rb
def determine_default_mailer(name)
  name.delete_suffix("Test").constantize
rescue NameError => e
  raise NonInferrableMailerError.new(name)
end

Definido em active_support/core_ext/string/inflections.rb.

5.12.13 humanize

O método humanize ajusta um nome de atributo para exibir aos usuários.

Especificamente, ele realiza estas transformações:

  • Aplica regras de inflexão humana ao argumento.
  • Exclui os underlines iniciais, se houver.
  • Remove um sufixo "_id", se houver.
  • Substitui underlines por espaços, se houver.
  • Reduz todas as palavras, exceto siglas.
  • Coloca em maiúscula a primeira palavra.

A capitalização da primeira palavra pode ser desativada configurando o :capitalize opção para false (o padrão é true).

"name".humanize                         # => "Name"
"author_id".humanize                    # => "Author"
"author_id".humanize(capitalize: false) # => "author"
"comments_count".humanize               # => "Comments count"
"_id".humanize                          # => "Id"

Se "SSL" for definido como uma sigla:

'ssl_error'.humanize # => "SSL error"

O método helper full_messages usa humanize como alternativa para incluir nomes de atributos:

def full_messages
  map { |attribute, message| full_message(attribute, message) }
end

def full_message
  # ...
  attr_name = attribute.to_s.tr('.', '_').humanize
  attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
  # ...
end

Definido em active_support/core_ext/string/inflections.rb.

5.12.14 foreign_key

O método foreign_key fornece um nome de coluna de chave estrangeira a partir de um nome de classe. Para fazer isso, ele desmoduliza, separa com underline e adiciona "_id":

"User".foreign_key           # => "user_id"
"InvoiceLine".foreign_key    # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"

Passe false como argumento se você não quiser o underline em "_id":

"User".foreign_key(false) # => "userid"

As associações usam este método para inferir chaves estrangeiras, por exemplo has_one e has_many fazem isto:

# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key

Definido em active_support/core_ext/string/inflections.rb.

5.13 Conversões

5.13.1 to_date, to_time, to_datetime

Os métodos to_date, to_time, e to_datetime são basicamente variações convenientes de Date._parse:

"2010-07-27".to_date              # => Tue, 27 Jul 2010
"2010-07-27 23:37:00".to_time     # => 2010-07-27 23:37:00 +0200
"2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000

to_time recebe an um argumento opcional :utc ou :local, para indicar qual fuso horário you quer se basear:

"2010-07-27 23:42:00".to_time(:utc)   # => 2010-07-27 23:42:00 UTC
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200

O padrão é :local.

Por favor, consulte a documentação de Date._parse para mais detalhes.

Os três exemplos retornam nil caso não recebam argumentos.

Definido em active_support/core_ext/string/conversions.rb.

6 Extensões para Symbol

6.1 starts_with? e ends_with?

O Active Support define aliases (nomes simbólicos) de terceira pessoa de Symbol#start_with? e Symbol#end_with?:

:foo.starts_with?("f") # => true
:foo.ends_with?("o")   # => true

Definido em active_support/core_ext/symbol/starts_ends_with.rb.

7 Extensões para Numeric

7.1 Bytes

Todos os números respondem a estes métodos:

Eles retornam a quantidade correspondente de bytes, usando um fator de conversão de 1024:

2.kilobytes   # => 2048
3.megabytes   # => 3145728
3.5.gigabytes # => 3758096384
-4.exabytes   # => -4611686018427387904

As formas singulares têm um alias para que você possa dizer:

1.megabyte # => 1048576

Definido em active_support/core_ext/numeric/bytes.rb.

7.2 Time

Os seguintes métodos:

habilitar declarações e cálculos de tempo, como 45.minutes + 2.hours + 4.weeks. Seus valores de retorno também podem ser adicionados ou subtraídos dos objetos Time.

Esses métodos podem ser combinados com from_now, ago, etc, para cálculos de datas precisos. Por exemplo:

# equivalent to Time.current.advance(days: 1)
1.day.from_now

# equivalent to Time.current.advance(weeks: 2)
2.weeks.from_now

# equivalent to Time.current.advance(days: 4, weeks: 5)
(4.days + 5.weeks).from_now

Para outras durações, consulte as extensões de tempo para Integer.

Definido em active_support/core_ext/numeric/time.rb.

7.3 Formatação

Permite a formatação de números de várias maneiras.

Produz uma representação de string de um número como um número de telefone:

5551234.to_s(:phone)
# => 555-1234
1235551234.to_s(:phone)
# => 123-555-1234
1235551234.to_s(:phone, area_code: true)
# => (123) 555-1234
1235551234.to_s(:phone, delimiter: " ")
# => 123 555 1234
1235551234.to_s(:phone, area_code: true, extension: 555)
# => (123) 555-1234 x 555
1235551234.to_s(:phone, country_code: 1)
# => +1-123-555-1234

Produz uma representação de string de um número como moeda:

1234567890.50.to_s(:currency)                 # => $1,234,567,890.50
1234567890.506.to_s(:currency)                # => $1,234,567,890.51
1234567890.506.to_s(:currency, precision: 3)  # => $1,234,567,890.506

Produz uma representação de string de um número como uma porcentagem:

100.to_s(:percentage)
# => 100.000%
100.to_s(:percentage, precision: 0)
# => 100%
1000.to_s(:percentage, delimiter: '.', separator: ',')
# => 1.000,000%
302.24398923423.to_s(:percentage, precision: 5)
# => 302.24399%

Produz uma representação de string de um número na forma delimitada:

12345678.to_s(:delimited)                     # => 12,345,678
12345678.05.to_s(:delimited)                  # => 12,345,678.05
12345678.to_s(:delimited, delimiter: ".")     # => 12.345.678
12345678.to_s(:delimited, delimiter: ",")     # => 12,345,678
12345678.05.to_s(:delimited, separator: " ")  # => 12,345,678 05

Produz uma representação de string de um número arredondado para uma precisão:

111.2345.to_s(:rounded)                     # => 111.235
111.2345.to_s(:rounded, precision: 2)       # => 111.23
13.to_s(:rounded, precision: 5)             # => 13.00000
389.32314.to_s(:rounded, precision: 0)      # => 389
111.2345.to_s(:rounded, significant: true)  # => 111

Produz uma representação de string de um número como um número de bytes legível para humanos:

123.to_s(:human_size)                  # => 123 Bytes
1234.to_s(:human_size)                 # => 1.21 KB
12345.to_s(:human_size)                # => 12.1 KB
1234567.to_s(:human_size)              # => 1.18 MB
1234567890.to_s(:human_size)           # => 1.15 GB
1234567890123.to_s(:human_size)        # => 1.12 TB
1234567890123456.to_s(:human_size)     # => 1.1 PB
1234567890123456789.to_s(:human_size)  # => 1.07 EB

Produz uma representação de string de um número em palavras legíveis para humanos:

123.to_s(:human)               # => "123"
1234.to_s(:human)              # => "1.23 Thousand"
12345.to_s(:human)             # => "12.3 Thousand"
1234567.to_s(:human)           # => "1.23 Million"
1234567890.to_s(:human)        # => "1.23 Billion"
1234567890123.to_s(:human)     # => "1.23 Trillion"
1234567890123456.to_s(:human)  # => "1.23 Quadrillion"

Definido em active_support/core_ext/numeric/conversions.rb.

8 Extensões para Integer

8.1 multiple_of?

O método multiple_of? testa se um inteiro é múltiplo do argumento:

2.multiple_of?(1) # => true
1.multiple_of?(2) # => false

Definido em active_support/core_ext/integer/multiple.rb.

8.2 ordinal

O método ordinal retorna a string de sufixo ordinal correspondente ao inteiro receptor:

1.ordinal    # => "st"
2.ordinal    # => "nd"
53.ordinal   # => "rd"
2009.ordinal # => "th"
-21.ordinal  # => "st"
-134.ordinal # => "th"

Definido em active_support/core_ext/integer/inflections.rb.

8.3 ordinalize

O método ordinalize retorna a string ordinal correspondente ao inteiro receptor. Em comparação, observe que o método ordinal retorna apenas a string de sufixo.

1.ordinalize    # => "1st"
2.ordinalize    # => "2nd"
53.ordinalize   # => "53rd"
2009.ordinalize # => "2009th"
-21.ordinalize  # => "-21st"
-134.ordinalize # => "-134th"

Definido em active_support/core_ext/integer/inflections.rb.

8.4 Time

Os seguintes métodos:

habilitar declarações de tempo e cálculos, como 4.months + 5.years. Seus valores de retorno também podem ser adicionados ou subtraídos dos objetos Time.

Esses métodos podem ser combinados com from_now, ago, etc, para cálculos de data precisos. Por exemplo:

# equivalente ao Time.current.advance(months: 1)
1.month.from_now

# eequivalente ao Time.current.advance(years: 2)
2.years.from_now

# equivalente ao Time.current.advance(months: 4, years: 5)
(4.months + 5.years).from_now

Para outras durações, consulte as extensões de tempo para Numeric.

Definido em active_support/core_ext/integer/time.rb.

9 Extensões para BigDecimal

9.1 to_s

O método to_s fornece um especificador padrão de "F". Isso significa que uma simples chamada para to_s resultará em representação de ponto flutuante em vez de notação de engenharia:

BigDecimal(5.00, 6).to_s       # => "5.0"

e que especificadores usando symbols também são suportados:

BigDecimal(5.00, 6).to_s(:db)  # => "5.0"

A notação de engenharia ainda é suportada:

BigDecimal(5.00, 6).to_s("e")  # => "0.5E1"

10 Extensions to Enumerable

10.1 sum

The method sum adds the elements of an enumerable:

[1, 2, 3].sum # => 6
(1..100).sum  # => 5050

Addition only assumes the elements respond to +:

[[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]

The sum of an empty collection is zero by default, but this is customizable:

[].sum    # => 0
[].sum(1) # => 1

If a block is given, sum becomes an iterator that yields the elements of the collection and sums the returned values:

(1..5).sum {|n| n * 2 } # => 30
[2, 4, 6, 8, 10].sum    # => 30

The sum of an empty receiver can be customized in this form as well:

[].sum(1) {|n| n**3} # => 1

10.2 index_by

The method index_by generates a hash with the elements of an enumerable indexed by some key.

It iterates through the collection and passes each element to a block. The element will be keyed by the value returned by the block:

invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}

Keys should normally be unique. If the block returns the same value for different elements no collection is built for that key. The last item will win.

10.3 index_with

The method index_with generates a hash with the elements of an enumerable as keys. The value is either a passed default or returned in a block.

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 ], … }

10.4 many?

The method many? is shorthand for collection.size > 1:

<% if pages.many? %>
  <%= pagination_links %>
<% end %>

If an optional block is given, many? only takes into account those elements that return true:

@see_more = videos.many? {|video| video.category == params[:category]}

10.5 exclude?

The predicate exclude? tests whether a given object does not belong to the collection. It is the negation of the built-in include?:

to_visit << node if visited.exclude?(node)

10.6 including

The method including returns a new enumerable that includes the passed elements:

[ 1, 2, 3 ].including(4, 5)                    # => [ 1, 2, 3, 4, 5 ]
["David", "Rafael"].including %w[ Aaron Todd ] # => ["David", "Rafael", "Aaron", "Todd"]

10.7 excluding

The method excluding returns a copy of an enumerable with the specified elements removed:

["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]

excluding is aliased to without.

10.8 pluck

The method pluck extracts the given key from each element:

[{ 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"]]

10.9 pick

The method pick extracts the given key from the first element:

[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) # => "David"
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) # => [1, "David"]

11 Extensions to Array

11.1 Accessing

Active Support augments the API of arrays to ease certain ways of accessing them. For example, to returns the subarray of elements up to the one at the passed index:

%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7)          # => []

Similarly, from returns the tail from the element at the passed index to the end. If the index is greater than the length of the array, it returns an empty array.

%w(a b c d).from(2)  # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0)           # => []

The method including returns a new array that includes the passed elements:

[ 1, 2, 3 ].including(4, 5)          # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]

The method excluding returns a copy of the Array excluding the specified elements. This is an optimization of Enumerable#excluding that uses Array#- instead of Array#reject for performance reasons.

["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ])                  # => [ [ 0, 1 ] ]

The methods second, third, fourth, and fifth return the corresponding element, as do second_to_last and third_to_last (first and last are built-in). Thanks to social wisdom and positive constructiveness all around, forty_two is also available.

%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil

11.2 Extracting

The method extract! removes and returns the elements for which the block returns a true value. If no block is given, an Enumerator is returned instead.

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]

11.3 Options Extraction

When the last argument in a method call is a hash, except perhaps for a &block argument, Ruby allows you to omit the brackets:

User.exists?(email: params[:email])

That syntactic sugar is used a lot in Rails to avoid positional arguments where there would be too many, offering instead interfaces that emulate named parameters. In particular it is very idiomatic to use a trailing hash for options.

If a method expects a variable number of arguments and uses * in its declaration, however, such an options hash ends up being an item of the array of arguments, where it loses its role.

In those cases, you may give an options hash a distinguished treatment with extract_options!. This method checks the type of the last item of an array. If it is a hash it pops it and returns it, otherwise it returns an empty hash.

Let's see for example the definition of the caches_action controller macro:

def caches_action(*actions)
  return unless cache_configured?
  options = actions.extract_options!
  # ...
end

This method receives an arbitrary number of action names, and an optional hash of options as last argument. With the call to extract_options! you obtain the options hash and remove it from actions in a simple and explicit way.

11.4 Conversions

11.4.1 to_sentence

The method to_sentence turns an array into a string containing a sentence that enumerates its items:

%w().to_sentence                # => ""
%w(Earth).to_sentence           # => "Earth"
%w(Earth Wind).to_sentence      # => "Earth and Wind"
%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"

This method accepts three options:

  • :two_words_connector: What is used for arrays of length 2. Default is " and ".
  • :words_connector: What is used to join the elements of arrays with 3 or more elements, except for the last two. Default is ", ".
  • :last_word_connector: What is used to join the last items of an array with 3 or more elements. Default is ", and ".

The defaults for these options can be localized, their keys are:

Option I18n key
:two_words_connector support.array.two_words_connector
:words_connector support.array.words_connector
:last_word_connector support.array.last_word_connector
11.4.2 to_formatted_s

The method to_formatted_s acts like to_s by default.

If the array contains items that respond to id, however, the symbol :db may be passed as argument. That's typically used with collections of Active Record objects. Returned strings are:

[].to_formatted_s(:db)            # => "null"
[user].to_formatted_s(:db)        # => "8456"
invoice.lines.to_formatted_s(:db) # => "23,567,556,12"

Integers in the example above are supposed to come from the respective calls to id.

11.4.3 to_xml

The method to_xml returns a string containing an XML representation of its receiver:

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>

To do so it sends to_xml to every item in turn, and collects the results under a root node. All items must respond to to_xml, an exception is raised otherwise.

By default, the name of the root element is the underscored and dasherized plural of the name of the class of the first item, provided the rest of elements belong to that type (checked with is_a?) and they are not hashes. In the example above that's "contributors".

If there's any element that does not belong to the type of the first one the root node becomes "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>

If the receiver is an array of hashes the root element is by default also "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>

If the collection is empty the root element is by default "nil-classes". That's a gotcha, for example the root element of the list of contributors above would not be "contributors" if the collection was empty, but "nil-classes". You may use the :root option to ensure a consistent root element.

The name of children nodes is by default the name of the root node singularized. In the examples above we've seen "contributor" and "object". The option :children allows you to set these node names.

The default XML builder is a fresh instance of Builder::XmlMarkup. You can configure your own builder via the :builder option. The method also accepts options like :dasherize and friends, they are forwarded to the builder:

Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors>
#   <contributor>
#     <id>4356</id>
#     <name>Jeremy Kemper</name>
#     <rank>1</rank>
#     <url-id>jeremy-kemper</url-id>
#   </contributor>
#   <contributor>
#     <id>4404</id>
#     <name>David Heinemeier Hansson</name>
#     <rank>2</rank>
#     <url-id>david-heinemeier-hansson</url-id>
#   </contributor>
# </contributors>

11.5 Wrapping

The method Array.wrap wraps its argument in an array unless it is already an array (or array-like).

Specifically:

  • If the argument is nil an empty array is returned.
  • Otherwise, if the argument responds to to_ary it is invoked, and if the value of to_ary is not nil, it is returned.
  • Otherwise, an array with the argument as its single element is returned.
Array.wrap(nil)       # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0)         # => [0]

This method is similar in purpose to Kernel#Array, but there are some differences:

  • If the argument responds to to_ary the method is invoked. Kernel#Array moves on to try to_a if the returned value is nil, but Array.wrap returns an array with the argument as its single element right away.
  • If the returned value from to_ary is neither nil nor an Array object, Kernel#Array raises an exception, while Array.wrap does not, it just returns the value.
  • It does not call to_a on the argument, if the argument does not respond to to_ary it returns an array with the argument as its single element.

The last point is particularly worth comparing for some enumerables:

Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar)      # => [[:foo, :bar]]

There's also a related idiom that uses the splat operator:

[*object]

11.6 Duplicating

The method Array#deep_dup duplicates itself and all objects inside recursively with the Active Support method Object#deep_dup. It works like Array#map, sending deep_dup method to each object inside.

array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil   # => true

11.7 Grouping

11.7.1 in_groups_of(number, fill_with = nil)

The method in_groups_of splits an array into consecutive groups of a certain size. It returns an array with the groups:

[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]

or yields them in turn if a block is passed:

<% sample.in_groups_of(3) do |a, b, c| %>
  <tr>
    <td><%= a %></td>
    <td><%= b %></td>
    <td><%= c %></td>
  </tr>
<% end %>

The first example shows how in_groups_of fills the last group with as many nil elements as needed to have the requested size. You can change this padding value using the second optional argument:

[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]

And you can tell the method not to fill the last group by passing false:

[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]

As a consequence false can't be used as a padding value.

11.7.2 in_groups(number, fill_with = nil)

The method in_groups splits an array into a certain number of groups. The method returns an array with the groups:

%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]

or yields them in turn if a block is passed:

%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]

The examples above show that in_groups fills some groups with a trailing nil element as needed. A group can get at most one of these extra elements, the rightmost one if any. And the groups that have them are always the last ones.

You can change this padding value using the second optional argument:

%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]

And you can tell the method not to fill the smaller groups by passing false:

%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]

As a consequence false can't be used as a padding value.

11.7.3 split(value = nil)

The method split divides an array by a separator and returns the resulting chunks.

If a block is passed the separators are those elements of the array for which the block returns true:

(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]

Otherwise, the value received as argument, which defaults to nil, is the separator:

[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]

Observe in the previous example that consecutive separators result in empty arrays.

12 Extensions to Hash

12.1 Conversions

12.1.1 to_xml

The method to_xml returns a string containing an XML representation of its receiver:

{"foo" => 1, "bar" => 2}.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
#   <foo type="integer">1</foo>
#   <bar type="integer">2</bar>
# </hash>

To do so, the method loops over the pairs and builds nodes that depend on the values. Given a pair key, value:

  • If value is a hash there's a recursive call with key as :root.

  • If value is an array there's a recursive call with key as :root, and key singularized as :children.

  • If value is a callable object it must expect one or two arguments. Depending on the arity, the callable is invoked with the options hash as first argument with key as :root, and key singularized as second argument. Its return value becomes a new node.

  • If value responds to to_xml the method is invoked with key as :root.

  • Otherwise, a node with key as tag is created with a string representation of value as text node. If value is nil an attribute "nil" set to "true" is added. Unless the option :skip_types exists and is true, an attribute "type" is added as well according to the following mapping:

XML_TYPE_NAMES = {
  "Symbol"     => "symbol",
  "Integer"    => "integer",
  "BigDecimal" => "decimal",
  "Float"      => "float",
  "TrueClass"  => "boolean",
  "FalseClass" => "boolean",
  "Date"       => "date",
  "DateTime"   => "datetime",
  "Time"       => "datetime"
}

By default the root node is "hash", but that's configurable via the :root option.

The default XML builder is a fresh instance of Builder::XmlMarkup. You can configure your own builder with the :builder option. The method also accepts options like :dasherize and friends, they are forwarded to the builder.

12.2 Merging

Ruby has a built-in method Hash#merge that merges two hashes:

{a: 1, b: 1}.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}

Active Support defines a few more ways of merging hashes that may be convenient.

12.2.1 reverse_merge and reverse_merge!

In case of collision the key in the hash of the argument wins in merge. You can support option hashes with default values in a compact way with this idiom:

options = {length: 30, omission: "..."}.merge(options)

Active Support defines reverse_merge in case you prefer this alternative notation:

options = options.reverse_merge(length: 30, omission: "...")

And a bang version reverse_merge! that performs the merge in place:

options.reverse_merge!(length: 30, omission: "...")

Take into account that reverse_merge! may change the hash in the caller, which may or may not be a good idea.

12.2.2 reverse_update

The method reverse_update is an alias for reverse_merge!, explained above.

Note that reverse_update has no bang.

12.2.3 deep_merge and deep_merge!

As you can see in the previous example if a key is found in both hashes the value in the one in the argument wins.

Active Support defines Hash#deep_merge. In a deep merge, if a key is found in both hashes and their values are hashes in turn, then their merge becomes the value in the resulting hash:

{a: {b: 1}}.deep_merge(a: {c: 2})
# => {:a=>{:b=>1, :c=>2}}

The method deep_merge! performs a deep merge in place.

12.3 Deep duplicating

The method Hash#deep_dup duplicates itself and all keys and values inside recursively with Active Support method Object#deep_dup. It works like Enumerator#each_with_object with sending deep_dup method to each pair inside.

hash = { a: 1, b: { c: 2, d: [3, 4] } }

dup = hash.deep_dup
dup[:b][:e] = 5
dup[:b][:d] << 5

hash[:b][:e] == nil      # => true
hash[:b][:d] == [3, 4]   # => true

12.4 Working with Keys

12.4.1 except and except!

The method except returns a hash with the keys in the argument list removed, if present:

{a: 1, b: 2}.except(:a) # => {:b=>2}

If the receiver responds to convert_key, the method is called on each of the arguments. This allows except to play nice with hashes with indifferent access for instance:

{a: 1}.with_indifferent_access.except(:a)  # => {}
{a: 1}.with_indifferent_access.except("a") # => {}

There's also the bang variant except! that removes keys in place.

12.4.2 stringify_keys and stringify_keys!

The method stringify_keys returns a hash that has a stringified version of the keys in the receiver. It does so by sending to_s to them:

{nil => nil, 1 => 1, a: :a}.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}

In case of key collision, the value will be the one most recently inserted into the hash:

{"a" => 1, a: 2}.stringify_keys
# The result will be
# => {"a"=>2}

This method may be useful for example to easily accept both symbols and strings as options. For instance ActionView::Helpers::FormHelper defines:

def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
  options = options.stringify_keys
  options["type"] = "checkbox"
  # ...
end

The second line can safely access the "type" key, and let the user to pass either :type or "type".

There's also the bang variant stringify_keys! that stringifies keys in place.

Besides that, one can use deep_stringify_keys and deep_stringify_keys! to stringify all the keys in the given hash and all the hashes nested in it. An example of the result is:

{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}
12.4.3 symbolize_keys and symbolize_keys!

The method symbolize_keys returns a hash that has a symbolized version of the keys in the receiver, where possible. It does so by sending to_sym to them:

{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}

Note in the previous example only one key was symbolized.

In case of key collision, the value will be the one most recently inserted into the hash:

{"a" => 1, a: 2}.symbolize_keys
# => {:a=>2}

This method may be useful for example to easily accept both symbols and strings as options. For instance ActionText::TagHelper defines

def rich_text_area_tag(name, value = nil, options = {})
  options = options.symbolize_keys

  options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
  # ...
end

The third line can safely access the :input key, and let the user to pass either :input or "input".

There's also the bang variant symbolize_keys! that symbolizes keys in place.

Besides that, one can use deep_symbolize_keys and deep_symbolize_keys! to symbolize all the keys in the given hash and all the hashes nested in it. An example of the result is:

{nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
12.4.4 to_options and to_options!

The methods to_options and to_options! are aliases of symbolize_keys and symbolize_keys!, respectively.

12.4.5 assert_valid_keys

The method assert_valid_keys receives an arbitrary number of arguments, and checks whether the receiver has any key outside that list. If it does ArgumentError is raised.

{a: 1}.assert_valid_keys(:a)  # passes
{a: 1}.assert_valid_keys("a") # ArgumentError

Active Record does not accept unknown options when building associations, for example. It implements that control via assert_valid_keys.

12.5 Working with Values

12.5.1 deep_transform_values and deep_transform_values!

The method deep_transform_values returns a new hash with all values converted by the block operation. This includes the values from the root hash and from all nested hashes and arrays.

hash = { person: { name: 'Rob', age: '28' } }

hash.deep_transform_values{ |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}

There's also the bang variant deep_transform_values! that destructively converts all values by using the block operation.

12.6 Slicing

The method slice! replaces the hash with only the given keys and returns a hash containing the removed key/value pairs.

hash = {a: 1, b: 2}
rest = hash.slice!(:a) # => {:b=>2}
hash                   # => {:a=>1}

12.7 Extracting

The method extract! removes and returns the key/value pairs matching the given keys.

hash = {a: 1, b: 2}
rest = hash.extract!(:a) # => {:a=>1}
hash                     # => {:b=>2}

The method extract! returns the same subclass of Hash that the receiver is.

hash = {a: 1, b: 2}.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess

12.8 Indifferent Access

The method with_indifferent_access returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:

{a: 1}.with_indifferent_access["a"] # => 1

13 Extensions to Regexp

13.1 multiline?

The method multiline? says whether a regexp has the /m flag set, that is, whether the dot matches newlines.

%r{.}.multiline?  # => false
%r{.}m.multiline? # => true

Regexp.new('.').multiline?                    # => false
Regexp.new('.', Regexp::MULTILINE).multiline? # => true

Rails uses this method in a single place, also in the routing code. Multiline regexps are disallowed for route requirements and this flag eases enforcing that constraint.

def verify_regexp_requirements(requirements)
  # ...
  if requirement.multiline?
    raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
  end
  # ...
end

14 Extensions to Range

14.1 to_s

Active Support extends the method Range#to_s so that it understands an optional format argument. As of this writing the only supported non-default format is :db:

(Date.today..Date.tomorrow).to_s
# => "2009-10-25..2009-10-26"

(Date.today..Date.tomorrow).to_s(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"

As the example depicts, the :db format generates a BETWEEN SQL clause. That is used by Active Record in its support for range values in conditions.

14.2 ===, include?, and cover?

The methods Range#===, Range#include?, and Range#cover? say whether some value falls between the ends of a given instance:

(2..3).include?(Math::E) # => true

Active Support extends these methods so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves:

(1..10) === (3..7)  # => true
(1..10) === (0..7)  # => false
(1..10) === (3..11) # => false
(1...9) === (3..9)  # => false

(1..10).include?(3..7)  # => true
(1..10).include?(0..7)  # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9)  # => false

(1..10).cover?(3..7)  # => true
(1..10).cover?(0..7)  # => false
(1..10).cover?(3..11) # => false
(1...9).cover?(3..9)  # => false

14.3 overlaps?

The method Range#overlaps? says whether any two given ranges have non-void intersection:

(1..10).overlaps?(7..11)  # => true
(1..10).overlaps?(0..7)   # => true
(1..10).overlaps?(11..27) # => false

15 Extensions to Date

15.1 Calculations

The following calculation methods have edge cases in October 1582, since days 5..14 just do not exist. This guide does not document their behavior around those days for brevity, but it is enough to say that they do what you would expect. That is, Date.new(1582, 10, 4).tomorrow returns Date.new(1582, 10, 15) and so on. Please check test/core_ext/date_ext_test.rb in the Active Support test suite for expected behavior.

15.1.1 Date.current

Active Support defines Date.current to be today in the current time zone. That's like Date.today, except that it honors the user time zone, if defined. It also defines Date.yesterday and Date.tomorrow, and the instance predicates past?, today?, tomorrow?, next_day?, yesterday?, prev_day?, future?, on_weekday? and on_weekend?, all of them relative to Date.current.

When making Date comparisons using methods which honor the user time zone, make sure to use Date.current and not Date.today. There are cases where the user time zone might be in the future compared to the system time zone, which Date.today uses by default. This means Date.today may equal Date.yesterday.

15.1.2 Named dates
15.1.2.1 beginning_of_week, end_of_week

The methods beginning_of_week and end_of_week return the dates for the beginning and end of the week, respectively. Weeks are assumed to start on Monday, but that can be changed passing an argument, setting thread local Date.beginning_of_week or config.beginning_of_week.

d = Date.new(2010, 5, 8)     # => Sat, 08 May 2010
d.beginning_of_week          # => Mon, 03 May 2010
d.beginning_of_week(:sunday) # => Sun, 02 May 2010
d.end_of_week                # => Sun, 09 May 2010
d.end_of_week(:sunday)       # => Sat, 08 May 2010

beginning_of_week is aliased to at_beginning_of_week and end_of_week is aliased to at_end_of_week.

15.1.2.2 monday, sunday

The methods monday and sunday return the dates for the previous Monday and next Sunday, respectively.

d = Date.new(2010, 5, 8)     # => Sat, 08 May 2010
d.monday                     # => Mon, 03 May 2010
d.sunday                     # => Sun, 09 May 2010

d = Date.new(2012, 9, 10)    # => Mon, 10 Sep 2012
d.monday                     # => Mon, 10 Sep 2012

d = Date.new(2012, 9, 16)    # => Sun, 16 Sep 2012
d.sunday                     # => Sun, 16 Sep 2012
15.1.2.3 prev_week, next_week

The method next_week receives a symbol with a day name in English (default is the thread local Date.beginning_of_week, or config.beginning_of_week, or :monday) and it returns the date corresponding to that day.

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.next_week              # => Mon, 10 May 2010
d.next_week(:saturday)   # => Sat, 15 May 2010

The method prev_week is analogous:

d.prev_week              # => Mon, 26 Apr 2010
d.prev_week(:saturday)   # => Sat, 01 May 2010
d.prev_week(:friday)     # => Fri, 30 Apr 2010

prev_week is aliased to last_week.

Both next_week and prev_week work as expected when Date.beginning_of_week or config.beginning_of_week are set.

15.1.2.4 beginning_of_month, end_of_month

The methods beginning_of_month and end_of_month return the dates for the beginning and end of the month:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_month     # => Sat, 01 May 2010
d.end_of_month           # => Mon, 31 May 2010

beginning_of_month is aliased to at_beginning_of_month, and end_of_month is aliased to at_end_of_month.

15.1.2.5 beginning_of_quarter, end_of_quarter

The methods beginning_of_quarter and end_of_quarter return the dates for the beginning and end of the quarter of the receiver's calendar year:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_quarter   # => Thu, 01 Apr 2010
d.end_of_quarter         # => Wed, 30 Jun 2010

beginning_of_quarter is aliased to at_beginning_of_quarter, and end_of_quarter is aliased to at_end_of_quarter.

15.1.2.6 beginning_of_year, end_of_year

The methods beginning_of_year and end_of_year return the dates for the beginning and end of the year:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_year      # => Fri, 01 Jan 2010
d.end_of_year            # => Fri, 31 Dec 2010

beginning_of_year is aliased to at_beginning_of_year, and end_of_year is aliased to at_end_of_year.

15.1.3 Other Date Computations
15.1.3.1 years_ago, years_since

The method years_ago receives a number of years and returns the same date those many years ago:

date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000

years_since moves forward in time:

date = Date.new(2010, 6, 7)
date.years_since(10) # => Sun, 07 Jun 2020

If such a day does not exist, the last day of the corresponding month is returned:

Date.new(2012, 2, 29).years_ago(3)     # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3)   # => Sat, 28 Feb 2015

last_year is short-hand for #years_ago(1).

15.1.3.2 months_ago, months_since

The methods months_ago and months_since work analogously for months:

Date.new(2010, 4, 30).months_ago(2)   # => Sun, 28 Feb 2010
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010

If such a day does not exist, the last day of the corresponding month is returned:

Date.new(2010, 4, 30).months_ago(2)    # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010

last_month is short-hand for #months_ago(1).

15.1.3.3 weeks_ago

The method weeks_ago works analogously for weeks:

Date.new(2010, 5, 24).weeks_ago(1)    # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_ago(2)    # => Mon, 10 May 2010
15.1.3.4 advance

The most generic way to jump to other days is advance. This method receives a hash with keys :years, :months, :weeks, :days, and returns a date advanced as much as the present keys indicate:

date = Date.new(2010, 6, 6)
date.advance(years: 1, weeks: 2)  # => Mon, 20 Jun 2011
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010

Note in the previous example that increments may be negative.

To perform the computation the method first increments years, then months, then weeks, and finally days. This order is important towards the end of months. Say for example we are at the end of February of 2010, and we want to move one month and one day forward.

The method advance advances first one month, and then one day, the result is:

Date.new(2010, 2, 28).advance(months: 1, days: 1)
# => Sun, 29 Mar 2010

While if it did it the other way around the result would be different:

Date.new(2010, 2, 28).advance(days: 1).advance(months: 1)
# => Thu, 01 Apr 2010
15.1.4 Changing Components

The method change allows you to get a new date which is the same as the receiver except for the given year, month, or day:

Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011

This method is not tolerant to non-existing dates, if the change is invalid ArgumentError is raised:

Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date
15.1.5 Durations

Duration objects can be added to and subtracted from dates:

d = Date.current
# => Mon, 09 Aug 2010
d + 1.year
# => Tue, 09 Aug 2011
d - 3.hours
# => Sun, 08 Aug 2010 21:00:00 UTC +00:00

They translate to calls to since or advance. For example here we get the correct jump in the calendar reform:

Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582
15.1.6 Timestamps

The following methods return a Time object if possible, otherwise a DateTime. If set, they honor the user time zone.

15.1.6.1 beginning_of_day, end_of_day

The method beginning_of_day returns a timestamp at the beginning of the day (00:00:00):

date = Date.new(2010, 6, 7)
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010

The method end_of_day returns a timestamp at the end of the day (23:59:59):

date = Date.new(2010, 6, 7)
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010

beginning_of_day is aliased to at_beginning_of_day, midnight, at_midnight.

15.1.6.2 beginning_of_hour, end_of_hour

The method beginning_of_hour returns a timestamp at the beginning of the hour (hh:00:00):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010

The method end_of_hour returns a timestamp at the end of the hour (hh:59:59):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010

beginning_of_hour is aliased to at_beginning_of_hour.

15.1.6.3 beginning_of_minute, end_of_minute

The method beginning_of_minute returns a timestamp at the beginning of the minute (hh:mm:00):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010

The method end_of_minute returns a timestamp at the end of the minute (hh:mm:59):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010

beginning_of_minute is aliased to at_beginning_of_minute.

beginning_of_hour, end_of_hour, beginning_of_minute and end_of_minute are implemented for Time and DateTime but not Date as it does not make sense to request the beginning or end of an hour or minute on a Date instance.

15.1.6.4 ago, since

The method ago receives a number of seconds as argument and returns a timestamp those many seconds ago from midnight:

date = Date.current # => Fri, 11 Jun 2010
date.ago(1)         # => Thu, 10 Jun 2010 23:59:59 EDT -04:00

Similarly, since moves forward:

date = Date.current # => Fri, 11 Jun 2010
date.since(1)       # => Fri, 11 Jun 2010 00:00:01 EDT -04:00
15.1.7 Other Time Computations

15.2 Conversions

16 Extensions to DateTime

DateTime is not aware of DST rules and so some of these methods have edge cases when a DST change is going on. For example seconds_since_midnight might not return the real amount in such a day.

16.1 Calculations

The class DateTime is a subclass of Date so by loading active_support/core_ext/date/calculations.rb you inherit these methods and their aliases, except that they will always return datetimes.

The following methods are reimplemented so you do not need to load active_support/core_ext/date/calculations.rb for these ones:

On the other hand, advance and change are also defined and support more options, they are documented below.

The following methods are only implemented in active_support/core_ext/date_time/calculations.rb as they only make sense when used with a DateTime instance:

16.1.1 Named Datetimes
16.1.1.1 DateTime.current

Active Support defines DateTime.current to be like Time.now.to_datetime, except that it honors the user time zone, if defined. The instance predicates past? and future? are defined relative to DateTime.current.

16.1.2 Other Extensions
16.1.2.1 seconds_since_midnight

The method seconds_since_midnight returns the number of seconds since midnight:

now = DateTime.current     # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
16.1.2.2 utc

The method utc gives you the same datetime in the receiver expressed in UTC.

now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc                # => Mon, 07 Jun 2010 23:27:52 +0000

This method is also aliased as getutc.

16.1.2.3 utc?

The predicate utc? says whether the receiver has UTC as its time zone:

now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc?           # => false
now.utc.utc?       # => true
16.1.2.4 advance

The most generic way to jump to another datetime is advance. This method receives a hash with keys :years, :months, :weeks, :days, :hours, :minutes, and :seconds, and returns a datetime advanced as much as the present keys indicate.

d = DateTime.current
# => Thu, 05 Aug 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
# => Tue, 06 Sep 2011 12:34:32 +0000

This method first computes the destination date passing :years, :months, :weeks, and :days to Date#advance documented above. After that, it adjusts the time calling since with the number of seconds to advance. This order is relevant, a different ordering would give different datetimes in some edge-cases. The example in Date#advance applies, and we can extend it to show order relevance related to the time bits.

If we first move the date bits (that have also a relative order of processing, as documented before), and then the time bits we get for example the following computation:

d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Sun, 28 Feb 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Mon, 29 Mar 2010 00:00:00 +0000

but if we computed them the other way around, the result would be different:

d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000

Since DateTime is not DST-aware you can end up in a non-existing point in time with no warning or error telling you so.

16.1.3 Changing Components

The method change allows you to get a new datetime which is the same as the receiver except for the given options, which may include :year, :month, :day, :hour, :min, :sec, :offset, :start:

now = DateTime.current
# => Tue, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Wed, 08 Jun 2011 01:56:22 -0600

If hours are zeroed, then minutes and seconds are too (unless they have given values):

now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000

Similarly, if minutes are zeroed, then seconds are too (unless it has given a value):

now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000

This method is not tolerant to non-existing dates, if the change is invalid ArgumentError is raised:

DateTime.current.change(month: 2, day: 30)
# => ArgumentError: invalid date
16.1.4 Durations

Duration objects can be added to and subtracted from datetimes:

now = DateTime.current
# => Mon, 09 Aug 2010 23:15:17 +0000
now + 1.year
# => Tue, 09 Aug 2011 23:15:17 +0000
now - 1.week
# => Mon, 02 Aug 2010 23:15:17 +0000

They translate to calls to since or advance. For example here we get the correct jump in the calendar reform:

DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000

17 Extensions to Time

17.1 Calculations

They are analogous. Please refer to their documentation above and take into account the following differences:

  • change accepts an additional :usec option.
  • Time understands DST, so you get correct DST calculations as in
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>

# In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST.
t = Time.local(2010, 3, 28, 1, 59, 59)
# => Sun Mar 28 01:59:59 +0100 2010
t.advance(seconds: 1)
# => Sun Mar 28 03:00:00 +0200 2010
  • If since or ago jump to a time that can't be expressed with Time a DateTime object is returned instead.
17.1.1 Time.current

Active Support defines Time.current to be today in the current time zone. That's like Time.now, except that it honors the user time zone, if defined. It also defines the instance predicates past?, today?, tomorrow?, next_day?, yesterday?, prev_day? and future?, all of them relative to Time.current.

When making Time comparisons using methods which honor the user time zone, make sure to use Time.current instead of Time.now. There are cases where the user time zone might be in the future compared to the system time zone, which Time.now uses by default. This means Time.now.to_date may equal Date.yesterday.

17.1.2 all_day, all_week, all_month, all_quarter and all_year

The method all_day returns a range representing the whole day of the current time.

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_day
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00

Analogously, all_week, all_month, all_quarter and all_year all serve the purpose of generating time ranges.

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00
17.1.3 prev_day, next_day

prev_day and next_day return the time in the last or next day:

t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_day               # => 2010-05-07 00:00:00 +0900
t.next_day               # => 2010-05-09 00:00:00 +0900
17.1.4 prev_month, next_month

prev_month and next_month return the time with the same day in the last or next month:

t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_month             # => 2010-04-08 00:00:00 +0900
t.next_month             # => 2010-06-08 00:00:00 +0900

If such a day does not exist, the last day of the corresponding month is returned:

Time.new(2000, 5, 31).prev_month # => 2000-04-30 00:00:00 +0900
Time.new(2000, 3, 31).prev_month # => 2000-02-29 00:00:00 +0900
Time.new(2000, 5, 31).next_month # => 2000-06-30 00:00:00 +0900
Time.new(2000, 1, 31).next_month # => 2000-02-29 00:00:00 +0900
17.1.5 prev_year, next_year

prev_year and next_year return a time with the same day/month in the last or next year:

t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_year              # => 2009-05-08 00:00:00 +0900
t.next_year              # => 2011-05-08 00:00:00 +0900

If date is the 29th of February of a leap year, you obtain the 28th:

t = Time.new(2000, 2, 29) # => 2000-02-29 00:00:00 +0900
t.prev_year               # => 1999-02-28 00:00:00 +0900
t.next_year               # => 2001-02-28 00:00:00 +0900
17.1.6 prev_quarter, next_quarter

prev_quarter and next_quarter return the date with the same day in the previous or next quarter:

t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
t.prev_quarter             # => 2010-02-08 00:00:00 +0200
t.next_quarter             # => 2010-08-08 00:00:00 +0300

If such a day does not exist, the last day of the corresponding month is returned:

Time.local(2000, 7, 31).prev_quarter  # => 2000-04-30 00:00:00 +0300
Time.local(2000, 5, 31).prev_quarter  # => 2000-02-29 00:00:00 +0200
Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200

prev_quarter is aliased to last_quarter.

17.2 Time Constructors

Active Support defines Time.current to be Time.zone.now if there's a user time zone defined, with fallback to Time.now:

Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
Time.current
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00

Analogously to DateTime, the predicates past?, and future? are relative to Time.current.

If the time to be constructed lies beyond the range supported by Time in the runtime platform, usecs are discarded and a DateTime object is returned instead.

17.2.1 Durations

Duration objects can be added to and subtracted from time objects:

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now + 1.year
# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
now - 1.week
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00

They translate to calls to since or advance. For example here we get the correct jump in the calendar reform:

Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582

18 Extensions to File

18.1 atomic_write

With the class method File.atomic_write you can write to a file in a way that will prevent any reader from seeing half-written content.

The name of the file is passed as an argument, and the method yields a file handle opened for writing. Once the block is done atomic_write closes the file handle and completes its job.

For example, Action Pack uses this method to write asset cache files like all.css:

File.atomic_write(joined_asset_path) do |cache|
  cache.write(join_asset_file_contents(asset_paths))
end

To accomplish this atomic_write creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists atomic_write overwrites it and keeps owners and permissions. However there are a few cases where atomic_write cannot change the file ownership or permissions, this error is caught and skipped over trusting in the user/filesystem to ensure the file is accessible to the processes that need it.

Due to the chmod operation atomic_write performs, if the target file has an ACL set on it this ACL will be recalculated/modified.

Note you can't append with atomic_write.

The auxiliary file is written in a standard directory for temporary files, but you can pass a directory of your choice as second argument.

19 Extensions to Marshal

19.1 load

Active Support adds constant autoloading support to load.

For example, the file cache store deserializes this way:

File.open(file_name) { |f| Marshal.load(f) }

If the cached data refers to a constant that is unknown at that point, the autoloading mechanism is triggered and if it succeeds the deserialization is retried transparently.

If the argument is an IO it needs to respond to rewind to be able to retry. Regular files respond to rewind.

20 Extensions to NameError

Active Support adds missing_name? to NameError, which tests whether the exception was raised because of the name passed as argument.

The name may be given as a symbol or string. A symbol is tested against the bare constant name, a string is against the fully qualified constant name.

A symbol can represent a fully qualified constant name as in :"ActiveRecord::Base", so the behavior for symbols is defined for convenience, not because it has to be that way technically.

For example, when an action of ArticlesController is called Rails tries optimistically to use ArticlesHelper. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that articles_helper.rb raises a NameError due to an actual unknown constant. That should be reraised. The method missing_name? provides a way to distinguish both cases:

def default_helper_module!
  module_name = name.delete_suffix("Controller")
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

21 Extensions to LoadError

Active Support adds is_missing? to LoadError.

Given a path name is_missing? tests whether the exception was raised due to that particular file (except perhaps for the ".rb" extension).

For example, when an action of ArticlesController is called Rails tries to load articles_helper.rb, but that file may not exist. That's fine, the helper module is not mandatory so Rails silences a load error. But it could be the case that the helper module does exist and in turn requires another library that is missing. In that case Rails must reraise the exception. The method is_missing? provides a way to distinguish both cases:

def default_helper_module!
  module_name = name.delete_suffix("Controller")
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

Feedback

Você é incentivado a ajudar a melhorar a qualidade deste guia.

Por favor, contribua caso veja quaisquer erros, inclusive erros de digitação. Para começar, você pode ler nossa sessão de contribuindo com a documentação.

Você também pode encontrar conteúdo incompleto ou coisas que não estão atualizadas. Por favor, adicione qualquer documentação em falta na main do Rails. Certifique-se de checar o Edge Guides (en-US) primeiro para verificar se o problema já foi resolvido ou não no branch main. Verifique as Diretrizes do Guia Ruby on Rails para estilo e convenções.

Se, por qualquer motivo, você encontrar algo para consertar, mas não conseguir consertá-lo, por favor abra uma issue no nosso Guia.

E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem vinda na lista de discussão rubyonrails-docs e nas issues do Guia em português.


dark theme icon