Rails logic works in view but not controller - ruby-on-rails

I have the following loop in my view to display all divisions in a given tournament. When I try to replicate the logic in the controller, then pass the variable to the view, I and "undefined methods" error. Any help with what I'm doing wrong would be appreciated.
Models
class Tournament < ApplicationRecord
has_and_belongs_to_many :divisions
end
class Division < ApplicationRecord
has_and_belongs_to_many :tournaments
end
WORKING Controller & View
Controller
def index
#tournaments = Tournament.all
end
View
<% tournament.divisions.ids.each do |tdi| %>
<%= Division.find(tdi).name %>
<% end %>
NOT WORKING Controller & View
Controller
def index
#tournaments = Tournament.all
#tournaments.divisions.ids.each do |tdi|
#divisions = Division.find(tdi).name
end
end
View
<%= #divisions %>
When I try the second (and I'm sure more correct) implementation, I get a "undefined method `divisions'" error for the following line in the index method:
#tournaments.divisions.ids.each do |tdi|

The problem is that #tournaments = Tournament.all this line returns a list of tournament objects. So, in the second line you cannot relate a list of object with any kind of association. You have to Iterate through the #tournaments and then find the divisions for each tournament.
def index
#tournaments = Tournament.all
# you can also eager load the tournamnets
# #tournaments = Tournament.includes(:divisions)
#tournaments.each do |tournament|
tournament.divisions.ids.each do |tdi|
#divisions = Division.find(tdi).name
end
end
end

What I think the error is telling you is that the method ids is not a method of divisions. Yo would have to define that method as scope in your Division model.
scope :ids, -> { pluck(:id) }
Another thing is that I don't understand why are you trying to do something like this:
<% tournament.divisions.ids.each do |tdi| %>
<%= Division.find(tdi).name %>
<% end %>
when you can just simply do this:
<% tournament.divisions.each do |tdi| %>
<%= tdi.name %>
<% end %>

First thing, in the 2nd case you are calling association on collection, You need to do this, in controller, ultimately you need all the division names, here you also have n + 1 query problem so to solve that
def index
#tournaments = Tournament.includes(:divisions)
end
In view
#tournaments.each do |tournament|
tournament.divisions.each do |division|
<%= division.name %>
<% end %>
<% end %>
Hope that helps!

Related

How to code controller to use both search and filter in rails app

I'm building a rails app, and I'm having some trouble getting the search function and filter option to work in unison.
#app/models/raffle.rb
class Raffle < ApplicationRecord
has_many :tickets
has_many :users, through: :tickets
def self.filter(filter)
if filter
raffle = Raffle.where(category: filter)
else
Raffle.all
end
end
def self.search(key)
if key.nil?
raffle = Raffle.where(product_name: search)
else
keys = key.split('+')
Raffle.where((['product_name LIKE ?'] * keys.size).join(' OR '), *keys.map{ |key| "%#{key}%" })
end
end
end
#app/controllers/raffles_controller.rb
def index
#raffles = Raffle.filter(params[:filter]) #something for search functionality here
end
#app/views/raffles/index.html.erb
<%= form_tag(raffles_path, method: :get) do %>
<%= text_field :search, params[:search]%>
<%= submit_tag ("Search") %>
<% end %>
<%= form_tag(raffles_path, method: 'get') do %>
<%= select_tag(:filter, options_for_select(Raffle.pluck(:category).uniq, params[:filter])) %>
<%= submit_tag ("Filter") %>
<% end %>
I'm certain the issue lies in the controller, since I've tried #raffles = Raffle.filter(params[:filter]).search(params[:search]), but I get an error 'wrong number of arguments (given 0, expected 1)' being triggered by the line def self.search(key)
Any help greatly appreciated!
Here's an excerpt from your code.
def self.search(key)
if key.nil?
raffle = Raffle.where(product_name: search)
Once execution reaches the end of it, it calls product_name: search. search is the name of the method you've defined that requires one argument and you're passing none. I assume you wanted to call something else here.
If you would have looked at the stack trace you would have gotten it yourself.

Removing an anti-pattern from views to controller

I have two models with a relation:
Company has_many :quotes
I want to remove an anti-pattern logic and place it in the controller:
<%= Quote.where(company_id: c.id).where(paid: true).count %>
The setup:
Controller:
#companies = Company.all
Views:
<% #companies.each do |c| %>
<%= Quote.where(company_id: c.id).where(paid: true).count %>
<% end %>
I want to show total quotes for each company. While the above works, I do not want that logic in the views. How to put that in the controller?
I have this in controller:
...
#jobs_count = #companies.map do |c| Quote.where(company_id: c.id).where(paid:true) end
...
That does not work. The view page does not use params.
Company.has_many :quotes already allows you to use company.quotes rather than Quote.where(company_id: company.id).
Now, define this scope on the Quote model:
class Quote
scope :paid, -> { where paid: true }
# ...
end
Now your view can say
<% #companies.each do |c| %>
<%= c.quotes.paid.count %>
<% end %>
... with no need for any changes in the controller.
Does that look better?
PJSCopeland has made a great suggestion by using a named scope:
class Quote
scope :paid, -> { where paid: true }
end
but I would suggest taking it a step further by keeping "quotes paid count" as a concern of your Company model:
class Company
def quote_count
quotes.paid.count
end
end
Then in your view:
<% #companies.each do |c| %>
<%= c.quote_count %>
<% end %>
This also makes it easier to unit test you Quote and Company models.

Devise - how to show user's post

I use Devise gem for authentication.
In database I have users table and posts table in my database schema (and Post controller).
In post controller I want to find all posts assigned to specific user. I have user_id in posts table.
How to get all user's posts or how to check if specific post is assigned for SIGNED IN user.
I thought about something like this (of course is only pseudocode:
current_user.id == Post.where(params:[post_id]).user_id
So how to get current user id in Devise and how to check the current user id is the same like eg. user_id assigned to viewing post (I want to add 'edit' function when current user is post owner) and how to find all post which current user is owner.
Associations
Firstly, your user_id column in your posts table is what's known as a foreign_key
Foreign keys are used in relational database systems to give you the ability to call associative data from a single record. Simply, it means that you'll be able to use the ActiveRecord associations to call the data you require, rather than having to call it individually:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
#app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
This will give you the ability to use the following call:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
#posts = current_user.posts
end
end
You'll be best served looking up the has_many association:
Fix
In regards to showing your posts for your users, you need to be sure that you have the correct "flow" set up. What I mean is you need some condition to know whether your user is signed in & that #posts is set:
#app/views/posts/index.html.erb
<% if #posts.present? %>
<% #posts.each do |post| %>
<%= post.title %>
<% end %>
<% end %>
Maybe this is the first time you use Devise. You can access current_user inside controllers or views. I imagine you could do something like this
In controller (posts_controller.rb):
#posts = current_user.posts
In view (posts/show.html.erb, I guess):
if current_user.id = #post.current_user
#render something here
end
Get all post which current user is owner.
#posts = Post.where(:user_id => current_user.id)
and on your view
<%-# commented : checking if #posts is empty -%>
<% if #posts.empty? %>
<span>Sorry, post is empty </span>
<% else %>
<%= #posts.each do |p| %>
<% if p.user_id == current_user.id %>
<% link_to "edit", edit_path(p) %>
<% end %>
<% end %>
<% end %>
There are many ways you could get current_user posts. I'll go the long way.
we need
an action
an action view and a partial
a route
a link_to
* action *
def my_posts
#posts = current_user.posts.all.order(created_at: 'DESC')
end
* view *
my_posts.html.erb
<% if #posts.present? %>
<%= render 'posts' posts: #posts %>
<% else %>
<h1>You don't have any posts yet! create one</h1>
<% end %>
_posts.html.erb
<%posts.each do |post| %>
<%= post.title %>
<% end %>
index.html.erb
<%= render 'posts' posts: #posts %>
route
get 'post' => 'posts#my_posts', as: :my_posts
link_to
<%= link_to 'My posts', my_posts_path %>
I may be late but someone can find it useful :)

How to view RoR controller instance variables in view?

I have a table Projects each with 0 or more Categories. On my view, I want to display 0 projects until a JQuery click event associated with each category--i.e. when the user clicks "Food," I want to display all projects with category Food; when the user clicks "Photos," I want to display BOTH food and photos-related projects.
So on the jQuery click event I define an ajax call:
params = 'category_name=' + cat;
$.ajax({
url: "/projects_controller/filter_list",
data: params
})
where "cat" is the names of the Categories selected (in the format "Food Photography Journal etc")
In my projects_controller I started a filter_list method:
def filter_list
#categories = []
words = params[:category_name].split(/\W+/)
words.each { |word| #categories.push(Category.where("name = ?", word)) }
#projects = ...
end
But now I'm stuck. 1) How do I get all the projects associated with any of the categories in #categories? and 2) How do I display the #projects variable on my view? Right now I just display all like this:
<% Project.all.each do |project| %>
<tr style="display:none" class="project <% project.categories.all.each do |cat| %><%= cat.name %> <% end %>">
<td><%= project.filename %></td>
<td><a href='project/<%= project.id %>'><%= project.location %></a>
<td><% project.categories.all.each do |cat| %><%= cat.name %>, <% end %></td>
<% end %>
Your instance variables $categories, #projects are already available in the view. So in the view you can use #project rather than accessing the Class Project itself.
<% #projects.each do |project| %>
...
<% end %>
But probably you did not design your models correctly. Establish the correct relationships in your model. If a project belongs to a category, you can associate them as follows:
#models/category.rb
class Category < ActiveRecord::Base
has_many :projects
end
#models/project.rb
class Project < ActiveRecord::Base
belongs_to :category
end
#controllers/categories_controller.rb
def index
#categories = Category.all #or use your own query
end
#views/categories/index.erb
<% #categories.each do |category| %>
# here you can get all projects under this category using
# category.projects
<% end %>
Note: i'm used to HAML, so sorry if my ERB syntax is wrong
The according view(s) to a controller class have have access to the class level instance variables (i.e. #my_variable).
Simply let #projects = Project.all.each in your controller and substitute Project.all with #projects in your view.

Rails: check presence of nested attribute

If I have the following nested model relationships (all has_many):
Countries < Cities < Streets < Homes
In a show view, how can I check if a particular Country has any homes?
Edit:
Adding the suggested method of chaining with the map method (first try to map to streets). So far it's not restricting the records
<% #countries.each do |country| %>
<% if country.cities.map(&:streets).any? %>
....
<% end %>
<% end %>
You can call or #country.cities.map(&:streets).flatten.map(&:homes).present? or #country.cities.map(&:streets).map(&:homes).any?
<% if #country.cities.map(&:streets).flatten.map(&:homes).flatten.any? %>
Tro-lol-lo yo-lo-puki
<% end %>
Also you can wrap this long line into your model method:
class Country < ActiveRecord::Base
def any_homes?
cities.map(&:streets).flatten.map(&:homes).flatten.any?
end
end
Usage
<% if #country.any_homes? %>
Tro-lol-lo yo-lo-puki
<% end %>
And of course it looks like a good data structure for refactoring! It wants to be refactored!

Resources