Reputation: 94
Here is my code:
// in swimmer.rb (model):
belongs_to :user
// in user.rb (model):
has_one :swimmer, :dependent => :destroy
accepts_nested_attributes_for :swimmer, :allow_destroy => true
attr_accessible :swimmer_attributes
// in swimmers_controller.rb:
def profile
@swimmer = Swimmer.find_by_user_id(current_user)
@swimmer_nickname = @swimmer.nickname
@swimmer_gender = @swimmer.gender
@title = "Swimmer Profile for #{@current_user.email}"
end
// in profile.html.erb (in swimmers views folder)
<% if @swimmer %>
<h3><%= @title %></h3>
<p>Nickname: <%= @swimmer_nickname %></p>
<p>Gender: <%= @swimmer_gender %></p>
<% else %>
<h3>No Swimmer Profile for<%= current_user.email %></h3>
<% end %>
If a swimmer object has a user_id that matches the @swimmer instance variable in the swimmers#controller method, because there is a logged in user (using the Devise gem) and a swimmer has been created for that user, then the profile view works as intended. If not, the page shows an error:
NoMethodError (undefined method `nickname' for nil:NilClass):
app/controllers/swimmers_controller.rb:66:in `profile'
But since the profile view has an if/else conditional, I want the lack of the swimmer object associated with the logged in user to force the view to show the else content. Apparently, the "Swimmer.find_by_user_id(current_user)" method is creating a nil object in the NilClass. How do I get it so that it doesn't create anything and thereby brings up the else conditional?
Repo is at https://github.com/drollwit/vst2/tree/ver2. This is an exercise, not a real project. There is probably an easy answer here but I can't figure it out (still learning Rails basics). Any help would be appreciated.
Upvotes: 1
Views: 1149
Reputation: 6728
The cause of error exists in the below line of code.
@swimmer = Swimmer.find_by_user_id(current_user)
It never finds Swimmer based on user_id.Because
find_by_user_id
expects an ID.Try
@swimmer = Swimmer.find_by_user_id(current_user.id)
You will get swimmer object then the error will not occur.
And you view file can be improved by
<% if @swimmer %>
<h3><%= "Swimmer Profile for #{@current_user.email}" %></h3>
<p>Nickname: <%= @swimmer.nickname %></p>
<p>Gender: <%= @swimmer.gender %></p>
<% else %>
<h3>No Swimmer Profile for<%= current_user.email %></h3>
<% end %>
This will reduce below lines of code from controller.
@swimmer_nickname = @swimmer.nickname
@swimmer_gender = @swimmer.gender
@title = "Swimmer Profile for #{@current_user.email}"
Upvotes: 1
Reputation: 3509
The problem isn't the view, it's your profile method:
def profile
@swimmer = Swimmer.find_by_user_id(current_user)
@swimmer_nickname = @swimmer.nickname
@swimmer_gender = @swimmer.gender
@title = "Swimmer Profile for #{@current_user.email}"
end
If no swimmer can be found then @swimmer is nil and the following line fails because you are invoking the method 'nickname' on nil.
A simple fix is as follows:
def profile
@swimmer = Swimmer.find_by_user_id(current_user)
@swimmer_nickname = @swimmer.try(:nickname)
@swimmer_gender = @swimmer.try(:gender)
@title = "Swimmer Profile for #{@current_user.email}"
end
Now @simmwer_nickname and @swimmer_gender will be set to nil if @swimmer doesn't exist. See http://api.rubyonrails.org/classes/NilClass.html#method-i-try.
Personally, I wouldn't assign these extra instance variables in the controller like this. I would move this to the view or perhaps a helper.
Upvotes: 1