Padrino

Getting Started

Blog Tutorial

When reading about a new framework, I often find that the best way to get familiar is to read a brief tutorial on how to develop a simple application. This can quickly give new users a sense of the development flow and processes involved in using a framework. This guide will show new users how to develop a simple blog using the Padrino framework. Along the way, each step will be explained and links will be provided to further information on different relevant topics.



Study Guide

To skip this tutorial or immediately see the complete blog tutorial project, you can checkout the blog tutorial repository using Git:

$ git clone git@github.com:padrino/blog-tutorial.git

Installation

In order to develop a Padrino application, we must have a few things installed. First, we must obviously have ruby and rubygems installed. Next, we must install the padrino framework gems:

$ gem install padrino

For more details on installation, check out the installation guide. Once this has been finished, all necessary dependencies should be ready and we can begin developing our sample blog.


Project Generation

To create a Padrino application, the best place to start is using the convenient Padrino generator. Similar to Rails, Padrino has a project generator which will create a skeleton application with all the files you need to being development of your new idea. Padrino is an agnostic framework and supports using a variety of different template, testing, JavaScript and database components. You can learn more by reading the generators guide.

For this sample application, we will use the ActiveRecord ORM, the Slim templating language, the RSpec testing framework and the jQuery JavaScript library. With that in mind, let us generate our new project:

$ padrino g project blog-tutorial -t rspec -e haml -c scss -s jquery -d sequel -b

This command will generate our basic Padrino project and the print out a nice report of the files generated. The output of this generation command can be viewed in this gist file. Notice the -b flag in the previous command which automatically instructs bundler to install all dependencies. All we need to do now is cd into our brand new application.

$ cd blog-tutorial

Now, the terminal should be inside the root of our newly generated application with all necessary gem dependencies installed. Let us take a closer look at the particularly important generated files before we continue on with development.

  • Gemfile – Be sure to include any necessary gem dependencies for your app in this file!
  • app/app.rb – This is the primary configuration file for your core application.
  • config/apps.rb – This defines which applications are mounted in your project.
  • config/database.rb – This defines the connection details for your chosen database adapter.

The following important directories are also generated:

  • app/controllers – This is where the Padrino route definitions should be defined.
  • app/helpers – This is where helper methods should be defined for your application.
  • app/views – This should contain your template views to be rendered in a controller.
  • lib – This should contain any extensions, libraries or other code to be used in your project.
  • public – This is where images, style sheets and JavaScript files should be stored.
  • spec – This is where your model and controller tests should be stored.

For now, the defaults for the database connection settings (config/database.rb) are OK for this tutorial.

This environment can be configured in config/apps.rb as: Padrino.configure_apps do if RACK_ENV == 'production' disable :reload disable :reload_templates else enable :reload enable :reload_templates end end or can be configured in app/app.rb as if Padrino.env == :production # do production else # non production here end

Let us also setup a few simple routes in our application to demonstrate the Padrino routing system. Let's go into the app/app.rb file and enter the following routes:

# app/app.rb
module BlogTutorial
  class App < Padrino::Application
    register ScssInitializer
    register Padrino::Mailer
    register Padrino::Helpers

    enable :sessions

    # Add these routes below to the app file...
    get "/" do
      'Hello World!'
    end

    get :about, :map => '/about-us' do
      render :haml, '%p This is a sample blog created to demonstrate how Padrino works!'
    end
  end
end

Note that the first route here sets up a simple string to be returned at the root URL of the application. The second route defines a one-line about page inline using Haml which is then explicitly mapped to the /about-us URL. The symbol :about is used to reference the route later.

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


Admin Dashboard Setup

Next, this is a good time to setup the Padrino admin panel which allows us to easily view, search and modify data for a project. Let's go back to the console and enter:

$ padrino g admin

This will create the admin sub-application within your project and mount it within the config/apps.rb file. The output of this command can be viewed in this gist file.

