Rails 4: How to put loop logic inside the controller - ruby-on-rails

I have four loops in my haml document for testing purposes with the only difference being the order of the elements.
Am I somehow able to put the logic in my controller and list the whole 'insidyness' of the loop only once in my haml document?
Currently I have everything duplicated 4 times and ya know, that feels bad. :)
For instance:
- #loop.where(featured: true).order("RANDOM()").limit(4).each do |item|
%li
%h1 This is an example
- #loop.order(:cached_votes_up => :desc).limit(4).each do |item|
%li
%h1 This is an example
- #loop.order(:impressions_count => :desc).limit(4).each do |item|
%li
%h1 This is an example
- #loop.order("created_at DESC").limit(4).each do |item|
%li
%h1 This is an example
Controller:
def index
#loop = Item.all
end
I would like to reduce my Haml Code to something like this and move the rest in the Controller:
- #loop.each do |item|
%li
%h1 This is an example
Thanks in advance for each answer!

You can't render the view multiple times but you could do something like this.
def index
#loops = [
Item.where(featured: true).order("RANDOM()"),
Item.order(:cached_votes_up => :desc),
Item.order(:impressions_count => :desc),
Item.order("created_at DESC")
]
end
And then in the template
- #loops.each do |loop|
- loop.limit(4).each do |item|
%li
%h1 This is an example

in Item model class:
scope :featured_with_random_order_desc, ->(limit_value) { where(featured: true).order("RANDOM()").limit(limit_value) }
scope :by_cached_votes_order_desc, ->(limit_value) { order(:cached_votes_up => :desc).limit(limit_value) }
scope :by_impression_count_order_desc, ->(limit_value) { order(:impressions_count => :desc).limit(limit_value) }
scope :by_created_at_desc, ->(limit_value) { order("created_at DESC").limit(limit_value) }
In your views:
- #loop.featured_with_random_order_desc(4).each do |item|
%li
%h1 This is an example
- #loop.by_cached_votes_order_desc(4).each do |item|
%li
%h1 This is an example
- #loop.by_impression_count_order_desclimit(4).each do |item|
%li
%h1 This is an example
- #loop.by_created_at_desc.limit(4).each do |item|
%li
%h1 This is an example
You can go one more step further and create variable for each loop in your controller:
def index
#loop_featured = Item.featured_with_random_order_desc(4)
#loop_cached_votes = Item.by_cached_votes_order_desc(4)
#loop_impression_counts = Item.by_impression_count_order_desclimit(4)
#loop_by_created = Item.by_created_at_desc(4)
end
and use them in view:
- #loop_featured.each do |item|
%li
%h1 This is an example
- #loop_cached_votes.each do |item|
%li
%h1 This is an example
- #loop_impression_counts.each do |item|
%li
%h1 This is an example
- #loop_by_created.each do |item|
%li
%h1 This is an example

