Hello. Still here? Good.
-
Holy fucking shit. Seriously, you wouldn't believe how long that took me to get working properly, mostly because I was missing a do
in a respond_to
function. Ok, let's move on. Um. So if you check your rails app in the browser at localhost:3000/posts all that shows up is some fucking bullshit. That's fucking lame. So let's look at the views folder.
- Here's what we have:
- create.html.erb
- destroy.html.erb
- edit.html.erb
- index.html.erb
- new.html.erb
- show.html.erb
- update.html.erb
- Let's look at the
index.html.erb
file first. There's some bullshit there that Rails generated. Let's get rid of that shit and have a blank template.
- What kind of layout makes sense? We've done tables in the past but that's prolly stupid for a blog. So let's just do all the posts, each separated by a horizontal rule. Here's what that code looks like.
- Also, it's fucking bullshit that Markdown isn't parsing this HTML as code blocks. Sorry.
index.html.erb
<h1>These are all the posts ever made</h1>
<% @posts.each do |post|%>
<h1><%= post.title %></h1>
<h2><%= post.subtitle %></h2>
<p><%= post.body %></p>
<p><%= link_to 'Permalink', post %></p>
<hr>
<% end %>
<p><%= link_to 'Write a Post', new_post_path %></p>
-
Ok, refresh that page. What happens? Nothing. Because you haven't written any posts. Ok, well click on "Write a post." And what happens? NOTHING. Duh. Ok, open up new.html.erb
in the posts view.
-
Delete all the crap.
-
Let's create a form! YAYAYAYAY.
new.html.erb
<h1>Create a new post, genius. Or name all the members of the Wu-Tang Clan. Whatevs. Do something.</h1>
<%= form_for @post do |form| %>
<ul>
<% @post.errors.full_messages.each do |message| %>
<li><%= message %>
<% end %>
</ul>
<%= form.label :title, "What's your title?" %><br>
<%= form.text_field :title, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :subtitle, "What's your subtitle?" %><br>
<%= form.text_field :subtitle, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :body, "What's your content?" %><br>
<%= form.text_area :body, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :user_id, "What's your user id?" %><br>
<%= form.text_field :user_id, placeholder:"Like, a number?" %>
<br>
<br>
<%= form.submit %>
<% end %>
-
Ok, that's not totally fucking terrible. I mean, yeah it kinda is, but it's not TOTALLY fucking terrible. OK, let's take a fucking break, yo because there's a bunch of shit we haven't done yet.
-
Ok, refreshed? Let's generate the rest of the fucking forms and ERBs and shit and complete the Posts section of the site. Remember, we still have the users section to attack.
-
Here's the HTML for the show.html.erb
show.html.erb
```
<h1><%= @post.title %></h1>
<h2><%= @post.subtitle %></h2>
<p><%= @post.body %></p>
<p><%= @post.user_id %></p>
<%= link_to 'Edit this Post', edit_post_path %>
<%= link_to 'Delete this Post', @post, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to 'Back', posts_path %>
```
-
This allows us to edit the post, delete the post, and go back to see all the posts.
-
Ok, let's create the edit.html.erb
edit.html.erb
<h1>Edit this fucking post</h1>
<%= form_for @post do |form| %>
<ul>
<% @post.errors.full_messages.each do |message| %>
<li><%= message %>
<% end %>
</ul>
<%= form.label :title, "What's your title?" %><br>
<%= form.text_field :title, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :subtitle, "What's your subtitle?" %><br>
<%= form.text_field :subtitle, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :body, "What's your content?" %><br>
<%= form.text_area :body, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :user_id, "What's your user_id?" %><br>
<%= form.text_field :user_id, placeholder:"Like, a number?" %>
<br>
<br>
<%= form.submit %>
<% end %>
<%= link_to 'Show', @post %>
<%= link_to 'Back', posts_path %>
-
Yayayayay. So we've got index.html.erb
and new.html.erb
and show.html.erb
and edit.html.erb
. There's some ERBs that we created when we were spinning out the controller like create.html.erb
and destroy.html.erb
and update.html.erb
. I'm pretty sure we can destroy them, but to be honest, I'm not totally sure. The pages don't really do shit so we can prolly get rid of them but until then, let's just leave them there since there's not much to do with this now.
-
And now, my friend — if I may be so bold to address you as such — let us pause for a second, and reflect. There are still some things we should do with this part of the app. Things like:
- Tying in the User model and controller. Right now we have to manually associate the two, which makes no fucking sense.
- Maybe doing some partials for the form. It seems like it's being repeated for the new and edit section.
- Start spinning in nested resources for comments just for practice. Ok. That's a fair amount to do. Let's figure out how to attack this. Now that I kinda have a grasp on this, I'd probably attack the partial first, then the User MVC and then the nested resources last.
- But I need to take a break and will maybe try to finish it up tomorrow. Here's a cool song to listen to in the meantime.
-
Ok, I'm back after a relatively restful, thoughtful Sunday. Let's get back to partials. We've got a perfect place for partials which is the form. Ok, let's get started.
- Create a file called
_form.html.erb
in app\views\posts
- Copy and paste the form section from the
new.html.erb
. That should look like this:
<%= form_for @post do |form| %>
<ul>
<% @post.errors.full_messages.each do |message| %>
<li><%= message %>
<% end %>
</ul>
<%= form.label :title, "What's your title?" %><br>
<%= form.text_field :title, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :subtitle, "What's your subtitle?" %><br>
<%= form.text_field :subtitle, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :body, "What's your content?" %><br>
<%= form.text_area :body, placeholder:"Why do you deserve to be listened to?" %>
<br>
<br>
<%= form.label :user_id, "What's your user id?" %><br>
<%= form.text_field :user_id, placeholder:"Like, a number?" %>
<br>
<br>
<%= form.submit %>
<% end %>
- In
new.html.erb
and edit.html.erb
get rid of the existing form code and replace it with this code:
<%= form_for @post do |form| %>
<%= render form %>
<% end %>
- So
new.html.erb
now looks like this:
<h1>Create a new post, genius. Or name all the members of the Wu-Tang Clan. Whatevs. Do something.</h1>
<%= form_for @post do |form| %>
<%= render form %>
<% end %>
- And
edit.html.erb
now looks like this:
<h1>Edit this fucking post</h1>
<%= form_for @post do |form| %>
<%= render form %>
<% end %>
<%= link_to 'Show', @post %>
<%= link_to 'Back', posts_path %>
- Ok, that seems a bit cleaner, right? Let's reload our app and see if it works. Uh, yeah, of course it does. But you know what? Rails has pretty good support for form partials. So maybe what we should do is try a nav partial to the posts section in order to render something that requires more work on our part, which'll help us with understanding this a little better.
- So what I want to do is add a little nav section to the bottom of each post, both in the
index.html.erb
page and the show.html.erb
that lets us edit or delete the post. That's it!
- So in
app/views/posts
let's create an ERB called _editnav.html.erb
. It occurs to me, bee tee dubs, that I don't know naming conventions for compound worded views. Oh well.
- Here's the code I'm putting in
_navedit.html.erb
, straight from show.html.erb
except that the instance variable @post
in the delete
section needs to be made a local variable post
as it won't work in the index section. I wouldn't have caught this until Brendan pointed it out, which highlights the importance of testing every single action, yo. Anyway, here's what we need to do:
<%= link_to 'Edit this Post', edit_post_path %>
<%= link_to 'Delete this Post', post, method: :delete, data: { confirm: 'Are you sure?' } %>
- Ok, so render the partial in the
show.html.erb
:
<h1><%= @post.title %></h1>
<h2><%= @post.subtitle %></h2>
<p><%= @post.body %></p>
<p><%= @post.user_id %></p>
<%= render partial:"editnav" %>
<br>
<br>
<%= link_to 'All Posts', posts_path %>
- Ok, that worked. Let's just do the same in the
index.html.erb
file, right?
<h1>These are all the posts ever made</h1>
<% @posts.each do |post| %>
<h1><%= post.title %></h1>
<h2><%= post.subtitle %></h2>
<p><%= post.body %></p>
<p><%= link_to 'Permalink', post %></p>
<%= render partial:"editnav" %>
<hr>
<% end %>
<p><%= link_to 'Write a Post', new_post_path %></p>
- No that didn't lololol. Fuck. I'm getting the error
No route matches {:action=>"edit", :controller=>"posts"}
. So what should the code look like? Well, here's the thing. That partial is inside a do loop so we need to pass it a local variable. But we also need to do that with the show.html.erb
file that we just changed. And we need to change the _editnav.html.erb
file to pass the edit_post_path
action a variable.
- That fucked with my head a little, no joke. So get ready to rewrite all that shit.
- Ok, here's what the
_editnav.html.erb
files looks like
<%= link_to 'Edit this Post', edit_post_path(post) %>
<%= link_to 'Delete this Post', post, method: :delete, data: { confirm: 'Are you sure?' } %>
- In the
show
view we're going to pass to the partial the instance variable @post
as the value for the post
key. Here's what show.html.erb
looks like now:
<h1><%= @post.title %></h1>
<h2><%= @post.subtitle %></h2>
<p><%= @post.body %></p>
<p><%= @post.user_id %></p>
<%= render partial:"editnav", locals: { post: @post }%>
<br>
<br>
<%= link_to 'All Posts', posts_path %>
- In the
index
view we're going to pass to the partial the local variable post
to post
. Here's what index.html.erb
looks like now:
<h1>These are all the posts ever made</h1>
<% @posts.each do |post| %>
<h1><%= post.title %></h1>
<h2><%= post.subtitle %></h2>
<p><%= post.body %></p>
<p><%= link_to 'Permalink', post %></p>
<%= render partial:"editnav", locals: { post: post } %>
<hr>
<% end %>
<p><%= link_to 'Write a Post', new_post_path %></p>
-
So let's create the User model, and let's use the Devise gem. It's not super hard to spin out, but there are a few steps.
-
First things first. Let's add this bit of code: gem "devise", "~> 2.2.3"
to the file located in amit_test_blog/Gemfile
. Note that it doesn't have an extension.
-
Here's what the first few lines of your Gemfile should look like after you copy it in:
source 'https://rubygems.org'
gem 'rails', '3.2.13'
gem "devise", "~> 2.2.3"
...
-
Ok, the next step is running bundle install
from the command line in your app.
-
After you've done that, install devise by running rails generate devise:install
from the command line step. Unfortunately, there's still a bunch of steps you have to do.
-
Open up the file config/environments/development.rb
and add this code before the final end
command. Of course, the :host
will depend on what your host actually is. Anyway, the last line of your code should look like this:
# Devise default URL options
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
end
-
Have your app root to the posts index in config/routes.rb
using root to: "posts#index"
(And did you rm public/index.html
yet? May as well do that too.)
-
Open up app/views/layours/application.html.erb
and insert "flash messages" into the body. Your code should look something like this:
<body>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</body
-
Open up config/application.rb
and insert this code for Heroku in the module AmitTestBlog
code block:
# Heroku Specific Config from Devise
config.assets.initialize_on_precompile = false
-
Run rails g devise:views
from the command line in your app.
-
Ok, that was the basic stuff you do each time you set up Devise. The next step is to create a new User
model that is optimized by Devise. The way to do this is run rails generate devise User
from the command line.
-
Rake your shit! rake db:migrate
-
WHOA. You should have received a serious error about trying to rake that. Why? I dunno. Since we didn't do anything with our User app, let's delete all the user stuff and restart everything to do with the User
model. So this is as good a time as any to learn about using migrations.
-
Now let's go back to our localhost
and play around a little bit. Sign out first and take a look at our posts. If you click the edit or delete links, it'll prompt you to login! However, let's do something kinda interesting.
- Login with a user and create a post.
- Logout and login with another user account and create a different post.
- Now, whilst your logged in with login #2, try to delete the post of login #1. Um, yeah. You can do that. And you shouldn't be able to, right?
- Ok, let's start protecting users from each other.
-
Let's make sure our associations are set up and that we have the line has_many :posts
in app/models/user.rb
and belongs_to :user
in app/models/post.rb
-
Now let's go back and edit app/controllers/posts_controller.rb
let's limit the scope of a what a user can do. We want to use the current_user
method in our new
, edit
, create
, update
, and destroy
methods. Here's what the new code looks like by replacing a bunch of User
stuff with current_user
where appropriate.
class PostsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show]
def index
@posts = Post.all
respond_to do |format|
format.html
end
end
def create
@post = current_user.posts.build(params[:post])
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Good job creating your post. You must be some sort of genius.' }
else
format.html { render action: "new" }
end
end
end
def new
@post = current_user.posts.new
respond_to do |format|
format.html
end
end
def edit
@post = current_user.posts.find(params[:id])
end
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html
end
end
def update
@post = current_user.posts.find(params[:id])
respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to @post, notice: 'Your post was successfully updated. Dummy.' }
else
format.html { render action: "edit" }
end
end
end
def destroy
@post = current_user.posts.find(params[:id])
@post.destroy
respond_to do |format|
format.html {redirect_to posts_url}
end
end
end
-
Now let's try something out and go back to the blog. Try to edit or delete a post that the currently signed in user didn't use. What happens? You get an exception. This is good and means everything is working properly.
-
Now let's edit the views and make sure that the login links only show up for current users and let us display which user is signed in. That could be kinda cool.
-
Let's also clear all our posts and start from scratch to make sure there aren't any weird old posts floating around. Open up rails console
in your app and run Post.destroy_all
.
-
Scary. Ok...
-
Open up app/views/posts/index.html.erb
. Let's add this code just below the first h1
tag:
<% if user_signed_in? %>
<p>Signed in as... <%= current_user.email %></p>
<% else %>
<p>This blog is way more fun if you're signed in. But you're not.<p>
<% end %>
-
Let's also go ahead and change the link to write a new post at the bottom of the page to only show if you're signed in as well. Here's what that looks like:
<p><%= link_to 'Write a Post', new_post_path if user_signed_in? %></p>
-
Actually there's one thing that we should change also, which is the user_id section of the view page. We're showing that in the view but we don't want users to have that. So go ahead and remove it from your _form
partial.
-
If you refresh your page, you should see something depending on whether you're signed in or now. Let's sign in two different times under two different users and create a few test posts and make sure that the content is showing up properly, at the very least.
-
Now let's change the do
loop that displays our posts. Here's what app/posts/index.html.erb
is going to look like after we're done with it:
<h1>These are all the posts ever made</h1>
<% if user_signed_in? %>
<p>Signed in as... <%= current_user.email %></p>
<% else %>
<p>This blog is way more fun if you're signed in. But you're not.<p>
<% end %>
<% @posts.each do |post| %>
<h1><%= post.title %></h1>
<h2><%= post.subtitle %></h2>
<p>Author: <%= post.user.email %></p>
<p><%= post.body %></p>
<p><%= link_to 'Permalink', post %></p>
<% if user_signed_in? %>
<% if post.user.email == current_user.email %>
<%= render partial:"editnav", locals: { post: post } %>
<% end %>
<% end %>
<hr>
<% end %>
<p><%= link_to 'Write a Post', new_post_path if user_signed_in? %></p>
-
Make the same changes to app/posts/show.html.erb
also.
<h1><%= @post.title %></h1>
<h2><%= @post.subtitle %></h2>
<p><%= @post.body %></p>
<p><%= @post.user_id %></p>
<hr>
<% if user_signed_in? %>
<% if @post.user.email == current_user.email %>
<%= render partial:"editnav", locals: { post: @post } %>
<% end %>
<% end %>
<br>
<br>
<hr>
<%= link_to 'All Posts', posts_path %>
-
Play around with it! Login, create some posts, logout, try to do stuff, login as a different user. It all works! You've basically got a really simply blog running.
-
What's next? Maybe we'll put in some rested resources.
-
Ok, let's figure out how to create nested resources with comments. And comments suck, of course, but let's do it anyway, at least for the knowledge.
-
Let's create the comments model first. Run rails generate model Comment post_id:integer content:text
-
Run rake db:migrate
-
Modify your routes to use the nested resources. Your routes should look something like this:
devise_for :users
root to: "posts#index"
get "posts/index"
get "posts/create"
get "posts/new"
get "posts/edit"
get "posts/show"
get "posts/update"
get "posts/destroy"
resources :posts do
resources :comments, only: [:new, :show, :create]
end
-
Create a new CommentsController. Run rails generate controller Comments new create show
. That'll create some routes that you want to get rid of. It's probably easy to generate the controllers and stuff manually instead of using generate, but whatevs. Another day. In the meantime, get rid of all the get
crap including this stuff:
get "comments/new"
get "comments/create"
get "comments/show"
-
Ok, if you run rake routes
the routes should look pretty set up.
-
Now, CommentsController
is pretty bare. Let's start setting this shit up. Your code, by the end of it, should look like this:
class CommentsController < ApplicationController
def new
@post = Post.find(params[:post_id])
@comment = Comment.new
end
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.build(params[:comment])
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: "Hello comment was posted dude"}
else
format.html { render action: "new"}
end
end
end
def show
@post = Post.find(params[:post_id])
@comment = @post.comments.find(params[:id])
respond_to do |format|
format.html
end
end
end
-
Ok, let's take a look at our views. We'll use a partial for the form. Create app/views/comments/_form.html.erb
and insert this code:
<%= form.label :content, "Blather on, friend. We will listen to you:" %>
<br>
<%= form.text_area :content %>
<br>
<%= form.submit %>
-
Open up app/views/comments/new.html.erb
and make your code look like this:
<h2>Leave a comment on <%= @post.title %></h2>
<%= form_for [@post, @comment] do |form| %>
<%= render form %>
<% end %>
-
Open up app/views/comments/show.html.erb
and make your code look like this:
<h3><%= @comment.content %></h3>
<%= link_to 'Back to Posts', @post %>
-
Open up app/views/posts/show.html.erb
and insert this code to show comments and allow people to make comments.
<h1><%= @post.title %></h1>
<h2><%= @post.subtitle %></h2>
<p><%= @post.body %></p>
<p><%= @post.user_id %></p>
<hr>
<% if user_signed_in? %>
<% if @post.user.email == current_user.email %>
<%= render partial:"editnav", locals: { post: @post } %>
<% end %>
<% end %>
<br>
<br>
<hr>
<%= link_to "Leave a comment", new_post_comment_path(@post) %>
<% @post.comments.each do |comment| %>
<hr>
<p><%= comment.content %></p>
<% = link_to 'Comment Permalink', show_post_comment_path(@post) %>
<% end %>
<br>
<br>
<hr>
<%= link_to 'All Posts', posts_path %>
-
Okay, this is going to be a little more complicated. First of all, it assumes you've done all the bullshit of installing Homebrew and installing Imagemagick.
-
Next step is to add the bundle to the Gemfile in your app. Add this code: gem "carrierwave", "~> 0.8.0"
and gem "rmagick"
-
Run bundle install
-
Generate a photo uploader by using rails g uploader Photo
-
This creates a file in app/uploaders/photo_uploader.rb
. We're going to modify this file to make it work with RMagick and do some processing stuff when we upload photos.
-
In the PhotoUploader
class modify the commented-out include
to make a line read include CarrierWave::RMagick
.
-
We're also going to make it process photos down to 800px max in case they're over but not resize them up if they're smaller. We need the line process :resize_to_fit => [800, 800]
-
Now we need to generate a migration to our posts table to let us attach photos. Run rails g migration AddPhotoToPosts photo:string
-
Remember every time we modify the database schema we need to run rake db:migrate
.
-
Now we need to mount PhotoUploader
to our Post
model. Open up app/model/post.rb
and add in the line mount_uploader :photo, PhotoUploader
-
Now let's let users upload stuff to their posts. Open up the partial app/views/_form.html.erb
and add this before the form.submit
section:
%= form.label :photo %><br>
<%= form.file_field :photo %>
<%= form.hidden_field :photo_cache %>
-
Upload
Amit, You are the man!
I want to tell you why this is huge for me: Starting a new rails app is really intimidating, because we haven't gotten enough repetitions yet, my notes are scattered over a handful of different files, and diving into each of our code-alongs is a pain in the ass. The starting is hard. You just made it a lot easier.
Thank you, Amit! This is going to really help me get up and running quickly after class when I want to build a new app.
I'll try to contribute if I can (not sure if Gist supports that).