Hinzufügen eines Verzeichnisses zu $ ​​LOAD_PATH (Ruby)

95

Ich habe zwei häufig verwendete Techniken zum Hinzufügen des Verzeichnisses der aktuell ausgeführten Datei zu $ ​​LOAD_PATH (oder $ :) gesehen. Ich sehe die Vorteile, wenn Sie nicht mit einem Edelstein arbeiten. Einer scheint natürlich ausführlicher zu sein als der andere, aber gibt es einen Grund, mit einem über den anderen zu gehen?

Die erste, ausführliche Methode (könnte übertrieben sein):

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

und das unkompliziertere, schnellere und schmutzigere:

$:.unshift File.dirname(__FILE__)

Gibt es einen Grund, übereinander zu gehen?

Mark W.
quelle
2
Eine etwas weniger ausführliche Version der ausführlichen ist:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan Long
Wie wäre es mit der "Es sei denn" -Klausel? Wie können die beiden oben genannten gleichwertig sein?
Inger
Als jemand, der hierher gekommen ist, um zu verstehen, wie man das benutzt, ist es super kryptisch. Ich sehe in den Beispielen nicht, woher der Verzeichnisname kommt. Ich würde mich freuen, wenn jemand dies klarstellen könnte.
SlySherZ
1
Durch die Verwendung __dir__(ab Ruby 2.0) kann eine dieser Funktionen präziser gestaltet werden.
Nathan Long

Antworten:

50

Ich würde sagen, gehen Sie mit $:.unshift File.dirname(__FILE__)dem anderen über, einfach weil ich gesehen habe, dass es im Code viel häufiger verwendet wird als im $LOAD_PATHeinen, und es ist auch kürzer!

Ryan Bigg
quelle
Als ich mit Ruby anfing, dachte ich offensichtlich, dass $ LOAD_PATH besser ist. Aber wenn Sie den Anfängerstatus erreicht haben, würde ich $ LOAD_PATH nur verwenden, wenn ich versuchen würde, meinen Code für Anfänger besser lesbar zu machen. Meh es ist ein Kompromiss. Es hängt davon ab, wie "öffentlich" der Code ist, solange die Speichernutzung für jeden gleich ist, was ich im Wesentlichen annehme.
boulder_ruby
9
Hängt von der Stilrichtlinie ab, die Sie für Ihr Projekt befolgen. Im beliebten Ruby Style Guide heißt es: "Vermeiden Sie die Verwendung von speziellen Variablen im Perl-Stil (wie $:, $;, usw.). Sie sind ziemlich kryptisch und von ihrer Verwendung in alles andere als einzeiligen Skripten wird abgeraten."
Bobmagoo
152

Der Ruby-Ladepfad wird sehr häufig als $: geschrieben, aber nur weil er kurz ist, wird er nicht besser. Wenn Sie Klarheit der Klugheit vorziehen oder wenn die Kürze um ihrer selbst willen Sie jucken lässt, müssen Sie dies nicht tun, nur weil es alle anderen tun. Sag Hallo zu ...

$LOAD_PATH

... und verabschieden Sie sich von ...

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

quelle
29
Außerdem ist es für Google viel schwieriger, Zeichenfolgen wie "$:" zu verwenden, die nur Symbole enthalten.
DSimon
23

Ich mag den "schnellen und schmutzigen" Weg nicht besonders. Jeder, der neu bei Ruby ist, wird darüber nachdenken, was $:.ist.

Ich finde das offensichtlicher.

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

Oder wenn es mir wichtig ist, den vollen Weg zu haben ...

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

UPDATE 10.09.2009

In letzter Zeit habe ich Folgendes gemacht:

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

Ich habe es in einer ganzen Reihe verschiedener Ruby-Projekte gesehen, als ich GitHub durchsucht habe.

Scheint die Konvention zu sein?

Luke Antins
quelle
@LukeAntins, das ist wirklich toll, aber wo soll ich load_path in der Anwendung "booten"?
Gaußblurinc
@gaussblurinc Irgendwo 'ganz oben' in Ihrer Bibliothek / Anwendung, aber es kommt wirklich darauf an. Wenn Sie eine binDatei hatten, die immer relativ zu Ihrer war codeund immer nur von der binDatei ausgeführt wurde ... Bootstrap im Bin. Wenn Sie eine Bibliothek haben, booten Sie oben in Ihrem Bibliothekscode wie in lib/code.rb, um Zugriff auf alles unter zu erhalten lib/code/. Hoffe, diese Wanderung hilft!
Luke Antins
1
RuboCop informiert mich darüber, dass __dir__ein Pfad zum Verzeichnis der aktuellen Datei abgerufen werden kann.
Raphael
8

Wenn Sie script/consoleIhr Rails-Projekt eingeben und eingeben $:, erhalten Sie ein Array, das alle zum Laden von Ruby erforderlichen Verzeichnisse enthält. Das Mitnehmen von dieser kleinen Übung ist, dass $:es sich um ein Array handelt. In diesem Fall können Sie Funktionen ausführen, z. B. andere Verzeichnisse mit der unshiftMethode oder dem <<Operator voranstellen . Wie Sie in Ihrer Aussage angedeutet haben $:und gleich $LOAD_PATHsind.

Der Nachteil, wie Sie bereits erwähnt haben, ist folgender: Wenn Sie das Verzeichnis bereits in Ihrem Startpfad haben, wiederholt es sich.

Beispiel:

Ich habe ein Plugin namens todo erstellt. Mein Verzeichnis ist wie folgt aufgebaut:

/---Verkäufer
  |
  | --- / Plugins
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- / Modelle
                          | --- / Steuerungen
              |
              | --- / Schienen
                    |
                    | --- init.rb.

