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.
To begin, we will install the latest Picombo beta system files: gem install picombo
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.
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:
site_domain - This is the domain that your website resides in. Set it to the full domain name of your application. The default is probably OK for this tutorial.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
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;
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:
gem install dm-coregem install mysqlgem install do_mysqlSo 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]
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
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
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 :).
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>
Let's perform some followup maintenance to make our app a little better.
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.