Carlos Brando

Nome do Jogo

Como funciona o autoload por dentro

Em dezembro de 2009 eu iniciei uma série de artigos nesse blog que visava explicar em detalhes como alguns mecanismos do Ruby que usamos todos os dias funcionam internamente. Desde então eu publiquei poucos artigos sobre o tema, mas sempre foi um desejo continuar essa série.

Seguindo a linha do último artigo, dessa vez vou explicar como funciona o método autoload.

Antes de começarmos, eu recomendo fortemente que você leia o artigo ”Entendendo os métodos load e require por dentro” que explica o passo-a-passo que o Ruby executa quando carrega um novo arquivo no seu código. Esse artigo lhe dará a base necessária para entender o que se segue.

Para que serve o autoload?

Sei que o método autoload não faz parte do dia a dia de todos, então vejamos primeiro qual é a sua finalidade.

1
2
3
4
5
autoload :MyClass, "my_class"

module X
  autoload :MySubClass, "my_sub_class.rb"
end

No exemplo acima, estamos usando o método em dois contextos diferentes. Em ambos os casos, não desejamos carregar os arquivos my_class.rb e my_sub_class.rb no momento da execução. Porém, quando as constantes MyClass ou MySubClass forem usadas pela primeira vez em nosso código, queremos que o Ruby se encarregue de “auto carregar” os arquivos da mesma forma como faria com a instrução require.

Os arquivos devem possuir uma declaração de suas devidas classes de acordo com a constante utilizada no primeiro argumento do método autoload, como no exemplo abaixo:

my_class.rb
1
2
3
class MyClass
  # ...
end
my_sub_class.rb
1
2
3
4
5
module X
  class MySubClass
    # ...
  end
end

A diferença entre usar autoload ao invés de require é que se a constante nunca for usada, o arquivo correspondente também nunca será carregado. O conceito é bem simples.

Vamos começar do principio

No primeiro exemplo, executamos o método autoload em dois contextos diferentes. A primeira chamada é feita diretamente no main e a segunda no contexto de um módulo. Assim, o objetos Kernel e Module possuem a sua própria versão do método autoload. Essas versões seguem basicamente o mesmo procedimento, mas por hora vamos nos concentrar na versão que está na classe Kernel.

O método autoload sempre irá esperar por dois argumentos: module que pode ser um objeto do tipo String ou um símbolo representando o módulo que será usado em um outro ponto de nosso código e filename que contém o nome do arquivo que desejamos carregar, seguindo as mesmas regras do método require.

Quando o método autoload é executado pela primeira vez, ele analisará se você informou os argumentos corretamente. Se o filename for um texto em branco, por exemplo, ele retornará um erro do tipo ArgumentError.

1
2
autoload :MyClass, ''
# => ArgumentError: empty file name

O segundo passo é verificar se o arquivo especificado já não foi carregado em uma execução do método anterior. Ele faz isso verificando a variável global $LOADED_FEATURES. Se o arquivo já foi carregado antes, a execução do método termina aqui.

Se esse não for o caso, ele verificará se já existe alguma outra declaração de autoload para o mesmo module. Se for encontrado, o filename anterior será substituído pelo novo. Veja um exemplo:

1
2
3
4
5
autoload :MyClass, "arquivo.rb"
autoload?(:MyClass)                     # => "arquivo.rb"

autoload :MyClass, "outro_arquivo.rb"
autoload?(:MyClass)                     # => "outro_arquivo.rb"

No código acima introduzimos o método autoload?, que é útil para confirmar se o módulo já existe na tabela de constantes do Ruby.

1
2
Object.constants.grep /MyClass/
# => [:MyClass]

Como o Ruby entende o autoload?

Nesse ponto eu recomendo que você dê uma outra pausa para ler um texto que facilitará o seu entendimento. Leia o artigo ”Classes são Módulos no Ruby” antes de continuar.

Ao executar autoload, o Ruby carrega o módulo na sua tabela interna de constantes. Mas diferente dos procedimentos normais, ele não armazenará uma instância da classe ou módulo diretamente nessa tabela. Nesse momento ele armazena uma estrutura secundária que contém informações como o nome do módulo, o escopo e o nome do arquivo que deve ser carregado.

Quando a constante for usada pela primeira vez, ela verificará se o objeto resultante é uma instância dessa estrutura Autoload. Se esse for o caso, ele recuperará os dados necessários e executará uma instrução require 'filename' da mesma forma como o artigo mencionado no terceiro parágrafo explicou. O retorno será então o novo módulo ou classe pronto para ser usado.

Onde usar?

Na maioria dos casos você será feliz em usar require ou load para adicionar algum comportamento ao seu código. Porém em algumas gems é comum existirem funcionalidades que não serão usadas por todos. Nesses casos faz muito sentido usar o método autoload para carregar somente o que o será necessário.

Próximos artigos

Como estou sem ideias, eu gostaria de receber sugestões de outros temas para artigos como esse. Se existe algum aspecto ou método do Ruby que você gostaria de entender como funciona por dentro, deixei um comentário abaixo. Até o próximo artigo.

Comments