Rails RESTful Routing With '/' and Slugs - ruby-on-rails

I am looking to do something similar a wordpress slug where I have a URL like this while maintaining RESTful routing:
http://foo.com/blog/2009/12/04/article-title
The reason I am interested in keep RESTFUL routing is that I am unable to use many plugins because I am using custom routes.
I have already done the RESTful appearance with:
map.connect '/blog/:year/:mon/:day/:slug',
:controller => 'posts', :action => 'show',
:year => /\d{4}/, :month => /\d{2}/,
:day => /\d{2}/, :slug => /.+/,
:requirements => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/, :slug => /.+/ }
In order to write the links, I had to write custom link_to helpers to generate the proper URLs. I really would like to make this RESTful and have the link_to post_path( #post ) yield the URL above and the link_to edit_post_path(#post) ...article-title/edit
I also have :has_many => [:comments] and I would that to work as well. The link_to that I have tried looks like this:
'posts', :action => 'show', :year => recent_post.datetime.year.to_s,
:month => sprintf('%.2d', recent_post.datetime.mon.to_i),
:day => sprintf('%.2d', recent_post.datetime.mday.to_i),
:slug => recent_post.slug %>
and yields this (which isn't what I want):
http://foo.com/posts/show?day=30&month=11&slug=welcome-to-support-skydivers&year=2009
I'm not sure what I am doing wrong. Is it even possible to accomplish this?

I think it's not working because you're not using a custom route. I do this all of the time. I simply setup a simple custom route:
map.present_page '/blog/:year/:month/:day/:slug',
:controller => 'posts', :action => 'show'
Then you should be able to do:
present_page_path(:year => 2009,
:month => "December",
:day => "13",
:slug => "just-an-example")
The reason you're getting a query string is most likely because rails isn't making the connection to your route for whatever reason. Using a named route explicitly tells rails to use that route. Let me know if that solves it for you!

Here's how I went about this...
First, I'm not trying to use the route-generated url method. Also, I'm not going to the same extent as you in terms of checking formatting of the date parameters. Since I'm auto-generating the datestamps and the URL creation, I'm not concerned about format validity, I'm simply formatting a ActiveSupport::TimeWithZone object.
Let's start with the relevant route:
map.post_by_date 'content/:year/:month/:day/:slug',
:controller => 'posts',
:action => 'show_by_date_slug'
Since I didn't want to worry about argument formatting, or repetition, I created a helper method and included the helper in the relevant controller:
def pubdate_slug_url(post)
year = post.published_on.strftime('%Y')
month = post.published_on.strftime('%m')
day = post.published_on.strftime('%d')
url = "/" + ["content", year, month, day, post.slug].join("/")
return url
end
Finally, in my view, I simply call the method, passing in my Post object:
<h2><%= link_to post.headline, pubdate_slug_url(post) %></h2>
I end up with a url like: http://wallscorp.us/content/2009/12/06/links
Cheers.

Related

Custom path for resourceful route

I'd like to be able to do ...
resources :posts
... and be able to customize the contextual url to be ...
:year/:month/:day/:id
... and still be able to do ...
post_path post
This would have it generate /2012/1/1/something-something.
However it appears I have to ...
get ':year/:id' => 'posts#show', as: 'posts'
Then in the view I have to ...
post_path post.year, post.id instead of post_path post
Is there anyway to have the post_path helper pick up the extra parameters it needs for the route?
If not this seems like it might be worth a feature request.
Sounds like you need something like:
match "posts/:year/:month/:day/:id" => "posts#show", :constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }

Custom Routing in Rails

I've looked around for a while now, but I'm not sure how best to describe my request to google, so thought I'd ask here ;)
In rails, I know that when you nest restful routes, you generally get something like:
http://localhost/categories/1/articles/2
If you want something more meaningful, you can use slugs or friendly_id to get something like
http://localhost/categories/all-your-needs/articles/rock-out-with-this-article
(assuming you have unique names).
My question is, how can I remove the controller from the url rewriter so you get something like:
http://localhost/all-your-needs/rock-out-with-this-article
Is this possible?
Yes it is. You can use something like this:
Rails 2:
map.show_article ':category/:article', :controller => "articles", :action => "show"
Edit:
Ok. Here you have the urls for the other REST actions:
map.edit_article ':category/:article/edit', :controller => "articles", :action => "edit".
For update add :conditions => { :method => :post } to the previous one.
For delete, use the first one with :conditions => { :method => :delete }.
For new and create, you can use:
map.new_article ':category/new', :controller => "articles", :action => "new"
and for create the same but with :conditions => { :method => :post }. I hope I've been able to help you!

creating a link_to for custom route

I have a custom route (if I am doing that correctly, this is the first time I have done this) that looks like this:
map.connect 'purchases/type/:type', :controller => 'purchases', :action => 'index'
so I want to create a link_to that would use that url /purchases/type/(somenumber)
or I am completely open for a better way to do it.
Edit:
I am trying to use a category (type) to filter on the index. So if I click the link that would be /purchases/type/1 that would show all the items from type 1. I dont want this in the show, and I could do it with /purchases/?type=1, but im trying to make the urls look better.
Untested but I believe this is what you want...
map.purchase_type 'purchases/type/:type', :controller => 'purchases', :action => 'index'
Then
link_to 'foo', purchase_type_path(:type => 'your_type')
Good luck.
Based on http://www.tutorialspoint.com/ruby-on-rails-2.1/rails-routes.htm (section "named routes"), I'd try the following:
map.purchases_for_type 'purchases/type/:type', :controller => 'purchases', :action => 'index'
And I assume you'd then call it with link_to 'link text', purchases_for_type(#type_param)
For reference, I'll include the Rails3 way to do it:
match '/purchases/type/:type' => 'purchases#index', :as => "purchases_for_type", :via => "get"
Or better yet (RESTful):
match '/type/:type/purchases' => 'purchases#index', :as => "purchases_for_type", :via => "get"
You'd then call it with link_to 'link text', purchases_for_type(#type_param)

Reverse rails routing: find the action name from the URL

I understand how to turn :controller, :action, :etc into a URL. I'm looking to do the reverse, how can the action that the rails router will call be found from the URL?
With Rails 3 you can do:
Rails.application.routes.recognize_path('/areas/1')
=> {:controller=>"areas", :action=>"show", :id=>"1"}
someone else might have a shorter way to do this, but if you are just evaluating a URL, then you go to the ActionController::Routing::RouteSet class
for a config.routes.rb
map.resources :sessions
the code to find is:
ActionController::Routing::Routes.recognize_path('/sessions/new', {:method => :get})
#=> {:controller => 'sessions', :action => 'new'}
Right:
ActionController::Routing::Routes.recognize_path('/sessions/1/edit', {:method => :get})
#=> {:controller => 'sessions', :action => 'edit', :id => 1}
Wrong - without the method being explicitly added, it will default match to /:controller/:action/:id:
ActionController::Routing::Routes.recognize_path('/sessions/1/edit')
#=> {:controller => 'sessions', :action => '1', :id => 'edit'}
If you are within the action and would like to know, it is quite a bit easier by calling params[:action]
everything you ever wanted to know about routeset can be found here: http://caboo.se/doc//classes/ActionController/Routing/RouteSet.html#M004878
Hope this helps!

Rails, routing many named routes to one action

Is there a simpler way of writing this:
map.old_site_cusom_packages '/customs_packages_options.html', :controller => :public, :action => :redirect_to_home
map.old_corporate '/corporate.html', :controller => :public, :action => :redirect_to_home
map.old_track '/track.html', :controller => :public, :action => :redirect_to_home
map.old_links '/links.html', :controller => :public, :action => :redirect_to_home
map.old_contact '/contact.html', :controller => :public, :action => :redirect_to_home
I want to send many named routes to one action on one controller, I'm making sure url's left over from an old site redirect to the correct pages.
Cheers.
Use the with_options method:
map.with_options :controller => :public, :action => :redirect_to_home do |p|
p.old_site_custom_packages '/customs_packages_options.html'
p.old_corporate '/corporate.html'
p.old_track '/track.html'
p.old_links '/links.html'
p.old_contact '/contact.html'
end
You can always write a multi-purpose route with a regular expression to capture the details:
old_content_names_regexp = Regexp.new(%w[
customs_packages_options
corporate
track
links
contact
].join('|'))
map.old_content '/:page_name.html',
:controller => :public,
:action => :redirect_to_home,
:requirements => {
:page_name => old_content_names_regexp
}
That should capture specific pages and redirect them accordingly. A more robust solution is to have some kind of lookup table in a database that is checked before serving any content or 404-type pages.
Edit: For named routes, it's an easy alteration:
%w[
customs_packages_options
corporate
track
links
contact
].each do |old_path|
map.send(:"old_#{old_path}",
"/#{old_path}.html",
:controller => :public,
:action => :redirect_to_home,
)
end
In most cases the old routes can be rewritten using the singular legacy route listed first. It's also best to keep the routing table as trim as possible. The second method is more of a crutch to try and bridge the old routes.

Resources