You can use union_scope from ActiveRecord
#item.rb
include ActiveRecord::UnionScope
scope :by_random, -> { where(featured: true).order("RANDOM()").limit(4) }
scope :by_cached_votes, ->{ order(:cached_votes_up => :desc).limit(4) }
scope :by_impression_count, ->{ order(:impressions_count => :desc).limit(4) }
scope :by_created_at, ->{ order("created_at DESC").limit(4) }
scope: all_conditions, -> { union_scope(by_random, by_cached_votes,by_impression_count,by_created_at}
your controller
#item = Item.all_conditions
your view:
- #loop.all_conditions.each do |item|

Related

Rails, using scopes to filter table

i am fairly new to rails and i have to update an existing project. I have an existing database, beforehand it was just one User group, now there should be multiple. Now i want to use the old view, but filter with the help of a dropdown menu, but for some reason i can't work out what i am doing wrong.
Here are the code snippets i changed, since it was working beforehand, i assume my mistake must be somewhere within these lines.
event.rb
scope :men, lambda { { :conditions => ["team_id == ?", 1] } }
scope :women, lambda { { :conditions => ["team_id == ?", 2] } }
scope :juniors, lambda { { :conditions => ["team_id == ?", 3] } }
events_controller.rb
def index
# #events = Event.where("startdate >= ?", Date.today).order("startdate, starttime")
# #events = Event.order("startdate, starttime")
if params[:search]
#events = Event.search(params[:search])
else
if params[:filter].nil?
#events = Event.all
else
if params[:filter] == "Alle" then #events = Event.all end
if params[:filter] == "Men" then #events = Event.men end
if params[:filter] == "Women" then #events = Event.women end
if params[:filter] == "Juniors" then #events = Event.juniors end
end
end
end
and the index.html.erb
<div class="left">
<%= form_tag events_path, :method => 'get' do %>
<%= select_tag "filter", options_for_select([ "Alle", "Men", "Women", "Juniors" ], params[:filter]), {:onchange => 'this.form.submit()'} %>
<% end %>
probably it is a simple mistake. My guess is, that in the index.html.erb i am doing something wrong.
as a follow up, i want to filter just the events which are upcoming, for that i can use the commented part in the controller. can i just add that to the assignmnet in the style of:
#events = Event.men.where("startdate >= ?", Date.today).order("startdate, starttime")
thanks for the help
Lenny
You should change your scopes to new syntax:
scope :men, -> { where(team_id: 1) }
scope :women, -> { where(team_id: 2) }
scope :juniors, -> { where(team_id: 3) }
Your controller logic is a little buggy and twisted (checking 5 times filter isnt best way, why checking e.g. if filter is "Men" if you already matched it with "Alle" ?). Here is some help:
#events = if params[:search].present?
Event.search(params[:search].to_s)
else
case params[:filter]
when "Men"
Event.men
when "Women"
Event.women
when "Juniors"
Event.juniors
else
Event.all
end
end
Speaking about view, you shouldnt use inline js, just because its XXI century, and such "quick solutions" are harder to maintain later, so:
<div class="left">
<%= form_tag events_path, :method => 'get' do %>
<%= select_tag "filter", options_for_select([ "Alle", "Men", "Women", "Juniors" ], params[:filter]), class: 'my_filter' %>
<% end %>
and then add to your events.coffee:
$('select.my_filter').on 'change', ->
$(this).parents('form').submit()
Hope this helps!

If params is present not working

My url paths are this
/locale/country/
/locale/country/region/
models:
class Region
belongs: country
end
class Country
has_many: regions
end
a foreign-key country_id in regions table
This is a part of a nav partial in the layout file.
//block region
- if params[:region].present?
%ul.thumbnails
- #region.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.h1, tag_country_region_houses_path(#country, #region, a.name), :class => 'btn-nav', class: active_class?(tag_houses_path(a.name))}
//block country
- else
%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.h1, tag_country_houses_path(#country, a.name), :class => 'btn-nav', class: active_class?(tag_houses_path(a.name))}
When the visitor is on the path /local/country i want show block country and when the vistor is on /locale/country/region i want to show block region.
I thought if params[:region].present? will do the trick. But no....how can i realize this?
Before the line
- if params[:region].present?
In your view, try ptinting the lines,
- p params[:region]
- p params[:region].present?
that could give you some clue about the behaviour.
Try doing this:
if params.has_key?(:region)
or if it exists but contains a nil you could test it:
unless params[:region].nil?
Is it not supposed to be
class Region
belongs_to: country
end
instead of
class Region
belongs: country
end
Cheers

Show only active (boolean) items in each loop

This is my loop. On my region page I show all the cities with there activities, events etc
- #cities.each do |city|
%li
%h4.h6.icon_mappoint
= link_to city.name, region_city_path(#region, city)
%ul
- city.events.each do |thing|
%li
= link_to event.name, region_city_event_path(#region, city, event)
%ul
- city.activities.each do |activity|
%li
= link_to activity.name, region_city_activity_path(#region, city, activity)
Each event and activity model has a attribute in the DB called active. (boolean). And want to show only the active events and activities in the view.
My controller looks like this now.
def show
#region = Region.find(params[:id])
#cities = #region.cities
end
Models
region has_many cities
city has_many events, activities
How can I show only the active events, activities in the each loop?
If you add a scope to your Event and Activity models
scope :active, where(:active => true)
you can write your views like
city.events.active.each do |thing|
and
city.activities.active.each do |activity|
Instead of city.events.each use city.events.select { |x| x.active }.each.

how to call method defined in model from view?

I am getting undefined method `member' error in my following code
Pls tell where am i going wrong in the following code...
I have groups model in which i have created member method as follows
def member(groupid,userid)
#members = Membership.all
#members.each do |m|
if m.group_id == groupid and m.user_id == userid
return true
end
end
end
in my groups_controller index action :
def index
#group = Group.all
end
and inside my groups/index.html.haml i am calling this method at following line
- #group.reverse.each do |group|
%tr
%ul{:style=>"list-style-type:square"}
%td
%li= link_to group.groupname,group
- if member(#group.id,#current_user.id)
= link_to ' Join this group', {:controller => "memberships",
:action => "create",
:group_id => group.id},
:method => "post"
but when i run code i get undefined method `member' error
member is an instance method of Group. So you should change that line to
- if group.member(#group.id, #current_user.id)
But it doesn't need to be instance method. It doesn't use any state or anything. So, make it a class method.
class Group
def self.member(groupid,userid)
#members = Membership.all
#members.each do |m|
if m.group_id == groupid and m.user_id == userid
return true
end
end
end
end
And call it like this:
- if Group.member(#group.id, #current_user.id)

refactor conditions within haml view

beside the fact that accessibility standards discourage the use
of a link pointing to the current page, how I am supposed to
refactor the following view code?
#navigation
%ul.tabbed
- if current_page?(new_profile_path)
%li{:class => "current_page_item"}
= link_to t("new_profile"), new_profile_path
- else
%li
= link_to t("new_profile"), new_profile_path
- if current_page?(profiles_path)
%li{:class => "current_page_item"}
= link_to t("profiles"), profiles_path
- else
%li
= link_to t("profiles"), profiles_path
...
Thank you.
# helpers
def current_page_class(page)
return :class => "current_page_item" if current_page?(page)
return {}
end
-# Haml
#navigation
%ul.tabbed
%li{current_page_class(new_profile_path)}
= link_to t("new_profile"), new_profile_path
%li{current_page_class(profiles_path)}
= link_to t("profiles"), profiles_path
...
#navigation
%ul.tabbed
%li{:class => current_page?(new_profile_path) ? "current_page_item" :nil }
= link_to t("new_profile"), new_profile_path
%li{:class => current_page?(profiles_path) ? "current_page_item" :nil }
= link_to t("profiles"), profiles_path
...
Looks like a good case for a partial to me.

Resources