Как передать параметры, такие как `only_path: true` и` anchor: `, в` url_for` при создании маршрута из массива или объекта (вместо хеша) - PullRequest
0 голосов
/ 08 января 2019

Я пытаюсь изменить функцию - которая в настоящее время принимает только URL (путь) string - чтобы сделать ее более гибкой, чтобы она также принимала в качестве входных данных те же аргументы, которые вы можете передать до url_for. Проблема в том, что url_for возвращает полный URL-адрес, включая протокол / хост, и мне нужна только часть пути ...

url_for имеет замечательную опцию only_path: true, которая позволяет пропустить добавление протокола / хоста. Это прекрасно работает, если вы передаете его как часть одного хеша url_for:

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

Но как передавать параметры, когда передаете массив или объект модели в 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'

Ты не можешь! Ясно, что синтаксически невозможно даже передать хэш опций, если вы передаете что-либо, кроме хэша, так как это s arity равно 0..1, и он принимает только один аргумент: url_for(options = nil).


Итак, мой вопрос: есть ли помощник Rails, который принимает те же опции, что и url_for (включая anchor:), но возвращает путь?

Полагаю, было бы не сложно добавить один, например, , этот , который использует URI.parse(path).path (вероятно, то, что я сейчас сделаю) ... но это кажется неэффективным, не элегантным и непоследовательный.

Неэффективно и inelegant , поскольку он генерирует строку, содержащую дополнительную нежелательную информацию, а затем должен проанализировать ее, преобразовать в структурированную структуру данных и затем преобразовать ее обратно в строку без нежелательной информации.

Несогласовано потому что:

  1. Rails включает в себя вариант _path для каждого _url помощника по маршруту. Почему у него нет встроенного варианта «пути» url_for (или он называется и называется ли что-то еще?)?

  2. Другие помощники по маршрутизации, которые принимают объект или массив - например, polymorphic_path - позволяют также передавать параметры:

Пример: * 1 069 *

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 на самом деле имеет 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    
    ...

- просто нет ActionDispatch::Routing::UrlFor, по-видимому. В любом случае, ActionDispatch::Routing::RouteSet#path_for похоронен слишком глубоко во внутренних органах, чтобы быть полезным для меня; Мне нужен помощник, который вызывается из контроллера / представления.

Итак, что является хорошим решением для этого, которое является элегантным, совместимым с другими помощниками маршрутизации Rails и относительно эффективным (без URI.parse)?


Еще лучше, есть ли причина, по которой сигнатура метода встроенного url_for не может быть просто изменена (в будущей версии Rails путем отправки запроса на выборку), чтобы разрешить как субъекту (объект модели, массив или хеш) и любое количество необязательных options, которые нужно передать в?

Вероятно, оригинал url_for был написан до того, как у Руби были ключевые аргументы. Но в настоящее время довольно просто сделать именно это: принять «объект» плюс любое количество опциональных ключевых слов:

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}

Есть ли причина, по которой мы не смогли / не должны изменить сигнатуру метода url_for на url_for(object = nil, **options)?

Как бы вы изменили url_for / full_url_for так, чтобы он оставался максимально совместимым с предыдущими версиями, но также позволял бы вам вызывать его с помощью массива + опции ключевого слова?

1 Ответ

0 голосов
/ 08 января 2019

Вот одно из возможных решений, которое показывает то, что я ищу:

  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

(Сравните с источником оригинальной версии Rails )

Я еще не пробовал запускать набор тестов Rails для этого кода, поэтому он вполне мог что-то пропустить, но, похоже, он пока работает для моих простых тестов:

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"

Дайте мне знать, если вы обнаружите в этом какие-либо ошибки или у вас появятся какие-либо улучшения, или вы можете придумать более чистый способ сделать это.

Пожалуйста, опубликуйте любую улучшенную версию, которую вы можете иметь в качестве ответа!

...