Hello, I’m making an instagram like website using Ruby on Rails and I’m very new to Ruby on Rails. I have my code in index.html.erb and it’s like this
<div class="posts-wrapper row">
<div class="post">
<div class="post-head">
<div class="name">
Ben Walker
</div>
</div>
<div class="image center-block">
<%= image_tag @post.image.url(:medium) %>
</div>
<p class="caption">
<%= @post.caption %> </p>
<div class="text-center">
<%= link_to "Cancel", posts_path %>
</div>
</div>
</div>
But I get an error: “NoMethodError in Posts#index” and it’s directing to the lines <%= image_tag @post.image.url(:medium) %>
and <%= @post.caption %>
Please tell me if that is enough code. Thank you
EDITS:
Here is my posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.all
end
def new
@post = Post.new
end
def create
@post = Post.create(post_params)
redirect_to posts_path
end
def show
@post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:image, :caption)
end
end
Need to see the page controller, not just the view
1 Like
Ok I’ve added my posts_controller.rb code to my question
1 Like
The index page uses @posts
- ie it’s for rendering a list of all posts (so you want to loop through them in the view). But you’re trying to use @post
, which is used on singular pages/routes instead. This is why you’re getting a no method error - it can’t render @post.caption
. You would normally do something like (been a while since I worked on RoR code so apologies if this is slightly off):
<% @posts.each do |post| %>
<div class="posts-wrapper row">
<div class="post">
# snip
<%= image_tag post.image.url(:medium) %>
</div>
<p class="caption">
<%= post.caption %>
# snip
<% end %>
1 Like
Ok, but when I add <% @posts.each do |post| %>
and <% end %>
I’m not able to click on the image. (I’m supposed to be able to click on the image so it directs me to a page with just the image and description.
Aha, it’s a little bit further down where the author links everything up - the relevant bit of code is this:
=link_to (image_tag post.image.url(:medium), class:'img-responsive'), post_path(post)
Basically, what it’s doing is using the post_path
helper that get automagically created by Rails, and that is going going to refer to the route for the show
action in the controller.
At the minute, all you’ve got is the URL to the image, whereas what it needs to be to get that linking up is the image wrapped in a link tag, and the href for the link will match the route you want (if they makes sense). I think you just need to go a bit further in the tutorial and you should be good 
1 Like
Oof the <%= @post.caption %>
part gives me the error “NameError in Posts#index”
Careful with your @
s - if you look in the controller, commonly you should have a view that corresponds to each of those actions. The @post
, @posts
etc - they are what you are going to be able to reference directly in the view that corresponds to that action. On the index page, you have access to (or you could look at it as exposing) the instance variable @posts
. That’s why you do the @posts.every |post|
- in that loop, each individual post you’ll reference with the variable post
, and therefore post.caption
. But that’s just a variable in a loop - it could be @posts.every |foo|
, then it would be foo.caption
or whatever.
- The instance variable/s available to you in that particular view you’ll find in the controller. They are used mainly to refer to either a thing or a collection of things you pull out of the database and that you want to do something with. So in the index action, the
@posts
refers to all the posts in the database. In the show
action, @post
will refer to a single post, located by ID.
- In a view, if the instance variable refers to a collection of things, normally you’ll want to loop over them, so whatever variable you use to represent the current thing in the loop, that’s what you use.
I’m not sure if I’m doing it right, I still get the error. Here is my index.html.erb:
<div class="posts-wrapper row">
<div class="post">
<div class="post-head">
<div class="name">
Ben Walker
</div>
</div>
<div class="image center-block">
<%= link_to (image_tag post.image.url(:medium), class:'img-responsive'), post_path(post) %>
</div>
<p class="caption">
<%= @post.caption %>
</p>
<div class="text-center">
<%= link_to "Cancel", posts_path %>
</div>
</div>
</div>
post.caption
.
It’s just a loop, so in pseudocode:
for post in @posts:
print post.img.url
print post.caption
Anything prefixed with @
in Rails is special, and it is made available in the view that relates to the specific method in the controller that you define it in (variables prefixed with @
are called instance variables and have special purpose in Ruby). So in the index
method in the controller, you have @posts
. You do not have anything called @post
, and you cannot use it in index.erb unless you actually specify it (but you do not want to do this).
1 Like
I get “undefined method image” for nil:NilClass" for <%= image_tag @post.image.url(:medium) %>
I’m not so sure what I am doing wrong because I am using erb and the tutorial was using haml so I used a converter and put the code in.
<% @posts.each do |post| %>
<div class="posts-wrapper row">
<div class="post">
<div class="post-head">
<div class="name">
Ben Walker
</div>
</div>
<div class="image center-block">
<%= image_tag @post.image.url(:medium) %>
</div>
<p class="caption">
<%= @posts.caption %>
</p>
<div class="text-center">
<%= link_to "Cancel", posts_path %>
</div>
</div>
</div>
<% end %>
Ok, the web/haml conversion isn’t the issue here.
So the way the framework works is
- It gets a request against a URL and the router decides what to do with it, using what is defined in
routes.rb
- The router decides which controller it should use to handle the request,
- and then which method in the controller.
- Then that method in the controller will render a view.
When you want to go to the posts index page, you
- hit the router, which
- recognises it needs to use the Posts controller,
- then the
index
method in that controller,
- which will render index.erb.html.
Look at the controller. Whatever is in that index
method: that is what is available in that view.
class PostsController < ApplicationController
def index
@posts = Post.all
end
...
There is no instance variable called @post
that you have access to.
@
has special meaning.
What you do have access to is a variable called @posts
which you have to loop through:
@posts.every |post|
# do stuff with`post`
This is a loop. So if you’ve used JavaScript, it could be
for (let post of posts) {
// Do stuff with each post here
Or
posts.forEach(post => {
// do stuff with post here
Or
for (var i = 0; i < posts.length; i++) {
// do stuff with posts[i] here
post
in your view is the variable in the loop that refers to an individual post. You could call it container_ship
instead, it doesn’t matter:
@posts.each |container_ship|
container_ship.image.url
container_ship.caption
That will still loop through every post and print out the image url and the caption.
What you cannot do is use the instance variable @post
where it is not available to you.
So like this?
<% @posts.each do |post| %>
<div class="posts-wrapper row">
<div class="post">
<div class="post-head">
<div class="name">
Ben Walker
</div>
</div>
<div class="image center-block">
<%= image_tag post.image.url(:medium) %>
</div>
<p class="caption">
<%= post.caption %>
</p>
<div class="text-center">
<%= link_to "Cancel", posts_path %>
</div>
</div>
</div>
<% end %>
The only problem is that I think when I add the loop, I am no longer able to click on the image. After I click on the image it’s supposed to direct me to a page with the post by itself. When I remove the <% @posts.each do |post| %>
and <% end %>
it gives me an error
undefined local variable or method `post' for #<#<Class:0x00000004f9bc90>:0x00000005606008>
Did you mean? post_url
posts_url
post_path
@posts
Yup, it works like that. The reason it doesn’t link is that you haven’t added a link to individual posts. There is a link there, but it links to posts_path
, which goes to the index of all posts, which is the current view.
The reason it doesn’t work if you remove the loop is that the variable post
only exists inside the loop.
Can I ask how much programming experience you have, not with Rails, just in general? Because re that last point, you can’t remove the loop from a loop in any programming language, the code inside will just not work
I’ve done HTML and CSS for a couple of years and a bit of JS and Ruby. It’s hard for me to learn by myself and easier if I do tutorials. I did a bit of coding before and stopped for a while and now I’m trying to catch up.
I’ll try and explain it a bit better once I’ve had a think about it, but what I would do is go back up the tutorial just a little bit, clear the code you have and work back again. Converting it to erb is probably a really good thing here, because you’re forcing yourself to write out the code for the views, but you’re not quite converting it as is, and there are a few errors creeping in - that’s why I say go back up the tutorial a bit. One key thing is that the controller actions (the methods defined in the controller) map directly to views most of the time, so whatever is defined with instance variables in the corresponding action is what you have available in that particular view.
1 Like
I’ve got all my code in this github link:
I know what I did wrong, I think I was supposed to create a new file and put that code in there. Thank you for your help.