2010-02-13 8 views
6

que me gustaría a mi página web tienen URL con este aspecto:Pretty (fecha) URL de descanso en los carriles

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

tengo mi modelo Post con slug campo ('mi primer post') y published_on campo (del cual deduciremos las partes de año y mes en la url).

Quiero que mi modelo Post sea RESTful, por lo que cosas como url_for(@post) funcionan como deberían, es decir: deberían generar la url antes mencionada.

¿Hay alguna manera de hacerlo? Sé que debe anular to_param y tener map.resources :posts con :requirements conjunto de opciones, pero no puedo hacer que todo funcione.


Lo tengo casi hecho, estoy 90% allí. Usando resource_hacks plugin puedo lograr esto:

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"} 

y en la vista:

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

genera adecuada enlace example.com/2010/02/my-first-post.

me gustaría que esto funcione también:

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

Pero necesita reemplazando el método to_param en el modelo. Debería ser bastante fácil, excepto por el hecho de que to_param debe devolver String, no Hash como me gustaría.

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

Resultados en can't convert Hash into String error.

Esto parece ser ignorado:

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

, que resultará en un error: post_url failed to generate from {:action=>"show", :year=>#<Post id: 1, title: (...) (que asigna erróneamente objeto @post a la tecla: el año). No tengo ni idea de cómo hackearlo.

Respuesta

4

sigue siendo un truco, pero las siguientes obras:

en 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 

Y el siguiente trabajo (tanto en el sector ferroviario s 2.3.x y 3.0.0):

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

Esta es la respuesta de algunos nice soul para una pregunta similar mío, url_for of a custom RESTful resource (composite key; not just id).

1

Ryan Bates habló sobre esto en su proyección de pantalla "cómo agregar rutas personalizadas, hacer que algunos parámetros sean opcionales y agregar requisitos para otros parámetros". http://railscasts.com/episodes/70-custom-routes

+0

Las rutas genéricas (map.connect, de las que habla Ryan Bates), named_routes y RESTful routes son tres cosas diferentes. Puedo obtener fácilmente cualquier URL que desee utilizando rutas genéricas y con nombre. Quiero tener URL bonitas usando rutas RESTful. –

1

Esto podría ser útil. Puede definir un método default_url_options en su ApplicationController que recibe un hash de opciones que se pasaron al helper url y devuelve un hash de opciones adicionales que desea usar para esas urls.

Si se proporciona una publicación como un parámetro a post_path, se asignará al primer parámetro (no firmado) de la ruta. no lo he probado, pero podría funcionar:

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 

estoy en la misma situación, donde un post tiene un parámetro de idioma y el parámetro babosa.Escribiendo post_path(@post) le envía un hash para el método default_url_options:

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

UPDATE: Hay un problema que no se puede ignorar los parámetros de URL a partir de ese método. Los parámetros pasados ​​a la url helper tienen prioridad. Por lo que podría hacer algo como:

post_path(:slug => @post) 

y:

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 

esto funcionaría si Post.to_param volvieron la babosa. Solo necesitarías agregar el año y el mes al hash.

+1

¡Funciona! Muy inteligente, aunque muy hackish también. Ahora hay una manera más limpia de resolver esto? –

+0

Eso es interesante, porque no funcionó para mí. Solo pude ver que se pasó la instancia de publicación. Tal vez sea por el plugin que usa. Solo puedo hacer que funcione cuando paso la publicación como ': slug => @ post'. –

+0

Ahora entiendo: si establece un valor en el hash 'options' directamente como lo hice por error en mi primera revisión, p. 'options [: year] = 2009' luego está configurado correctamente (ya que el hash se pasa por referencia). Entonces puedes sobrescribir claves dadas. –

4

Pretty URLs para Rails 3.x y Rails 2.x sin la necesidad de ningún complemento externo, pero con un pequeño truco, desafortunadamente.

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 

vista

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

, por rieles 3.x es posible que desee utilizar esta definición de rutas:

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

¿Hay alguna insignia para responder a su propia pregunta? ;)

Por cierto: el archivo routing_test es un buen lugar para ver qué puedes hacer con el enrutamiento de Rails.

Actualización: Usando default_url_options es un callejón sin salida. La solución publicada funciona solo cuando hay una variable @post definida en el controlador. Si hay, por ejemplo, @posts variable de matriz de puestos, que están de suerte (becase default_url_options no tiene acceso a ver las variables, al igual que en p@posts.each do |p|.

Así que este es aún un problema abierto. Ayudar a alguien ?

+0

Badge? Hay autoaprendizaje (si respondió su propia pregunta y tiene 3 votos ascendentes) y puede aceptar su respuesta. – klew

+0

Eso fue solo una broma;) Hay un problema mayor. Mi solución se rompe cuando estoy tratando con publicaciones en otros controladores. Específicamente, cuando no hay presente @post (mientras que puede haber una variable @posts allí). –

1

Podrías ahorrarte estrés y utilizar friendly_id. Es impresionante, hace el trabajo y puedes mirar el a screencast de Ryan Bates para comenzar.

+0

Creo que te has perdido el objetivo de esta pregunta. No se trata de la generación de babosas (que en sí misma es trivial), sino de las URL con fecha amigable (las partes de año/mes son lo difícil aquí). –