Padrino logo image

Recently I’ve been wanting to experiment building websites with Ruby, but without using Rails. I’ve built a few small Sinatra applications, but I really miss having those basic MVC tools that Rails provides. It’s amazing how much easier things are when you have some nice routing and controller helpers. This led me to trying out Padrino, and I have to say it’s looking quite interesting.

Although the Padrino website does have a build-your-own-blog tutorial, it is using ActiveRecord and the HAML view template language. It’s not that I dislike either of these, but I did want try some other tools, such as DataMapper and straight up ERB for the views. As I couldn’t find any good tutorials out there I decided to use that Padrino tutorial as inspiration for my own post. Hopefully you will find it useful.

I will be borrowing heavily from the original Blog Tutorial, so if you’re happy enough using ActiveRecord and HAML, then please do go read that.

Okay, enough of the introductions, let’s get stuck in.

Installation

Presuming you already have Ruby installed on your system (I’ll be using Ruby 2.2.0 for this tutorial, although 1.9.3+ should be fine), our first task will be to install the latest version of the Padrino framework;

$ gem install padrino

At the time of writing this will install v0.12.4. For more details on installation please take a look at the official installation guide. Once this has finished installing, you’ll have all the necessary dependencies and we can begin building our Sample Blog.

New Project

Like with Rails, the easiest way to create a new project is via the Padrino generator. This will generate a skeleton application with all the appropriate files needed to build our application. Padrino is an agnostic framework and supports using a variety of different view template languages, testing frameworks, database components, and so on.

For our blog, we’ll be using the following components;

  • PostgreSQL database
  • DataMapper ORM
  • ERB template language
  • Twitter Bootstrap framework
  • RSpec testing framework
  • SASS stylesheet language for generating CSS
  • jQuery JavaScript library.

You can learn more about generators in the Padrino generators guide.

Although we won’t be using RSpec, SASS or jQuery in this particular tutorial, it will be useful to have them in place ready for when you build in more functionality. Now we know what components we’re going to be using, let’s generate our new project;

$ padrino g project sample_blog -a postgres -d datamapper -e erb -t rspec -c sass -s jquery -b

Note the -b flag at the end of the previous command. This instructs bundler to install all dependencies after it has finished generating the project. If you leave that off then you’ll need to make sure to run bundle install manually. Next;

$ cd sample_blog

Now that we’re in the root or our application let’s take a closer look at a few of the more important files and directories, before we continue.

  • Gemfile – include necessary GEM dependencies for your app here.
  • app/app.rb – the core application configuration.
  • config/apps.rb – defines which applications will be mounted.
  • config/database.rb – connection settings for your chosen database adapter.

And a few important directories;

  • app/controllers – define your application routes here.
  • app/helpers – your application helper methods.
  • app/views – controllers will look here for view templates to render.
  • lib – put extensions, libraries or other code for your project here.
  • public – place application images, stylesheets and Javascript files here.
  • spec – your application tests.

So we can connect to our database we need to make sure we have all the correct settings configured properly - I’m going to presume you already have Postgres installed at this point but if you haven’t then I recommend you read the documentation. We’ll be using a separate configuration file for the database settings. Copy the following to config/database.yml;

---
development: &defaults
  adapter: postgres
  database: sample_blog_development
  username: USERNAME
  password: PASSWORD
  host: 127.0.0.1

test:
  <<: *defaults
  database: sample_blog_test

production:
  <<: *defaults
  database: sample_blog_production

USERNAME and PASSWORD should be replaced with the settings you used when installing Postgres. We’ll also need to edit the config/database.db so that Padrino knows that it should be using our new YML configuration file. Uncomment the follow line;

DataMapper.setup(:default, YAML.load_file(Padrino.root('config/database.yml'))[RACK_ENV])

On my system I don’t need to specify a user when connecting to the database, so I had to remove the root user from the DataMapper.setup() commands;

when :development then DataMapper.setup(:default, "postgres://localhost/sample_blog_development")
when :production then DataMapper.setup(:default, "postgres://localhost/sample_blog_production")
when :test then DataMapper.setup(:default, "postgres://localhost/sample_blog_test")

