How do I handle nils in views? - ruby-on-rails

I have the following models set up:
class Contact < ActiveRecord::Base
belongs_to :band
belongs_to :mode
validates_presence_of :call, :mode
validates_associated :mode, :band
validates_presence_of :band, :if => :no_freq?
validates_presence_of :freq, :if => :no_band?
protected
def no_freq?
freq.nil?
end
def no_band?
band.nil?
end
end
class Band < ActiveRecord::Base
has_many :logs
end
class Mode < ActiveRecord::Base
has_many :logs
end
When I enter a frequency on my new view it allows for no band to be specified if a freq is entered. This creates a problem in my other views though because band is now nil. How do I allow for band not to be specified and just show up as empty on my index and show views, and then in the edit view allow one to be specified at a later point in time.
I have been able to get my index to display a blank by doing:
contact.band && contact.band.name
But I'm not sure if this is a best approach, and I'm unsure of how to apply a similar solution to my other views.
Many thanks from a rails newb!

In my views, I use the following for potentially nil objects in my views:
<%= #contact.band.name unless #contact.band.blank? %>
if your object is an array or hash, you can use the empty? function instead.
<%= unless #contacts.empty? %>
..some code
<% end %>
Hope this helps!
D

A couple years old but still a top Google result for "rails view handle nil" so I'll add my suggestion for use with Rails 3.2.3 and Ruby 1.9.3p0.
In application_helper.rb, add this:
def blank_to_nbsp(value)
value.blank? ? " ".html_safe : value
end
Then to display a value in a view, write something like this:
<%= blank_to_nbsp contact.band %>
Benefits:
"blank" catches both nil values and empty strings (details).
Simply omitting a nil object, or using an empty string, may cause formatting issues. pushes a non-breaking space into the web page and preserves formatting.
With the "if" and "unless" suggestions in other answers, you have to type each object name twice. By using a helper, you only have to type each object name once.

<%= #contact.try(:band).try(:name) %>
This will return nil if band or name do not exist as methods on their respective objects.

You can use Object#andand for this:
<%= #contact.band.andand.name %>

<%= #contact.band if #contact.band %> also works

Related

Is it possible to prevent empty action text entries

I have a very simple action text model and form
class Course < ApplicationRecord
validates :title, presence: true
has_rich_text :content
end
<%= form_with model: #course do |f| %>
<%= f.text_field :title %>
<%= f.rich_text_area :content %>
<% end %>
It's all working great but since the content field is optional is it possible to create a course model without creating action_text_rich_texts entries that are empty/blank? Even if the user only enters the title without any content it's currently creating them and there's a lot of unnecessary and empty action_text_rich_texts rows in the database
The way I handled this in my application is with a before_save callback that removes the ActionText::RichText database record if the body is blank.
This avoids polluting the controller and works on both create and update actions. The body attribute of the action_text attribute is still accessible even without a corresponding database record, because ActionText will instantiate a new object if the record cannot be found (which allows you to test for blank? in either scenario).
Try this:
class Course < ApplicationRecord
validates :title, presence: true
has_rich_text :content
before_save :clean_up_content
private
def clean_up_content
self.content.destroy if self.content.body.blank?
end
end
I'm not sure about anything built into Actiontext for this, but I would imagine you could handle this at the controller level.
The first thing I would try is to see if not setting anything to content prevents Rails from creating an associated record:
class CourseController
def create
# remove course_params[:content] if it's blank
course_values = course_params[:content].blank? ? course_params.except(:content) : course_params
Course.create(course_values)
...
end
end
Extending Eric Powell's approach:
# app/models/concerns/do_not_save_blank_rich_text.rb
module DoNotSaveBlankRichText
extend ActiveSupport::Concern
included do
before_validation :do_not_save_blank_rich_text
end
private
def do_not_save_blank_rich_text
rich_text_attributes = self.class.reflections.values.select do |reflection|
reflection.options[:class_name] == "ActionText::RichText"
end.map(&:name)
rich_text_attributes.each do |rich_text_attribute|
if self.public_send(rich_text_attribute) && self.public_send(rich_text_attribute).body.blank?
self.public_send(rich_text_attribute).mark_for_destruction
end
end
end
end

Rails 4: Assigning a records "has many" attribute without database saving

I have a couple models shown below and I'm using the search class method in Thing to filter records
class Category << ActiveRecord::Base
has_many :thing
end
class Thing << ActiveRecord::Base
belongs_to :category
:scope approved -> { where("approved = true") }
def self.search(query)
search_condition = "%" + query + "%"
approved.where('name LIKE ?', search_condition)
end
end
It works fine in my Things controller. The index route looks like so:
def index
if params[:search].present?
#things = Thing.search(params[:seach])
else
#thing = Thing.all
end
end
On the categories show route I display the Things for this category. I also have the search form to search within the category.
def show
#category = Categories.find(params[:id])
if params[:search].present?
#category.things = #category.things.search()
end
end
So the problem is that the category_id attribute of all the filtered things are getting set to nil when I use the search class method in the categories#show route. Why does it save it to database? I thought I would have to call #category.save or update_attribute for that. I'm still new to rails so I'm sure its something easy I'm overlooking or misread.
My current solution is to move the if statement to the view. But now I'm trying to add pages with kaminiri to it and its getting uglier.
<% if params[:search].present? %>
<% #category.things.search(params[:search]) do |thing| %>
... Show the filtered things!
<% end %>
<% else %>
<% #category.things do |thing| %>
... Show all the things!
<% end %>
<% end %>
The other solution I thought of was using an #things = #categories.things.search(params[:search]) but that means I'm duplicated things passed to the view.
Take a look at Rails guide. A has_many association creates a number of methods on the model to which collection=(objects) also belongs. According to the guide:
The collection= method makes the collection contain only the supplied
objects, by adding and deleting as appropriate.
In your example you are actually assigning all the things found using #category.things.search() to the Category which has previously been queried using Categories.find(params[:id]).
Like Yan said, "In your example you are actually assigning all the things found using #category.things.search() to the Category which has previously been queried using Categories.find(params[:id])". Validations will solve this problem.
Records are being saved as nil because you have no validations on your model. Read about active record validations.
Here's the example they provide. You want to validate presence as well because records are being created without values.
class Person < ActiveRecord::Base
validates :name, presence: true
end
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

