How do I use a controller method in the main layout? - ruby-on-rails

I have built a 'NewsItem' controller that contains a method called 'top'. This is called by a javascript request to update a DIV on the page every 10 seconds. This is all working well.
def top(number = false)
# set the default value to use for the number
default_number = 10;
# perform checks on the variable
if number == false
if params.include?(:number)
number = params[:number]
else
number = default_number
end
end
# Run query to get the required news items
items = NewsItem.all( :order => ("created_at DESC"),
:include => [:profile],
:limit => number)
# iterate around the items that have been returned
#top_news = ""
items.each do |item|
#top_news += render_to_string :partial => "partials/news_item", :locals => {:item => item}
end
respond_to do |format|
format.html { render :partial => "partials/news_top"}
end
end
This is called with '/news/top' or '/news/top/20' to change the number of items that are returned.
The problem is that when the page is first loaded the 'news' DIV is empty for 10 seconds, until the JavaScript runs to update the DIV. So I want to ensure that the DIV is already populated by calling this function.
Now as I want the 'news' DIV to be available in all pages it is defined in the 'layouts/application.html.erb' template. So I need to call the 'top' method in the 'NewsItem' controller so that it can be rendered into the initial HTML. This is where I am struggling as I cannot work out how to use the 'helper_method' to make this available at this stage.
I have a feeling that I am missing something here and not understanding the whole process.
Thanks very much for any assistance.
Regards, Russell

Try separating out the logic. Maybe you make a method called top_news that returns the value you use in top_news. And since you're passing back a partial, use it to build the string. Partials are meant to be used to iterate over a list.
def index
#top_news = top_news
end
def top
#top_news = top_news
respond_to do |format|
format.html { render :partial => "partials/news_top"}
end
end
private
def top_news(number = false)
# set the default value to use for the number
default_number = 10;
# perform checks on the variable
if number == false
if params.include?(:number)
number = params[:number]
else
number = default_number
end
end
# Run query to get the required news items
items = NewsItem.all( :order => ("created_at DESC"),
:include => [:profile],
:limit => number)
# iterate around the items that have been returned
return render_to_string :partial => "partials/news_item", :collection => items, :as => :item
end
The other solution you can follow is to not render any partials in the controller at all except for your one javascript method and instead make a method in your model to do exactly what you're doing here.
class NewsItem
def top_news(number=20) # pass in number with default 20
NewsItem.where( :order => ("created_at DESC"),
:include => [:profile],
:limit => number)
end
end
Then just call it from your controllers to get that so you can use it in your views and iterate over it using partials in your views.

Related

Create a link for next page in rails

