web-dev-qa-db-fra.com

Ruby: require vs require_relative - meilleure pratique pour contourner le problème Ruby <1.9.2 et> = 1.9.2

Quelle est la meilleure pratique si je veux require un fichier relatif dans Ruby ) et je veux ça marche en 1.8.x et> = 1.9.2?

Je vois quelques options:

  • faites juste $LOAD_PATH << '.' et oubliez tout
  • faire $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • vérifiez si Ruby_VERSION <1.9.2, puis définissez require_relative comme require, utilisez require_relative partout où vous en aurez besoin par la suite
  • vérifier si require_relative existe déjà, si c'est le cas, essayez de procéder comme dans le cas précédent
  • utilisez des constructions étranges telles que
    require File.join(File.dirname(__FILE__), 'path/to/file') </ code>
    - hélas, ils ne semblent pas fonctionner correctement Ruby 1.9, par exemple, parce que, par exemple:
    $ cat caller.rb
    require File.join(File.dirname(__FILE__), 'path/to/file')
    $ cat path/to/file.rb
    puts 'Some testing'
    $ Ruby caller
    Some testing
    $ pwd
    /tmp
    $ Ruby /tmp/caller
    Some testing
    $ Ruby tmp/caller
    tmp/caller.rb:1:in 'require': no such file to load -- tmp/path/to/file (LoadError)
        from tmp/caller.rb:1:in '<main>' </ Code>
  • Même construction plus étrange:
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file') </ code>
    semble fonctionner, mais c'est bizarre et pas très beau.
  • Utilisez backports gem - c'est un peu lourd, il nécessite une infrastructure rubygems et inclut des tonnes d'autres solutions de contournement, alors que je veux juste que require fonctionne avec des fichiers relatifs.

Il y a une question étroitement liée chez StackOverflow qui donne quelques exemples supplémentaires, mais elle ne donne pas de réponse claire - ce qui est une pratique exemplaire.

Existe-t-il une solution universelle décente, acceptée par tous, pour que mon application s'exécute à la fois Ruby <1.9.2 et> = 1.9.2?)?

MISE À JOUR

Précision: je ne veux pas juste des réponses du type "tu peux faire X" - en fait, j'ai déjà mentionné la plupart des choix en question. Je veux une justification , c’est-à-dire pourquoi c’est une bonne pratique, quelles sont ses avantages et inconvénients et pourquoi il devrait être choisi parmi les autres.

153
GreyCat

Une solution de contournement à ce problème vient d’être ajoutée au joyau de "aws", alors j’ai pensé que je devrais la partager car elle s’inspire de ce message.

https://github.com/appoxy/aws/blob/master/lib/awsbase/require_relative.rb

unless Kernel.respond_to?(:require_relative)
  module Kernel
    def require_relative(path)
      require File.join(File.dirname(caller[0]), path.to_str)
    end
  end
end

Cela vous permet d'utiliser require_relative comme vous le feriez dans Ruby 1.9.2 dans Ruby 1.8 et 1.9.1.

64
Travis Reeder

Avant de faire le saut à 1.9.2, j’utilisais ce qui suit pour les besoins relatifs:

require File.expand_path('../relative/path', __FILE__)

C'est un peu bizarre la première fois que vous le voyez, car il semble qu'il y ait un ".." supplémentaire au début. La raison en est que expand_path développera un chemin relatif au deuxième argument, et le deuxième argument sera interprété comme s’il s’agissait d’un répertoire. __FILE__ _ n'est évidemment pas un répertoire, mais cela n'a pas d'importance puisque expand_path _ Peu importe si les fichiers existent ou non, il va simplement appliquer des règles pour développer des choses comme .., . et ~. Si vous pouvez vous en sortir, "waitaminute" n'y a-t-il pas un supplément de .. _? "Je pense que la ligne ci-dessus fonctionne assez bien.

En admettant que __FILE__ est /absolute/path/to/file.rb, ce qui se passe c'est que expand_path va construire la chaîne /absolute/path/to/file.rb/../relative/path, puis appliquez une règle qui dit que .. devrait supprimer le composant de chemin avant celui-ci (file.rb _ dans ce cas), retournant /absolute/path/to/relative/path.

Est-ce une bonne pratique? Cela dépend de ce que vous entendez par là, mais il semble que tout se trouve sur la base de code Rails), donc je dirais que c'est au moins un idiome assez commun.

46
Theo

La pioche a un extrait pour cela pour 1.8. C'est ici:

