Adicionando um diretório a $ LOAD_PATH (Ruby)

95

Eu vi duas técnicas comumente usadas para adicionar o diretório do arquivo que está sendo executado no $ LOAD_PATH (ou $ :). Vejo as vantagens de fazer isso caso você não esteja trabalhando com uma joia. Um parece mais prolixo do que o outro, obviamente, mas há uma razão para escolher um em vez do outro?

O primeiro método detalhado (pode ser exagero):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

e o mais simples, rápido e sujo:

$:.unshift File.dirname(__FILE__)

Alguma razão para escolher um em vez do outro?

Mark W
fonte
2
Uma versão um pouco menos prolixa da verbosa é:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan Long,
que tal a cláusula "a menos que"? Como os dois acima podem ser equivalentes?
inger
Como alguém que veio aqui para tentar entender como usar isso, é supercríptico. Não vejo de onde vem o nome do diretório nos exemplos. Eu apreciaria se alguém pudesse deixar isso claro.
SlySherZ
1
Usar __dir__(a partir do Ruby 2.0) pode tornar qualquer um deles mais conciso.
Nathan Long

Respostas:

50

Eu diria que vá com $:.unshift File.dirname(__FILE__)o outro, simplesmente porque vi muito mais uso dele no código do que o $LOAD_PATHoutro, e é mais curto também!

Ryan Bigg
fonte
Quando comecei com Ruby, obviamente pensei que $ LOAD_PATH era melhor. Mas, depois de passar do status de iniciante, eu só usaria $ LOAD_PATH se estivesse tentando tornar meu código mais legível para um iniciante. Meh é uma troca. Depende de quão "público" o código é, desde que o uso de memória seja o mesmo para cada um, o que presumo que seja essencialmente.
boulder_ruby
9
Depende do guia de estilo que você segue para o seu projeto. O popular Guia de Estilo Ruby diz "Evite usar variáveis ​​especiais no estilo Perl (como $ :, $ ;, etc.). Elas são bastante enigmáticas e seu uso em qualquer coisa que não seja scripts de uma linha é desencorajado."
bobmagoo
152

O caminho de carregamento do Ruby é comumente visto escrito como $:, mas só porque é curto, não o torna melhor. Se você prefere clareza à inteligência, ou se a brevidade por si só o deixa comichão, você não precisa fazer isso só porque todo mundo está. Diga olá para ...

$LOAD_PATH

... e diga adeus a ...

# I don't quite understand what this is doing...
$:

fonte
29
Além disso, é muito mais difícil pesquisar no Google strings como "$:", que contêm apenas símbolos.
DSimon
23

Não gosto muito do método 'rápido e sujo'. Qualquer pessoa nova em Ruby estará pensando no que $:.é.

Acho isso mais óbvio.

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

Ou se eu quiser ter o caminho completo ...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

ATUALIZAÇÃO 10/09/2009

Ultimamente, tenho feito o seguinte:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

Eu vi isso em um monte de projetos Ruby diferentes enquanto navegava no GitHub.

Parece ser a convenção?

Luke Antins
fonte
@LukeAntins, isso é realmente ótimo, mas onde devo "inicializar" load_path no aplicativo?
gaussblurinc
@gaussblurinc Em algum lugar 'próximo ao topo' de sua biblioteca / aplicativo, mas realmente depende. Se você tinha um binarquivo que sempre foi relativo ao seu codee só foi executado pelo binarquivo ... inicialize no bin. Se você tem uma biblioteca, inicialize no topo do código da biblioteca, como em lib/code.rbpara obter acesso a tudo que está abaixo lib/code/. Espero que este passeio ajude!
Luke Antins
1
RuboCop me informa que __dir__pode ser usado para obter um caminho para o diretório do arquivo atual.
Raphael
8

Se você digitar script/consoleseu projeto Rails e entrar $:, você obterá um array que inclui todos os diretórios necessários para carregar Ruby. A conclusão deste pequeno exercício é que $:existe uma série. Sendo assim, você pode executar funções nele, como adicionar o unshiftmétodo ou a <<operadora a outros diretórios . Como você sugeriu em sua declaração $:e$LOAD_PATH são os mesmos.

A desvantagem de fazer isso da maneira rápida e suja como você mencionou é esta: se você já tiver o diretório em seu caminho de inicialização, ele se repetirá.

Exemplo:

Eu tenho um plugin que criei chamado todo. Meu diretório está estruturado assim:

/---fornecedor
  |
  | --- / plugins
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- / modelos
                          | --- / controladores
              |
              | --- / rails
                    |
                    | --- init.rb

No arquivo init.rb, digitei o seguinte código:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

Observe como eu digo ao bloco de código para realizar as ações dentro do bloco para as strings 'modelos', 'controladores' e 'modelos', onde repito 'modelos'. (Para sua informação, %w{ ... }é apenas outra maneira de dizer ao Ruby para segurar um array de strings). Quando eu corro script/console, digito o seguinte:

>> puts $:

E eu digito isso para que seja mais fácil ler o conteúdo da string. O resultado que obtenho é:

...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

Como você pode ver, embora este seja um exemplo tão simples que eu poderia criar ao usar um projeto no qual estou trabalhando atualmente, se você não tiver cuidado, o caminho rápido e sujo levará a caminhos repetidos. O caminho mais longo verificará caminhos repetidos e garantirá que eles não ocorram.

Se você é um programador Rails experiente, provavelmente tem uma boa ideia do que está fazendo e provavelmente não cometerá o erro de repetir caminhos. Se você for um novato, eu seguiria pelo caminho mais longo até que você entenda realmente o que está fazendo.

Dyba
fonte
sua resposta muito útil e também bem explicada. Edição sugerida: o método load_pathse load_once_paths.deleteforam descontinuados. Seria ajudado a atualizar as linhas que se referem a eles como: ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Uzzar 07/06/2015
8

Melhor que encontrei para adicionar um dir via caminho relativo ao usar Rspec. Acho que é prolixo o suficiente, mas também um bom forro.

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
Dave Robertson
fonte
1

Existe uma gema que permitirá que você configure seu caminho de carregamento com um código mais agradável e limpo. Verifique isso: https://github.com/nayyara-samuel/load-path .

Também tem boa documentação

Rubista
fonte
-2

Sei que faz muito tempo que essa pergunta foi feita pela primeira vez, mas tenho uma resposta adicional que gostaria de compartilhar.

Tenho vários aplicativos Ruby que foram desenvolvidos por outro programador ao longo de vários anos e eles reutilizam as mesmas classes em diferentes aplicativos, embora possam acessar o mesmo banco de dados. Como isso viola a regra DRY, decidi criar uma biblioteca de classes para ser compartilhada por todos os aplicativos Ruby. Eu poderia ter colocado na biblioteca Ruby principal, mas isso ocultaria o código personalizado na base de código comum, o que eu não queria fazer.

Tive um problema em que havia um conflito de nome entre um nome já definido "profile.rb" e uma classe que estava usando. Esse conflito não foi um problema até que tentei criar a biblioteca de código comum. Normalmente, Ruby procura primeiro os locais do aplicativo e depois vai para os locais $ LOAD_PATH.

O application_controller.rb não conseguiu encontrar a classe que criei e gerou um erro na definição original porque não é uma classe. Como removi a definição de classe da seção app / models do aplicativo, Ruby não conseguiu encontrá-la e foi procurá-la nos caminhos de Ruby.

Portanto, modifiquei a variável $ LOAD_PATH para incluir um caminho para o diretório da biblioteca que estava usando. Isso pode ser feito no arquivo environment.rb no momento da inicialização.

Mesmo com o novo diretório adicionado ao caminho de pesquisa, Ruby estava gerando um erro porque preferencialmente pegava o arquivo definido pelo sistema primeiro. O caminho de pesquisa na variável $ LOAD_PATH pesquisa preferencialmente os caminhos Ruby primeiro.

Portanto, eu precisava alterar a ordem de pesquisa para que Ruby encontrasse a classe em minha biblioteca comum antes de pesquisar as bibliotecas integradas.

Este código fez isso no arquivo environment.rb:

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

Não acho que você possa usar qualquer uma das construções de codificação avançadas fornecidas antes neste nível, mas funciona muito bem se você quiser configurar algo no momento da inicialização em seu aplicativo. Você deve manter a ordem original da variável $ LOAD_PATH original quando ela for adicionada de volta à nova variável, caso contrário, algumas das classes principais do Ruby serão perdidas.

No arquivo application_controller.rb, eu simplesmente uso um

require 'profile'
require 'etc' #etc

e isso carrega os arquivos da biblioteca personalizada para todo o aplicativo, ou seja, não preciso usar os comandos require em todos os controladores.

Para mim, essa era a solução que eu procurava e pensei em adicioná-la a essa resposta para passar as informações adiante.

Timothy Dooling
fonte