Now, you should follow the instructions of the output:

  1) Run 'bundle'
  2) Run 'bundle exec rake db:create' if you have not created a database yet
  3) Run 'bundle exec rake db:migrate'
  4) Run 'bundle exec rake db:seed'
  5) Visit the admin panel in the browser at '/admin'

During this process, you will be prompted to enter an email and password to use for the admin dashboard. Be sure to remember this for use later in development.

To read more about the features of the admin panel, check out the Admin Panel Guide.


Booting Padrino

Now the Padrino project has been generated, the database has been configured and created and the admin panel has been properly setup. We can now start up our Padrino application server. This is quite easy to do with the built-in Padrino tasks. Simply execute the following in the terminal:

$ padrino s

You should see no errors, and the terminal should output:

=> Padrino/0.14.1.1 has taken the stage development at http://127.0.0.1:3000
[2017-05-20 09:40:49] INFO  WEBrick 1.3.1
[2017-05-20 09:40:49] INFO  ruby 2.4.1 (2017-03-22) [i686-linux]
[2017-05-20 09:40:49] INFO  WEBrick::HTTPServer#start: pid=26641 port=3000

To read more about available terminal commands, checkout the Development and Terminal Commands guide.

Your application now exists on http://localhost:3000. Visit this URL in the browser and you should see the Hello World!.

We can also visit the admin panel by going to the URL: http://localhost:3000/admin and then log in using the admin credentials specified during the rake db:seed command performed earlier. Feel free to explore this area and checkout the existing accounts. We will come back to this in more detail later. To read more about the features of the admin panel, check out the Admin Panel Guide.

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 change your code source and when you refresh in the browser, the changes will be automatically displayed. You might want to open up a new terminal and cd to your directory and keep the server running.


Creating Posts

Now that the application is ready and the layouts have been defined, let's implement the functionality to view our blog posts and even add the ability to create new posts!

Let's start off by generating the model into our app directory. As of version 0.13.1, the models will default to generating at the top level 'models' directory in a project. If you want to place your models to another location, you can append the -a option to the command - this is handy if you would like to have models which should be coped only to sub-apps.

$ padrino g model post title:string body:text created_at:datetime
       apply  orms/sequel
       apply  tests/rspec
      create  models/post.rb
      create  spec/models/post_spec.rb
      create  db/migrate/002_create_posts.rb

Go ahead and migrate the database now.

$ padrino rake sq:migrate
=> Executing Rake sq:migrate ...
   INFO -  (0.000159s) PRAGMA foreign_keys = 1
   INFO -  (0.000021s) PRAGMA case_sensitive_like = 1
   INFO -  (0.000080s) SELECT sqlite_version()
   INFO -  (0.000037s) CREATE TABLE IF NOT EXISTS `schema_info` (`version` integer DEFAULT (0) NOT NULL)
   INFO -  (0.000077s) SELECT * FROM `schema_info` LIMIT 1
   INFO -  (0.000061s) SELECT 1 AS 'one' FROM `schema_info` LIMIT 1
   INFO -  (0.000058s) SELECT count(*) AS 'count' FROM `schema_info` LIMIT 1
   INFO -  (0.000063s) SELECT `version` FROM `schema_info` LIMIT 1
   INFO -  Begin applying migration version 2, direction: up
   INFO -  (0.014988s) CREATE TABLE `posts` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `title` varchar(255), `body` Text, `created_at` date)
   INFO -  (0.017424s) UPDATE `schema_info` SET `version` = 2
   INFO -  Finished applying migration version 2, direction: up, took 0.028866 seconds
<= sq:migrate:up executed

Next, let's create the controller to allow the basic viewing functionality.

$ padrino g controller posts get:index get:show
      create  app/controllers/posts.rb
      create  app/views/posts
       apply  tests/rspec
      create  spec/app/controllers/posts_controller_spec.rb
      create  app/helpers/posts_helper.rb
       apply  tests/rspec
      create  spec/app/helpers/posts_helper_spec.rb

