webentwicklung-frage-antwort-db.com.de

Ruby: require vs require_relative - bewährte Methode zur Umgehung der Ausführung in Ruby <1.9.2 und> = 1.9.2

Was ist die beste Vorgehensweise, wenn ich eine relative Datei in den Formaten Ruby und require erstellen möchte? funktioniert es sowohl in 1.8.x als auch in> = 1.9.2?

Ich sehe ein paar Möglichkeiten:

  • tu einfach $LOAD_PATH << '.' und vergiss alles
  • do $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • überprüfen Sie, ob Ruby_VERSION <1.9.2 ist, und definieren Sie require_relative als require. Verwenden Sie require_relative überall dort, wo es später benötigt wird
  • überprüfen Sie, ob require_relative bereits vorhanden ist. Wenn dies der Fall ist, versuchen Sie, wie im vorherigen Fall vorzugehen
  • verwende seltsame Konstruktionen wie
    require File.join(File.dirname(__FILE__), 'path/to/file') </ code>
    - leider scheinen sie nicht vollständig in Ruby 1.9) zu funktionieren, weil zum Beispiel:
    $ 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>
  • Noch seltsamerer Aufbau:
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file') </ code>
    scheint zu funktionieren, aber es ist seltsam und sieht nicht ganz gut aus.
  • Verwenden Sie backports gem - es ist ziemlich schwer, erfordert eine Rubygems-Infrastruktur und enthält Tonnen anderer Problemumgehungen, während ich nur möchte, dass require mit relativen Dateien arbeitet.

Es gibt eine eng verwandte Frage bei StackOverflow , die einige weitere Beispiele gibt, aber keine klare Antwort gibt - was eine bewährte Methode ist.

Gibt es eine anständige, von allen akzeptierte universelle Lösung, mit der meine Anwendung auf beiden Ruby <1.9.2 und> = 1.9.2 ausgeführt werden kann?

AKTUALISIEREN

Klarstellung: Ich möchte nicht nur Antworten wie "Sie können X tun" - tatsächlich habe ich bereits die meisten in Frage kommenden Entscheidungen erwähnt. Ich möchte Begründung , dh warum es ist eine bewährte Vorgehensweise, was sind seine Vor- und Nachteile und warum es unter den anderen gewählt werden sollte.

153
GreyCat

Ein Workaround für dieses Problem wurde gerade zu dem Juwel 'aws' hinzugefügt, also dachte ich, ich würde es teilen, da es von diesem Beitrag inspiriert wurde.

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

Dies ermöglicht es Ihnen, require_relative wie in Ruby 1.9.2 in Ruby 1.8 und 1.9.1.

64
Travis Reeder

Bevor ich den Sprung zu 1.9.2 gemacht habe, habe ich folgendes für relative Anforderungen verwendet:

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

Es ist ein bisschen komisch, wenn man es zum ersten Mal sieht, weil es so aussieht, als gäbe es am Anfang ein zusätzliches "..". Der Grund ist, dass expand_path Einen Pfad relativ zum zweiten Argument erweitert und das zweite Argument so interpretiert wird, als wäre es ein Verzeichnis. __FILE__ Ist offensichtlich kein Verzeichnis, aber das spielt keine Rolle, da es expand_path Egal ist, ob die Dateien existieren oder nicht, es werden nur einige Regeln angewendet, um Dinge wie .., . Und ~. Wenn Sie über das anfängliche "waitaminute" hinwegkommen, gibt es da nicht ein zusätzliches ..? Ich denke, dass die obige Zeile ganz gut funktioniert.

Angenommen, __FILE__ Ist /absolute/path/to/file.rb, Dann wird mit expand_path Die Zeichenfolge /absolute/path/to/file.rb/../relative/path Erstellt und anschließend eine Regel angewendet, die besagt, dass .. sollte die Pfadkomponente vorher entfernen (file.rb in diesem Fall) und /absolute/path/to/relative/path zurückgeben.

Ist dies eine bewährte Methode? Kommt darauf an, was du damit meinst, aber es scheint, als ob es überall auf der Rails Codebasis ist, also würde ich sagen, dass es zumindest eine genug verbreitete Redewendung ist.