Rails custom order of records?

My Rails site has different categories that the user can browse through. Right now they're rendered in the view through a simple loop, but I'd like to be able to choose the order from left to right in which they're displayed.
Here's the current code:
<h4>Explore Listings</h4>
<% #categories.each do |f| %>
<p class="category-page-category"><%= link_to f.name, view_category_path(f.id) %></p>
<% end %>
What would be an easy solution to accomplish what I want to do?
The easiest way I know of how to do this is to set up an AJAX form which will pass an order parameter which will be handled by the controller to re-render the form. Your controller would then listen for the order parameter and append that to #categories.
For example, your controller code might look something like:
def index
#categories = Category.<some_other_scope>
#categories = #categories.order(order_param) if order_param.present?
respond_to do |format
format.js { render :index }
end
end
private
def order_param
params.permit(:order)
end
First of all native sorting by the DB is to be preferred in every case (it's much faster).
If your sorting does not depend on already existing Category attributes, migrate a new attribute like position:
add_column :categories, :position, :integer
and add a scope to the model:
class Category < ActiveRecord::Base
def self.ordered default_order=nil
return scoped if default_order.nil? or
not( %w(asc desc).include?(default_order.to_s) )
order("position #{default_order}")
end
end
and then in the controller:
def index
#categories = Category.ordered params[:order]
end
following the sorting attribute approach you also should ensure your position attribute is valid defined. So like:
class Category < ActiveRecord::Base
before_create :default_position
validates :position,
presence: true,
numericality: true,
uniqueness: true,
on: :update
def self.ordered default_order=nil
return scoped if default_order.nil? or
not( %w(asc desc).include?(default_order.to_s) )
order("position #{default_order}")
end
private
def default_position
self.position = self.class.maximum("position") + 1
end
end
Changing positions has to be implemented.
Otherwise if positions never change, this means Categories have to be sorted by their creation datetime. Then you also could sort by created_at instead of position.
It sounds like you want to sort the array of categories before they are shown, no?
If so you can use http://www.ruby-doc.org/core-2.1.2/Enumerable.html#method-i-sort_by or http://www.ruby-doc.org/core-2.1.2/Enumerable.html#method-i-sort to sort your categories however you want before rendering them.
So this could end up looking something like:
<% #categories.sort_by(&:name).each do |f| %>
assuming categories have a name field. This should probably be done in the controller or in the model with a scope (if you are sorting based on a database field) to keep this logic out of the view.
looks like acts_as_list is exactly what you need

How can I set "If" statement with nil?

country attribute's default value is nil.
In countries table, some record has image_url, and the rest of the record's country attributes are nil.
So I coded this in helper
def image(user)
if user.country.image_url
image_tag "flags/#{user.country.image_url}.png"
end
end
However, it returns error when image_url was nil
Something went wrong
How can I fix?
You'll need two conditions: The user has to have a country, and that country has to have an image_url. Only then will there be something to show. Luckily, it's a simple tweak:
def image(user)
if(user.country && user.country.image_url)
image_tag "flags/#{user.country.image_url}.png"
end
end
If you're paranoid, you should make sure that user isn't nil either.
Hope that helps!
While method chaining like that certainly works, your code will look a lot cleaner and become less coupled if you implement some method delegation.
Inside of your User model:
class User < ActiveRecord::Base
belongs_to :country
delegate :image_url, :to => :country, :prefix => true, :allow_nil => true
end
Now your helper becomes simply:
def image(user)
if user.country_image_url
image_tag "flags/#{user.country_image_url}.png"
end
end
Law of Demeter states:
Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
Also check out Rail Best Practices Law of Demeter; if nothing else you're saving yourself the extra clause in your if statement & your code looks pretty.

No foreign key in new scaffold

Good day... I have huge trouble with this and it drives me insane.
My user creation should have some additional data, like this:
<div class="field"><%= label_tag 'species', %>
<%= f.collection_select :species_id, Species.all, :id, :name %></div>
It displays the list of species correctly, yes, but when I do a submit, it is utterly ignored. Not even the number appears in the table of the database, it just disappears. I have put the correct associations:
class Species < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
# ... Other stuff
belongs_to :species
# ... Other stuff
end
I have also tried manipulating the controller:
class UsersController < ApplicationController
def create
logout_keeping_session!
#user = User.new(params[:user])
#user.species = Species.find(params[:species_id])
# Other stuff
end
end
But that only gives me 'Cannot find Species without id' even though the params array contains an element 'species_id' with a value.
I am at the end of my wits. Quite new to this, but is this RESTful? Not to find out how to get things done that seem easy? I love Rails and would like to continue.
Thanks for listening
your find fails because the params is probably: params[:user][:species_id] but if it is there like it is supposed, it should be set already, too.

Resources