We'll want to attached some of the standard routes (:index and :show) to the controller.

# app/controllers/posts.rb
BlogTutorial::App.controllers :posts do
  get :index do
    @posts = Post.reverse_order(:created_at).all
    render 'posts/index'
  end

  get :show, :with => :id do
    @post = Post[id: params[:id]]
    render 'posts/show'
  end
end

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

Next, we'll want to create the views for the two controller actions we defined: index and show.

-# app/views/posts/index.haml
- @title = "Welcome"

#posts= partial 'posts/post', :collection => @posts

-# app/views/posts/_post.haml
.post
  .title= link_to post.title, url_for(:posts, :show, :id => post.id)
  .date= time_ago_in_words(post.created_at || Time.now) + ' ago'
  .body= simple_format(post.body)

-# app/views/posts/show.haml
- @title = @post.title
#show
  .post
    .title= @post.title
    .date= time_ago_in_words(@post.created_at || Time.now) + ' ago'
    .body= simple_format(@post.body)
%p= link_to 'View all posts', url_for(:posts, :index)

Padrino Admin makes it easy to create, edit and delete records automatically. To manage posts using Padrino Admin, run this command.

$ padrino g admin_page post
      create  admin/controllers/posts.rb
      create  admin/views/posts/_form.haml
      create  admin/views/posts/edit.haml
      create  admin/views/posts/index.haml
      create  admin/views/posts/new.haml
      insert  admin/app.rb

Let's make sure the server is running (padrino s) and give this admin interface a try.

Visit http://localhost:3000/admin and login using the credentials you had setup during the seed.

There should now be two tabs, one for Posts and the other, Accounts. Click on Posts.

Padrino Admin allows you to easily create new records by clicking "New". It has a form all ready complete with the fields you had generated prior in the creation of the Post model.

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

Now that you have added a few posts through the admin interface, check out http://localhost:3000/posts and notice that the posts you created now show up in the "index" action!

You can see all the routes that we now have defined using the padrino rake routes command:

$ padrino rake routes

Application: BlogTutorial::Admin
    URL                           REQUEST  PATH
    (:sessions, :new)               GET    /admin/sessions/new
    (:sessions, :create)           POST    /admin/sessions/create
    (:sessions, :destroy)         DELETE   /admin/sessions/destroy
    (:base, :index)                 GET    /admin/
    (:accounts, :index)             GET    /admin/accounts
    (:accounts, :new)               GET    /admin/accounts/new
    (:accounts, :create)           POST    /admin/accounts/create
    (:accounts, :edit)              GET    /admin/accounts/edit/:id
    (:accounts, :update)            PUT    /admin/accounts/update/:id
    (:accounts, :destroy)         DELETE   /admin/accounts/destroy/:id
    (:accounts, :destroy_many)    DELETE   /admin/accounts/destroy_many
    (:posts, :index)                GET    /admin/posts
    (:posts, :new)                  GET    /admin/posts/new
    (:posts, :create)              POST    /admin/posts/create
    (:posts, :edit)                 GET    /admin/posts/edit/:id
    (:posts, :update)               PUT    /admin/posts/update/:id
    (:posts, :destroy)            DELETE   /admin/posts/destroy/:id
    (:posts, :destroy_many)       DELETE   /admin/posts/destroy_many

Application: BlogTutorial::App
    URL                 REQUEST  PATH
    (:about)              GET    /about-us
    (:posts, :index)      GET    /posts
    (:posts, :show)       GET    /posts/show/:id

This can be helpful to understand the mapping between controllers and urls.


Attaching Accounts to Posts

So far, a post does not have a user associated as the author. Suppose that now we want 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/activerecord
      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. Let's modify this migration to add the accounts FK to posts:

# db/migrate/003_add_account_to_post.rb

Sequel.migration do
  up do
    alter_table :posts do
      add_column :account_id, Integer
    end
  end

  down do
    alter_table :posts do
      drop_column :account_id
    end
  end
