Rails will_paginate - how to make nicer route? - ruby-on-rails

I have on the homepage list of articles that are listed with using will_paginate gem.
Everything works well, but there is one thing I would want to improve - on the homepage, the data are loaded into home controller and index action.
So, when I load my website - www.website.com, data are loaded into home/index. When I click on the pagination link for the second page, the link is www.website.com/home/index?page=2. I would like to see there in the best way www.website.com/page/2 (or www.website.com/?page=2).
Basically, the point is to remove from the URL /home/index - is there any way to do this?
Thanks

You may do it this way - add this class to some helper module, for example app/helpers/application_helper.rb:
module ApplicationHelper
class SmartLinkRenderer < WillPaginate::ActionView::LinkRenderer
protected
def link(text, target, attributes = {})
if target.is_a? Fixnum
attributes[:rel] = rel_value(target)
target = url(target)
end
attributes[:href] = target.gsub(/[?&]page=(\d+)/,'/page/\1')
tag(:a, text, attributes)
end
end
end
You may customize it according to your needs, for example do
attributes[:href] = target.gsub(/home\/index/,'')
or whatever. And then you may do this in your views:
<%= will_paginate #items, :renderer => 'ApplicationHelper::SmartLinkRenderer' %>

Try this
root to: 'home#index'
in you config/routes.rb file
EDIT
to be able to route these kind of requests:
www.website.com/page/2
add this to routes.rb:
match "page/:page" => "your_root_controller#index"
this way :page will be in your params hash.
and for example index method to view your Message model can be:
def index
#messages = Messages.paginate(page: params[:page])
end
hope that can help. for more information please refer to RoR routing

Related

ruby on rails will_paginate change default link

In the routes.rb I set lists#index as the root:
root to: "lists#index"
I would like will_paginate to create page hrefs pointing to /?page=# instead of /lists?page=#. To create the paginate links I have the following line in the "lists#index" file:
<%= will_paginate #lists %>
The solution was indeed a custom renderer. Thanks Ayush!
The following helped me to solve it: how-to-customize-the-will-paginate-links-with-images
I created a file: app/helpers/will_paginate_helper.rb with the following content:
module WillPaginateHelper
class RootedLinkRenderer < WillPaginate::ActionView::LinkRenderer
protected
def link(text, target, attributes = {})
if target.is_a? Fixnum
attributes[:rel] = ""
target = "/?page=#{target}"
end
attributes[:href] = target
tag(:a, text, attributes)
end
end
end
Finally, I updated the index file and replaced:
<%= will_paginate #lists %>
with:
<%= will_paginate #lists, renderer: WillPaginateHelper::RootedLinkRenderer %>
So the href is http://localhost:3000/?page=2 and not http://localhost:3000/lists?page=2.
What I understand from your question that you are trying to create a custom renderer, although I have never used it, but for that you need to override the link method of renderer.
here is the link to the original code might be of some help -
https://github.com/voormedia/paginary/blob/master/lib/paginary/helpers/pagination_helper.rb
In this particular case the links are influenced by the order of the routing instructions in the routes.rb file.
If you place the line
root to: "lists#index"
at the top of routes.rb then will_paginate will generate links to /?page=# without the need for a custom renderer.
This can be found in the FAQs for the module in coursera.

Rails Nested Resource Scoped to Controller