46
Theo

Die Spitzhacke hat dafür einen Ausschnitt für 1.8. Hier ist es:

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

Es verwendet im Grunde nur das, was Theo geantwortet hat, aber so können Sie immer noch require_relative.

6
Paul Hoffer
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

Es ist keine gute Sicherheitsgewohnheit: Warum sollten Sie Ihr gesamtes Verzeichnis offenlegen?

require './path/to/file'

Dies funktioniert nicht, wenn Ruby_VERSION <1.9.2 ist

verwende seltsame Konstruktionen wie

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

Noch seltsamerer Aufbau:

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

Verwenden Sie Backports Gem - es ist ziemlich schwer, erfordert Rubygems-Infrastruktur und enthält Tonnen von anderen Workarounds, während ich nur mit relativen Dateien arbeiten möchte.

Sie haben bereits geantwortet, warum dies nicht die besten Optionen sind.

überprüfen Sie, ob Ruby_VERSION <1.9.2 ist, und definieren Sie require_relative als require. Verwenden Sie require_relative überall dort, wo es später benötigt wird

überprüfen Sie, ob require_relative bereits vorhanden ist. Wenn dies der Fall ist, versuchen Sie, wie im vorherigen Fall vorzugehen

Das mag funktionieren, aber es gibt einen sichereren und schnelleren Weg: die LoadError-Ausnahme zu behandeln:

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

Ich bin ein Fan von rbx-require-relative gem ( source ). Es wurde ursprünglich für Rubinius geschrieben, unterstützt aber auch MRT 1.8.7 und führt in 1.9.2 nichts aus. Das Anfordern eines Edelsteins ist einfach und ich muss keine Code-Schnipsel in mein Projekt werfen.

Füge es zu deiner Gemfile hinzu:

gem "rbx-require-relative"

Dann require 'require_relative' bevor du require_relative.

Eine meiner Testdateien sieht beispielsweise so aus:

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

Dies ist die sauberste Lösung unter diesen IMOs, und der Edelstein ist nicht so schwer wie Backports.

5
Edward Anderson

Das Juwel backports erlaubt nun das individuelle Laden von Backports.

Sie könnten dann einfach:

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

Dieses require wirkt sich weder auf neuere Versionen aus, noch aktualisiert es andere eingebaute Methoden.

Eine andere Möglichkeit besteht darin, dem Interpreter mitzuteilen, welche Pfade durchsucht werden sollen

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

Ein Problem, auf das ich bei den auf __FILE__ basierenden Lösungen nicht hingewiesen habe, ist, dass sie in Bezug auf Symlinks nicht funktionieren. Zum Beispiel sage ich habe:

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

Das Hauptskript, der Einstiegspunkt und die Anwendung ist foo.rb. Diese Datei ist mit ~/Scripts/foo verknüpft, das sich in meinem $ PATH befindet. Diese require-Anweisung ist fehlerhaft, wenn ich 'foo' ausführe:

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

Da __FILE__ ~/Scripts/foo ist, sucht die obige require-Anweisung nach ~/Scripts/foo/lib/someinclude.rb, das offensichtlich nicht existiert. Die Lösung ist einfach. Wenn __FILE__ eine symbolische Verknüpfung ist, muss sie dereferenziert werden. Der Pfadname # realpath hilft uns in dieser Situation:

 erfordern "Pfadname" 
 erfordern File.join (File.dirname (Pfadname.neu (__ FILE __). Realpath), "lib/someinclude") 
3
jptros

Wenn Sie einen Edelstein bauen, möchten Sie den Ladepfad nicht verschmutzen.

Bei einer eigenständigen Anwendung ist es jedoch sehr praktisch, das aktuelle Verzeichnis wie in den ersten beiden Beispielen zum Ladepfad hinzuzufügen.

Meine Stimme geht an die erste Option auf der Liste.

Ich würde gerne eine solide Ruby Best Practice-Literatur sehen.

2
Casey Watson

Ich würde meinen eigenen relative_require Definieren, wenn er nicht existiert (d. H. Unter 1.8) und dann überall die gleiche Syntax verwenden.

1
Phrogz

Ruby auf Rails Weise:

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