end

Now, we'll return to the Post Model to setup the account association and add a few validations.

# models/post.rb

class Post < Sequel::Model
  many_to_one :account

  plugin :validation_helpers
  def validate
    super
    validates_presence [:title, :body]
  end
end

And add the association to the Account model:

class Account < Sequel::Model
  one_to_many :posts
  ...
end

Now we are ready to run the migration: $ padrino rake sq:migrate

Let's create another migration to assign the first user to all existing posts:

$ padrino g migration MigrateExistingPostsToFirstAccount
       apply  orms/activerecord
      create  db/migrate/004_migrate_existing_posts_to_first_account.rb

And change the content of the migration:

# db/migrate/004_migrate_existing_posts_to_first_account.rb
Sequel.migration do
  up do
    first_account_id = from(:accounts).get(:id)

    if first_account_id
      from(:posts).update(account_id: first_account_id)
    end
  end

  down do
    from(:posts).update(account_id: nil)
  end
end

And run the migrations again: $ padrino rake sq:migrate

Our database now has the appropriate associations and validations.

We'll need to go inside the generated Padrino Admin and make some changes to include the account with the post.

Head on over to admin/controllers/posts.rb. We're going to include the current_account to the creation of a new Post.

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

  post :create do
    @post = Post.new(params[:post])
    @post.account = current_account
    ...
  end
 ...
end

We'll also update the post view to show the changes that we made and display the author:

-# app/views/posts/show.haml
- @title = @post.title
#show
  .post
    .title= @post.title
    .date= time_ago_in_words(@post.created_at || Time.now) + ' ago'
    .body= simple_format(@post.body)
    .details
      .author Posted by #{@post.account.email}
%p= link_to 'View all posts', url_for(:posts, :index)

-# app/views/posts/_post.haml
.post
  .title= link_to post.title, url_for(:posts, :show, :id => post)
  .date= time_ago_in_words(post.created_at || Time.now) + ' ago'
  .body= simple_format(post.body)
  .details
    .author Posted by #{post.account.email}

Now, lets add another user. Revisit http://localhost:3000/admin and click on the Account tab. Now create a new Account record (don't forget to give the new account the admin role). Once you have a new account, try logging into it and then adding one more post in the admin interface. There you have it, multiple users and posts!

See the effects of our changes by visiting http://localhost:3000/posts to see our newly created posts linked to the author that wrote them.


Site Layout

Now that the application has been properly configured and the server has been started, let's create a few basic styles and define a layout to prepare the application for continued development.

First, let us create a layout for our application to use. A layout is a file that acts as a container for the content templates yielded by each route. The layout should be used to create a consistent structure between each page of the application. To create a layout, simply add a file to the app/views/layouts directory:

-# app/views/layouts/application.haml

!!! Strict
%html
  %head
    %title
      = [@title, "Padrino Sample Blog"].compact.join(" | ")
    = stylesheet_link_tag 'normalize', 'application'
    = javascript_include_tag 'jquery', 'application'
    = yield_content :include
    %body
      #header
        %h1 Sample Padrino Blog
        %ul.menu
        %li= link_to 'Blog', url_for(:posts, :index)
        %li= link_to 'About', url_for(:about)
      #container
        #main= yield
        #sidebar
          = form_tag url_for(:posts, :index), :method => 'get'  do
            Search for:
            = text_field_tag 'query', :value => params[:query]
            = submit_tag 'Search'
          %p
            Recent Posts
          %ul.bulleted
            %li Item 1 - Lorem ipsum dolorum itsum estem
            %li Item 2 - Lorem ipsum dolorum itsum estem
            %li Item 3 - Lorem ipsum dolorum itsum estem
          %p
            Categories
            %ul.bulleted
              %li
                Item 1 - Lorem ipsum dolorum itsum estem
              %li
                Item 2 - Lorem ipsum dolorum itsum estem
              %li
                Item 3 - Lorem ipsum dolorum itsum estem
            %p
              Latest Comments
              %ul.bulleted
                %li Item 1 - Lorem ipsum dolorum itsum estem
                %li Item 2 - Lorem ipsum dolorum itsum estem
                %li Item 3 - Lorem ipsum dolorum itsum estem
      #footer
        Copyright (c) 2009-2017 Padrino

