How to filter collections in rails - ruby-on-rails

I'm new in Rails, and I'm trying to filter the animals I have on my DB, by one of their properties. I have read that I can make it by a scope in the controller, and have access to it by a parameter on the URL, but I think that doesn't work for me because I'm using a loop to create my HTML.
Is there a way to add a filter to the collection I'm using (#animals)?
<% #animals.each do |animal| %>
<li>
<a href="#">
<%=link_to animal.ncommon, animal %>
</a>
</li>
<% end %>
I hope I was clear with my question. Thank you for your help!

So the best practice is to move the scope to the models , not in the controller. Here is and example. (please note that this may change depending on your file structure and names).
here I'm going to filter animals by type. Lets say type 1 and type 2.
#model app/models/animal.rb
class Animal < ActiveRecord::Base
scope :by_type, -> (ty) { where(type: ty) }
end
#controller app/controllers/animals_controller.rb
class AnimalsController < ApplicationController
... other code
def index
# calls the by_type method/scope in the Animal model
# filter the records and assignes them to #animals varaible
#animals = Animal.by_type(params[:type])
end
... other code
end
then you can use your loop in the view. When you want to filter, you call the index action with a parameter
E.g http://localhost:3000/animals?type=1
So the idea is, not to filter inside the loop, but to get the filtered results to the #animals variable.

I just found a solution that i can use in the loop.
I changed:
<% #animals.each do |animal| %>
to:
<% #animals.where("promo > ?", 0).each do |animal| %>
Here i´m getting all the animals that has a promo greater than zero.
Thanks!

Related

Using exists? in Rails 4

I am trying to query the DB to see if an Open House exists for a specific listing. If it does, I would like it to display a span. I created a helper based on what I read, but the span is displaying on all of the listings, not just the ones that have an upcoming open house. Any help is appreciated.
ApplicationController:
class ApplicationController < ActionController::Base
def upcoming_oh
if #open_houses = OpenHouse.exists?
end
end
helper_method :upcoming_oh
end
Listings Index:
<% upcoming_oh %><span class="label label-nklyn-yellow">Upcoming Open House</span>
You can check if your model contains a specific record using exists function
if OpenHouse.exists?(your_record)
# do stuff
end
I'm not sure what is OpenHouse, but I think it's a model of your's, anyway this logic should solve your issue
you can use if/else to display/hide a span or div or whatever you want after that like this
<% if #condition %>
<span>
<% end %>

Display multiple model results in your views in rails app

