J'essaie de trouver une méthode robuste pour joindre des segments de chemin d'URL partiels. Y a-t-il un moyen rapide de faire cela?
J'ai essayé ce qui suit:
puts URI::join('resource/', '/edit', '12?option=test')
Je m'attends à:
resource/edit/12?option=test
Mais je reçois l'erreur:
`merge': both URI are relative (URI::BadURIError)
J'ai utilisé File.join()
dans le passé pour cela, mais quelque chose ne semble pas correct concernant l'utilisation de la bibliothèque de fichiers pour les URL.
L'API d'URI n'est pas vraiment génial.
URI :: join ne fonctionnera que si le premier commence comme un uri absolu avec un protocole, et que les derniers sont relatifs de la bonne manière ... sauf que j'essaie de le faire et que je ne peux même pas le faire fonctionner.
Ceci au moins n’est pas une erreur, mais pourquoi saute-t-il le composant du milieu?
URI::join('http://somewhere.com/resource', './edit', '12?option=test')
Je pense que peut-être que URI est un peu nul. Il manque des instances d'api on significatives, telles qu'une instance #join ou une méthode à évaluer par rapport à une base uri, que vous êtes en droit d'attendre. C'est juste un peu merdique.
Je pense que vous allez devoir l'écrire vous-même. Ou utilisez simplement File.join et d'autres méthodes de chemin de fichier, après avoir testé tous les cas Edge auxquels vous pouvez penser pour vous assurer qu'il répond à vos attentes.
edit 9 déc 2016 J'ai pensé que le adressable gem le fait très 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>
Le problème est que resource/
est relatif au répertoire en cours, mais /edit
fait référence au répertoire de niveau supérieur en raison de la barre oblique. Il est impossible de joindre les deux répertoires sans déjà être certain que edit
contient resource
.
Si vous recherchez uniquement des opérations sur des chaînes, supprimez simplement les barres obliques de fin ou de fin, puis associez-les avec /
comme colle.
La façon de le faire en utilisant URI.join est:
URI.join('http://example.com', '/foo/', 'bar')
Faites attention aux barres obliques. Vous pouvez trouver la documentation complète ici:
http://www.Ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-join
Avoir uri comme URI::Generic
ou une sous-classe de celui-ci
uri.path += '/123'
Prendre plaisir!
25/06/2016 UPDATE pour les sceptiques
require 'uri'
uri = URI('http://ioffe.net/boris')
uri.path += '/123'
p uri
Les sorties
<URI::HTTP:0x2341a58 URL:http://ioffe.net/boris/123>
L'utilisation de File.join
n'est pas robuste car elle utilisera le séparateur de système de fichiers du système d'exploitation, qui sous Windows est \
au lieu de /
, perd la portabilité.
Comme vous l'avez remarqué, URI::join
ne combinera pas les chemins avec des barres obliques répétées, il ne correspond donc pas à la pièce.
Il s'avère que cela ne nécessite pas beaucoup de code Ruby pour y parvenir:
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
L'algorithme prend en charge les barres consécutives, conserve les barres de début et de fin, et ignore les chaînes nil
et les chaînes vides.
puts GluePath::join('resource/', '/edit', '12?option=test')
les sorties
resource/edit/12?option=test
Utilisez ce code:
File.join('resource/', '/edit', '12?option=test').
gsub(File::SEPARATOR, '/').
sub(/^\//, '')
# => resource/edit/12?option=test
exemple avec des chaînes vides:
File.join('', '/edit', '12?option=test').
gsub(File::SEPARATOR, '/').
sub(/^\//, '')
# => edit/12?option=test
Ou utilisez ceci si possible pour utiliser des segments tels que resource/
, edit/
, 12?option=test
et où http:
est uniquement un espace réservé pour obtenir un URI valide. Cela fonctionne pour moi.
URI.
join('http:', 'resource/', 'edit/', '12?option=test').
path.
sub(/^\//, '')
# => "resource/edit/12"
J'ai amélioré le script de @ Maximo Mussini pour qu'il fonctionne correctement:
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
Vous pouvez utiliser ceci:
URI.join('http://exemple.com', '/a/', 'b/', 'c/', 'd')
=> #<URI::HTTP http://exemple.com/a/b/c/d>
URI.join('http://exemple.com', '/a/', 'b/', 'c/', 'd').to_s
=> "http://exemple.com/a/b/c/d"
Voir: http://Ruby-doc.org/stdlib-2.4.1/libdoc/uri/rdoc/URI.html#method-c-join-label-Synopsis