This layout creates a basic structure for the blog and requires the necessary stylesheets and javascript files for controlling the behavior and presentation of our site. The layout also includes some dummy elements such as a fake search and stubs for list items left as an exercise for the reader.

Next, we simply need to setup the style sheets. There are two we will use for this demo. The first is a generic normalize CSS reset by Nicolas Gallagher. The full reset style sheet can be found in the sample blog repository and should be put into public/stylesheets/normalize.css.

The second style sheet is the application style sheet to give our blog a better look and feel. The full contents of the style sheet can be found in the sample blog repository and should be put into app/stylesheets/application.scss.

With the layout and these two stylesheets in place, the blog will now have a much improved look and feel! See the new style by visiting http://localhost:3000/posts.


Generating RSS Feed for Posts

Finally, before the application is deployed, let's set up RSS and Atom feeds for our new blog so people can subscribe to our posts. For the feeds, we're going to head back to the posts controller and make a few changes by appending a provides option to our index block. This command below instructs the route that it should respond to HTML, RSS and Atom formats.

# app/controllers/posts.rb
SampleBlogUpdated::App.controllers :posts do
# ...
  get :index, :provides => [:html, :rss, :atom] do
    @posts = Post.order('created_at DESC').all
    render 'posts/index'
  end
# ...
end

Note that this route also instructs the rendering engine to avoid rendering the layout when using RSS or atom formats.

Back in the index.haml file, we'll use the auto_discovery_link_tag helpers to generate the RSS feed using builder.

-# app/views/posts/index.haml
- @title = "Welcome"

- content_for :include do
  = feed_tag(:rss, url(:posts, :index, :format => :rss), :title => "RSS")
  = feed_tag(:atom, url(:posts, :index, :format => :atom), :title => "ATOM")

#posts= partial 'posts/post', :collection => @posts

Next, let us add the templates for atom using builder templates:

# app/views/posts/index.atom.builder

xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
  xml.title   "Padrino Sample Blog"
  xml.link    "rel" => "self", "href" => url_for(:posts, :index)
  xml.id      url_for(:posts, :index)
  xml.updated @posts.first.created_at.strftime "%Y-%m-%dT%H:%M:%SZ" if @posts.any?
  xml.author  { xml.name "Padrino Team" }

  @posts.each do |post|
    xml.entry do
      xml.title   post.title
      xml.link    "rel" => "alternate", "href" => url_for(:posts, :show, :id => post)
      xml.id      url_for(:posts, :show, :id => post)
      xml.updated post.created_at.strftime "%Y-%m-%dT%H:%M:%SZ"
      xml.author  {}
    end
  end
end

and also the template for rss using builder:

# app/views/posts/index.rss.builder

xml.instruct!
xml.rss "version" => "2.0", "xmlns:dc" => "http://dublincore.org/documents/dc-xml-guidelines/" do
  xml.channel do
    xml.title "Padrino Blog"
    xml.description "The fantastic padrino sample blog"
    xml.link url_for(:posts, :index)

    for post in @posts
      xml.item do
        xml.title post.title
        xml.description post.body
        xml.pubDate post.created_at
        xml.link url_for(:posts, :show, :id => post.id)
      end
    end
  end
end

Please note, that you have to add builder in your Gemfile and run bundle. Let's check out our changes. View the available feeds at http://localhost:3000/posts.atom or http://localhost:3000/posts.rss. You now have rss and atom feeds available for your blog!

last updated: 2017-05-27

comments powered by Disqus