In die Datei init.rb habe ich folgenden Code eingegeben:

## 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 

Beachten Sie, wie ich den Codeblock anweise, die Aktionen innerhalb des Blocks für die Zeichenfolgen "Modelle", "Controller" und "Modelle" auszuführen, wobei ich "Modelle" wiederhole. (FYI, %w{ ... }ist nur eine andere Möglichkeit, Ruby anzuweisen, eine Reihe von Zeichenfolgen zu halten). Wenn ich laufe script/console, gebe ich Folgendes ein:

>> puts $:

Und ich tippe dies ein, damit es einfacher ist, den Inhalt der Zeichenfolge zu lesen. Die Ausgabe, die ich bekomme, ist:

...
...
./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

Wie Sie sehen, ist dies zwar ein so einfaches Beispiel, das ich während der Verwendung eines Projekts erstellen könnte, an dem ich gerade arbeite. Wenn Sie jedoch nicht aufpassen, führt der schnelle und schmutzige Weg zu wiederholten Pfaden. Der längere Weg sucht nach wiederholten Pfaden und stellt sicher, dass sie nicht auftreten.

Wenn Sie ein erfahrener Rails-Programmierer sind, haben Sie wahrscheinlich eine sehr gute Vorstellung davon, was Sie tun, und machen wahrscheinlich nicht den Fehler, Pfade zu wiederholen. Wenn Sie ein Neuling sind, würde ich den längeren Weg gehen, bis Sie wirklich verstehen, was Sie tun.

Dyba
quelle
Ihre Antwort sehr hilfreich und auch gut erklärt. Vorgeschlagene Bearbeitung: Die Methode load_pathsund load_once_paths.deletesind veraltet. Würde geholfen werden, die Zeilen, die auf sie verweisen, wie ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
folgt
8

Am besten, ich bin auf das Hinzufügen eines Verzeichnisses über einen relativen Pfad gestoßen, wenn ich Rspec verwende. Ich finde es ausführlich genug, aber auch immer noch ein schöner Einzeiler.

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

Es gibt ein Juwel, mit dem Sie Ihren Ladepfad mit schönerem und saubererem Code einrichten können. Überprüfen Sie dies: https://github.com/nayyara-samuel/load-path .

Es hat auch eine gute Dokumentation

Rubyist
quelle
-2

Ich weiß, dass diese Frage schon lange nicht mehr gestellt wurde, aber ich habe eine zusätzliche Antwort, die ich teilen möchte.

Ich habe mehrere Ruby-Anwendungen, die über mehrere Jahre von einem anderen Programmierer entwickelt wurden, und sie verwenden dieselben Klassen in den verschiedenen Anwendungen erneut, obwohl sie möglicherweise auf dieselbe Datenbank zugreifen. Da dies gegen die DRY-Regel verstößt, habe ich beschlossen, eine Klassenbibliothek zu erstellen, die von allen Ruby-Anwendungen gemeinsam genutzt wird. Ich hätte es in die Ruby-Hauptbibliothek stellen können, aber das würde benutzerdefinierten Code in der allgemeinen Codebasis verbergen, was ich nicht wollte.

Ich hatte ein Problem, bei dem ein Namenskonflikt zwischen einem bereits definierten Namen "profile.rb" und einer von mir verwendeten Klasse bestand. Dieser Konflikt war kein Problem, bis ich versuchte, die allgemeine Codebibliothek zu erstellen. Normalerweise durchsucht Ruby zuerst die Anwendungsspeicherorte und wechselt dann zu den Speicherorten $ LOAD_PATH.

Die application_controller.rb konnte die von mir erstellte Klasse nicht finden und hat einen Fehler in der ursprünglichen Definition ausgelöst, da es sich nicht um eine Klasse handelt. Da ich die Klassendefinition aus dem Abschnitt app / models der Anwendung entfernt habe, konnte Ruby sie dort nicht finden und suchte sie in den Ruby-Pfaden.

Daher habe ich die Variable $ LOAD_PATH so geändert, dass sie einen Pfad zu dem von mir verwendeten Bibliotheksverzeichnis enthält. Dies kann zur Initialisierungszeit in der Datei environment.rb erfolgen.

Selbst mit dem neuen Verzeichnis, das dem Suchpfad hinzugefügt wurde, gab Ruby einen Fehler aus, da die systemdefinierte Datei bevorzugt zuerst verwendet wurde. Der Suchpfad in der Variablen $ LOAD_PATH durchsucht vorzugsweise zuerst die Ruby-Pfade.

Daher musste ich die Suchreihenfolge ändern, damit Ruby die Klasse in meiner gemeinsamen Bibliothek fand, bevor sie die integrierten Bibliotheken durchsuchte.

Dieser Code hat es in der Datei environment.rb getan:

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

Ich glaube nicht, dass Sie eines der zuvor auf dieser Ebene angegebenen erweiterten Codierungskonstrukte verwenden können, aber es funktioniert einwandfrei, wenn Sie zur Initialisierungszeit etwas in Ihrer App einrichten möchten. Sie müssen die ursprüngliche Reihenfolge der ursprünglichen Variablen $ LOAD_PATH beibehalten, wenn sie wieder zur neuen Variablen hinzugefügt wird, da sonst einige der wichtigsten Ruby-Klassen verloren gehen.

In der Datei application_controller.rb verwende ich einfach a

require 'profile'
require 'etc' #etc

Dadurch werden die benutzerdefinierten Bibliotheksdateien für die gesamte Anwendung geladen, dh ich muss nicht in jedem Controller erforderliche Befehle verwenden.

Für mich war dies die Lösung, nach der ich gesucht hatte, und ich dachte, ich würde sie dieser Antwort hinzufügen, um die Informationen weiterzugeben.

Timothy Dooling
quelle