How to change will_paginate URLs? - ruby-on-rails

I'm trying to change the will_paginate links from the format of /list?page=1 to /list/page/1. I have the routes setup right for this, but will_paginate is using the query string in links rather than my pretty URL style. How to tell it otherwise?
I'm using will_paginate 2.3.16 and Rails 2.3.14.

will_paginate uses the url_for helper. If you define routes containing the parameters it uses, it should generate pretty urls.
map.connect '/lists/page/:page', :controller => 'lists', :action => 'index'
Please make sure, you define this route above any routes that could also match your controller action. Even better: exclude the index action like this:
map.resources :lists, :except => [:index]

You have several options here:
1st option: monkey patch WillPaginate::ActionView::LinkRenderer#url, which currently has the following url logic:
def url(page)
#base_url_params ||= begin
url_params = merge_get_params(default_url_params)
merge_optional_params(url_params)
end
url_params = #base_url_params.dup
add_current_page_param(url_params, page)
#template.url_for(url_params)
end
So, I imagine that you can do something like "/list/page/#{page}" instead.
The other way is to fully implement the renderer (by subclassing WillPaginate::ViewHelpers::LinkRenderer), and then providing it as :renderer => MyRendererClass when calling will_paginate.

Related

Short url with Ruby on Rails routes

I am trying to create short url links for books in Ruby on Rails. I want to get something like this: www.domain.com/book123, where book is the controller name (or custom controller name) and 123 is an id of the book.
Right now my routes look as follow:
resources :books, except: [:edit], path: "book" do
put :new, on: :new
member do
get ':id' => 'books#show'
get 'general' => 'books#general'
get 'additional' => 'books#additional'
get 'photos' => 'books#photos'
get 'map' => 'books#map'
end
resources :photos, only: [:create, :destroy]
end
This is what I get: http://localhost:3000/book/40 or www.domain.com/book/40.
I was trying to find similar questions and I found that the only way to achieve this is to use regex. I am new in Ruby on Rails and I want to find the right and efficient way of doing it.
Also, I might be wrong but I've noticed that some of the urls can affect on the website performance, so I don't want to have such problems.
Any help, information or examples will be highly appreciated. Thank you for your help and time.
You could try this route:
get 'book*id' => 'bookscontroller#show'
Check this article: https://www.railsmine.net/2014/10/route-globbing-in-ruby-on-rails.html
As #qdx47 has mentioned you'd better follow convention, but if you must not, I think you can override to_param on book model, like:
def to_param
"book#{id}"
end
and then define routes like
get ':id', to: 'books#show', constraints => { :book_id => /book[0-9]+/ }
I think you can give a try to below gem.
Friendly Id Gem
Then you will be able to generate slug that can be any unique string. By default it will be uuid but you can override it. Follow gem documentation. It will allow you generate routes like http://localhost:3000/books/book123.
In general, I think you would be going against convention and best practices by formatting your route in this way.
With that caveat, you should be able to define a route like so:
get(':book_id', 'books#show', constraints => { :book_id => /book[0-9]+/ })
You would then need to extract the id from the 'book' literal in the controller.

Rails 4 path helpers and Kaminari pagination

I am working on my first RoR website. You can consider it as a shop with products.
I use Ruby 2.0, Rails 4.0, and Kaminari for pagination.
Here is my route.rb file
scope '(:locale)' do
get 'product/all', as: :product
get 'product/all', as: :home
get 'all', :to => 'product#all'
get 'page/:page', :action => :all, :controller => :product
root 'product#all'
end
As you see, I want to have pagination links like this one:
http://website/uk/page/2
And Kaminari creates links like that when I use paginate helper in views
However when I try to use path helper, I receive correct but not pretty links:
http://website/en/product/all?page=2
Here is the helper call:
product_path(page: 2, locale: I18n.locale)
As you see, locale scope works ok, but page is still appended as parameter.
So, here is the question:
Is it possible to modify route.rb to have path helpers work the same way as kaminari helper?

Rails 3 Finding the right :id in a controller using a specific route

I have my routes arranged so that when visiting the site the :id is displayed before the slug like so
match "/causes/:id/:slug" => "causes#show", :as => :cause, :via => 'get'
But I also have a nested attribute called "post" that belongs to causes like so
match "/causes/:id/:slug/posts" => "causes#posts", :via => 'get', :as => :posts
When I use this, everything works great for the causes, but not for the posts.
If I use
#post = Post.find(params[:id])
in causes or posts controller it always looks for the ID of the causes, and not the :id of the posts. So if the post :id is 9, and the cause :id is 1, and I use
#post = Post.find(params[:id])
it will always look for post[1] and not 9 or whatever the post id really is.
What am I doing wrong? Is there a way to make this work in the routes, or maybe a different way to find the id of a nested object in the controller?
I need the route to be the way I have it set up, :id/:slug...
rake routes information:
cause GET /causes/:id/:slug(.:format) causes#show
edit_cause GET /causes/:id/:slug/edit(.:format) causes#edit
PUT /causes/:id/:slug(.:format) causes#update
posts GET /causes/:id/:slug/posts(.:format) causes#posts
POST /causes/:id/:slug/posts(.:format)
PUT /causes/:id/:slug/posts(.:format) causes#update_post
DELETE /causes/:id/:slug/posts(.:format) causes#destroy_post
causes GET /causes(.:format) causes#index
POST /causes(.:format) causes#create
Any help would be great.
To solve your immediate problem, you'll want to add something like this to routes.rb
# config/routes.rb
match "/causes/:cause_id/:slug/post/:id" => "causes#update_post", :via => 'put', :as => :update_post
And then you can generate the URL in your views like this...
link_to 'Update this post', update_post_path(#cause, #post)
...and access the parameters in your controller as params[:id] (for the post) and params[:cause_id] (for the cause).
More generally, though, the way you are specifying your routes is pretty cumbersome, and I suspect you're making your life harder than it needs to be. If this were me, I would do something like
# config/routes.rb
resources :causes do
resources :posts
end
This would accomplish something pretty close to what you have now, the main difference being that it wouldn't contain slugs. I'm not sure why you need to have both slugs and IDs, maybe you could just identify your causes by their slugs? Stringex is a good gem for generating slugs, and you can set it so that slugs are guaranteed to be unique.
Here is the section of the Rails guide on nested resources
http://guides.rubyonrails.org/routing.html#nested-resources
And here is a Railscast about using slugs with nested resources
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid?view=comments
Hope this helps.
This is because you're using the id of the cause, and if you're doing /causes/:id/posts shouldn't you be doing #posts = #cause.postsanyway?
I would look into the new router syntax for rails 3 if I were you, as there is a nicer way to nest resources http://guides.rubyonrails.org/routing.html
edit:
use the friendly_id gem and nest your resources, to avoid confusion follow REST best practises that resource in question is at the end so
/causes/:slug/posts/:slug

make pretty url with routes.rb

I would like to do something to this effect, I believe:
map.connect 'show/:company_name/:id',
:controller => 'companies',
:action => 'show'
Basically, each time the show action is called, I would like it to take the company_name param and place it into the url as such (show/:company_name/:id)
However, it seems I am using old (rails 2.x routing api) and cannot use map.connect without getting an error. How can I upgrade this?
Is there some way to do this with "match"?
Thanks!
===================
This is the error I see when I try to use map.connect:
undefined local variable or method `map' for #<ActionDispatch::Routing::Mapper:0x103757458>
I think your routes lack a "/" symbol in the first line.
Try this:
match '/show/:company_name/:id' => 'companies#show'
You can check your routes path with command rake routes.
--
Besides, the show action is the default RESTful method in Rails. I'll suggest you change a equivalent word, and reserve "show" action for future or other situation.
In Rails convention, you can write resources :companies, and the path will be /companies/:id using show action.
Some adjustment, in app/models/company.rb
def to_param
self.name
end
So your url will look like http://yourdoamin.com/companies/37signals.
In app/controllers/companies_controller.rb
#company = Company.find_by_name(params[:id])
If I'm understanding your goal, try
match 'companies/show/:company_name/:id' => 'companies#show'

Routes with Dash `-` Instead of Underscore `_` in Ruby on Rails

I want my urls to use dash - instead of underscore _ as word separators. For example controller/my-action instead of controller/my_action.
I'm surprised about two things:
Google et al. continue to distinguish them.
That Ruby on Rails doesn't have a simple, global configuration parameter to map - to _ in the routing. Or does it?
The best solution I've is to use :as or a named route.
My idea is to modify the Rails routing to check for that global config and change - to _ before dispatching to a controller action.
Is there a better way?
With Rails 3 and later you can do like this:
resources :user_bundles, :path => '/user-bundles'
Another option is to modify Rails, via an initializer.
I don't recommend this though, since it may break in future versions (edit: doesn't work in Rails 5).
Using :path as shown above is better.
# Using private APIs is not recommended and may break in future Rails versions.
# https://github.com/rails/rails/blob/4-1-stable/actionpack/lib/action_dispatch/routing/mapper.rb#L1012
#
# config/initializers/adjust-route-paths.rb
module ActionDispatch
module Routing
class Mapper
module Resources
class Resource
def path
#path.dasherize
end
end
end
end
end
end
You can overload controller and action names to use dashes:
# config/routes.rb
resources :my_resources, path: 'my-resources' do
collection do
get 'my-method', to: :my_method
end
end
You can test in console:
rails routes -g my_resources
my_method_my_resources GET /my-resources/my-method(.:format) my_resources#my_method
You can use named routes. It will allow using '-' as word seperators. In routes.rb,
map.name_of_route 'a-b-c', :controller => 'my_controller', :action => "my_action"
Now urls like http://my_application/a-b-c would go to specified controller and action.
Also, for creating dynamic urls
map.name_of_route 'id1-:id2-:id3', :controller => 'my_controller', :action => "my_action"
in this case 'id1, id2 & id2 would be passed as http params to the action
In you actions and views,
name_of_route_url(:id1=>val1, :id2=>val2, :id3=>val3)
would evaluate to url 'http://my_application/val1-val2-val3'.
if you use underscores in a controller and view file then just use dashes in your routes file, and it will work..
get 'blog/example-text' this is my route for this controller
def example_text
end <-- this is my controller
and example_text.html.erb is the file
and this is the actual link site.com/blog/example-text
i figured this is works for me, and it's more effective than underscores SEO wise

Resources