Sinatra and postgreSQL perform on a heroku stage – tutorial

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

[code]
$ install shotgun
$ shotgun myapp.rb
[/code]
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. [code lang=”html”]
    # 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
    [/code]

  6. Run the following code at the terminal command line to install the gems listed inside of the Gemfile
  7. [code lang=”css”]$ bundle install[/code]
    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.
    [code lang=”html”]
    # config.ru

    require ‘./myapp’
    run Sinatra::Application
    [/code]

  10. Inside of the directory, create a file called “database-config.rb”
  11. [code lang=”css”]
    #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
    [/code]

  12. Inside of the directory, create a file called “myapp.rb”
  13. [code lang=”css”]
    # 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
    [/code]

  14. Inside of the directory, create a file called “Rakefile”
  15. [code lang=”html”]
    # 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’
    [/code]

  16. Run the following command at the terminal command line[code lang=”html”]$ rake db:create_migration NAME=create_posts[/code]
  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. [code lang=”html”]
    #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
    [/code]

  20. Run the following command at the command line in the terminal to start the migration; [code lang=”html”]$ rake db:migrate[/code]
  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. [code lang=”html”]
    $ 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.’)
    >>
    [/code]
    Note:Something similar to the following should be returned in the terminal:
    [code lang=”html”]
    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">
    >>
    [/code]
    GIT – Version Control

  24. Commit this version with GIT.
  25. [code lang=”html”]
    $ git init
    $ git add .
    $ git commit -am "initial commit"
    [/code]

    Templates

  26. Setup the first route by adding the following code to “myapp.rb”:
  27. [code lang=”html”]
    #Setup the route for the index page.
    get "/" do
    @posts = Post.order("created_at DESC")
    @title = "Welcome."
    erb :"posts/index"
    end
    [/code]
    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. [code lang=”html”]
    #Sets the variable for the title.
    helpers do
    def title
    if @title
    "#{@title}"
    else
    "Welcome."
    end
    end
    end
    [/code]

  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.[code]$ cd views/posts[/code]
  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. [code lang=”html”]
    <!– 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>
    [/code]

  34. Start the ruby server locally on your Mac using the code [code lang=”html”]$ ruby myapp.rb[/code]
  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.
    [code lang=”html”]
    <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>
    [/code]

    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. [code lang=”css”]
    get "/posts/:id" do
    @post = Post.find(params[:id])
    @title = @post.title
    erb :"posts/view"
    end
    [/code]

  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. [code lang=”css”]
    <h1><%= @post.title %></h1>
    <p><%= @post.body %></p>
    [/code]
    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. [code]
    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
    [/code]

  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. [code lang=”css”]
    <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>
    [/code]

    Note: This is what your code should look like at this point. # tags are optional.
    [code lang=”css”]
    # 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
    [/code]

    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. [code lang=”html”]
    #Error and validation handling
    require ‘sinatra/flash’
    require ‘sinatra/redirect_with_flash’

    enable :sessions
    [/code]

  50. Next replace the following code to the top of your “myapp.rb” file and replace:
  51. [code lang=”css”]
    class Post < ActiveRecord::Base
    end
    [/code]
    with
    [code lang=”css”]
    class Post < ActiveRecord::Base
    validates :title, presence: true, length: { minimum: 5 }
    validates :body, presence: true
    end
    [/code]

  52. Replace the following code in the “myapp.rb” file:
  53. [code lang=”css”]
    post "/posts" do
    @post = Post.new(params[:post])
    if @post.save
    redirect "posts/#{@post.id}"
    else
    erb :"posts/create"
    end
    end
    [/code]
    with
    [code lang=”css”]
    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
    [/code]

  54. Finally, add the following code to the “layout.erb” file:
  55. [code lang=”css”]
    <% if flash[:notice] %>
    <p class="alert alert-success"><%= flash[:notice] %>
    <% end %>
    <% if flash[:error] %>
    <p class="alert alert-error"><%= flash[:error] %>
    <% end %>
    [/code]
    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. [code lang=”html”]
    <!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>
    [/code]

  58. Lets stop and commit the current files to GIT using:
  59. [code lang=”html”]
    $ git add .
    $ git commit -am "updated"
    [/code]

    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. [code lang=”css”]
    #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
    [/code]

  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. [code lang=”html”]
    <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>
    [/code]

  65. Open and edit the view.erb template fill by replacing this:
  66. [code lang=”html”]
    <h1><%= @post.title %></h1>
    <p><%= @post.body %></p>
    [/code]
    with
    [code lang=”html”]
    <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>
    [/code]

    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. [code lang=”html”]
    helpers do
    include Rack::Utils
    alias_method :h, :escape_html
    end
    [/code]

  70. Now add the following code to the “view.erb” template.
  71. [code lang=”html”]
    <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>
    [/code]
    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. [code lang=”css”]
    <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>
    [/code]
    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. [code lang=”html”]
    <!– 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>
    [/code]
    Note: You are basically adding an ‘h’ after 2 of the “<%=" symbols.

  76. Lets commit the current files to GIT using:
  77. [code lang=”html”]
    $ git add .
    $ git commit -am "updated"
    [/code]

    REFRESH – You should no longer be able to execute html tags. If entered will be treated as text. e.g.:
    [code lang=”html”]
    <script>alert("haha")</script>
    [/code]

    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. [code lang=”html”]
    $ heroku create kyle-blog
    $ git push heroku master
    [/code]
    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:
    [code lang=”css”]
    git remote add origin https://github.com/kmb40/kyles-blog.git
    git push -u origin master
    [/code]
    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.



By Kyle

My name is Kyle M. Brown and I am passionate about solving business problems with new technologies.

Leave a comment

Your email address will not be published. Required fields are marked *