Симпатичные (датированные) УСПОКОИТЕЛЬНЫЕ URL в направляющих

Я хотел бы, чтобы мой веб-сайт имел URL, бывшие похожие на это:

example.com/2010/02/my-first-post

У меня есть мой Post модель с slug поле ('my-first-post') и published_on поле (от которого мы вычтем части года и месяца в URL).

Я хочу мой Post модель, чтобы быть УСПОКОИТЕЛЬНЫМ, таким образом, вещи как url_for(@post) работайте как, они должны, т.е.: это должно генерировать вышеупомянутый URL.

Существует ли способ сделать это? Я знаю, что необходимо переопределить to_param и имейте map.resources :posts с :requirements набор опции, но я не могу заставить все это работать.


Мне сделали его почти, я - 90% там. Используя resource_hacks плагин я могу достигнуть этого:

map.resources :posts, :member_path => '/:year/:month/:slug',
  :member_path_requirements => {:year => /[\d]{4}/, :month => /[\d]{2}/, :slug => /[a-z0-9\-]+/}

rake routes
(...)
post GET    /:year/:month/:slug(.:format)      {:controller=>"posts", :action=>"show"}

и в представлении:

<%= link_to 'post', post_path(:slug => @post.slug, :year => '2010', :month => '02') %>

генерирует надлежащий example.com/2010/02/my-first-post ссылка.

Я хотел бы, чтобы это работало также:

<%= link_to 'post', post_path(@post) %>

Но этому нужно переопределение to_param метод в модели. Должно быть довольно легким, за исключением факта, этого to_param должен возвратиться String, нет Hash поскольку я хотел бы его.

class Post < ActiveRecord::Base
  def to_param
   {:slug => 'my-first-post', :year => '2010', :month => '02'}
  end
end

Результаты в can't convert Hash into String ошибка.

Это, кажется, проигнорировано:

def to_param
  '2010/02/my-first-post'
end

поскольку это заканчивается по ошибке: post_url failed to generate from {:action=>"show", :year=># (это неправильно присваивает объект @post: ключ года). Я довольно невежествен в том, как взломать его.

6
задан Paweł Gościcki 13 February 2010 в 21:58
поделиться

4 ответа

Это все еще взлом, но работает следующее:

В application_controller.rb :

def url_for(options = {})
  if options[:year].class.to_s == 'Post'
    post = options[:year]
    options[:year] = post.year
    options[:month] = post.month
    options[:slug] = post.slug
  end
  super(options)
end

И следующее будет работать (как в Rails 2.3.x, так и в 3.0.0):

url_for(@post)
post_path(@post)
link_to @post.title, @post
etc.

Это ответ какой-то милой души на аналогичный мой вопрос, url_for настраиваемого ресурса RESTful (составной ключ; не только id) .

4
ответ дан 9 December 2019 в 22:33
поделиться

Это может быть полезно. Вы можете определить метод default_url_options в вашем ApplicationController , который получает хэш параметров, которые были переданы вспомогательному URL-адресу, и возвращает хэш дополнительных параметров, которые вы хотите использовать для этих URL-адресов.

Если сообщение задано как параметр для post_path , оно будет назначено первому (неназначенному) параметру маршрута. Не тестировал, но может сработать:

def default_url_options(options = {})
  if options[:controller] == "posts" && options[:year].is_a?Post
    post = options[:year]
    {
      :year  => post.created_at.year,
      :month => post.created_at.month,
      :slug  => post.slug
    }
  else
    {}
  end
end

У меня аналогичная ситуация, когда сообщение имеет параметр языка и параметр заголовка. Запись post_path (@post) отправляет этот хэш методу default_url_options :

{:language=>#<Post id: 1, ...>, :controller=>"posts", :action=>"show"}

ОБНОВЛЕНИЕ: существует проблема, что вы не можете переопределить параметры URL из этого метода. Параметры, переданные вспомогательному URL-адресу, имеют приоритет. Таким образом, вы можете сделать что-то вроде:

post_path(:slug => @post)

и:

def default_url_options(options = {})
  if options[:controller] == "posts" && options[:slug].is_a?Post
    {
      :year  => options[:slug].created_at.year,
      :month => options[:slug].created_at.month
    }
  else
    {}
  end
end

Это сработает, если Post.to_param вернет slug. Вам нужно только добавить год и месяц к хешу.

1
ответ дан 9 December 2019 в 22:33
поделиться

Райан Бейтс рассказал об этом в своем ролике экрана, «как добавить пользовательские маршруты, сделать некоторые параметры необязательными и добавить требования к другим параметрам». http://railscasts.com/episodes/ 70-custom-routes

1
ответ дан 9 December 2019 в 22:33
поделиться

Красивые URL для Rails 3.x и Rails 2.x без необходимости какого-либо внешнего плагина, но с небольшим хаком, к сожалению.

routes.rb

map.resources :posts, :except => [:show]
map.post '/:year/:month/:slug', :controller => :posts, :action => :show, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/

application_controller.rb

def default_url_options(options = {})
  # resource hack so that url_for(@post) works like it should
  if options[:controller] == 'posts' && options[:action] == 'show'
    options[:year] = @post.year
    options[:month] = @post.month
  end
  options
end

post.rb

def to_param # optional
  slug
end

def year
  published_on.year
end

def month
  published_on.strftime('%m')
end

view

<%= link_to 'post', @post %>

Примечание, для Rails 3.x вы можете использовать это определение маршрута:

resources :posts
match '/:year/:month/:slug', :to => "posts#show", :as => :post, :year => /\d{4}/, :month => /\d{2}/, :slug => /[a-z0-9\-]+/

Есть ли значок за ответ на собственный вопрос? ;)

Btw: файл routing_test - хорошее место, чтобы посмотреть, что можно сделать с маршрутизацией Rails.

Обновление: Использование default_url_options - это тупик. Выложенное решение работает только в том случае, если в контроллере определена переменная @post. Если есть, например, @posts переменная с Array of posts, то нам не повезло (потому что default_url_options не имеет доступа к переменным вида, как p в @posts.each do |p|.

Так что эта проблема все еще остается открытой. Кто-нибудь поможет?

4
ответ дан 9 December 2019 в 22:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: