Rails 4 path helpers and Kaminari pagination - ruby-on-rails

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?

Related

How can overwrite the will_paginate urls? Will_paginate gem generate wrong url

I have a ruby on rails app and I use will_paginate to render my products from categories.
I have 3 routes for some action:
/category1
/category2
/category3
When I am on /category2 or /category3 page, the will_paginate generate URLs with /category1?page=nr_page NOT for /category1 url.
From what I noticed, if I change the order of the routes, will_paginate will generate URLs with the first route from my routes file.
will_paginate uses url_for and params to figure out the path for pagination. According to what you said, your routes file looks something like this:
get '/themes' => 'category#show_category', as: :all_themes
get '/templates' => 'category#show_category', as: :all_templates
get '/design-system' => 'category#show_category', as: :all_design_system
When you add `<%= self.params.inspect %> to your view. you will see something like this (maybe with more params in the hash):
<ActionController::Parameters {"controller"=>"category", "action"=>"show_category"} permitted: false>
This means that will paginate takes these parameters and uses them in the url_for method. The call looks like this:
url_for controller: 'category', action: 'show_category', page: 123
it doesn't see the current path in the browser, only the controller and action names. In this case it goes through all of the routes from the top until it finds first defined rule for given controller and action and takes the url from this rule. This means it will always use the URL for the first defined category in your list.
Possible solutions
Define different actions for every category. It will work fine for you if you want to use it this way
get '/themes' => 'category#themes', as: :all_themes
get '/templates' => 'category#templates', as: :all_templates
get '/design-system' => 'category#design_system', as: :all_design_system
Define one route with the parameter
get '/category/:category' => 'category#show_category', as: 'show_category'
and then in your views or controller use following links:
show_category_path(category: 'themes')
Just URLs will not look as nice as before.

Internationalized I18n URL helpers

Is it possible to have URL helpers in Rails behave differently for different locales, eg.
<%= link_to "Something", example_path %>
in English would go to site.com/something, and in another language to site.com/lang/blahblah
Currently, my routes are defined like
scope '(:locale)', :locale => /otherlang/ do
get '/' => 'home#show'
get 'otherlang-about' => 'about#show'
get 'otherlang-something/:id' => 'example#show'
end
get 'about' => 'about#show'
get 'something/:id' => 'example#show'
root 'home#show'
Yes it is possible, We are using this gem https://github.com/enriclluelles/route_translator in my company to get different routes_url for each languages but still pointing to the same controller#method.
You just need to define a route.yml file to define the translation for each routes.

kaminari pagination on custom url

I have a case on url that generated by kaminari paginate as the url depends on a condition. Here is my routes
concern :search do
scope '/search', as: :search do
get '/', to: 'users#search'
get '/schedule/:id', to: 'schedules#doctor', as: :schedule
end
end
concerns :search
scope :dashboard, as: :dashboard do
concerns :search
end
As you can see, the :search is accessible through /search and /dashboard/search. The problem is, paginate #doctors gives /search which basically will goes to search_path even though I'm on dashboard_search_path (it should gives /dashboard/search path).
My question is, how can I pass a custom path to paginate? I'd like paginate to use search_path when I open /search and use dashboard_search_path when I'm on /dashboard/search/path.
You don't have to provide how to decide /search or /dashboard/search, I just need to know how to pass it to paginate as an argument. Ta
Kaminari accepts parameters for paginate, one of them is params for the links. It works the same as url_for in rails. Didn't test it, but try for dashboard pages, should work.
paginate #doctors, params: { script_name: "/dashboard" }
From docs for url_for:
:script_name - specifies application path relative to domain root. If provided, prepends application path.
use url param for paginate and have your custom code in proc which will define the final url:
paginate #doctors, url: proc{|page| some_condition ? search_path : dashboard_search_path}

How to change will_paginate URLs?

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.

rails3 routes issue

I have following routes.
pota.resources :on_k,
:as => ':klass',
:path_prefix => 'pota/klass',
:controller => 'main'
When I do rake routes this is what I get for show method:
pota_on_k GET /pota/klass/:klass/:id(.:format)
{:action=>"show", :controller=>"pota/main"}
Above code works fine in rails 2.x . However if I am using rails3 then I get following error
ActionController::RoutingError: No route matches
{:action=>"show", :controller=>"pota/main", :klass=>"vehicle/door", :id=>1}
Notice that I am passing 'vehicle/door' as :klass. If I pass a standard model like :klass => 'pet' then it works fine. However if I pass a nested model name like :klass => 'vehicle/door' then I get route error in rails3.
I guess that is because I have '/' in the value . I can solve that by having a regex but I might also pass :klass which is not nested.
On a class like Vehicle::Car I do
Vehicle::Car.underscore #=> vehicle/car
"vehicle/car".camelize.constantize #=> Vehicle::Car
This underscore and camelize/constantize on the other side makes it easier to pass nested class name.
Any idea on how to go about fixing it for rails3?
STOP!
Think about what you're doing here - you should not be calling constantize on url parameters. Assuming that you're likely to be calling find on the result you're basically giving a hacker a way to query every ActiveRecord model in your application.
A better way is to use meta programming to dynamically build static routes that can't be hacked, e.g:
%w[pet vehicle/car vehicle/bike].each do |klass|
resources :pota,
:path => "pota/#{klass.pluralize}",
:as => "pota_#{klass.tr('/','_').pluralize}",
:defaults => { :klass => klass }
end
Then you can add a helper method that calls the appropriate named route helper to generate urls based upon a passed model instance.

Resources