154 votes

Ruby : require vs require_relative - meilleure pratique pour contourner les problèmes de fonctionnement dans Ruby <1.9.2 et >=1.9.2

Quelle est la meilleure pratique si je veux require un fichier relatif en Ruby y Je veux qu'il fonctionne à la fois dans 1.8.x et >=1.9.2 ?

Je vois quelques options :

  • juste faire $LOAD_PATH << '.' et oublier tout

  • faire $LOAD_PATH << File.dirname(__FILE__)

  • require './path/to/file'

  • vérifier si RUBY_VERSION < 1.9.2, alors définir require_relative comme require , utiliser require_relative partout où cela est nécessaire par la suite

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

  • utiliser des constructions bizarres telles que`

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

    `- Hélas, elles ne semblent pas fonctionner complètement en Ruby 1.9, 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>'
  • Une construction encore plus bizarre :`

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

    `semble fonctionner, mais c'est bizarre et pas très joli.

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

Il y a un question étroitement liée à StackOverflow qui donne d'autres exemples, mais ne donne pas de réponse claire - ce qui est une bonne pratique.

Existe-t-il une solution universelle, acceptée par tous, pour faire fonctionner mon application à la fois sur Ruby <1.9.2 et >=1.9.2 ?

MISE À JOUR

Clarification : Je ne veux pas seulement des réponses du type "vous pouvez faire X" - en fait, j'ai déjà mentionné la plupart des choix de la question. Je veux raison d'être , c'est-à-dire pourquoi il s'agit d'une meilleure pratique, quels sont ses avantages et ses inconvénients et pourquoi elle devrait être choisie parmi les autres.

64voto

Travis R Points 8935

Une solution de contournement vient d'être ajoutée à la gemme 'aws' et j'ai pensé la partager car elle a été inspirée par cet article.

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 en ruby 1.9.2 en ruby 1.8 et 1.9.1.

46voto

Theo Points 60103

Avant de passer à la version 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 qu'on le voit, parce qu'on a l'impression qu'il y a un ' ' supplémentaire au début. La raison en est que expand_path développera un chemin relatif au deuxième argument, et ce dernier sera interprété comme s'il s'agissait d'un répertoire. __FILE__ n'est manifestement pas un répertoire, mais cela n'a pas d'importance puisque expand_path ne se préoccupe pas de savoir si les fichiers existent ou non, il appliquera simplement quelques règles pour étendre les choses comme .. , . y ~ . Si vous parvenez à passer le cap du "waitaminute", n'y a-t-il pas un supplément à payer ? .. là ?" Je pense que la phrase ci-dessus fonctionne très bien.

En supposant que __FILE__ es /absolute/path/to/file.rb Ce qui se passe, c'est que expand_path construira la chaîne de caractères /absolute/path/to/file.rb/../relative/path puis appliquer une règle qui dit que .. doit supprimer le composant du chemin d'accès qui le précède ( file.rb dans ce cas), en renvoyant /absolute/path/to/relative/path .

S'agit-il d'une bonne pratique ? Cela dépend de ce que vous entendez par là, mais il semble que ce soit partout dans la base de code de Rails, donc je dirais que c'est au moins un idiome assez commun.

6voto

phoffer Points 3664

The Pickaxe a un snippet pour cela pour la version 1.8. Le voici :

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 pour que vous puissiez toujours utiliser require_relative .

6voto

Claudio Floreani Points 516
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

Ce n'est pas une bonne habitude en matière 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 bizarres telles que

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

Une construction encore plus bizarre :

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

Utiliser la gemme backports - c'est un peu lourd, ça nécessite des rubygems et inclut des tonnes d'autres solutions de contournement, alors que je veux juste que Je veux juste que require fonctionne avec des fichiers relatifs.

Vous avez déjà répondu à la question de savoir pourquoi ces options ne sont pas les meilleures.

vérifie si RUBY_VERSION < 1.9.2, puis définit require_relative comme suit require, utiliser require_relative partout où c'est nécessaire par la suite

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

Cela peut fonctionner, mais il existe un moyen plus sûr et plus rapide : traiter 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

5voto

nilbus Points 5476

Je suis un adepte de l'utilisation de la gemme rbx-require-relative ( source ). Il a été écrit à l'origine pour Rubinius, mais il supporte également MRI 1.8.7 et ne fait rien en 1.9.2. Exiger une gem est simple, et je n'ai pas besoin de jeter des bouts de code dans mon projet.

Ajoutez-le à votre fichier Gemfile :

gem "rbx-require-relative"

Dans ce cas require 'require_relative' avant de vous 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 toutes, IMO, et la gemme n'est pas aussi lourde que backports.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X