Padrino Admin Panel

Padrino comes with a nice Admin Dashboard to help us manage and create/edit users and posts for our blog. Back in the console, enter the following;

$ padrino g admin
$ bundle install

This will create admin as a sub-application within your project, which will be mounted at boot by config/apps.rb.

The admin panel installation tells us that we need to create the database, run migrations and seed the database, as follows;

$ padrino rake db:create
$ padrino rake db:migrate
$ padrino rake db:seed

When you run rake db:seed you will be prompted to enter an email and password. Be sure to remember these otherwise you won’t be able to log in to the admin panel!

Read more about the dashboard in the Admin Panel Guide.

Since Padrino 0.10.0, all generated models are placed at the top level directory of the project in a models directory. This seems a bit odd to me so if you wish, you can move the admin models to live under the root admin directory before continuing. It is however, not required.

Basic Routes

Before trying out our new app it might be useful to set up some simple routes. Let’s add some “homepage” and “about” page routes. Open up the app/app.rb file and enter the following;

# app/app.rb
module SampleBlog
  class App < Padrino::Application
    register SassInitializer
    register Padrino::Mailer
    register Padrino::Helpers

    enable :sessions

    # Add these routes below to the app file...
    get "/" do
      erb "<p>Hello World!</p>"
    end

    get :about, map: "/about" do
      erb "<p>This is a sample blog created to demonstrate how Padrino works!</p>"
    end
  end
end

Be sure to check out the Padrino controllers guide for a comprehensive overview of the routing system.

Start Your Engines

Now that our Padrino application has been generated, the database configured, and our admin panel installed, we can boot up the Server. Before we do that though, we’re going to switch our application from using the default WEBrick server, and use Thin instead. Open up the Gemfile and uncomment the line;

gem 'thin'

and then make sure you run;

$ bundle install

That done, we simply execute the following in the terminal;

$ padrino start

(Note: if you are getting any “unresolved specs” errors then you may need to prefix all padrino commands with bundle exec .)

If all is successful the terminal should output something like this;

=> Padrino/0.12.4 has taken the stage development at http://127.0.0.1:3000
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 127.0.0.1:3000, CTRL+C to stop

At this point you’re ready to view your new website!

Open your web browser and visit the URL; http://localhost:3000. If all is well then you should see the homepage message; Hello World!. You can jump into the admin panel by going to the URL; http://localhost:3000/admin.

You’ll need to log in with the admin credentials you specified during the rake db:seed command performed earlier. Feel free to explore this area and checkout the existing accounts. We’ll come back to this in more detail later.

Worth noting here is that Padrino has full support for code reloading in development mode. This means you can keep the Padrino server running and any time you change your source code, you can just refresh the browser and all changes will be automatically displayed. You might want to open up a new terminal and cd to your directory and keep the server running.

To read more about terminal commands, take a look at the Development and Terminal Commands guide.

Creating Posts

So far so good, but this isn’t going to be much of a blog without any blog posts. Let’s implement the functionality to write and view posts!

We start off by creating a Post model that has a title, body text, and a couple of timestamps. As mentioned earlier, Padrino creates new models in the top level directory so to give our application a better structure we’re going to tell it to generate these in a different location. By appending the command with an -a option the models will be placed in the supplied sub-app directory (in this case app);

$ padrino g model post title:string body:text created_at:datetime updated_at:datetime -a app

     apply  orms/datamapper
     apply  tests/rspec
    create  app/models/post.rb
    create  spec/app/models/post_spec.rb
    create  db/migrate/002_create_posts.rb

The Post model will be created automatically when you run the migration;

$ padrino rake db:migrate

Next we need to create a controller so that we can display our posts;

$ padrino g controller posts get:index get:show

    create  app/controllers/posts.rb
    create  app/views/posts

This will create a posts controller with the standard :index and :show routes, so we can now open up the file and give it some instructions on what to do when someone hits these routes;

# app/controllers/posts.rb
SampleBlog::App.controllers :posts do
  get :index do
    @posts = Post.all(order: :created_at.desc)
    erb :'posts/index'
  end

  get :show, with: :id do
    @post = Post.get(params[:id])
    erb :'posts/show'
  end
