Ruby: membutuhkan vs require_relative - praktik terbaik untuk menyelesaikan masalah di Ruby <1.9.2 dan> = 1.9.2

153

Apa praktik terbaik jika saya ingin requirefile relatif di Ruby dan saya ingin itu berfungsi di 1.8.x dan> = 1.9.2?

Saya melihat beberapa opsi:

  • lakukan saja $LOAD_PATH << '.'dan lupakan semuanya
  • melakukan $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • periksa apakah RUBY_VERSION<1.9.2, lalu definisikan require_relativesebagai require, gunakan di require_relativemana-mana yang diperlukan setelahnya
  • periksa apakah require_relativesudah ada, jika sudah, coba untuk melanjutkan seperti pada kasus sebelumnya
  • gunakan konstruksi aneh seperti - sayangnya mereka sepertinya tidak bekerja di Ruby 1.9 secara menyeluruh, karena, misalnya:
    require File.join(File.dirname(__FILE__), 'path/to/file')
    $ 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>'
  • Bahkan konstruksi yang lebih aneh: tampaknya berfungsi, tetapi aneh dan tidak cukup tampan.
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')
  • Gunakan backports gem - ini agak berat, membutuhkan infrastruktur rubygem dan memasukkan banyak solusi lain, sementara saya hanya ingin requirebekerja dengan file relatif.

Ada pertanyaan terkait erat di StackOverflow yang memberikan beberapa contoh lagi, tetapi itu tidak memberikan jawaban yang jelas - yang merupakan praktik terbaik.

Apakah ada solusi universal yang layak dan diterima oleh semua orang untuk membuat aplikasi saya berjalan pada Ruby <1.9.2 dan> = 1.9.2?

MEMPERBARUI

Klarifikasi: Saya tidak hanya menginginkan jawaban seperti "Anda dapat melakukan X" - pada kenyataannya, saya telah menyebutkan sebagian besar pilihan yang dipertanyakan. Saya ingin alasan , yaitu mengapa itu adalah praktik terbaik, apa pro dan kontra dan mengapa harus dipilih di antara yang lain.

GreyCat
sumber
3
Hai, saya baru. Bisakah seseorang menjelaskan dari awal — apa perbedaan antara requiredan require_relative?
Kolonel Panic
3
Di Ruby 1.8 yang lebih lama jika Anda menjalankan file a.rbdan ingin membuat penerjemah membaca dan mem-parsing isi file b.rbdalam direktori saat ini (biasanya dir yang sama dengan a.rb), Anda hanya akan menulis require 'b'dan itu akan baik-baik saja karena jalur pencarian default termasuk direktori saat ini. Dalam Ruby 1.9 yang lebih modern, Anda harus menulis require_relative 'b'dalam hal ini, karena require 'b'hanya akan mencari di jalur perpustakaan standar. Itulah hal yang merusak kompatibilitas maju dan mundur untuk skrip sederhana yang tidak akan diinstal dengan benar (misalnya, instal skrip sendiri).
GreyCat
Anda sekarang dapat menggunakan backportshanya untuk require_relative, lihat jawaban saya ...
Marc-André Lafortune

Jawaban:

64

Solusi untuk ini baru saja ditambahkan ke permata 'aws' jadi saya pikir saya akan membagikannya karena terinspirasi oleh pos ini.

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

Ini memungkinkan Anda untuk menggunakan require_relativeseperti yang Anda lakukan di ruby ​​1.9.2 di ruby ​​1.8 dan 1.9.1.

Travis Reeder
sumber
3
Bagaimana Anda memerlukan file require_relative.rb? Anda harus meminta require_relative.rb dan kemudian memerlukan_relative sisanya dari yang diperlukan. Atau apakah saya melewatkan sesuatu?
ethishack3r
7
The require_relativeFungsi termasuk dalam proyek perluasan ke perpustakaan inti Ruby, ditemukan di sini: rubyforge.org/projects/extensions Anda harus dapat menginstal mereka dengan gem install extensions. Kemudian dalam kode Anda tambahkan baris berikut sebelum require_relative: memerlukan 'ekstensi / semua' (bersumber dari posting Aurril di sini )
thegreendroid
@ ethhack3r cukup salin dan tempel kode itu di bagian atas skrip ruby ​​Anda atau jika di rails, masukkan ke dalam environment.rb atas atau apalah.
Travis Reeder
46

