Picombo Demo Application Development

Introduction

Hello, today we are going to create a demo application using Picombo, a Ruby MVC Web Framework.

We will make a twitter clone called Twooter, to illustrate some common usage scenarios and how to solve them easily with Picombo.

Application Setup

To begin, we will install the latest Picombo beta system files: gem install picombo

Download empty application template

It might be good for the first time through to download an empty picombo application directory. After you have made a few apps, this probably won't be necessary.

Just go to http://dev.picombo.net/projects/picombo/files and download the picombo-application.tar.gz and unpack it into your directory.

To start using Picombo immediately, simply point your ruby webserver at the rackup file included (I will use thin for my example): thin -R config.ru start

If you then open up a browser and navigate to your localhost, you should see the "hello world" which is included with Picombo.

Directory Layout

To start developing our application, we need to be familiar with the way Picombo directories are laid out:

Before writing any ruby code, we need to properly configure our application using the files in the config directory. Like all picombo directories, this directory cascades down to the system folder. This means that Picombo looks in the application folder first for any files, then down to the extension directories, then finally the system folder for defaults. The config/config.yaml file should always be located in your application/config folder.

When you open up the config.yaml file, you will see some options. The relevant options for initial set up are:

You might want to set your database config options while you are here too. Just open up config/datamapper.yaml and set the options there:

	default:
	 driver: mysql
	 host: localhost
	 database: twooter
	 username: username
	 password: password

Schema

