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

后端 未结 11 1817
臣服心动
臣服心动 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:31

    If you were building a gem, you would not want to pollute the load path.

    But, In the case of a standalone application it is very convenient to just add the current directory to the load path as you do in the first 2 examples.

    My vote goes to the first option on the list.

    I would love to see some solid Ruby best practices literature.

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

    The Pickaxe has a snippet for this for 1.8. Here it is:

    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
    

    It basically just uses what Theo answered, but so you can still use require_relative.

    0 讨论(0)
  • 2020-11-27 09:39
    $LOAD_PATH << '.'
    
    $LOAD_PATH << File.dirname(__FILE__)
    

    It's not a good security habit: why should you expose your whole directory?

    require './path/to/file'
    

    This doesn't work if RUBY_VERSION < 1.9.2

    use weird constructions such as

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

    Even weirder construction:

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

    Use backports gem - it's kind of heavy, it requires rubygems infrastructure and includes tons of other workarounds, while I just want require to work with relative files.

    You have already answered why these are not the best options.

    check if RUBY_VERSION < 1.9.2, then define require_relative as require, use require_relative everywhere where it's needed afterwards

    check if require_relative already exists, if it does, try to proceed as in previous case

    This may work, but there's safer and quicker way: to deal with the LoadError exception:

    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
    
    0 讨论(0)
  • 2020-11-27 09:40

    A workaround for this was just added to the 'aws' gem so thought I'd share as it was inspired by this post.

    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
    

    This allows you to use require_relative as you would in ruby 1.9.2 in ruby 1.8 and 1.9.1.

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

    The backports gem now allows individual loading of backports.

    You could then simply:

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

    This require will not affect newer versions, nor will it update any other builtin methods.

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

    Another option is to tell the interpreter which paths to search

    ruby -I /path/to/my/project caller.rb
    
    0 讨论(0)
提交回复
热议问题