Share
  • logo
  • postgresql-logo1
  • heroku-logo-for-facebook

Objective: To write a short tutorial the uses Sinatra DSL for the backend, is styled with the twitter bootstrap and PostgreSQL for the database and the heroku hosting platform for hosting.This is a branch off of a post about Learning Rails. I branched it because I found Sinatra interesting and wanted to document buildiing a production app.

Tools that are being used for this tutorial.

  • Macbook Air running OS X 10.8.5
  • Heroku account (FREE)


NOTE: Periodically you will see the word REFRESH in this post. It is to let you know that you can refresh your browser at that point in the tutorial.

Prior to refreshing your browser the ruby server must be stopped (press CTRL+C at the terminal) and restarted ($ ruby myapp.rb). You can do it that way OR you can use shotgun instead.

Shotgun allows your to refresh your browser without stopping and starting the ruby server to see changes made to files. To use shotgun. Install and run that gem at the terminal using:

$ install shotgun
$ shotgun myapp.rb

After these commands are issued, you will not need to start and stop the ruby server after files are create, edited or deleted. You will need to use localhost:9393 instead of localhost:4576. To stop using shotgun get back to using the standard method, stop the server and start it with the traditional method (ruby myapp.rb).

Steps

  1. Launch the terminal application.
  2. Create a new directory for your application and named it your-app.
  3. Create a file inside of the directory and name it Gemfile.
  4. Add the following code to the file.
  5. # Gemfile
    #This file contains all of the gems that will be used in this app - Kyle M. Brown
    
    source 'https://rubygems.org'
    ruby "2.0.0"
    
    gem "sinatra"
    gem "activerecord"
    gem "sinatra-activerecord"
    gem 'sinatra-flash'
    gem 'sinatra-redirect-with-flash'
    
    #Tells your Mac to use sqlite locally during development
    group :development do
     gem 'sqlite3'
     gem "tux"
    end
    
    #Tells heroku to use postgreSQL in production/live
    group :production do
     gem 'pg'
    end
    
  6. Run the following code at the terminal command line to install the gems listed inside of the Gemfile
  7. $ bundle install

    Note:This command will create a Gemfile.lock inside of the directory displaying the gems and exact versions of each that were installed.

  8. Inside of the directory, create a “config.ru” file and add the following code:
  9. Note: This is a file that heroku looks for.

    # config.ru
    
    require './myapp'
    run Sinatra::Application
    
  10. Inside of the directory, create a file called “database-config.rb”
  11. #Contains database configuration - Kyle M. Brown
    
    #Sets development database to sqlite
    configure :development do
     set :database, 'sqlite:///dev.db'
     set :show_exceptions, true
    end
    
    #Sets production database to postgreSQL
    configure :production do
     db = URI.parse(ENV['DATABASE_URL'] || 'postgres:///localhost/mydb')
    
     ActiveRecord::Base.establish_connection(
       :adapter  => db.scheme == 'postgres' ? 'postgresql' : db.scheme,
       :host     => db.host,
       :username => db.user,
       :password => db.password,
       :database => db.path[1..-1],
       :encoding => 'utf8'
     )
    end
    
  12. Inside of the directory, create a file called “myapp.rb”
  13. # myapp.rb
    #This file is the core of the application - Kyle M. Brown
    #ActiveRecord is an ORM (Object Relational Mapping (ORM). It does the translations between Ruby objects and the database which deals with records and relations.
    
    require 'sinatra'
    require 'sinatra/activerecord' 
    require './database-config'
    
    
    class Post < ActiveRecord::Base
    end
    
  14. Inside of the directory, create a file called “Rakefile”
  15. # Rakefile 
    #<a href="http://guides.rubyonrails.org/command_line.html#rake" target="_blank">Rake</a> is a build tool, written in Ruby, using Ruby as a build language. Rake is similar to make in scope and purpose. Rake a simple ruby build program with capabilities similar to make.
    #ActiveRecord with be used for <a href="http://guides.rubyonrails.org/migrations.html" target="_blank">migrations</a>.
    
    require './myapp'
    require 'sinatra/activerecord/rake'
    
  16. Run the following command at the terminal command line
    $ rake db:create_migration NAME=create_posts
  17. Note:Look in your project folder and you should see a new folder called “db” and within that folder another folder called “migrate.” You should also see a Ruby script with a timestamp in the name. e.g.”+create_post.rb”. This is a migration file. The timestamp tells ActiveRecord the order in which to apply the migrations in case there is more than one file.

  18. Edit the file named with the “+create_post.rb” and include the following:
  19. #The up method is used when we complete the migration (rake db:migrate), while the down method is ran when we rollback the last migration (rake db:rollback)
    
    class CreatePosts < ActiveRecord::Migration
     def self.up
       create_table :posts do |t|
         t.string :title
         t.text :body
         t.timestamps
       end
     end
    
     def self.down
       drop_table :posts
     end
    end
    
  20. Run the following command at the command line in the terminal to start the migration;
    $ rake db:migrate
  21. Note:ActiveRecord created these table columns: id, title, body, created_at, updated_at. When a new post is created, only the title and body; needs to be specified. The remaining fields are generated automatically by ActiveRecord.

    TUX

  22. You can optionally run tux. Which is essentially a tool that allows you to perform task inside of the terminal shell without launching the application into a browser. OR you can skip to the next step.
  23. $ tux
    >> Post.create(title: 'Testing the title', body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum venenatis eros eget lectus hendrerit, sed mattis quam pretium. Aenean accumsan eget leo non cursus. Aliquam sagittis luctus mi, quis suscipit neque venenatis et. Pellentesque vitae elementum diam. Quisque iaculis eget neque mattis fermentum. Donec et luctus eros. Suspendisse egestas pharetra elit vel bibendum.')
    >>
    

    Note:Something similar to the following should be returned in the terminal:

    D, [2014-01-07T00:58:13.056397 #8573] DEBUG -- :    (0.2ms)  begin transaction
    D, [2014-01-07T00:58:13.069284 #8573] DEBUG -- :   SQL (6.5ms)  INSERT INTO "posts" ("body", "created_at", "title", "updated_at") VALUES (?, ?, ?, ?)  [["body", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum venenatis eros eget lectus hendrerit, sed mattis quam pretium. Aenean accumsan eget leo non cursus. Aliquam sagittis luctus mi, quis suscipit neque venenatis et. Pellentesque vitae elementum diam. Quisque iaculis eget neque mattis fermentum. Donec et luctus eros. Suspendisse egestas pharetra elit vel bibendum."], ["created_at", 2014-01-07 05:58:13 UTC], ["title", "Testing the title"], ["updated_at", 2014-01-07 05:58:13 UTC]]
    D, [2014-01-07T00:58:13.070767 #8573] DEBUG -- :    (1.0ms)  commit transaction
    => #<Post id: 1, title: "Testing the title", body: "Lorem ipsum dolor sit amet, consectetur adipiscing ...", created_at: "2014-01-07 05:58:13", updated_at: "2014-01-07 05:58:13">
    >>
    

    GIT – Version Control

  24. Commit this version with GIT.
  25. $ git init
    $ git add .
    $ git commit -am "initial commit"
    

    Templates

  26. Setup the first route by adding the following code to “myapp.rb”:
  27. #Setup the route for the index page.
    get "/" do
      @posts = Post.order("created_at DESC")
      @title = "Welcome."
      erb :"posts/index"
    end
    

    Note:This maps the “/” url to the template index.html (index.erb in Ruby terms)

  28. Add this code to “myapp.rb” to set the variable for title.
  29. #Sets the variable for the title.
    helpers do
      def title
        if @title
          "#{@title}"
        else
          "Welcome."
        end
      end
    end
    
  30. Create two new directories within the apps main directory (your-app) as follows, “views” –> “post” (e.g. your-app/views/posts/).
  31. Now change to the “post” directory from the terminal. e.g.
    $ cd views/posts
  32. Create a new file named “index.erb”. This is the template that correlates to the route created in the previous step 15, specifically line 5. Use the following code:
  33. <!-- Template for index.erb //-->
    
    <ul>
    <% @posts.each do |post| %>
     <li>
       <h4><a href="/posts/<%= post.id %>"><%= post.title %></a></h4>
       <p>Created: <%= post.created_at %></p>
     </li>
    <% end %>
    </ul>
    
  34. Start the ruby server locally on your Mac using the code
    $ ruby myapp.rb
  35. Visit the local sever on your Mac by typing “http://localhost:4567” into the address bar on your browser and you should see the following:
  36. shot1

  37. Create a new file and name it “layout.erb” and save it in the “views” folder. Place the following code in that file:
  38. Note: This file will serve as the parent template for all of the other templates in the application. Your child templates, such as index.erb inherent the HTML and CSS from the parent template.
    Note: The opening and closing html tags.
    Note: The yield tag (<% yield %>) is where other templates will embed.

    <html>
    <head>
     <title><%= title %></title>
    </head>
    <body>
     <ul>
       <li><a href="/">Home</a></li>
       <li><a href="/posts/create">New Post</a></li>
     </ul>
     <%= yield %>
    </body>
    </html>
    

    REFRESH – You should now see something in your browser similar to the image below:
    shot2

  39. Next we will add the route to handle the title when clicked. Place the following code at the end of your “myapp.rb” file:
  40. get "/posts/:id" do
     @post = Post.find(params[:id])
     @title = @post.title
     erb :"posts/view"
    end
    
  41. Next we will add the template to handle the title when clicked. Create a file named “view.erb” inside of the “posts” directory (your-app/posts) and place the following code inside of it:
  42. <h1><%= @post.title %></h1>
    <p><%= @post.body %></p>
    

    REFRESH Then click one of your titles and you should see an image similar to the one below:
    Screen Shot 2014-01-09 at 7.22.48 PM

  43. Place the following code inside of the “myapp.rb” file. Make sure it is above the code in step 23, if not you may receive an “Could not find post” error or similar error.
  44. get "/posts/create" do
     @title = "Create post"
     @post = Post.new
     erb :"posts/create"
    end
    
    post "/posts" do
     @post = Post.new(params[:post])
     if @post.save
       redirect "posts/#{@post.id}"
     else
       erb :"posts/create"
     end
    end
    
  45. Next we will add the template to handle the “New Note” function when clicked. Create a file named “create.erb” inside of the “posts” directory (your-app/posts) and place the following code inside of it:
  46. <h2>Create Post</h2>
    <br/>
    <form action="/posts" method="post"role="form">
     <div class="form-group">
       <label for="post_title">Title:</label>
       <br>
       <input id="post_title" class="form-control" name="post[title]" type="text" value="<%= @post.title %>" style="width=90%"/>
     </div>
     <div class="form-group">
       <label for="post_body">Body:</label>
       <br>
       <textarea id="post_body" name="post[body]" class="form-control" rows="10"><%= @post.body %></textarea>
     </div>
     <button type="submit" class="btn btn-success">Submit</button>
    </form>
    

    Note: This is what your code should look like at this point. # tags are optional.

    # myapp.rb
    #This file is the core of the application - Kyle M. Brown
    #ActiveRecord is an ORM (Object Relational Mapping (ORM). It does the translations between Ruby objects and the database which deals with records and relations.
    
    require 'sinatra'
    require 'sinatra/activerecord' 
    require './database-config'
    
    
    class Post < ActiveRecord::Base
    end
    
    #Setup the route for the index page.
    get "/" do
      @posts = Post.order("created_at DESC")
      @title = "Welcome."
      erb :"posts/index"
    end
    
    #Sets the variable for the title.
    helpers do
      def title
        if @title
          "#{@title}"
        else
          "Welcome."
        end
      end
    end
    
    #Setup the route for the create post when clicked link.
    get "/posts/create" do
     @title = "Create post"
     @post = Post.new
     erb :"posts/create"
    end
    
    post "/posts" do
     @post = Post.new(params[:post])
     if @post.save
       redirect "posts/#{@post.id}"
     else
       erb :"posts/create"
     end
    end
    
    #Setup the route for the post (title) link when clicked.
    get "/posts/:id" do
     @post = Post.find(params[:id])
     @title = @post.title
     erb :"posts/view"
    end
    

    REFRESH – At this point you should have a working application that is running locally on your Mac where every link is clickable and functioning without error. From this point forward, we will be working on optional items such as error messages and validation, styling and some light security before pushing to heroku for the world to see.

    Error Messages and validation

  47. We are now going to add some code so that both the title and body cannot be null/empty, and the title has to be at least 5 characters long during submission of a new note.
  48. Open your “myapp.rb” file beneath the existing required elements.
  49. #Error and validation handling
    require 'sinatra/flash'
    require 'sinatra/redirect_with_flash'
    
    enable :sessions
    
  50. Next replace the following code to the top of your “myapp.rb” file and replace:
  51. class Post < ActiveRecord::Base
    end
    

    with

    class Post < ActiveRecord::Base
     validates :title, presence: true, length: { minimum: 5 }
     validates :body, presence: true
    end
    
  52. Replace the following code in the “myapp.rb” file:
  53. post "/posts" do
     @post = Post.new(params[:post])
     if @post.save
       redirect "posts/#{@post.id}"
     else
       erb :"posts/create"
     end
    end
    

    with

    post "/posts" do
     @post = Post.new(params[:post])
     if @post.save
       redirect "posts/#{@post.id}", :notice => 'Congrats! Love the new post. (This message will disappear in 4 seconds.)'
     else
       redirect "posts/create", :error => 'Something went wrong. Try again. (This message will disappear in 4 seconds.)'
     end
    end
    
  54. Finally, add the following code to the “layout.erb” file:
  55. <% if flash[:notice] %>
     <p class="alert alert-success"><%= flash[:notice] %>
    <% end %>
    <% if flash[:error] %>
     <p class="alert alert-error"><%= flash[:error] %>
    <% end %>
    

    REFRESH – At this point you should see an error message when an attempt is made to submit a post title with less than 3 characters or when either empty title or content fields are submitted.

    Styling with Bootstrap

  56. Update your layout.erb file with the following code:
  57. <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="">
        <meta name="author" content="">
        <title><%= title %></title>
        <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
        <style>
          body {
            padding-top: 75px;
          }
          .starter-template {
            padding: 40px 15px;
            text-align: center;
          }
          .container {
            max-width:1000px;
          }
        </style>
      </head>
    
      <body>
    
        <div class="navbar navbar-default navbar-fixed-top">
          <div class="container">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="/">Kyles blog</a>
            </div>
            <div class="collapse navbar-collapse">
              <ul class="nav navbar-nav">
                <li class="active"><a href="/">Home</a></li>
              </ul>
            </div><!--/.nav-collapse -->
          </div>
        </div>
    
    
        <div class="container">
    	  <a href="/posts/create"><button type="button" class="btn btn-primary">New Post</button></a>
    
          <% if flash[:notice] %>
            <p class="alert alert-success"><%= flash[:notice] %>
          <% end %>
          <% if flash[:error] %>
            <p class="alert alert-warning"><%= flash[:error] %>
          <% end %>
          <%= yield %>
    
        </div><!-- /.container -->
    
    
        <!-- Bootstrap core JavaScript
        ================================================== -->
        <!-- Placed at the end of the document so the pages load faster -->
        <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
        <script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
        <script>
        //** removes alerts after 4 seconds */
        window.setTimeout(function() {
            $(".alert").fadeTo(4500, 0).slideUp(500, function(){
                $(this).remove();
            });
        }, 4000);
        </script>
      </body>
    </html>
    
  58. Lets stop and commit the current files to GIT using:
  59. $ git add .
    $ git commit -am "updated"
    

    REFRESH You should now see a freshly redesigned app as seen below:
    Screen Shot 2014-01-13 at 7.26.42 PM

    Editing Post function

  60. Now we will add a edit post feature.
  61. Place the following code at the end of the “myapp.rb” file
  62. #Set the route to edit post (both get and post)
    get "/posts/:id/edit" do
      @post = Post.find(params[:id])
      @title = "Edit Form"
      erb :"posts/edit"
    end
    put "/posts/:id" do
      @post = Post.find(params[:id])
      @post.update(params[:post])
      redirect "/posts/#{@post.id}"
    end
    
  63. Inside of the “posts” directory create a new template file and name it “edit.erb” and place the following code inside of it:
  64. <h2>Edit Post</h2>
    <br/>
    <form action="/posts/<%= @post.id %>" method="post">
     <div class="form-group">
      <input type="hidden" name="_method" value="put" />
      <label for="post_title">Title:</label>
      <br>
      <input id="post_title" class="form-control" name="post[title]" type="text" value="<%= @post.title %>" />
     </div>
     <div class="form-group">
      <label for="post_body">Body:</label>
      <br>
      <textarea id="post_body" name="post[body]" class="form-control" rows="5"><%= @post.body %></textarea>
     </div>
      <button type="submit" class="btn btn-success">Submit</button>
    </form>
    
  65. Open and edit the view.erb template fill by replacing this:
  66. <h1><%= @post.title %></h1>
    <p><%= @post.body %></p>
    

    with

    <h1><%= @post.title %></h1>
    <p><%= @post.body %></p>
    <br>
    <a href="/posts/<%= @post.id %>/edit"><button type="button" class="btn btn-default btn-xs">Edit Post</button></a>
    

    REFRESH – At this point the apps “edit” function, as seen after clicking on a post title, should allowing existing post editing when clicked.
    Screen Shot 2014-01-13 at 7.34.26 PM

    Security

  67. We will now add some security to the app. At this point you can enter html tags into the title or body fields, press enter and execute on the form. We do not want people executing javascript into our forms, also known as XSS.
  68. To prevent XSS, add the following code to your “myapp.rb” file:
  69. helpers do
      include Rack::Utils
      alias_method :h, :escape_html
    end
    
  70. Now add the following code to the “view.erb” template.
  71. <h1><%=h @post.title %></h1>
    <p><%=h @post.body %></p>
    <br>
    <a href="/posts/<%= @post.id %>/edit"><button type="button" class="btn btn-default btn-xs">Edit Post</button></a>
    

    Note: You are basically adding an ‘h’ after 2 of the “<%=" symbols.

  72. Open the “edit.erb” file and update it to reflect the following:
  73. <h2>Edit Post</h2>
    <br/>
    <form action="/posts/<%= @post.id %>" method="post" role="form">
     <div class="form-group">
      <input type="hidden" name="_method" value="put" />
      <label for="post_title">Title:</label>
      <br>
      <input id="post_title" class="form-control" name="post[title]" type="text" value="<%=h @post.title %>" />
     </div>
     <div class="form-group">
      <label for="post_body">Body:</label>
      <br>
      <textarea id="post_body" name="post[body]" class="form-control" rows="5"><%=h @post.body %></textarea>
     </div>
      <button type="submit" class="btn btn-success">Submit</button>
    </form>
    

    Note: You are basically adding an ‘h’ after the “<%=" symbols for the input tags.

  74. Finally update your “index.erb” file to reflect the escaping with the following:
  75. <!-- Template for index.erb //-->
    
    <ul>
    <% @posts.each do |post| %>
     <li>
       <h4><a href="/posts/<%= post.id %>"><%=h post.title %></a></h4>
       <p>Created: <%=h post.created_at %></p>
     </li>
    <% end %>
    </ul>
    

    Note: You are basically adding an ‘h’ after 2 of the “<%=" symbols.

  76. Lets commit the current files to GIT using:
  77. $ git add .
    $ git commit -am "updated"
    

    REFRESH – You should no longer be able to execute html tags. If entered will be treated as text. e.g.:

    <script>alert("haha")</script>
    

    Heroku

  78. Last but not least, we will push the app to heroku.
  79. Note:You must have a Heroku account and you Mac must be setup. See the heroku section in my previous post.

  80. Enter the following code into the terminal to send the files to heroku:
  81. $ heroku create kyle-blog
    $ git push heroku master
    

    Note: You terminal should look something like the image below. There will be much more than seen here after you run git push heroku master.
    Screen Shot 2014-01-13 at 8.34.38 PM

    Note: At your heroku dashboard, you should see your new app on the “heroku.com/apps” page similar to the image below.
    Screen Shot 2014-01-13 at 8.29.51 PM

    Final Product

    Screen Shot 2014-01-13 at 10.40.25 PM

    See the app live at heroku: http://kyles-blog.herokuapp.com/

    Optionally you can push the code to github to house your code in the cloud for sharing.

    Note: In this example I already have a github account (Learn how to create one). I have already created a repository at github for kyles-blog(Learn how to create one) and my Mac is already setup (Learn how here) to work with github. You must have these things completed before using the following code:

    git remote add origin https://github.com/kmb40/kyles-blog.git
    git push -u origin master
    

    Get the code @ : https://github.com/kmb40/kyles-blog

Well thats it. We accomplished the following task:

  1. Created a blog app locally on a Mac in Sinatra with a sqlite database.
  2. Add error and validation function and messages.
  3. Added some security to prevent XSS.
  4. Deployed the final app to heroku for the public to see.
  5. Pushed the files to github for the public to use.

I hope that this is as useful to you as it was to me.
Please forward any comments or questions.
*This tutorial was inspired by another tutorial here.

Learning Rails

Get beginners tips about learning sinatra, rails, github, postgreSQL and heroku as I learn, test and document them.



Share

Leave a reply

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> 

required