3 votes

Comment passer des options telles que `only_path : true` et `anchor:` à `url_for` lors de la génération d'une route à partir d'un tableau ou d'un objet (au lieu d'un hash) ?

J'essaie de modifier une fonction qui, actuellement, n'accepte qu'une URL (chemin). chaîne de caractères - pour le rendre plus flexible, afin qu'il accepte également en entrée les mêmes arguments que ceux que vous pouvez passer à url_for . Le problème est que, url_for renvoie une URL complète incluant protocole/hôte, et je ne veux que la partie chemin...

url_for a une belle only_path: true qui lui permet de ne pas ajouter le protocole/hôte. Cela fonctionne très bien tant que vous le passez comme partie d'un seul hachage à url_for :

main > app.url_for(controller: 'users', action: 'index', only_path: true)
=> "/users"

Mais comment passer des options lorsque l'on passe une tableau ou un objet modèle a url_for ?

main > app.url_for(user, only_path: true)
ArgumentError: wrong number of arguments (given 2, expected 0..1)
from /gems/actionpack-5.1.6/lib/action_dispatch/routing/url_for.rb:166:in `url_for'

main > app.url_for([user, :notification_preferences], only_path: true)
ArgumentError: wrong number of arguments (given 2, expected 0..1)
/actionpack-5.1.6/lib/action_dispatch/routing/url_for.rb:166:in `url_for'

Vous ne pouvez pas ! Clairement, il n'est même pas syntaxiquement possible de passer un hash d'options si vous passez autre chose qu'un hash, puisque il s arity est 0..1 et il ne prend qu'un seul argument : url_for(options = nil) .


Ma question est donc la suivante : existe-t-il un assistant Rails qui prend les mêmes options que url_for (y compris anchor: ) mais renvoie un chemin ?

Je suppose qu'il ne serait pas trop difficile d'en ajouter un, comme celui-ci qui utilise URI.parse(path).path (probablement ce que je vais faire pour le moment)... mais cela semble inefficace, inélégant et incohérent.

Inefficace y inélégant car il génère une chaîne de caractères contenant des informations supplémentaires non souhaitées et doit ensuite l'analyser, la convertir en une structure de données structurée, puis la convertir arrière à une chaîne de caractères sans l'information non désirée.

Inconsistant parce que :

  1. Rails comprend un _path variante pour chaque _url aide à l'itinéraire. Pourquoi ne dispose-t-il pas d'une variante intégrée de "chemin" de url_for (ou le fait-il et s'appelle-t-il autrement ?) ?

  2. Autre les aides au routage qui acceptent un objet ou un tableau - comme polymorphic_path - vous permettent également de passer en option :

Exemple :

main > app.polymorphic_url [user, :notification_preferences], anchor: 'foo'
=> "http://example.com/users/4/notification_preferences#foo"

main > app.polymorphic_path [user, :notification_preferences], anchor: 'foo'
=> "/users/4/notification_preferences#foo"

main > app.polymorphic_path user, anchor: 'foo'
=> "/users/4#foo"

ActionDispatch::Routing::RouteSet a en fait un path_for :

  # strategy for building urls to send to the client                        
  PATH    = ->(options) { ActionDispatch::Http::URL.path_for(options) }     
  UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }    

  def path_for(options, route_name = nil)                                   
    url_for(options, route_name, PATH)                                      
  end                                                                       

  # The +options+ argument must be a hash whose keys are *symbols*.         
  def url_for(options, route_name = nil, url_strategy = UNKNOWN)            
    options = default_url_options.merge options    
    ...

- juste pas ActionDispatch::Routing::UrlFor apparemment. En tout cas, ActionDispatch::Routing::RouteSet#path_for est enfouie trop profondément dans les internes pour m'être utile ; j'ai besoin d'une aide qui puisse être appelée depuis le contrôleur/la vue.

Alors, quelle est la bonne solution pour cela qui soit élégante, cohérente avec les autres aides au routage de Rails, et relativement efficace (pas de URI.parse ) ?


Mieux encore, y a-t-il une raison pour que la signature de la méthode de l'élément intégré url_for ne pourrait pas simplement être modifié (dans une future version de Rails, en soumettant une demande de téléchargement) pour permettre à la fois un sujet (objet de modèle, tableau ou hachage) y un nombre quelconque d'options options à faire passer ?

Probablement l'original url_for a été écrit avant que Ruby ne dispose d'arguments par mot-clé. Mais de nos jours, il est assez simple de faire exactement cela : accepter un "objet" plus un nombre quelconque d'options de mots-clés :

def url_for(object = nil, **options)
  puts "object: #{object.inspect}"
  puts "options: #{options.inspect}"
end

main > url_for ['user', :notification_preferences], anchor: 'anchor'
object: ["user", :notification_preferences]
options: {:anchor=>"anchor"}
=> nil

main > url_for ['user', :notification_preferences], only_path: true
object: ["user", :notification_preferences]
options: {:only_path=>true}

Y a-t-il une raison pour laquelle nous ne pourrions pas/ne devrions pas changer url_for à la signature de la méthode url_for(object = nil, **options) ?

Comment modifieriez-vous url_for / full_url_for de manière à ce qu'il reste aussi rétrocompatible que possible, tout en vous permettant de l'appeler avec un tableau + des options de mots-clés ?

0voto

Tyler Rick Points 3033

Voici une solution potentielle qui montre un peu ce que je recherche :

  def url_for(object = nil, **options)
    full_url_for(object, **options)
  end

  def full_url_for(object = nil, **options)
    path_or_url_for = ->(*args) {
      if options[:only_path]
        _routes.path_for(*args)
      else
        _routes.url_for( *args)
      end
    }
    polymorphic_path_or_url = ->(*args) {
      if options[:only_path]
        polymorphic_path(*args)
      else
        polymorphic_url( *args)
      end
    }

    case object
    when nil, Hash, ActionController::Parameters
      route_name = options.delete :use_route
      merged_url_options = options.to_h.symbolize_keys.reverse_merge!(url_options)
      path_or_url_for.(merged_url_options, route_name)
    when String
      object
    when Symbol
      HelperMethodBuilder.url.handle_string_call self, object
    when Array
      components = object.dup
      polymorphic_path_or_url.(components, components.extract_options!)
    when Class
      HelperMethodBuilder.url.handle_class_call self, object
    else
      HelperMethodBuilder.url.handle_model_call self, object
    end
  end

(Comparer avec la source de version originale de Rails )

Je n'ai pas encore essayé d'exécuter la suite de tests Rails avec ce code, il se peut donc très bien que quelque chose m'ait échappé, mais cela semble fonctionner pour mes tests simples jusqu'à présent :

main > app.url_for(controller: 'users', action: 'index')
=> "http://example.com/users"

main > app.url_for(controller: 'users', action: 'index', only_path: true)
=> "/users"

main > app.url_for [user, :notification_preferences]
=> "http://example.com/users/4/notification_preferences"

main > app.url_for [user, :notification_preferences], only_path: true
=> "/users/4/notification_preferences"

Faites-moi savoir si vous trouvez des bogues, si vous avez des améliorations à apporter ou si vous pensez à un moyen plus propre d'accomplir cette tâche.

Veuillez poster toute version améliorée que vous pourriez avoir comme réponse !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X