def require_relative(relative_feature)
  c = caller.first
  fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
  file = $`
  if /\A\((.*)\)/ =~ file # eval, etc.
    raise LoadError, "require_relative is called in #{$1}"
  end
  absolute = File.expand_path(relative_feature, File.dirname(file))
  require absolute
end

Il utilise essentiellement ce que Theo a répondu, mais vous pouvez donc toujours utiliser require_relative.

6
Paul Hoffer
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

Ce n’est pas une bonne habitude de sécurité: pourquoi exposer tout votre répertoire?

require './path/to/file'

Cela ne fonctionne pas si Ruby_VERSION <1.9.2

utiliser des constructions étranges telles que

require File.join(File.dirname(__FILE__), 'path/to/file')

Même construction plus étrange:

require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')

Utilisez des joyaux de backport - c'est un peu lourd, il nécessite une infrastructure Rubygems et inclut des tonnes d'autres solutions de contournement, alors que je veux juste qu'il soit nécessaire de travailler avec des fichiers relatifs.

Vous avez déjà répondu pourquoi ce ne sont pas les meilleures options.

vérifiez si Ruby_VERSION <1.9.2, puis définissez require_relative comme require, utilisez require_relative partout où vous en aurez besoin par la suite

vérifier si require_relative existe déjà, si c'est le cas, essayez de procéder comme dans le cas précédent

Cela peut fonctionner, mais il existe un moyen plus sûr et plus rapide: gérer l’exception LoadError:

begin
  # require statements for 1.9.2 and above, such as:
  require "./path/to/file"
  # or
  require_local "path/to/file"
rescue LoadError
  # require statements other versions:
  require "path/to/file"
end
6
Claudio Floreani

Je suis fan de l'utilisation de la gem relative à rbx-require-relative ( source ). Il a été écrit à l'origine pour Rubinius, mais il prend également en charge l'IRM 1.8.7 et ne fait rien dans la version 1.9.2. Demander une gemme est simple et je n'ai pas à insérer d'extraits de code dans mon projet.

Ajoutez-le à votre Gemfile:

gem "rbx-require-relative"

Ensuite require 'require_relative' Avant toi require_relative.

Par exemple, un de mes fichiers de test ressemble à ceci:

require 'rubygems'
require 'bundler/setup'
require 'minitest/autorun'
require 'require_relative'
require_relative '../lib/foo'

C’est la solution la plus propre parmi toutes ces IMO, et le bijou n’est pas aussi lourd que les backports.

5
Edward Anderson

La gemme backports permet désormais le chargement individuel des backports.

Vous pouvez alors simplement:

require 'backports/1.9.1/kernel/require_relative'
# => Now require_relative works for all versions of Ruby

Ce require n'affectera pas les versions les plus récentes et ne mettra pas à jour les autres méthodes intégrées.

4

Une autre option est de dire à l'interprète quels chemins rechercher

Ruby -I /path/to/my/project caller.rb
3
eradman

Un problème que je n’ai pas vu qui a été signalé dans les solutions basées sur __FILE__ est qu’elles rompent avec les liens symboliques. Par exemple disons que j'ai:

~/Projects/MyProject/foo.rb
~/Projects/MyProject/lib/someinclude.rb

Le script principal, le point d’entrée, l’application est foo.rb. Ce fichier est lié à ~/Scripts/foo qui se trouve dans mon $ PATH. Cette instruction require est cassée lorsque j'exécute 'foo':

require File.join(File.dirname(__FILE__), "lib/someinclude")

Parce que __FILE__ est ~/Scripts/foo, l’instruction require ci-dessus recherche donc ~/Scripts/foo/lib/someinclude.rb, ce qui n’existe évidemment pas. La solution est simple Si __FILE__ est un lien symbolique, il doit être déréférencé. Pathname # realpath nous aidera dans cette situation:

 nécessite "chemin" 
 nécessite File.join (File.dirname (Chemin.nouveau (__ FICHIER __). realpath), "lib/someinclude") 
3
jptros

Si vous construisiez un joyau, vous ne voudriez pas polluer le chemin de chargement.

Mais, dans le cas d’une application autonome, il est très pratique d’ajouter simplement le répertoire en cours au chemin de chargement, comme vous le feriez dans les 2 premiers exemples.

Mon vote va à la première option sur la liste.

J'aimerais beaucoup voir de la littérature solide sur les meilleures pratiques Ruby).

2
Casey Watson

Je définirais mon propre relative_require si elle n’existe pas (c’est-à-dire en dessous de 1.8) et utilise ensuite la même syntaxe partout.

1
Phrogz

Ruby on Rails manière:

config_path = File.expand_path("../config.yml", __FILE__)
0
Vaibhav