2012-01-17 18 views
24

Estoy tratando de encontrar un método robusto para unir segmentos de ruta de URL parcial juntos. ¿Hay una manera rápida de hacer esto?¿Cómo puedo unir de forma segura segmentos de URL relativos?

He intentado lo siguiente:

puts URI::join('resource/', '/edit', '12?option=test') 

espero:

resource/edit/12?option=test 

Pero me sale el error:

`merge': both URI are relative (URI::BadURIError) 

he utilizado File.join() en el pasado por esto, pero algo no parece correcto usar la biblioteca de archivos para las URL.

+1

"algo no le parece correcto acerca del uso de la biblioteca de archivos para las direcciones URL", eso es correcto. 'File.join' es sensible al sistema operativo y cambiará el carácter utilizado como separador, según el sistema operativo. Eso te daría malos resultados. –

Respuesta

7

El problema es que resource/ es relativo al directorio actual, pero /edit se refiere al directorio de nivel superior debido a la barra diagonal. Es imposible unir los dos directorios sin saber con certeza que edit contiene resource.

Si está buscando operaciones con cadenas, simplemente quite las barras anteriores o posteriores de todas las partes, luego únelas con / como pegamento.

+1

+1, sí, los '/' no son necesarios. –

+0

Bueno, a menos que no esté trabajando en la raíz del dominio, en cuyo caso el '/' inicial en la primera parte marcaría la diferencia. – Tim

+0

Eso es fácil de forzar usando una cadena vacía como primer parámetro. –

17

La API de URI no es necesariamente genial.

URI :: join funcionará solo si el primero comienza como un uri absoluto con protocolo, y los posteriores son relativos de la manera correcta ... excepto que trato de hacer eso y ni siquiera puedo obtenerlo trabajar.

Esto, al menos, no error, pero ¿por qué está faltando el componente medio?

URI::join('http://somewhere.com/resource', './edit', '12?option=test') 

Creo que quizás URI simplemente es una mierda. Carece de aplicaciones significativas en instancias, como una instancia #unión o método para evaluar en relación con una base uri, lo que cabría esperar. Es un poco horrible.

creo que vas a tener que escribir por sí mismo. O simplemente use File.join y otros métodos de ruta de archivo, después de probar todos los casos extremos que pueda pensar para asegurarse de que hace lo que desea/espera.

editar 9 de diciembre de 2016 Me di cuenta de la joya addressable lo hace muy bien.

base = Addressable::URI.parse("http://example.com") 
base + "foo.html" 
# => #<Addressable::URI:0x3ff9964aabe4 URI:http://example.com/foo.html> 

base = Addressable::URI.parse("http://example.com/path/to/file.html") 
base + "relative_file.xml" 
# => #<Addressable::URI:0x3ff99648bc80 URI:http://example.com/path/to/relative_file.xml> 

base = Addressable::URI.parse("https://example.com/path") 
base + "//newhost/somewhere.jpg" 
# => #<Addressable::URI:0x3ff9960c9ebc URI:https://newhost/somewhere.jpg> 

base = Addressable::URI.parse("http://example.com/path/subpath/file.html") 
base + "../up-one-level.html" 
=> #<Addressable::URI:0x3fe13ec5e928 URI:http://example.com/path/up-one-level.html> 
+2

No olvides hacer 'to_s' si necesitas' String' versión de uri, de lo contrario obtienes el objeto 'URI :: HTTP' –

+0

Tienes que agregar una barra inclinada final como' http: // somewhere.com/resource/' – urmurmur

6

La manera de hacerlo es usando URI.join:

URI.join('http://example.com', '/foo/', 'bar')

Prestar atención a las barras al final.Puede encontrar la documentación completa aquí:

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-join

+10

Si necesita administrar las barras inicial y posterior, ¿de qué sirve utilizar este método en primer lugar? – eremzeit

+0

Obtiene un objeto URI :: HTTP en este caso. Aunque tiene un punto válido, y generalmente uso 'File.join ('http://example.com', '/ foo /', 'bar')' cuando necesito una cadena y se encarga de las barras. PD: Trabajo únicamente en sistemas y servidores basados ​​en Linux, por lo que no me enfrento al problema del separador de archivos descrito anteriormente. –

-1

Puede utilizar File.join('resource/', '/edit', '12?option=test')

+9

Esta es una coincidencia afortunada, pero no es el método correcto para llamar para expresar su intención. –

+2

¿Esto no usará \ en Windows? – lpil

2

Usar este código:

File.join('resource/', '/edit', '12?option=test').sub(/^\//, '') 
# => resource/edit/12?option=test 

ejemplo, con cadenas vacías:

File.join('', '/edit', '12?option=test').sub(/^\//, '') 
# => edit/12?option=test 
3

Usando File.join no es robusto ya que Utilizará el separador del sistema de archivos del sistema operativo, que en Windows es \ en lugar de /, y pierde la portabilidad.

Como habrá notado, URI::join no va a combinar trazados con barras repetidas, por lo que no se ajusta a la pieza.

resulta que no requiere una gran cantidad de código Ruby para lograrlo:

module GluePath 

    def self.join(*paths, separator: '/') 
    paths = paths.compact.reject(&:empty?) 
    last = paths.length - 1 
    paths.each_with_index.map { |path, index| 
     _expand(path, index, last, separator) 
    }.join 
    end 

    def self._expand(path, current, last, separator) 
    if path.starts_with?(separator) && current != 0 
     path = path[1..-1] 
    end 

    unless path.ends_with?(separator) || current == last 
     path = [path, separator] 
    end 

    path 
    end 
end 

El algoritmo se encarga de barras consecutivas, conservas de inicio y final rayas verticales, e ignora nil y vacíos cuerdas.

puts GluePath::join('resource/', '/edit', '12?option=test') 

salidas

resource/edit/12?option=test 
1

Haga que uri como URI::Generic o subclase de los mismos

uri.path += '/123' 

disfrutar!

06/25/2016 ACTUALIZACIÓN para la gente escéptica

require 'uri' 
uri = URI('http://ioffe.net/boris') 
uri.path += '/123' 
p uri 

salidas

<URI::HTTP:0x2341a58 URL:http://ioffe.net/boris/123> 

Run me

+0

Esto reemplaza todo el componente de ruta con '/ 123', no extiende la ruta existente. –

+0

No, no lo es. Lo intenté antes de publicar esto. – bioffe

+0

Esto es lo que obtengo: uri = URI ("httpx: //example.com/mypath/"); uri + = '/ 123' => #

0

mejoré el guión de @Maximo Mussini para que funciona con gracia:

SmartURI.join('http://example.com/subpath', 'hello', query: { token: secret }) 
=> "http://example.com/subpath/hello?token=secret" 

https://gist.github.com/zernel/0f10c71f5a9e044653c1a65c6c5ad697

require 'uri' 

module SmartURI 
    SEPARATOR = '/' 

    def self.join(*paths, query: nil) 
    paths = paths.compact.reject(&:empty?) 
    last = paths.length - 1 
    url = paths.each_with_index.map { |path, index| 
     _expand(path, index, last) 
    }.join 
    if query.nil? 
     return url 
    elsif query.is_a? Hash 
     return url + "?#{URI.encode_www_form(query.to_a)}" 
    else 
     raise "Unexpected input type for query: #{query}, it should be a hash." 
    end 
    end 

    def self._expand(path, current, last) 
    if path.starts_with?(SEPARATOR) && current != 0 
     path = path[1..-1] 
    end 

    unless path.ends_with?(SEPARATOR) || current == last 
     path = [path, SEPARATOR] 
    end 

    path 
    end 
end 
Cuestiones relacionadas