Skip to content

Instantly share code, notes, and snippets.

@runchal
Last active December 15, 2015 21:49
Show Gist options
  • Save runchal/5328049 to your computer and use it in GitHub Desktop.
Save runchal/5328049 to your computer and use it in GitHub Desktop.
A very very basic, slightly embarrassing step-by-step guide to Rails to help me think some things through.

2012.04.10 / 6:56 PM
Began and mostly finished the nested resource section.

2012.04.10 / 4:09 PM 0.45. Finished up the users section of Devise. The CMS now performs how a CMS should perform in terms of making sure that users only have access to their own stuff. So that's kinda cool. What's not cool? Markdown parsing. It's still a mess, especially in the last section.

2012.04.09 / 3:50 PM
0.4. Added the beginning of a users section on Devise. Got distracted by a debugging issue. Toned down the manicness by 5%.

2012.04.08 / 4:44 PM
0.3. Added partials form section. Fixed some Markdown formatting.

2012.04.06 / 5:29 PM
This is the 0.2 version of a step by step guide to pushing out a Rails app. It basically tells you how to set up a MVC for a blogging app. It's very very rough, with terrible Markdown syntax, and lots of profanity. Like, a fucking lot.

Spinning up a new blog app in Rails

  • Ok, first generate a new rails app using this format:
    • rails new app_name
    • rails new amit_test_blog

Figure out the models you want, at least initially

  • Ok, next sketch out your models.
    • We're going to have two models for this blog: User and Post
    • One users can have many posts and many posts can belong to one users
    • Figure out the fields in each model.
    • User will have these: field:type
      • username:string
      • password:string
      • first_name:string
      • last_name:string
      • email:string
    • Post will have these: field:type
      • title:text
      • subtitle:text
      • body:text
      • user_id:integer
  • Seriously, that was painful. And it doesn't account for the fact that this only allows for one author! Oh well, let's generate the fucking models. We're going to use this format:
    • rails generate model Modelname field:type
      • Modelname should be singular. You can have multiple field:type thingys. Ok?
    • rails generate model User username:string password:string first_name:string last_name:string email:string
    • rails generate model Post title:text subtitle:text body:text user_id:integer
  • Did everything work out alright for you? Really? Ok. Let's rake some shit to create the databases and shit:
    • rake db:migrate

Routes, routes, routes

  • Ok, now we have to set up our fucking routes. I actually don't know if this should be done before or after setting up controllers but fuck it.
    • Open up /config/routes.rb
    • Add, just below the first line, this:
      • resources :users
      • resources :posts
    • Wait, isn't there some shit with get "users/new" and shit? YES but I haven't figured it out yet so hold on, mmkay?
    • Ok, you can run rake routes to see that your routes have been set up and you saved your file. You did save your file, right dummy? Ok it looks like all the shit is setup right. You should see a bunch of GET and PUT and DELETE shit that I still don't understand, but apparently it's like an HTTP call or some shit like that. I dunno. Google it.
  • Maybe it's time to run the server.
    • rails server
  • Ok, if you try to go to your localhost:3000/users file or localhost:3000/posts you won't see shit. Why? Because you don'thave any Controllers. Ugh, this shit is starting to suck.

Controllers

  • Ok, let's spin up our Controllers. You have to figure out the Name of your controller (which is the same as your model name, BUT PLURAL SERIOUSLY PLURAL) and also the actions which are the things you want your controller to do. So what the actions you want? Well Rails ran a bunch of shit already when we ran rake resources. So let's use index, create, new, edit, show, update, destroy. I don't know if we need all of them, but fuck it. Here's the format:

    • rails generate controller Controllername action
    • rails generate controller Users index create new edit show update destroy
    • rails generate controller Posts index create new edit show update destroy
    • Ok, that seemed to work. And if you look in your config/routes.rb file it did a bunch of shit by putting gets all over the place. And if you look at localhost:3000/users and localhost:3000/posts you'll see a welcome page. Ok, this is getting promising.
    • Psych, it's about to get way fucking worse. Doofus.
  • OK OK let's modify our fucking controllers so they actually do something. Your post controller, for example, is going to look like this:

      ``` 
      class PostsController < ApplicationController
        def index
        end
    
        def create
        end  
    
        def new
        end  
    
        def edit
        end  
    
        def show
        end  
    
        def update
        end  
    
        def destroy
        end
    
      end
      ```
    
  • Well shit, that doesn't do anything, does it? Ok, so now you have to start adding in the shit to make this shit actually DO something. Ok, let's fucking spin some shit up, yo. Insert this shit into your code:

Hello. Still here? Good.

    class PostsController < ApplicationController
        def index
            @posts = Post.all
    
            respond_to do |format|
                format.html
            end
        end
    
        def create
            @post = Post.new(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 = Post.new
            
            respond_to do |format|
                format.html
            end
        end
    
        def edit
            @post = Post.find(params[:id])
        end
    
        def show
            @post = Post.find(params[:id])
            
            respond_to do |format|
                format.html
            end
        end
    
        def update
            @post = Post.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 = Post.find(params[:id])
            @post.destroy
    
            respond_to do |format|
                format.html {redirect_to posts_url}
            end
        end
    end
  • 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>
    

User accounts with Devise

  • 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.

Just a little bit on Migrations, but really, more of a treatise on how I fixed a stupid problem of my own making.

  • Ok, so I tried something simple first. I ran rails generate migration DropUsersTable and then ran rake db:migrate. That didn't work either. So then I started running rake db:rollback and I kept running it until it didn't give me anything in response.
  • Then I ran rake db:migrate again but that didn't do anything because I hadn't done anything! I was making the same mistake again that I had made before.
  • So I deleted all the migrations except for the one that created the Post model from db/migrate. Then I ran rake db:migrate. Then I ran rake routes and saw a bunch of non-Devise user stuff. So I went back and deleted the app/models/user.rb file and the app/controllers/users_controller.rbfile and deleted all references to users in config/routes.rb.
  • Ugh, right? Then ran rails g devise User again and then ran rake db:migrate again and that finally fucking worked.
  • Ok, so restart our server, and try to create a post and everything's back to normal. Oh, yeah, doing all this deleted all our content. So it's probably a good thing to add version control to the list of things to do soon.
  • Aaaaaaaaaaand let's get back on track.

Ok, back to configuring our Users model with login functionality and all that good stuff.

  • Ok, first let's add the login/logout links to our application view so we can have that option. Of course, that won't necessarily do anything yet but it's a good first step. Open up app/views/layouts/application.html.erb and insert the signup links between the body tags. Your code should look something like this now:

    <body>
    
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    
    <% if user_signed_in? %>
        <%= link_to "Sign Out", destroy_user_session_path, method: :delete %>
    <% else %>
        <%= link_to "Sign In", new_user_session_path %>
    <% end %>
    
    
    <%= yield %>
    
    </body>
    
  • Ok, that's pretty cool. Play around with it and create a couple users. If you hop in rails console and type in User.all you can see the users you've created and that the passwords are encrypted, which is pretty cool.

  • Next up: let's make sure that users have to sign in to do anything other than view the #index and #show actions in PostsController. So open up app/controllers/posts_controller.rb and just after you declare the class insert a line that'll filter out those actions. Your code should look something like this:

    class PostsController < ApplicationController
      before_filter :authenticate_user!, except: [:index, :show]
    ...
    

Making sure that users can only mess around with their own stuff using Devise

  • 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.

Nested 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 %>
    

Adding an image uploader

  • 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

Shit to do

  • Add partials for displaying posts
  • Add admin section giving someone super user power.
@thebucknerlife
Copy link

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).

@runchal
Copy link
Author

runchal commented Apr 8, 2013

@nahlyee I didn't notice this as an error on mine — it seemed to work fine.

@bobbytables
Copy link

resources: posts should be resources :posts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment