Now I have this site where people can upload pins, which are just descriptions right now but will eventually also be image uploads. The only problem is that they’re not associated with users, so it’s just one giant repository of pins with no attribution. Time to fix that!
First thing, I’m being introduced to the Rails console. How exciting. =) I access this by entering ‘rails console’ in Terminal, which changes me to be in the Rails environment. I can now type in Ruby code, like say, math, and it will work. This will be useful because it will help me see the guts of my application in ways that the views will not. Like, I can access the models that we have created (pins and users). In each of those models, there is a class at the top of the file which is something I can specifically access via the console.
Like for example, since the class for user is User, I can enter User.all in the console and it will show me:
User Load (0.2ms) SELECT "users".* FROM "users"
=> [#<User id: 1, email: "test.user@me.com", encrypted_password: "$2a$10$ezCykZohTGcirSq.NfBBBOiE5TEU24vpQ1GWuEblX7xT...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 7, current_sign_in_at: "2013-04-01 00:48:53", last_sign_in_at: "2013-04-01 00:43:44", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2013-04-01 00:03:51", updated_at: "2013-04-01 00:54:05", name: "Tami Baribeau">
Hey, that’s me! I could also do Pin.all and that gives me the info you’d expect it to. So now we’re finally digging into the meat of all of this, which is really helpful for someone like me who wants to not just know HOW to do things but also why they’re doing what they’re doing.
Basically, in the pins controller it created a number of actions automatically. My index, show, new, create, update, and destroy. Each of those things has a respond_to block which basically created an API for us for free by using json format to give us access to all this infoz. This is a bonus of running a scaffold.
Let’s just look at the index action:
def index
@pins = Pin.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @pins }
end
end
Ignoring the respond_to part and just looking at the first line. @pins is the variable that was created, and it’s being set to the Pin.all, the same method I ran in the Rails console. So basically, the first thing being called when I load up that index page (at http://localhost:3000/pins) is the Pin.all command, which is why all the pins show on that page. The @ sign makes it a global variable, giving us access to it throughout the entire application. We need it to be a global variable so that our VIEW has access to it. Makes total sense!
Now we’re moving on to the show action. It looks like this:
@pin = Pin.find(params[:id])
Basically, I can do Pin.find(1) in the rails console and it will give me back the details for only the Pin with the ID 1. Basically in the show action, it’s getting the ID parameter from the URL when I click on ‘show’ on a specific pin. So that’s how that works.
So now we’ll establish the connection between a user and a pin. The first thing to do is update our pin item. I need to add a user_id to each database record, so in Terminal I have to run;
rails generate migration AddUserIdToPins user_id:integer:index
I made up AddUserIdToPins, apparently that doesn’t have to be anything specific. I still don’t understand how it knows which database to modify. What if I had two? In fact, I DO have two…pins and users right? I r confused. Adding index told it that someday I’d want to be searching by that value. After that I have to create the migration and then run rake db:migrate. I then can’t immediately see the new column, I have to shut down the Rails console with ctrl-d and then open it back up…I can run @pin = Pin.last and see that my database has a user_id attribute although it is nil.
Now we need to establish that users have pins and pins have a user. To do this we go inside our Models folder. Inside of the user.rb model, at the end I add:
has_many :pins
Under the pin.rb file, at the end I add:
belongs_to :user
This is one example of a specific association, but more of them can be found here.
So now, we have other things we can do in Rails console (after we restart it again with Ctrl-D). I can now do Pin.last and then Pin.user and it will give me the user id of the last Pin that was entered. It’s nil right now because we haven’t saved any pins with user ids yet. I could also do User.last to get the last user, and then User.pins which would show me all of their pins with some fancy SQL action.
So how do we associate a pin with a user when it’s created? When you click “new pin”, we’ll want to make sure it’s saving the ID of the person who is creating the pin to the pin itself. But first we should require them to be logged in to access the new pin page. To do this we add this to the top of our pins controller:
before_filter :authenticate_user!, except: [:index]
The authenticate_user! part of this is something that Devise gives us. This is telling us that before running any of the actions on the page, make sure the user is logged in. Except in the case of the index page which allows people to see all the pins.
So now we know someone will be logged in when they create a new pin, which is essential to saving their ID when the pin is created. 😉 We need to edit both the “new” action and the “create action”. In the “new” action, Rails gives us an easy way to do this. Instead of:
@pin = Pin.new
I’m going to do:
@pin = current_user.pins.new
Likewise, in the “create” action we change:
@pin = Pin.new(params[:pin])
to:
@pin = current_user.pins.new(params[:pin])
Now it’s saving user_id in the database which is great, but I can still edit other user’s pins which is no bueno. I gotta change my “edit” action from:
@pin = Pin.find(params[:id)
to:
@pin = current_user.pins.find(params[:id)
And I do the same thing in update and destroy, naturally.
Next, since we have a user associated with each pin, we can show a user’s name when they’re on the page of their pin. That’s easy, I just open the “show” view and then add <%= @pin.user.name %> under the description part. I wanted to delete the previous pins because they don’t have users and therefore generate ugly errors when I try to hit their show page, so I just do Pin.first.delete a couple times.
Now every pin I make in the future should have a user id associated with it, but just in case I’m going to open my pin.rb model and add:
validates :user_id, presence: true
Now we want to make sure the edit and destroy buttons only show up for the person who owns the pin. So we open our _pin partial view (since that’s what the index is rendering somehow…even though it doesn’t have the same name and I’m still fucking confused about that). I need to wrap the “edit” and “destroy” links inside a check like so:
<% if current_user == pin.user %>
blahblah
<% end %>
Gotta do the same thing on my “show” page too to hide the “edit” link. Easy peasy.
There you have it. Next up, uploading images! 🙂