I'm adding a new model to my equasion and I'm wondering if there is a way to associate two models into one model then display any/all results within a view. For example, here is what I've currently have;
#tweet_category.order("position").each do |tweet|
<%= tweet.title %>
end
just a short example... now what if I added facebook into this. I was first thinking of creating a model thats named stuff then associate it to tweet_category and facebook_category like so;
class Stuff < ActiveRecord::Base
attr_accessible :title
belongs_to :user
has_many :tweet_category
has_many :facebook_category
end
Now in my controller I'm guessing I would do the following;
class StuffController < ApplicationController
def index
#stuff_list = Stuff.find(:all)
end
end
and in my view I would just simply do the following from above view;
#stuff_list.order("position").each do |stuff|
<%= stuff.title %>
end
am I understanding the logic here??? would that work having two models / two tables db.. etc..
First of all, I don't understand why you would need that "stuff" model. It belongs to users and has_many tweet_category and facebook_category, and just does nothing but offering a "title", when your User model could do the job ( I mean, each user could have many tweets and fb category, instead of having one or several "stuff" which has/have many of them ).
Anyway, if you want to make links between your models and then display everything in a view, first in your User model you just have to do :
class User < ActiveRecord::Base
...
has_many :facebook_categories #( I don't know how rails would pluralize it, btw, I'm just making an assumption )
has_many :tweeter_categories
end
and
class Facebook_category
...
belongs_to :user
end
and do the same fot the tweeter category
Then in your controller :
def show_everything #Here it's a custom action, but you can call it wherever you want
#users = User.all
end
And finally in your view :
<% #users.each do |user| %>
<% user.facebook_categories.all.each do |fb_c| %>
<%= fb_c.title %>
<% end %>
<% user.tweeter_categories.all.each do |t_c| %>
<%= t_c.title %>
<% end %>
<% end %>
Maybe just try to grab a better name for your models, so the pluralization doesn't get messy ( and I saw that the ".all" method is deprecated, so maybe replace it with something
Hope it helps !
Edit :
Basically, when you're doing
#users = User.all
What rails' doing is putting every hash defining every "User" in an array. So, if you want to mix two tables' arrays inside a single array, you can do something like this :
#categories = [] << Facebook_category.all, Tweeter_category.all
You will then have an array ( #category ), filled with 2 arrays ( one ActiveRecord relation for Facebook_category and one for Tweeter_category ). Themselves filled with hashes of their model. So, what you need to do is :
#categories.flatten!
Here's the API for what flatten does ( basically removing all your nested arrays inside your first tarray )
Now, you got a single array of hashes, being the informations from both your model's instances. And, if these informations can be ordered, in your view, you just have to :
<% #categories.order("updated_at").each do |i| %>
<%= i.title %>
<% end %>

What's the proper way to do this in Rails?

So I've got two records from Model, a and b. I want to do this:
def do_codependant_stuff(a,b)
a.attribute += b.attribute2
b.attribute = b.stuff+a.stuff
end
I keep hearing fat model, skinny controller, and no business logic in views, so I've put this in my Model model. Based on what a user clicks in one of my views, I want to call do_codependant_stuff(a, b) or do_codependant_stuff(b,a).
I've been just using the basic crud controller actions up to this point, and I'm not quite sure how to make this happen. Do I add this logic to my ModelController update action? Because it's technically updating them, just in a more specific way. Or make another action in the controller? How do I call it/set it up? Most of the default new, update etc are somehow called behind the scenes based on their respective views.
And is updating two things at a time bad practice? Should I split the do_codependant_stuff method into two instance methods and call them on each record with the other as a parameter?
Thanks for reading.
Edit:
Alright, real world code. I'm displaying on the home page of my app two pictures. The user selects the one they like most. The ratings of these pictures change based on a chess ranking algorithm. This is the relevant section of my picture class.
class Picture < ActiveRecord::Base
...
...
def expected_first_beats_second(picture first, picture second)
1/(1+10**((second.rating-first.rating)/400))
end
def first_beat_second(picA,picB)
Ea = expected_first_beats_second(picA,picB)
picA.rating += 50*(1-Ea)
picB.rating += 50*(-(1-Ea))
end
end
The partial I'm using for the view just displays two pictures at random so far with
<% picA = Picture.offset(rand(Picture.count)).first %>
<% picB = Picture.offset(rand(Picture.count)).first %>
<div class = "row">
<div class = "col-md-5">
<%= image_tag picA.url, :class => "thumbnail" %>
</div>
<div class = "col-md-5">
<%= image_tag picB.url, :class => "thumbnail" %>
</div>
</div>
I need to somehow link the onclick of those images to the method in the model.
Here's some code to get you started. Note the comments, they're ruby and rails best practices
Controller:
class PC < AC
...
def compare
# count query once, save the number
count = Picture.count
#pic_a = Picture.offset(rand(count)).first
#pic_b = Picture.offset(rand(count)).first
end
def compare_submit
# Note variables are snake cased, not camel cased
pic_a = Picture.find(params[:winner_id])
pic_b = Picture.find(params[:loser_id])
pic_a.beats(pic_b)
redirect to compare_pictures_path # Or wherever
end
...
end
Any sort of querying should be done in the controller or model. You essentially should never directly access a model in your view. At this point your view has access to the instance variables set in the controller: #pic_a and #pic_b
View:
<%= link_to compare_submit_pictures_path(winner_id: #pic_a.id, loser_id: #pic_b.id) do %>
<%= image_tag #pic_a.url, :class => "thumbnail" %>
<% end %>
<%= link_to compare_submit_pictures_path(winner_id: #pic_b.id, loser_id: #pic_a.id) do %>
<%= image_tag #pic_b.url, :class => "thumbnail" %>
<% end %>
So we just linked to a new path (see routes below) that will pass two parameters: winner_id and loser_id so whichever picture the user clicks on you'll know which one they chose and which one they didn't choose.
Model:
class Picture < AR::Base
# Method arguments don't need a type declaration
def beats(loser)
# Always lowercase variables
ea = 1/(1+10**((loser.rating-self.rating)/400))
self.rating += 50*(1-ea)
loser.rating += 50*(-(1-ea))
self.save
loser.save
end
end
This explicitly uses self for clarity, which isn't necessary. Calling save or rating = ... implicitly calls it on self since we're in the context of an instance method on the picture Model.
Routes:
resource :pictures do
collection do
get :compare
get :compare_submit
end
end

How to iterate over multiple model results in Rails 3?

I am not sure what I am doing wrong here. I find many examples that show that I am doing this right and it is really basic stuff, I know.
I am simplyfying a bit, but I have two models, 'Post' and 'Category'. I am trying to get the list of categories from the database and list them by name.
class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
# get all categories and output the names
cats = Category.all
cats.each do |cat|
cat.name
end
It instead seems to output the entire array of retrieved results. All results not even just the one I am iterating over. What gives?
Where are you putting that .each loop code? Where is the "output" code you're referring to? If you're using a loop in a view, make sure you're using
<% %>
and not
<%= %>
for the loop lines themselves. As in:
<% Category.all.each do |cat| %>
<%= cat.name %>
<% end %>
Category.all returns an array of all Category objects, which is everything the categories table contains. cats is therefore an array of all the categories. I'm not sure why you think you're only iterating over "one" of anything. To get one result, you can use find() or first:
cat = Category.first
puts cat.name
If you want all the names, you can do this:
Category.all.map(&:name)
or, a bit more efficiently, especially if there are many fields:
Category.all(:select => :name).map(&:name)

Rails Getting Started Question

I'm a newbie in rails, and I've been looking all over for an answer for this issue. Here's what I have:
class Item < ActiveRecord::Base
belongs_to :book
class Book < ActiveRecord::Base
has_many :items
Now I want to list all the items with their properties and the book associated with them. They would go under index in the items_controller.rb
class ItemsController < ApplicationController
# GET /items
# GET /items.xml
def index
#items = Item.all
now how would I handle books in the ItemsController so I can list them in index.html.erb keeping in mind that an item belongs to only one book? if I add:
#books = items.book.find
right under #items = Item.all so I can reference them in the index.html.erb I get:
undefined method 'book' for #<Array:0x10427f998>
I have a feeling that the answer is so simple but so far I haven't figured it out. Is there any tutorial that you guys are aware of that covers this matter?
Thank you!
In your view, when you iterate over all of your #items, just reference the book for each one. Example ERB (app/views/items/index.html.erb):
<% #items.each do |item| -%>
Item: <%= item.name %>
Book: <%= item.book.title %>
<% end -%>
If instead your intention is to display each book with the associated items under each book, you'd be better off using the index action on the BooksController. Find all the books, iterate over each book, and for each book, iterate over the items for that book.
The array of items doesn't have a book, each individual item does. It looks like you're trying to get the book of ALL the items, which doesn't exist.
Try #items[0].book or using a loop in your view:
<ul>
<% for item in #items %>
<li><%= item.book.title %></li>
<% end %>
</ul>
You could also use a partial to iterate through the array. See the section "Rendering a collection of partials" at http://api.rubyonrails.org/classes/ActionView/Partials.html
The problem is that you're calling items.book on an array of items, and the array doesn't have a method named book (hence the error). You'd need to get a single book from that array (such as items[0]), then call .book on that.

Resources