Sebelum saya melakukan lompatan ke 1.9.2 saya menggunakan yang berikut ini untuk keperluan relatif:

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

Agak aneh pertama kali Anda melihatnya, karena sepertinya ada tambahan '..' di awal. Alasannya adalah bahwa expand_pathakan memperluas path relatif ke argumen kedua, dan argumen kedua akan ditafsirkan seolah-olah itu adalah direktori. __FILE__jelas bukan direktori, tapi itu tidak masalah karena expand_pathtidak peduli apakah ada file atau tidak, itu hanya akan menerapkan beberapa aturan untuk memperluas hal-hal seperti .., .dan ~. Jika Anda bisa melupakan awal "waitaminute tidak ada tambahan di ..sana?" Saya pikir kalimat di atas bekerja dengan baik.

Dengan asumsi bahwa __FILE__adalah /absolute/path/to/file.rb, apa yang terjadi adalah bahwa expand_pathakan membangun string /absolute/path/to/file.rb/../relative/path, dan kemudian menerapkan aturan yang mengatakan bahwa ..harus menghapus komponen path sebelum ( file.rbdalam kasus ini), kembali /absolute/path/to/relative/path.

Apakah ini praktik terbaik? Tergantung pada apa yang Anda maksud dengan itu, tetapi sepertinya itu semua di atas basis kode Rails, jadi saya akan mengatakan itu setidaknya idiom yang cukup umum.

Theo
sumber
1
Saya melihat ini secara umum juga. Itu jelek, tetapi tampaknya bekerja dengan baik.
yfeldblum
12
sedikit lebih bersih: memerlukan File.expand_path ('relatif / path', File.dirname ( FILE ))
Yannick Wurm
1
Saya tidak berpikir itu jauh lebih bersih, itu hanya lebih lama. Keduanya sangat jelek, dan ketika memilih di antara dua opsi yang buruk, saya lebih memilih yang membutuhkan pengetikan yang lebih sedikit.
Theo
6
Tampaknya File.expand_path ('../ relpath.x', File.dirname ( FILE )) adalah idiom yang lebih baik, meskipun lebih verbose. Mengandalkan fungsionalitas path file yang bisa dibilang rusak ditafsirkan sebagai path direktori dengan direktori tidak ada tambahan mungkin rusak ketika / jika fungsionalitas itu diperbaiki.
jpgeek
1
Rusak, mungkin, tapi sudah seperti itu selamanya di UNIX. Tidak ada perbedaan antara direktori dan file ketika datang ke jalur dan resolusi '..' - jadi saya tidak kehilangan tidur di atasnya.
Theo
6

Beliung memiliki cuplikan untuk 1,8. Ini dia:

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

Ini pada dasarnya hanya menggunakan apa yang dijawab Theo, tetapi Anda masih bisa menggunakannya require_relative.

Paul Hoffer
sumber
Bagaimana cara memeriksa apakah cuplikan ini harus diaktifkan atau tidak dengan benar? Menggunakan $RUBY_VERSIONatau dengan memeriksa apakah require_relativeada secara langsung?
GreyCat
1
Selalu tipe bebek, periksa apakah require_relativesudah ditentukan.
Theo
@Theo @GreyCat ya, saya akan memeriksa apakah diperlukan. Saya baru saja meletakkan cuplikan di sini untuk diperlihatkan orang. Secara pribadi, saya menggunakan jawaban Greg, saya benar-benar hanya memposting ini karena seseorang telah menyebutkannya tanpa memilikinya sendiri.
Paul Hoffer
6
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

Ini bukan kebiasaan keamanan yang baik: mengapa Anda harus mengekspos seluruh direktori Anda?

require './path/to/file'

Ini tidak berfungsi jika RUBY_VERSION <1.9.2

gunakan konstruksi aneh seperti

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

Konstruksi yang lebih aneh:

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

Gunakan backports gem - ini agak berat, membutuhkan infrastruktur rubygem dan menyertakan banyak solusi lain, sementara saya hanya ingin bekerja dengan file relatif.

Anda sudah menjawab mengapa ini bukan pilihan terbaik.

periksa apakah RUBY_VERSION <1.9.2, lalu tentukan require_relative seperti yang diperlukan, gunakan require_relative di mana saja yang diperlukan setelahnya

periksa apakah require_relative sudah ada, jika ya, coba lanjutkan seperti pada kasus sebelumnya

Ini mungkin berhasil, tetapi ada cara yang lebih aman dan lebih cepat: untuk menghadapi pengecualian 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
Claudio Floreani
sumber
5

Saya penggemar menggunakan permata rbx-membutuhkan-relatif ( sumber ). Awalnya ditulis untuk Rubinius, tetapi juga mendukung MRI 1.8.7 dan tidak melakukan apa pun di 1.9.2. Membutuhkan permata itu sederhana, dan saya tidak perlu membuang potongan kode ke proyek saya.

Tambahkan ke Gemfile Anda:

gem "rbx-require-relative"

Lalu require 'require_relative'sebelum Anda require_relative.

Misalnya, salah satu file pengujian saya terlihat seperti ini:

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

Ini adalah solusi terbersih dari semua IMO ini, dan permata itu tidak seberat backports.

Edward Anderson
sumber
4

The backportspermata sekarang memungkinkan pemuatan individu backports.

Anda kemudian dapat dengan mudah:

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

Ini requiretidak akan memengaruhi versi yang lebih baru, juga tidak akan memperbarui metode bawaan lainnya.

Marc-André Lafortune
sumber
3

Pilihan lain adalah memberi tahu penerjemah jalur mana yang harus dicari

ruby -I /path/to/my/project caller.rb
Eradman
sumber
3

Satu masalah yang belum saya lihat ditunjukkan dengan solusi berdasarkan __FILE__ adalah bahwa mereka putus berkaitan dengan symlinks. Misalnya katakan saya punya:

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

Script utama, titik masuk, aplikasi adalah foo.rb. File ini ditautkan ke ~ / Scripts / foo yang ada di $ PATH saya. Pernyataan persyaratan ini rusak ketika saya menjalankan 'foo':

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

Karena __FILE__ adalah ~ / Scripts / foo sehingga pernyataan yang diperlukan di atas mencari ~ / Scripts / foo / lib / someinclude.rb yang jelas tidak ada. Solusinya sederhana. Jika __FILE__ adalah tautan simbolik, tautan tersebut perlu ditinjau ulang. Pathname # realpath akan membantu kami dengan situasi ini:

membutuhkan "pathname"
memerlukan File.join (File.dirname (Pathname.new (__ FILE __). realpath), "lib / someinclude")
jptro
sumber
2

Jika Anda sedang membangun permata, Anda tidak ingin mencemari jalur muatan.

Tetapi, dalam kasus aplikasi mandiri, sangat mudah untuk hanya menambahkan direktori saat ini ke path load seperti yang Anda lakukan dalam 2 contoh pertama.

Pilihan saya masuk ke opsi pertama dalam daftar.

Saya ingin melihat beberapa literatur praktik terbaik Ruby yang solid.

Casey Watson
sumber
1
Re: "Saya akan senang melihat beberapa literatur praktik terbaik Ruby yang solid." Anda dapat mengunduh Ruby Best Practices Gregory Brown . Anda juga dapat melihat situs Praktik Terbaik Rails .
Michael Stalker
1

Saya akan mendefinisikan sendiri relative_requirejika tidak ada (yaitu di bawah 1,8) dan kemudian menggunakan sintaks yang sama di mana-mana.

Phrogz
sumber
0

Cara Ruby on Rails:

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