Você sabe explicar a diferença entre as duas linhas abaixo?
require "my_library"
load "my_library.rb"
Se você já trabalhou ao menos um pouco com Ruby, então sabe que load é útil quando se deseja executar o código de um arquivo enquanto require é utilizado para importar uma biblioteca para dentro do código do seu programa.
Esta definição está correta, mas eu gostaria de me aprofundar um pouco mais no assunto…
Para entender como esses métodos realmente funcionam por dentro, precisamos primeiro saber que ambos possuem a mesma base. No Ruby 1.8.7 esta base é o método rb_load (internamente escrito em C e encontrado no arquivo eval.c). A função do rb_load é determinar se o arquivo especificado na chamada dos métodos require e load se encontra no diretório atual do aplicativo ou em um dos diretórios listados no $LOAD_PATH ($:) e então carregar este arquivo seguindo a regra própria de cada um dos métodos.
require "my_library"
Quanto rb_load é executado através do método require, fazendo com que ele carregue uma biblioteca no seu código, o nome do arquivo especificado é adicionado a uma lista. Esta lista pode ser vista através da variável global $LOADED_FEATURES ($").
Você pode verificar quais arquivos já foram carregados em seu código através do método require analisando o conteúdo desta variável.
[carlosbrando:~]$ irb
>> puts $LOADED_FEATURES
enumerator.so
e2mmap.rb
irb/init.rb
...
Ao executar o método require, o Ruby primeiro verificará se o nome do arquivo que você especificou se encontra em $LOADED_FEATURES. Se ele não encontrar o nome na lista, então ele tentará carregar o arquivo e se for bem sucedido retornará true. Caso o nome já se encontre na lista de recursos carregados, então o método simplesmente retornará false e não carregará o arquivo novamente.
O segundo passo é determinar em que diretório o arquivo se encontra. Para isto o Ruby consultará uma outra variável global chamada $LOAD_PATH. Esta variável armazena todos os diretórios onde rb_load deve procurar por bibliotecas. Se o arquivo não for encontrado no diretório raiz do seu aplicativo e nem na lista de diretórios do $LOAD_PATH, então o método rb_load disparará um erro do tipo LoadError.
Caso você não informe uma extensão para o nome do arquivo, como no exemplo no inicio deste tópico, o método require procurará por arquivos com as seguintes extensões: .rb, .o, .so, .dylib, .dll, exatamente nesta ordem. Por último o Ruby procurará pelo nome do arquivo sem nenhuma extensão. Se o arquivo possuir a extensão .rb ele será carregado como um código Ruby puro. As demais extensões serão carregadas como extensões Ruby de acordo com o sistema operacional na qual você estiver executando o seu código.
Atenção: Se você informar a extensão do arquivo na chamada do método require (i.e. require 'my_lib.rb') e o Ruby não conseguir encontrar o arquivo da forma como foi especificado, ele ignorará a extensão que você definiu e procurará por arquivos com o mesmo nome seguindo a regra mencionada no parágrafo anterior.
O método também permite que você seja mais especifico, informando o diretório exato onde o arquivo se encontra.
require '../my_library.rb'
No exemplo acima o Ruby não tentará localizar o recurso em outros diretórios, caso não o encontre no local que você especificou. Porém, é importante estar atento a um problema comum ao utilizar este recurso. Veja o código abaixo:
require 'my_library'
require './my_library.rb'
Neste exemplo, nas duas linhas de código eu estou carregando o mesmo recurso. Na primeira linha eu informo somente o nome do arquivo e na segunda eu informo também o diretório. O método require não armazena o caminho absoluto do recurso em $LOADED_FEATURES, ele armazena a string exata informada na chamada do método. Neste caso o arquivo my_library.rb será carregado duas vezes. Analisando a variável $LOADED_FEATURES encontraremos o seguinte:
>> $".grep /my_library/
=> ["my_library.rb", "./my_library.rb"]
O método load funciona de uma forma muito parecida com o método require. A principal diferença é que ele não mantém uma lista de recursos, fazendo com que um arquivo seja carregado tantas vezes quanto for solicitado.
Outra importante diferença é que a extensão do arquivo é obrigatória, embora ela não seja restrita apenas a .rb e aos outros formatos que vimos acima. Qualquer outra extensão é aceita, embora o conteúdo do arquivo seja sempre tratado como código Ruby puro.
Você também pode especificar o caminho exato onde o arquivo se encontra. Desta vez, sem nenhum efeito colateral. Porém, se você informar somente o nome do arquivo, o Ruby tentará encontrá-lo primeiro no seu diretório atual e depois na lista de diretórios do $LOAD_PATH.
O método load tem algumas opções extras. Muito provavelmente o arquivo que você estará carregando definirá algumas variáveis e classes. Variáveis locais em nenhuma circunstancia serão propagadas para o seu ambiente, porém o mesmo não acontece com constantes. O resultado é que ao carregar um recurso em seu projeto você pode ter um conflito se já houver uma constante com o mesmo nome no seu código. O mesmo vale para nomes de classes, que como já aprendemos também são constantes.
Você pode obrigar o método load a manter suas constantes para si mesmo, passando um segundo parametro na chamada do método:
load 'my_library.rb', true
Quando você carrega um recurso desta maneira, o Ruby criará um módulo anônimo e usará este módulo como um namespace, evitando conflitos entre o código do arquivo e o seu código. Ao terminar a execução este módulo é destruído. No exemplo acima, true é um atalho. Se desejar, você pode ser mais especifico fazendo também desta maneira:
load "my_library.rb", :wrap => true
O efeito é o mesmo.
O método require não tem opções extras. Se você está importando uma biblioteca então faz sentido que constantes e classes permaneçam em seu código. Por outro lado, se você deseja apenas executar o código que está em um arquivo, provavelmente você não vai querer poluir o seu programa com recursos desnecessários.
Os dois métodos são muito parecidos, embora eles tenham sido construídos para finalidades diferentes. Entender como cada um funciona por dentro pode ajudá-lo a utilizar o recurso certo na hora certa.
Muito legal a abordagem desse assunto, embora em algumas outras linguagens o comportamento para ambos sejam semelhantes aos do ruby, ainda sim existem peculiaridades no método load que são muito interessantes. Belo artigo parabéns.
Æ!!
Legal o artigo Carlos!
É interessante ver as entranhas das coisas e entender exatamente o que elas fazem. Continue postando suas experiencias nisso que com certeza serão muito válidas para todos.
Há braços
Muito bom este artigo, aprendi bastante.
Olá Brando, interessante também é o require_relative presente no Ruby 1.9 em diante, que não procura por bibliotecas e/ou arquivos no $LOAD_PATH mas sim no diretório atual do arquivo que os carrega.
rodrigo3n,
Interessante, não conhecia este.
Gostei, poderia colocar o código em linkado que ficaria mt fácil entender =D
Nice article.