I have HTML code in my Rails application that looks like this:
<td><%= MyObj.find_by_id(#my_obj.some_id).name %></td>
And this shows up fine. However, the field some_id is not always present in the database, so I'd like to display NA rather than MyObj's name in those cases.
So here's what I mean in pseudocode:
<td><%= IF #my_obj.some_id is NIL THEN "NA" ELSE MyObj.find_by_id(#my_obj.some_id).name %></td>
How can I do this?
You can use the following snippet:
<td><%= #my_obj.some_id.present? ? MyObj.find(#my_obj.some_id).name : 'NA' %></td>
However, you're exposing yourself to some pretty big issues if you're finding records in your view code like this. Your controller should handle that kind of logic.
You should not be hitting the database in your view code, leave that to your controllers. Try loading the object in your controller instead:
# use this code to your controller
#view_obj = MyObj.find_by_id(#my_obj.some_id)
You can then use the try method to set the name, which will return nil instead of raising the usual NoMethodError if the object is nil:
<%= #view_obj.try(:name) || "NA" %>
<%= !MyObj.find_by_id(#my_obj.some_id).name.blank? ? MyObj.find_by_id(#my_obj.some_id).name : "NA" %>
Related
I have the following controller - recipes_controller.rb:
class RecipesController < ApplicationController
def list
#search_term = params[:looking_for] || 'chicken'
#courses = Recipe.for(#search_term)
end
end
The following model: recipes.rb:
require 'httparty'
class Recipe
include HTTParty
default_options.update(verify: false)
base_uri 'http://food2fork.com/api/search'
default_params key: ENV['FOOD2FORK_KEY']
format :json
def self.for term
get("", query: { q: term})["recipes"]
end
end
& the following view- list.html.erb:
<h1>Searching for - <%= #search_term %></h1>
<table border="1">
<tr>
<th>Image</th>
<th>Publisher</th>
<th>Title</th>
</tr>
<% #courses.each do |course| %>
<tr class=<%= cycle('even', 'odd') %>>
<td><%= image_tag(course["image_url"])%></td>
<td><%= course["publisher"] %></td>
<td><%= course["title"] %></td>
</tr>
<% end %>
</table>
When I do http://localhost:3000/recipes/list, it gives me the following error:
814: unexpected token at 'FORBIDDEN'
Application Trace | Framework Trace | Full Trace
app/models/recipe.rb:13:in for'
app/controllers/recipes_controller.rb:4:inlist'
the json is of the following format:
{"count": 1, "recipes": [{"publisher": "Tasty Kitchen", "f2f_url": "http://food2fork.com/view/459b3d", "title": "End the Search Chocolate Chip Cookies", "source_url": "http://tastykitchen.com/recipes/desserts/end-the-search-chocolate-chip-cookiese280a6/", "recipe_id": "459b3d", "image_url": "http://static.food2fork.com/cookie2410x307a33e.jpg", "social_rank": 34.80777735743579, "publisher_url": "http://tastykitchen.com"}]}
Please let me know what to do.
Ok, so I just went and signed up for a food2fork.com api key and read their documentation, there are a couple of issues with your code.
If you take a look under the search api documentation, you'll see the explanation of the query parameters:
q: (optional) Search Query (Ingredients should be separated by commas). If this is omitted top rated recipes will be returned.
What that means is that you're sending a request for a recipe with ingredients of "keyword" with every request you're making. Obviously there are no recipes with an ingredient named keyword.
If you look in the same section, there seems to be no query parameters for "fields," so you shouldn't be adding them. It doesn't hurt, but why have code that does nothing in your class?
You're likely wanting to get your key from the environment rather than hardcoding it into your app.
All that being said, I think you want something like this as your Recipe class:
require 'httparty'
class Recipe
include HTTParty
default_options.update(verify: false)
base_uri 'http://food2fork.com/api/search'
default_params key: ENV['FOOD2FORK_KEY']
format :json
def self.for term
get("", query: { q: term })["recipes"]
end
end
This must be a common need but I can't seem to find a definitive answer on the most rubyesque way. I need to create a fairly complex algorithm to dynamically calculate course grades in a rails 4.1 app.
Specifically, I have a model, "course", and whenever an instance of it is displayed in the view, I want to dynamically calculate the current grade (a decimal value, calculated from many course.field values) and display it as a letter value using a switch/case. My assumption was that I could do this in the controller (but it almost seems like it's complex enough to warrant it's own -- module? In C++ I would create a class). At the same time, since it is created dynamically, it seemed like bad form to create a current_grade field for it in the model, so it's not one I can pass back and forth as one of the allowable params (that I know of-- can one pass a variable in the params that is not represented in the db?).
In my initial research I see suggestions of hidden_field_tags and helper_methods and all_helpers and modules and global modules and more. Under time pressure, I dread beginning down the wrong path. Which is the better approach? Or a good high level doc for reference?
As an example, here is one view in which I would like to calculate current grade, compare it to desired grade, and display accordingly.
# index.html.erb
<% #courses.each do |course| %>
<li>
<%= my_algorithm_to_calculate_curr_grade(many course.fields used to caluculate)
<= course.desired_grade ? "set li to <Color: red>" : "set li to <Color: green>" %>
<%= course.course_name %>
Current Calculation: <%= display_results_of_previous_calculation %>
(Goal: <%= course.desired_grade %>)
<%= link_to 'Show', course %>
<%= link_to 'Edit', edit_course_path(course) %>
<%= link_to 'Drop Course Without Penalty', course, method: :delete, data: { confirm: 'Are you sure?' } %>
</li>
<% end %>
It's hard to tell from your question if course.fields are attributes of Course or different model(s). If all the fields are Course attributes, I would put it as an instance method on Course.
class Course < ActiveRecord::Base
def calculated_grade
# fun algorithm
end
end
If course.fields need to be loaded from the database, I'd probably go with a Plain Old Ruby Object (PORO), maybe call it CourseGradeCalculator (put it in app/models, why not? It's business logic)
class CourseGradeCalculator
attr_reader :course, :fields, :grade
def initialize(course, fields)
#course = course
#fields = fields
#grade = calculate_grade
end
private
def calculate_grade
# fun algorithm
end
end
# controller
#course = Course.preload(:fields).find(params[:id]
# view
CourseGradeCalculator.new(#course, #course.fields)
I'm trying to have a drop down list but when i try it it give me
undefined method `collect' for nil:NilClass
the controller:
def existing
#courses = Course.all
end
def duplicate
course = Course.find_by_id(permitd_up[:id])
new_course = course.dup
if new_course.save
redirect_to :action => 'show'
else
redirect_to :back
end
end
the view:
<h3>Choose a Course</h3>
<%= form_for :course , url: {:action => "duplicate" , method: "post"} do |f|%>
<%= f.select :id , #courses.collect{|c| [c.id , c.name]} %>
<br><br>
<%= f.submit%>
<%end%>
You will receive the following error
undefined method `collect' for nil:NilClass
on
<%= f.select :id , #courses.collect{|c| [c.id , c.name]} %>
Only when #courses instance variable was not set in the action that rendered this particular view.
I see that #courses variable is set in the existing method. If you are using existing as an action which renders this view then your view name must be existing.html.erb.
Or if you are rendering the view from a different action then in that case you should set #courses value in that particular action by either directly setting the value within action OR by calling existing method from there.
If you have your courses as a database table, you might want to try using rails' built in field helper collection_select. It will populate your select field with all of the data available in your model. If you want a drop-down like the one you are describing, I believe using collection select is the best way to handle it.
You can read up on it here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html
Alternatively, if you have a ton of courses, maybe try looking into using a text field with autocomplete. Jquery UI has a plugin that makes this very easy. You can check out the railscasts for it here: http://railscasts.com/episodes/102-auto-complete-association-revised.
It requires a pro account but if you do a lot of rails developing it will be the best $9 you spend every month.
If you would like to continue to do it this way, make sure that you are defining
#courses = Courses(:all) in the correct controller action, otherwise you will have nothing to render.
This helped for display on an individual 'items' show page
Accessing an attribute of a linked model in Rails
However I'm having trouble doing the same for an 'all items table'
...
<% #items.each do |item| %>
...
<td><%= item.room.name %></td>
...
Clearly where one room has many items.
only this works:
<td><%= item.room_id %></td>
I can't seem to use it there, gives:
undefined method `name' for nil:NilClass
Have a look at Rails' Delegate module:
class Item < ActiveRecord::Base
# ...
delegate :name, :to => :room, :allow_nil => true, :prefix => :room
end
This will add the instance method room_name to Item, which will fail more gracefully (returning nil if there is no room, instead of the NilClass error).
I am getting this error:
[code]
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
[/code]
In controller:
#optionals = Car.find_all_by_car_id(1)
In view:
<% #optionals.each do |c| %>
<div><%= c.type %></div>
<% end %>
In the table Car is one row... so I don't understand, how is possible to getting this error message... I tried to search on google, but unfortunately I still don't know how to fix this error...
So I'll glad for each help!
EDIT: car_id in table Cars have the value 1
Try adding a line in the template like so:
<%= #optionals.inspect %> and make sure it's not nil.
If it is, check the log to make sure the action that you're calling matches the template you're looking at