end

This defines routes that can be accessed via our application. The “http method” get starts off the declaration followed by a symbol representing the “action”. Inside the block we store an instance variable fetching the necessary objects and then render an ERB view template. This should look quite familiar to anyone coming from Rails or Sinatra.

Now we need to create views for these :index and :show controller actions;

# app/views/posts/index.erb
<h1>Latest Posts</h1>
<%= partial "posts/post", collection: @posts %>


# app/views/posts/_post.erb
<h2 class="title"><%= link_to post.title, url_for(:posts, :show, id: post.id) %></h2>
<p class="post-info">
  <span class="date"><%= time_ago_in_words(post.created_at) + " ago" %></span>
</p>

<div class="body">
  <%= simple_format(post.body) %>
</div>


# app/views/posts/show.erb
<div class="post">
  <h1 class="title"><%= @post.title %></h1>

  <p class="post-info">
    <span class="date"><%= time_ago_in_words(@post.created_at) + ' ago' %></span>
  </p>

  <div class="body">
    <%= simple_format(@post.body) %>
  </div>

  <p><%= link_to("View all posts", url_for(:posts, :index)) %></p>
</div>

Padrino Admin makes it easy to create, edit and delete records, but before we can use this for our Posts, we need to tell Admin how to do this by running the following command;

$ padrino g admin_page post

    create  admin/views/posts
    create  admin/controllers/posts.rb
    create  admin/views/posts/_form.erb
    create  admin/views/posts/edit.erb
    create  admin/views/posts/index.erb
    create  admin/views/posts/new.erb
    insert  admin/app.rb

Note: make sure to use padrino g admin_page after the creation of your model and their migration.

If your server is running (padrino start) then go to the admin panel http://localhost:3000/admin and login using your credentials as set previously. You should now see two tabs; one for Posts and the other for Accounts. Click on the Posts link.

Go ahead, write some posts!

Now we have a few posts in the system we can visit our :index route. Visit the http://localhost:3000/posts URL and voila! All your wonderful posts!

You can see all the routes we’ve defined so far by using the padrino rake routes command:

$ padrino rake routes
    URL                 REQUEST  PATH
    (:about)              GET    /about_us
    (:posts, :index)      GET    /posts
    (:posts, :show)       GET    /posts/show/:id

This can be helpful in understanding the mapping between controllers and URLs.

Assigning Posts to an Account

So far a post is not associated to any particular user, this might be okay for some, but if you wish to have others write for your blog then we need to let every post have an author. Let’s revisit our Post model. We’ll start by adding a new migration to attach an Account to a Post.

$ padrino g migration AddAccountToPost account_id:integer

    apply  orms/datamapper
    create  db/migrate/003_add_account_to_post.rb

This creates a new migration with the desired field attaching the account_id to the post. Before running the migration we’ll want to modify this file and assign any existing posts to a user;

# db/migrate/003_add_account_to_post.rb
migration 3, :add_account_to_post do
  up do
    modify_table :posts do
      add_column :account_id, Integer
    end

    # and assign a user to all existing posts
    first_account = Account.first
    Post.all.each { |p| p.update(account: first_account) }
  end
  # ...
end

We also need to give the Post model an association with the Account, and while we’re there, let’s add a couple of Post validations.

# app/models/post.rb
class Post
  # ...
  belongs_to :account
  validates_presence_of :title
  validates_presence_of :body
end

Remember, every time we change the database we need to run the migration;

$ padrino rake db:migrate

=> Executing Rake db:migrate ...

Our post model now has appropriate associations and validations. We also need to go inside the Admin Panel and make some changes so that the account (using current_account) is saved when a new post is created.

# admin/controllers/posts.rb
SampleBlog::Admin.controllers :posts do
  # ...

  post :create do
    @post = Post.new(params[:post])
    @post.account = current_account
    if @post.save
      @title = pat(:create_title, :model => "post #{@post.id}")
      flash[:success] = pat(:create_success, :model => 'Post')
      params[:save_and_continue] ? redirect(url(:posts, :index)) : redirect(url(:posts, :edit, :id => @post.id))
    else
      @title = pat(:create_title, :model => 'post')
      flash.now[:error] = pat(:create_error, :model => 'post')
      render 'posts/new'
    end
  end

  # ...
