Ruby: require vs require_relative – bonne pratique pour contourner l’exécution dans Ruby = 1.9.2

Quelle est la meilleure pratique si je veux require un fichier relatif dans Ruby et que je souhaite qu’il fonctionne à la fois dans 1.8.x et> = 1.9.2?

Je vois quelques options:

  • faites juste $LOAD_PATH << '.' et oublie tout
  • do $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • vérifier si RUBY_VERSION <1.9.2, puis définir require_relative tant que de require , utiliser require_relative partout où cela est nécessaire après
  • vérifiez si require_relative existe déjà, si c’est le cas, essayez de procéder comme dans le cas précédent
  • utiliser des constructions étranges telles que
     require File.join(File.dirname(__FILE__), 'path/to/file') 

    – hélas, ils ne semblent pas fonctionner avec Ruby 1.9 car, 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 '' 
  • Construction encore plus étrange:
     require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file') 

    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, alors que je veux juste travailler avec des fichiers relatifs.

Il y a une question étroitement liée à StackOverflow qui donne quelques exemples supplémentaires, mais cela ne donne pas de réponse claire – ce qui est une meilleure pratique.

Existe-t-il une solution universelle décente, acceptée par tous pour que mon application fonctionne sur Ruby = 1.9.2?

METTRE À JOUR

Clarification: Je ne veux pas simplement des réponses comme “vous pouvez 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 pratique exemplaire, quels sont ses avantages et ses inconvénients et pourquoi elle devrait être choisie parmi les autres.

Une solution de contournement a été ajoutée à la pierre précieuse ‘aws’, alors j’ai pensé que je partagerais cela car il a été inspiré par ce post.

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 avec ruby ​​1.9.2 dans ruby ​​1.8 et 1.9.1.

Avant que je fasse le saut à 1.9.2, j’ai utilisé 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 y avoir un «..» supplémentaire au début. La raison en est que expand_path développera un chemin relatif au second argument, et le second 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 ne se soucie pas de savoir si les fichiers existent ou pas, il appliquera simplement certaines règles pour développer des choses comme .. , . et ~ . Si vous parvenez à surmonter le “waitaminute” initial n’est-il pas un extra .... bas? Je pense que la ligne ci-dessus fonctionne assez bien.

En supposant que __FILE__ soit /absolute/path/to/file.rb , il se trouve que expand_path va construire la chaîne /absolute/path/to/file.rb/../relative/path , puis appliquer une règle indiquant que .. devrait supprimer le composant path avant lui ( file.rb dans ce cas), en retournant /absolute/path/to/relative/path file.rb /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 soit sur la base de code Rails, donc je dirais que c’est au moins un idiome assez commun.

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 toujours utiliser require_relative .

 $LOAD_PATH << '.' $LOAD_PATH << File.dirname(__FILE__) 

Ce n'est pas une bonne habitude de sécurité: pourquoi devriez-vous 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') 

Construction encore plus étrange:

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

Utilisez backports gem - c'est un peu lourd, il nécessite une infrastructure rubygems et inclut des tonnes d'autres solutions, alors que je veux juste travailler avec des fichiers relatifs.

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

vérifier si RUBY_VERSION <1.9.2, puis définir require_relative en tant que de besoin, utiliser require_relative partout où cela est nécessaire après

vérifiez 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 de 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 

Je suis fan de l’utilisation du joyau relatif à rbx-require ( 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 1.9.2. Exiger un joyau est simple, et je n’ai pas besoin de lancer des extraits de code dans mon projet.

Ajoutez-le à votre Gemfile:

 gem "rbx-require-relative" 

Ensuite, require 'require_relative' avant que vous ayez require_relative .

Par exemple, l’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 de ces OMI, et la gemme n’est pas aussi lourde que les backports.

Le bijou de backports permet maintenant le chargement individuel des backports.

Vous pourriez alors simplement:

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

Cela require va pas affecter les nouvelles versions, ni mettre à jour les autres méthodes intégrées.

Une autre option consiste à indiquer à l’interprète quels chemins rechercher

 ruby -I /path/to/my/project caller.rb 

Un problème que je n’ai pas vu avec les solutions basées sur __FILE__ est qu’elles se brisent en ce qui concerne 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 obligatoire est brisée lorsque j’exécute ‘foo’:

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

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

 nécessite "chemin"
 require File.join (File.dirname (Pathname.new (__ FILE __). realpath), "lib / someinclude")

Si vous construisiez une gemme, vous ne voudriez pas polluer le chemin de chargement.

Mais, dans le cas d’une application autonome, il est très pratique d’append le répertoire actuel au chemin de chargement, comme vous le faites dans les deux premiers exemples.

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

J’aimerais voir de la littérature solide sur les meilleures pratiques de Ruby.

Je définirais mon propre relative_require s’il n’existe pas (c.-à-d. Sous 1.8), puis utiliserais la même syntaxe partout.

Façon Ruby on Rails:

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