Ruby: require vs require_relative: la mejor práctica para ejecutar una solución en Ruby<1.9.2 y>=1.9.2
ruby-1.9 ruby-1.8 (11)
$LOAD_PATH << ''.'' $LOAD_PATH << File.dirname(__FILE__)
No es un buen hábito de seguridad: ¿por qué debería exponer todo su directorio?
require ''./path/to/file''
Esto no funciona si RUBY_VERSION <1.9.2
usa construcciones extrañas como
require File.join(File.dirname(__FILE__), ''path/to/file'')
Incluso una construcción más extraña:
require File.join(File.expand_path(File.dirname(__FILE__)), ''path/to/file'')
Use la gema backports, es algo pesado, requiere infraestructura de rubygems e incluye toneladas de otras soluciones, mientras que solo quiero que se requiera trabajar con archivos relativos.
Ya has respondido por qué estas no son las mejores opciones.
compruebe si RUBY_VERSION <1.9.2, luego defina require_relative como require, use require_relative donde sea necesario después
compruebe si require_relative ya existe, si lo hace, intente continuar como en el caso anterior
Esto puede funcionar, pero hay una forma más segura y rápida de lidiar con la excepción 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
¿Cuál es la mejor práctica si quiero require
un archivo relativo en Ruby y quiero que funcione tanto en 1.8.x como en> = 1.9.2?
Veo algunas opciones:
- solo haga
$LOAD_PATH << ''.''
y olvida todo - do
$LOAD_PATH << File.dirname(__FILE__)
-
require ''./path/to/file''
- compruebe si
RUBY_VERSION
<1.9.2, luego definarequire_relative
comorequire
, userequire_relative
donde sea necesario después - compruebe si
require_relative
ya existe, si lo hace, intente continuar como en el caso anterior - usa construcciones extrañas como
require File.join(File.dirname(__FILE__), ''path/to/file'')
- por desgracia, parece que no funcionan en Ruby 1.9 a fondo, porque, por ejemplo:$ 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>''
- Incluso una construcción más extraña:
require File.join(File.expand_path(File.dirname(__FILE__)), ''path/to/file'')
parece funcionar, pero es raro y no se ve bien. - Use la gema backports , es algo pesado, requiere infraestructura de rubygems e incluye toneladas de otras soluciones, mientras que solo quiero que se
require
trabajar con archivos relativos.
Hay una pregunta estrechamente relacionada en StackOverflow que da algunos ejemplos más, pero no da una respuesta clara, que es una mejor práctica.
¿Hay alguna solución universal decente aceptada por todos para hacer que mi aplicación se ejecute en Ruby <1.9.2 y> = 1.9.2?
ACTUALIZAR
Aclaración: no quiero simplemente respuestas como "puedes hacer X"; de hecho, ya he mencionado la mayoría de las opciones en cuestión. Quiero razonar , es decir, por qué es una buena práctica, cuáles son sus ventajas y desventajas y por qué debería elegirse entre los demás.
Antes de dar el salto a 1.9.2 usé lo siguiente para lo relativo:
require File.expand_path(''../relative/path'', __FILE__)
Es un poco raro la primera vez que lo ves, porque parece que hay un ''..'' extra al principio. El motivo es que expand_path
expandirá una ruta relativa al segundo argumento, y el segundo argumento se interpretará como si fuera un directorio. __FILE__
obviamente no es un directorio, pero eso no importa ya que expand_path
no se preocupa si los archivos existen o no, solo aplicará algunas reglas para expandir cosas como ..
, .
y ~
. Si puede superar el "waitaminute" inicial, ¿no hay un extra ..
allí? Creo que la línea anterior funciona bastante bien.
Suponiendo que __FILE__
es /absolute/path/to/file.rb
, lo que sucede es que expand_path
construirá la cadena /absolute/path/to/file.rb/../relative/path
, y luego aplicará una regla que diga que ..
debería eliminar el componente de ruta antes de él ( file.rb
en este caso), file.rb
/absolute/path/to/relative/path
.
¿Es esta la mejor práctica? Depende de lo que quieras decir con eso, pero parece que está en toda la base de código de Rails, así que diría que es al menos una expresión lo suficientemente común.
Definiría mi propio relative_require
si no existe (es decir, bajo 1.8) y luego usaría la misma sintaxis en todos lados.
El Pico tiene un fragmento para esto para 1.8. Aquí está:
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
Básicamente solo usa lo que Theo respondió, pero todavía puedes usar require_relative
.
La gema backports
ahora permite la carga individual de backports.
Entonces podrías simplemente:
require ''backports/1.9.1/kernel/require_relative''
# => Now require_relative works for all versions of Ruby
Esto no afectará a las versiones más nuevas ni actualizará ningún otro método incorporado.
Otra opción es decirle al intérprete qué rutas buscar
ruby -I /path/to/my/project caller.rb
Ruby on Rails way:
config_path = File.expand_path("../config.yml", __FILE__)
Si estuvieras construyendo una gema, no querrías contaminar el camino de la carga.
Pero, en el caso de una aplicación independiente, es muy conveniente agregar el directorio actual a la ruta de carga como lo hace en los primeros 2 ejemplos.
Mi voto va a la primera opción en la lista.
Me encantaría ver algunas publicaciones sólidas sobre mejores prácticas de Ruby.
Soy un fanático de usar la gema rbx-require-relative ( source ). Originalmente fue escrito para Rubinius, pero también es compatible con MRI 1.8.7 y no hace nada en 1.9.2. Requirir una gema es simple, y no tengo que lanzar fragmentos de código en mi proyecto.
Agrégalo a tu Gemfile:
gem "rbx-require-relative"
Luego, require ''require_relative''
antes de require_relative
.
Por ejemplo, uno de mis archivos de prueba se ve así:
require ''rubygems''
require ''bundler/setup''
require ''minitest/autorun''
require ''require_relative''
require_relative ''../lib/foo''
Esta es la solución más limpia de cualquiera de estos IMO, y la gema no es tan pesada como los backports.
Un problema que no he visto señalar con las soluciones basadas en __FILE__ es que se rompen con respecto a los enlaces simbólicos. Por ejemplo, decir que tengo:
~/Projects/MyProject/foo.rb
~/Projects/MyProject/lib/someinclude.rb
El script principal, el punto de entrada, la aplicación es foo.rb. Este archivo está vinculado a ~ / Scripts / foo que está en mi $ PATH. Esta declaración require se rompe cuando ejecuto ''foo'':
require File.join(File.dirname(__FILE__), "lib/someinclude")
Porque __FILE__ es ~ / Scripts / foo, por lo que la instrucción require anterior busca ~ / Scripts / foo / lib / someinclude.rb que obviamente no existe. La solución es simple. Si __FILE__ es un enlace simbólico, debe ser desreferenciado. Pathname # realpath nos ayudará con esta situación:
require "pathname" require File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")
Una solución para esto fue agregada a la gema ''Aws'', así que pensé en compartirla ya que fue inspirada por esta publicación.
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
Esto le permite usar require_relative
como lo haría en ruby 1.9.2 en ruby 1.8 y 1.9.1.