Here's some mysql database schema for your database that we will use in this tutorial:

	CREATE TABLE IF NOT EXISTS `twoots` (
	  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
	  `user_id` bigint(20) unsigned NOT NULL,
	  `body` varchar(200) NOT NULL,
	  `date` varchar(200) NOT NULL,
	  PRIMARY KEY (`id`),
	  KEY `user_id` (`user_id`)
	) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

	CREATE TABLE IF NOT EXISTS `users` (
	  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
	  `username` varchar(50) NOT NULL,
	  `password` varchar(50) NOT NULL,
	  `email` varchar(50) NOT NULL,
	  PRIMARY KEY (`id`)
	) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

	ALTER TABLE `twoots`
	  ADD CONSTRAINT `twoots_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
	

Model Creation

We need to create model file to model these database tables we just created. We recommend and use DataMapper in this example.

Make sure you have datamapper and the mysql libraries installed:

So let's create this file: application/models/user.rb:

	module Picombo
		module Models
			class User < Picombo::Model
				storage_names[:default] = 'users'

				property :id,       Serial
				property :username, String, :unique => :u1
				property :password, String
				property :email,    String
			end
		end
	end

You can see the module and submodules that Picombo models are a part of. They must be included in model files for proper autoloading of the classes.

This will be our model for interfacing with our user data.

Now lets create our twoot model: application/models/twoot.rb:

	module Picombo
		module Models
			class Twoot < Picombo::Model
				storage_names[:default] = 'twoots'

				property :id,      Serial
				property :user_id, String
				property :body,    String
				property :date,    String
			end
		end
	end

Since we are using DataMapper, we need to include the datamapper hook to initialize the module. To do this, just add 'datamapper' to your hooks array in your config.yaml file:

	hooks:
	  [datamapper]

Creating Users

We need users to be able to create accounts to post twoots! Let's do that now. Create your first controller: application/controllers/user.rb

	module Picombo
		module Controllers
			class User < Picombo::Controllers::Template
				def initialize
					super
				end

				def create
					body = Picombo::View::Core.new('user/create')
					@@template.set('body', body.render(true))
				end
			end
		end
	end

Notice that we have the same style of modules wrapping this controller class. Controllers must be wrapped in the Controllers module in order to be autoloaded. The controller class also extends a template controller, which allows us to easily setup common website templates. This file should be application/views/template.rhtml:

	<html>
		<head>
			<title>Twooter</title>
			<%= Picombo::Html.style('css/style.css') %>
		</head>
		<body>
			<div id="page_header">
				<h1>Twooter</h1>
				<ul>
					<li><a href="<%= Picombo::Url.base %>">Home</a></li>
					<li><a href="<%= Picombo::Url.site 'user/login' %>">Login</a></li>
					<li><a href="<%= Picombo::Url.site 'user/create' %>">Create Account</a></li>
					<li><a href="<%= Picombo::Url.site 'twoot/post' %>">Create Twoot</a></li>
				</ul>
			</div>
			<div id="page_content">
				<%= @body %>
			</div>
		</body>
	</html>

Next, we create a new view object, which uses the application/views/user/create.rhtml file, which is below:

	<form method="post">
		<ul>
			<li><label for="username">Username</label> <input name="username" id="username"></li>
			<li><label for="email">Email</label> <input name="email" id="email"></li>
			<li><label for="password">Password</label> <input type="password" name="password" id="password"></li>
			<li><input type="submit" value="Create Account" /></li>
		</ul>
	</form>

This will post the results back to our create method, so lets detect that and create the user.

	def create
		if Picombo::Input.instance.post.length > 0
			user = Picombo::Models::User.new
			user.username = Picombo::Input.instance.post('username')
			user.email = Picombo::Input.instance.post('email')
			user.password = Picombo::Input.instance.post('password')
			user.save
			body = Picombo::View::Core.new('user/create_success')
		else
			body = Picombo::View::Core.new('user/create')
		end
		@@template.set('body', body.render(true))
	end

Extensions

In order to login to the site, we need an Auth system. Fortunately, there's an extension already written for Picombo to handle this for you. We can simply install this with rubygems as well:

gem install picombo-auth, then enable the extension in your config.yaml file:

	extensions:
	  [picombo-auth]

For now, we will automatically log the user in after they create the account. Just add this after you save the user:

	Picombo::Auth.instance.login(user.username, Picombo::Input.instance.post('password'))

You might also want a login method for your user controller:

	def login
		if Picombo::Input.instance.post.length > 0
			if Picombo::Auth.instance.login(Picombo::Input.instance.post('username'), Picombo::Input.instance.post('password'))
				body = 'You have logged in!'
			else
				body = 'Invalid password or unknown username'
			end

		else
			body = Picombo::View::Core.new('user/login').render(true)
		end
		@@template.set('body', body)
	end

Posting Twoots

Now that we have a user created and logged in, they can post a twoot to the main page (which we will build later).

Let's create a twoot controller with a post method in it:

	module Picombo
		module Controllers
			class Twoot < Picombo::Controllers::Template
				def initialize
					super
				end

				def post
					if Picombo::Input.instance.post.length > 0
						user = Picombo::Session.instance.get(:user)
						twoot = Picombo::Model::Twoot.new
						twoot.user_id = user.id
						twoot.body = Picombo::Input.instance.post('body')
						twoot.date = Time.new
						twoot.save

						body = Picombo::View::Core.new('twoot/post_success')
						body.set('user', user.inspect)
					else
						body = Picombo::View::Core.new('twoot/post')
					end

					@@template.set('body', body.render(true))
				end
			end
		end
	end

Now if you navigate to /twoot/post in your browser you should get a twoot creation form, after you create the view file for it, anyway :).

Listing Twoots

The last thing we need to do for this tutorial is list all the twoots in the system on the home page. We can do this easily with DataMapper and a loop in our view. Let's create the controller method first:

	def index
		twoots = Picombo::Models::Twoot.all

		body = Picombo::View::Core.new('twoot/index')
		body.set('twoots', twoots)

		@@template.set('body', body.render(true))
	end

And let's make our view too:

	<h2>All Twoots</h2>
	<ul>
		<% @twoots.each do |twoot| %><li>
			<p><%=twoot.user_id %> said:</p>
			<p><%=twoot.body%></p>
		</li>
		<% end %>
	</ul>

Cleanup

Let's perform some followup maintenance to make our app a little better.

Default Route

We can define the default route for our application so that when people go an empty URI, they get a specified page. Copy routes.yaml from the system/config/ folder and place it into your application/config folder. You will see a _default entry in it. Simply change this line to _default: twoot/index.

System Benchmarks Time
Picombo Setup 0.0014
Environment Setup 0.0004
Controller Execution 0.0136
Total Execution 0.0167