0

It's a very strange bug bugged me for couple of hours already.

There are 3 models: user, school, post. Basically on the school page(school#show), there are a list of posts, and user can make new post.

The school controller:

def show
  @school = School.find(params[:id])
  $current_school = @school
  @post = @school.posts.build if logged_in?
  @posts = @school.posts

The school/show.html.erb:

<div>
  <aside>
      <%= render 'shared/post_form' %>
  </aside>
</div>


<div>
  <% if @school.posts.any? %>
      <%= render @posts %>
  <% end %>
</div>

_post_form.html.erb:

<%= form_for(@post) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new post..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

posts/_post.html.erb:

<li>
  <%= image_tag post.user.avatar.url(:thumb) %>
  <span class="user"><%= link_to post.user.name, post.user %></span>
  <span class="content"><%= post.content %></span>
</li>

Post controller:

def create
  @post = $current_school.posts.create(content:params[:post[:content],user_id:current_user.id)
  if @post.save
    flash[:success] = "Post created!"
    redirect_to $current_school
  else
    render 'static_pages/home'
  end
end

The problem is, when navigate to school#show, it says

undefined method `avatar' for nil:NilClass

If I delete the

      @post = @school.posts.build if logged_in?

line, and the _post_form.html.erb fragment, the list of posts shows fine. If I delete the _post.html.erb fragment, the new post function works fine as well.

But when they exist together, error comes up.

I guess the problem is at this line:

@post = @school.posts.build if logged_in?

because when I comment it out, the list of post shows fine. When I add it back, @posts can return a list of valid post, but post is nil.

Couldn't figure out what's wrong, please help.

Thanks

Edit: when visit url/schools/1, it crashes with NilClass error, post command returns

#<Post id: nil, content: nil, user_id: nil, school_id: 1, created_at: nil, updated_at: nil>

However, it should return the first post from @posts

Is it possible that

    @post = @school.posts.build if logged_in?

assign @post a nil post, and when it renders @posts, it is looking for @post which become nil instead of first post in @posts? If so, how can I correct this?

Edit2: I tried to change @post to @test:

@test = @school.posts.build if logged_in?

and the same nilClass error persists. So my previous guess is not valid. There should be something wrong with using build here. But what's wrong?

alexZ
  • 189
  • 2
  • 15
  • Your view doesn't check logged in like the action does, are you 'logged in' according to your code? – Maya Webster Jun 10 '15 at 20:55
  • Unrelated, but using a global seems like a really bad idea. – Dave Newton Jun 10 '15 at 21:00
  • In your post controller's `create` action, `@post = $current_school.posts.create(...` is saving the record (or attempting to). I believe you want that to be `@post = $current_school.posts.new(...` so that the next lines can evaluate whether the save was successful. Just something I noticed, not sure if it's a cause of your issue. Also, `<% if @school.posts.any? %>` can be shortened to `<% if @posts %>`, since `@posts` will be `nil` if none are returned from your school controller's `@posts = @school.posts` line. – digijim Jun 10 '15 at 22:07

3 Answers3

1

Your Post does not have a User associated with it on the new/show action, unless you're somehow plugging a user in on the model. Try:

@post = @school.posts.build(user: current_user) if logged_in?
jmdeldin
  • 5,354
  • 1
  • 28
  • 21
  • You are right! It works following your suggestion! Thank you so much – alexZ Jun 10 '15 at 21:48
  • Glad it worked for you! Echoing the advice in http://stackoverflow.com/a/30767639/73492 -- pry is really handy for debugging these issues. Just throw a `require 'pry'; binding.pry` line in your controller before the problematic line, and then you can debug `@post`, etc. – jmdeldin Jun 11 '15 at 02:29
0

This line: @post = @school.posts.build if logged_in?, should be without build method. Like this: @post = @school.posts if logged_in?

caspg
  • 399
  • 1
  • 6
  • I've tried it. it then reports error undefined method `to_key' , because <%= form_for(@post) do |f| %> needs a new or build method. If I add build here: <%= form_for(@post.build) do |f| %>, it reports the same nilClass error – alexZ Jun 10 '15 at 21:19
  • But still I think you don't want to use this method, `build` is an alias for `new` [source](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L124) – caspg Jun 10 '15 at 21:23
  • If not using it, how can I create a new post? – alexZ Jun 10 '15 at 21:25
  • First of all, here is an answer why global variables [are bad](http://stackoverflow.com/questions/484635/are-global-variables-bad/485020#485020). – caspg Jun 10 '15 at 21:34
  • Thanks, I'll try to eliminate the global variable – alexZ Jun 10 '15 at 21:48
0

Please try @post=@school.posts if logged_in? and see if it works. Also another important debugging thing that I can tell you is the gem 'pry'.Install it and you can check the value of variables anywhere in your views/models/controllers.

Hope it helps.

Vivek Nayyar
  • 81
  • 1
  • 10