I'm using the Twilio API in a rails app to show a user a list of their recordings. Say a user has 11 recordings total, and I'm showing them 3 per page.
twilio_controller.rb
def calls
#user = current_user
#account_sid = #user.twilio_account_sid
#auth_token = #user.twilio_auth_token
#page_size = 3
#page = params[:page_id] || 0
#sub_account_client = Twilio::REST::Client.new(#account_sid, #auth_token)
#subaccount = #sub_account_client.account
#recordings = #subaccount.recordings
#recordingslist = #recordings.list({:page_size => #page_size, :page => #page})
end
calls.html.erb
<% #recordingslist.each do |recording| %>
<tr>
<td><%= recording.sid %></td>
</tr>
<% end %>
<%= link_to "Next Page", twilio_calls_path(#page + 1) %>
routes.rb
#twilio routes
post 'twilio/callhandler'
get 'twilio/calls'
match 'twilio/calls' => 'twilio#page', :as => :twilio_page # Allow `recordings/page` to return the first page of results
match 'twilio/calls/:page_id' => 'twilio#page', :as => :twilio_page
Paging info is built into the Twilio response such that
#recordingslist.next_page
gives me the next 3 recordings (verified in rails console). How do I link to that so that when a user clicks the link, the table loads the next 3 results?
Thanks!
You can use a gem like Kaminari for Pagination.
https://github.com/amatsuda/kaminari
I would recommend utilizing the paging functionality that ships with twilio-ruby. According to the docs:
ListResource.list() accepts paging arguments.
Start by create a route for your Twilio list view. Make sure you can pass a page_id parameter – this is how your controller action will know which page to render:
# config/routes.rb
match 'recordings/page/:page_id' => 'twilio#page', :as => :twilio_page
match 'recordings/page' => 'twilio#page', :as => :twilio_page # Allow `recordings/page` to return the first page of results
Then, in the page action, pull the page_id parameter (or set if to 1 if there is no page_id, and pass the page_number and page_size as arguments to #recordings.list:
# app/controllers/twilio_controller.rb
def page
page_size = 3
#page = params[:page_id] || 1
#sub_account_client = Twilio::REST::Client.new(#account_sid, #auth_token)
#subaccount = #sub_account_client.account
#recordings = #subaccount.recordings
#recordingslist = #recordings.list({:page_size => page_size, :page => page)})
end
Finally, in your view, pass the page number to twilio_page_path in your link_helper – just make sure to adjust the page number accordingly (+1 for the next page, -1 for the previous page:
# view
<%= link_to "Next Page", twilio_page_path(#page.to_i + 1) %>
<%= link_to "Previous Page", twilio_page_path(#page.to_i - 1) %>
Note that – if you're at the start or end of your list – you may inadvertently end up passing an invalid page_id. Therefore, you may want to implement some exception handling in your page controller action:
# app/controllers/twilio_controller.rb
def page
begin
#page = params[:page_id] || 1 # If `page_id` is valid
rescue Exception => e
#page = 1 # If `page_id` is invalid
end
# Remaining logic...
end

global variable in application controller?

I grap the first country with
#country = Country.find(1)
Then i my head navigation i make this loop to get the right tags:
%ul.thumbnails
- #country.tags.find_each(:conditions => "active_house = true") do |a|
%li.span2
.thumbnail
- a.attachments.limit(1).each do |b|
= image_tag(b.file.url)
.caption
%p
#{link_to a.url, tag_country_region_houses_path(#country, a.name), :class => 'btn-nav-drop'}
This works fine. But the navigation is global so i created a method in application_controller like this:
helper_method :tags
def tags
#country = Country.find(1)
#tags = #country.tags.find_each(:conditions => "active_house = true")
end
And in the navigation view:
%ul.thumbnails
- tags do |a|
%li.span2
.thumbnail
- a.attachments.limit(1).each do |b|
= image_tag(b.file.url)
.caption
%p
#{link_to a.url, tag_country_houses_path(#country, a.name), :class => 'btn-nav-drop '}
But i get a error message "no block given (yield)"
Thanks..remco
Well, this is nothing about global variable which should be avoided whenever possible.
Your problem is in this line
tag_country_houses_path(#country, a.name)
There is NO #country exists in View.
You may wonder why. The reason is helper can't pass instance variables to View, different from controller.
What your helper all did is to return an array object #tags. The value of this object is available in view, but not instance variable #tags, neither #country.
The fix? Use something to replace #country. If the association is country has_many tags, you can do it like this:
tag_country_houses_path(a.country, a.name)
If not, you can set a method in tag model to get the country.
And you can even use some includes to make query more efficient but that is another story.
Also, your helper can be simplified without assigning any variables.
def tags
Country.find(1).find_each(:conditions => "active_house = true")
end
find_each accepts a block that will be yielded. Because you write find_each in helper without the block, so it will throw an error. You have two solutions.
Solution1: You can just use find to return an array.
def tags
Country.find(1).tags.find(:all, :conditions => "active_house = true")
end
in your view:
- tags.each do |t|
.........
Solution2: you can pass the block to your helper.
def tags
Country.find(1).tags.find_each(:conditions => "active_house = true") do |t|
yield t
end
end
in your view.
- tags do |t|
.........
If you have not many many records, just use solution1.

Rails: Sorting and Filting Data based on boolean attribute

I have a basic model of "Projects", which currently only has the attributes name:string, active:boolean. On the index view, I want to be able to have three links: Active Projects, Inactive Projects, and All Projects. These links will display the appropriate projects based on the status of the :active boolean value. Initially I set up the view by giving the links params like:
link_to "Active Projects", {:action => 'index', :active => true}
Then in the controller:
if params[:active] == "true"
#projects = Project.find(:all, :conditions => {:active => true})
elsif params[:active] == "false"
#projects = Project.find(:all, :conditions => {:active => false})
else
#projects = Project.all
This seems a little cumbersome, especially since in future I want to have multiple filters, like due date, and client. What is a good way / gem to implement advanced sorting / filtering actions, without filling up the controller with a lot of code?
You could structure your params as a hash, and pass it to conditions:
# example:
params = {
:filters => {
:active => true,
:name => 'Boby',
# etc...
}
}
filters = params[:filters]
#projects = Project.where(filters)
I would recommend using where statements.. Also here is your code refactored :
#projects = Project.all
#projects = #projects.where(active: params[:active]) if params[:active].present?
Then you can keep stacking on items if additional params exist like so :
#projects = #projects.where(awesome_sauce: params[:awesome_sauce]) if params[:awesome_sauce].present?

Not able to render partial with Gmaps4Rails

I have a map which has got marker based on state of US. Each state has n number of city.
I have got a state model, controller and city model, controller.
When I click on the marker of the state, I want the list of cities to be displayed in the info window.
All this information is appearing on the homepage.
This is what I have done so far :-
home_controller.rb
def index
#states = State.all.to_gmaps4rails do |state,marker|
marker.infowindow render_to_string(:partial => "/states/gmaps4rails_infowindow", :locals => {:object => state})
marker.json({:id => state.id})
end
end
home/index.html.haml
=gmaps({"map_options" =>{ "auto_zoom" => false, "zoom" => 3}, "markers" => { "data" => #states } })
state_controller.rb
def gmaps4rails_infowindow
#state = Gmaps.map.markers
end
states/_gmaps4rails_infowindow.html.haml
=#state.cities.each do |city|
=city.name
Needless to say that it is not working. Can someone please help me out?
Well, your home_controller.rb is fine. you write here you want to use a partial with a local variable named object.
In the partial itself, you write:
=#state.cities.each do |city|
=city.name
The instance variable isn't defined there, you defined a local variable just above.
Replace with:
=object.cities.each do |city|
=city.name
From there it should work.
Notice:
def gmaps4rails_infowindow
#state = Gmaps.map.markers
end
is:
useless: you define the infowindow in the controller
wrong: Gmaps.map.markers only lives as js variable

Simplenavigation - How to create dynamic menu items?

I am trying to create a dynamic menu with simple navigation.
The problem is that the menu only works on the show action that should show dynamic menu items. All other pages gives the error:
undefined method `model_name' for #<Class:0x9236118>
I have read this, but have not found any solution:
https://github.com/andi/simple-navigation/wiki/Dynamic-Navigation-Items
My navigation.rb:
sub_nav.item :virk, 'Virksomheder', virk_path, :link => {:style => 'font-weight:bolder;', :class => 'submini'} do |virknavn|
virknavn.item :virksom, #virksomhed.try(:name), url_for(#virksomhed), :highlights_on => /virksomheder\/[0-9]+/
end
I only want the virknavn menu items to be highlighted on:
/virksomheder/:some virksomhed name
My virksomhed controller:
def index
#virksomheds = Virksomhed.all
render :layout => 'page'
end
# GET /webhosts/1
# GET /webhosts/1.xml
def show
#virksomhed = Virksomhed.find(params[:id])
render :layout => 'page'
end
I did manage to figure out that I just could set a if statement in the navigation.rb:
if params[:id].blank?
params[:id] = Virksomhed.first.id
end
#virksomhed = Virksomhed.find(params[:id])
if #virksomhed.blank?
#virksomhed = Virksomhed.first
end
And it did the magick.

Resources