end

As we now have a post author, we can update the post views to show their name;

# app/views/posts/show.erb
<div class="post">
  <h1 class="title"><%= @post.title %></h1>

  <p class="post-info">
    Posted by <%= @post.account.name %>,
    <span class="date"><%= time_ago_in_words(@post.created_at) + ' ago' %></span>
  </p>

  <div class="body">
    <%= simple_format(@post.body) %>
  </div>

  <p><%= link_to("View all posts", url_for(:posts, :index)) %></p>
</div>

and also;

# app/views/posts/_post.erb
<h2 class="title"><%= link_to post.title, url_for(:posts, :show, id: post.id) %></h2>
<p class="post-info">
  Posted by <%= post.account.name %>,
  <span class="date"><%= time_ago_in_words(post.created_at) + " ago" %></span>
</p>

<div class="body">
  <%= simple_format(post.body) %>
</div>

If you create a bunch of posts as another user (create new users in the Admin Panel…and don’t forget to log in as them!), then you’ll be able to see which posts were written by which user.

Site Layout

Things are going well, but navigating our site is annoying and it certainly won’t be winning any beauty contests. Let’s fix that.

We’re going to create a site-wide template, called a “layout”, which acts as a container for the content templates yielded by each route. This will be used to create a consistent structure between each page of the application, giving us a navigation menu and a sidebar on every page.

I’m going to use Twitter Bootstrap to get us up and going quickly. Create a new file app/views/layouts/application.erb and paste in this Bootstrap template;

# app/views/layouts/application.erb
<!DOCTYPE html>
<html lang="en">
  <head>
    <title><%= @title || "SampleBlog" %></title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <%= yield_content :include %>
  </head>

  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
        </div>

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li><a href="/posts">Blog</a></li>
            <li><a href="/about">About</a></li>
            <li><a href="/admin">Admin</a></li>
          </ul>
        </div>
      </div>
    </nav>

    <div class="container">
      <div class="page-header">
        <h1>SampleBlog <small class="text-muted">a padrino tutorial</small></h1>
      </div>

      <div class="row">
        <div class="col-sm-8">
          <%= yield  %>
        </div>

        <div class="col-sm-3 col-sm-offset-1">
          <div class="module">
            <h4>Recent Posts</h4>
            <ul class="list-unstyled">
              <li>Lorem ipsum dolorum itsum estem</li>
              <li>Lorem ipsum dolorum itsum estem</li>
              <li>Lorem ipsum dolorum itsum estem</li>
              <li>Lorem ipsum dolorum itsum estem</li>
            </ul>
          </div>
          <div class="module">
            <h4>Latest Comments</h4>
            <ul class="list-unstyled">
              <li>Lorem ipsum dolorum itsum estem</li>
              <li>Lorem ipsum dolorum itsum estem</li>
              <li>Lorem ipsum dolorum itsum estem</li>
              <li>Lorem ipsum dolorum itsum estem</li>
            </ul>
          </div>
        </div>
      </div>

      <footer class="footer">
        <p>Copyright (c) 2015. Built with <a href="http://www.padrinorb.com">Padrino</a></p>
      </footer>
    </div>


    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
  </body>
</html>

Refresh and bask in the glory of your brand new theme!

As you can see, I’ve included some dummy sidebar elements to flesh out the design a little. Using the default Bootstrap CSS/Javascript files this template is a typical, if somewhat simple, theme as used on many blogs.

The official Padrino Blog Tutorial also covers how to deploy your application to Heroku, so if you need help then I recommend you go read that guide.

There should be enough here for you to forge on and expand the functionality to include many more blogging features.

I may even write some follow up tutorials, such as how to generate RSS feeds, implement a commenting system, and perhaps even how to protect your blog from comment spammers by using Wordpress’ Akismet. Keep your eyes peeled!