Ruby: require vs require_relative - best practice to workaround running in both Ruby <1>=1.9.2

后端 未结 11 1819
臣服心动
臣服心动 2020-11-27 08:50

What is the best practice if I want to require a relative file in Ruby and I want it to work in both 1.8.x and >=1.9.2?

I see a few options:

相关标签:
11条回答
  • 2020-11-27 09:44

    Ruby on Rails way:

    config_path = File.expand_path("../config.yml", __FILE__)
    
    0 讨论(0)
  • 2020-11-27 09:45

    One issue I've not seen pointed out with the solutions based on __FILE__ is that they break with regards to symlinks. For example say I have:

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

    The main script, the entry point, the application is foo.rb. This file is linked to ~/Scripts/foo which is in my $PATH. This require statement is broken when I execute 'foo':

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

    Because __FILE__ is ~/Scripts/foo so the require statement above looks for ~/Scripts/foo/lib/someinclude.rb which obviously doesn't exist. The solution is simple. If __FILE__ is a symbolic link it needs to be dereferenced. Pathname#realpath will help us with this situation:

    require "pathname"
    require File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")
    
    0 讨论(0)
  • 2020-11-27 09:47

    Before I made the jump to 1.9.2 I used the following for relative requires:

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

    It's a bit weird the first time you see it, because it looks like there's an extra '..' at the start. The reason is that expand_path will expand a path relative to the second argument, and the second argument will be interpreted as if it were a directory. __FILE__ obviously isn't a directory, but that doesn't matter since expand_path doesn't care if the files exist or not, it will just apply some rules to expand things like .., . and ~. If you can get over the initial "waitaminute isn't there an extra .. there?" I think that the line above works quite well.

    Assuming that __FILE__ is /absolute/path/to/file.rb, what happens is that expand_path will construct the string /absolute/path/to/file.rb/../relative/path, and then apply a rule that says that .. should remove the path component before it (file.rb in this case), returning /absolute/path/to/relative/path.

    Is this best practice? Depends on what you mean by that, but it seems like it's all over the Rails code base, so I'd say it's at least a common enough idiom.

    0 讨论(0)
  • 2020-11-27 09:49

    I'm a fan of using the rbx-require-relative gem (source). It was originally written for Rubinius, but it also supports MRI 1.8.7 and does nothing in 1.9.2. Requiring a gem is simple, and I don't have to throw code snippets into my project.

    Add it to your Gemfile:

    gem "rbx-require-relative"
    

    Then require 'require_relative' before you require_relative.

    For example, one of my test files looks like this:

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

    This is the cleanest solution out of any of these IMO, and the gem isn't as heavy as backports.

    0 讨论(0)
  • 2020-11-27 09:50

    I would define my own relative_require if it doesn't exist (i.e. under 1.8) and then use the same syntax everywhere.

    0 讨论(0)
提交回复
热议问题