In my app, websites have many pages. I'm trying to setup my URLs to look like
example.com/websites/1/pagename
I want it so page names don't need to be globally unique. They just need to be unique within the website they belong to.
This is what my routes look like so far
resources :websites do
resources :pages, :path => ''
end
UPDATE
I got it working by changing this line in the pages controller.
def show
#page = Page.find_by(website_id: params[:website_id], id: params[:id])
end
However, then I updated that line to use Friendly ID...
def show
#page = Page.friendly.find_by(website_id: params[:website_id], id: params[:id])
end
Now I get an error undefined method name for nil:NilClass because I have <% provide(:title, #page.name) %>
No, You don't need.
The rails g controller websites/pages to use with namespace.
Your URL: websites/1 the id = 1 is unique. and the pagename also unique for each website
=> websites/1/pagename is unique
It's fine for:
websites/1/page_about_author
and
websites/2/page_about_author

Ruby on Rails: What to call in view?

I have a next method in the model.
def self.next(comment, key = :id)
self.where("#{key} > ?", comment.send(key)).first
end
In my view I can say for example: (does not work)
= link_to "next", #comment.next(#comment)
What's the correct way to call this method?
routes.rb:
Rails.application.routes.draw do
resources :articles do
resources :comments do
end
end
end
You've defined next as a class method (vs an instance method), so you need:
= link_to "next", Comment.next(#comment)
If you want to be able to call #comment.next, define the instance method as:
def next(key = :id)
Comment.where("#{key} > ?", self.send(key)).first
end
It is not good style that the model knows this, you should put it in the controller. You should try a gem called kaminari, this gem lets you paginate over the elements, so in your comments controller you could have something like:
def show
#comment = Comment.order(id: :asc).page(params[:page]).per(1)
end
Then in your view, by just adding this kaminari helper:
<%= paginate #comment %>
You get the pagination bar below and everything works fine (gem's magic).
If you don't like this you could try to add that next method in the controller or find both next and current elements and link to the next element.
In my opinion the model is just a class that knows how to save and get information from the database and maybe some calculations with it's information, so all that logic related to the view should be elsewhere.

Dynamic routes on runtime in Rails

I'm developing a site using refinery. Now for one specific page that is created in the back-end of refinery, i want to use my own controller and views. All the User can do with this page is to set the menu-position, title, meta-info etc. The URL for this page has to look the same as all the other pages.
So for example, the menu structure looks like:
menux
menu1
menu2
specific page
menux
And the URL for "specific page" looks like "locale/menu1/menu2/specific page"
The site is available in multiple languages, so i have to create these routes for all languages.
Currently i'm creating the routes like this:
specific_page_id = 1
Refinery::I18n.frontend_locales.each do |lang|
slugs = []
page = Refinery::Page.find_by_path_or_id(nil, specific_page_id)
# get slug for page in current language
slugs << page.translations.select { |p| p.locale == lang }.first.slug
# get all slugs from parrent pages
while !page.parent_id.blank?
page = Refinery::Page.find_by_path_or_id(nil, page.parent_id)
slugs << page.translations.select { |p| p.locale == lang }.first.slug
end
match "/:locale/#{slugs.reverse.join("/")}" => "controller#action", :via => :get, :constraints => { :locale => /#{lang}/ }
end
With this, i'm getting a route to the specified page in every language like described above.
But the problem is, when the user changes the name of the page or the position in the menu, the routes have to be generated again, which isn't done too often.
Now my question is, how can i do this more dynamically on run-time? I've read a bit about constraints but i don't know if this is what i need.
Thanks for your help!
I needed to figure out building routes off a database model myself in a Rails 4 application (which is called "ComingSoon" in the examples below. I wanted pages that could be edited on the back-end and given a user-friendly name, which is stored in the Page#name field. So "About Us" titled page typically becomes "about_us" name, which leads to "http://localhost:3000/about_us" The following is the technique I came up with:
Create a new model in app/models/dynamic_router.rb
class DynamicRouter
def self.load
ComingSoon::Application.routes.draw do
Page.all.each do |pg|
get "/#{pg.name}", :to => "pages#show", defaults: { id: pg.id }, as: "pages_#{pg.name}"
end
end
end
def self.reload
ComingSoon::Application.routes_reloader.reload!
end
end
The key above is that I pass the page's id as one of the parameters, so look up is still on the Page#id field, which is, IMHO, a lot better than using the friendly plugin or lookups on slugerized values.
Add the following line to your config/routes.rb
ComingSoon::Application.routes.draw do
# ...
DynamicRouter.load
end
Finally, when the Page is updated, we need to reload the routes, so add an after_safe callback on the Page model:
class Page < ActiveRecord::Base
after_save :reload_routes
def reload_routes
DynamicRouter.reload
end
end
I plan to refine this further to only reload routes if the name attribute is changed and perhaps simply edit the existing route rather than reloading everything if performance proves to be an issue (which at the moment, its not).

How to add a link back to the application in ActiveAdmin?

I need to add a few links to certain pages of the application in the ActiveAdmin pages. I can do this using sidebars, but I'll have to repeat the code for each of my resources. Is there anyway of adding custom links to the header ? Or define a sidebar that will appear for all resources ?
I also wouldn't want to overlook setting config.site_title_link in initializers/active_admin.rb.
I'm pretty sure it takes a symbol representing the name of a route from your application, for example:
config.site_title_link = :root
would link the site title to your application's root_path.
Thanks #phoet ! Implemented it by overriding the HeaderRenderer instead:
module ActiveAdmin
module Views
class HeaderRenderer
def to_html
title + global_navigation + application_link + utility_navigation
end
def application_link
link_to('Back to Application', root_url)
end
end
end
end
i think there is no build-in way to do it, but you can override the render-logic in the TabsRenderer (2.2) / TabbedNavigation (3.0):
def render_menu(menu)
content_tag :ul, :id => #options[:id] do
menu.items.collect do |item|
render_item(item)
end.join.<<('your_custom_stuff').html_safe
end
end

Resources