Rails 4.1.2 - to_param избегает слешей (и ломает приложение)
Я использую в своем приложении to_param
создать собственный URL (этот пользовательский путь содержит косую черту):
class Machine < ActiveRecord::Base
def to_param
MachinePrettyPath.show_path(self, cut_model_text: true)
end
end
Дело в том, что с Rails 4.1.2
изменилось поведение, и Rails не позволяет использовать косые черты в URL (при использовании пользовательских URL), поэтому он избегает косых черт.
У меня были такие маршруты:
Rails.application.routes.draw do
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
resources :machines, except: :destroy do
collection do
get :search
get 'search/:ad_type(/:machine_type(/:machine_subtype(/:brand)))', action: 'search', as: :pretty_search
get ':subcategory/:brand(/:model)/:id', action: 'show', as: :pretty
patch ':subcategory/:brand(/:model)/:id', action: 'update' # To be able to update machines with new rich paths.
end
end
end
end
Я попытался по рекомендации в потоке использовать параметр glob только для метода show, чтобы убедиться, что он работает:
resources :machines, except: :destroy do
#...
end
scope format: false do
get '/machines/*id', to: "machines#show"
end
Но это абсолютно не работает. У меня еще есть такие неработающие ссылки:
http://localhost:3000/machines/tractor%2Fminitractor%2Fmodel1%2F405
Конечно, если я заменю на себя спасшиеся косые черты:
http://localhost:3000/machines/tractor/minitractor/model1/405
И попробуйте зайти в путь, тогда страница откроется.
Любые идеи, как я могу это исправить?
3 ответа
У меня возникла та же проблема при использовании автоматически сгенерированных помощников URL. Я использовал отладчик для отслеживания нового поведения до его источника (где-то около ActionDispatch:: Journey:: Customers:: Formatter), но не нашел многообещающих решений. Похоже, что параметризованная модель теперь строго обрабатывается как одиночный сегмент пути с разделителями-косыми чертами и соответственно экранируется, без параметров, которые могли бы указать форматеру иначе.
Насколько я могу судить, единственный способ получить помощника url для получения старого результата - использовать исходный файл маршрутов и передавать каждый сегмент отдельно, что-то вроде:
pretty_machine_path(machine.subcategory, machine.brand, machine.model, machine.id)
Это чертовски уродливо и, очевидно, не то, что вы захотите делать снова и снова. Вы можете добавить метод в MachinePrettyPath, чтобы генерировать сегменты в виде массива и анализировать результат для помощника (скажем, pretty_machine_path(*MachinePrettyPath.show_path_segments(machine))
) но это все еще довольно многословно.
Между вышеупомянутыми головными болями и отношением "вы делаете это неправильно" от разработчиков в билете Rails, на который вы ссылались, для меня самым простым вариантом было укусить маркер и написать собственный помощник по URL вместо использования to_param. Я еще не нашел хороший пример "правильного" способа сделать это, но что-то вроде этого простого примера должно служить цели:
#app/helpers/urls_helper.rb
module UrlsHelper
def machine_path(machine, options = {})
pretty_machine_path(*MachinePrettyPath.show_path_segments(machine), options)
end
end
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper :urls #for the views
include UrlsHelper #for controllers
#...
end
Если вы уверены, что возвращенный URL-адрес безопасен, вы должны добавить.html_safe к возвращаемой строке:
MachinePrettyPath.show_path(self, cut_model_text: true).html_safe
(больше нигде не видел, где его можно экранировать, но проверьте весь поток в вашем приложении, возможно, вручную тестируйте метод по методам)
Как насчет определения URL самостоятельно?
def to_param
"#{ subcategory.title.parameterize }/#{ brand.name.parameterize }/#{ model.name.parameterize) }/#{ id }"
end
И тогда в ваших маршрутах что-то вроде этого:
get 'machines/*id', to: "machines#show"
Вы также должны разделить свои параметры [:id], когда вы делаете поиск по своей модели.
Machine.find( params[:id].split("/").last )