Shoes Meets Merb: Driving a GUI App through Web Services in Rubyby Gregory Brown and Brad Ediger
Ruby on Rails has helped launch the Ruby programming language into stardom, and for good reason. Rails opened many eyes to the power of Ruby and made web programming that much easier. But one of the unfortunate aspects of Rails is that it tends to color Ruby as a language primarily for database-backed web applications. Some software just doesn't work well in that mold. Additionally, the extreme popularity of Rails has left some Rubyists in the corner wondering what happened to the other great software written in their language. It hasn't gone away; on the contrary, there are a tremendous number of open-source Ruby projects under development. We are going to look at two of them here.
The Merb web framework, written by Ezra Zygmuntowicz, was first popularized as a lightweight way to handle file uploads for Rails applications. It has since grown to become an excellent framework in its own right for creating web applications. It is simpler and seems to be faster than Rails, and it is more flexible in some ways. While Rails is deliberately "opinionated software," Merb acknowledges that there are different options for object-relational mapping systems and web template engines, and does not try to pick one over the other.
If Merb is a paragon of professionalism and class, Shoes is a monkey on LSD. Shoes, by why the lucky stiff, is an incredibly compact cross-platform GUI toolkit for Ruby, but it looks nothing like the other cross-platform toolkits out there. For one thing, it is lightweight. Shoes lets you build GUIs in Ruby whose code actually looks like Ruby, not XML or Java. Shoes is under heavy development right now, but it will eventually form the basis for the new Hackety Hack, _why's programming environment for kids.
So, what are a web framework and a GUI framework doing together, you might ask? We are going to build a pastebin as a repository for our own code snippets and pieces of text we want to save. We'll build a GUI frontend using Shoes, and connect it to a Merb backend that will handle the database. We could just as easily slap on a web interface to the Merb application as well, but we will use the Shoes GUI to demonstrate the ease with which we can connect the two components using Ruby. In fact, the basic proof of concept took the two of us about an hour to get working, and it took another hour to finish.
Without further ado, we present our pastebin application, using Shoes and Merb, Shmerboes.
Creating a Simple YAML-Based Web Service with Merb
Though a lot can be said about Merb being a potential Rails-killer, we're not going to attempt to be so dramatic here. Instead, we'll let Merb speak for itself as we create the server side component of Shmerboes.
Configuring Our Merb Application
We'll start by creating our application skeleton and putting together a simple model that has the bare minimum fields we'll need.
$ merb merb_paste create create app/controllers create app/models create app/helpers # ... create script/stop_merb create script/generate create script/destroy
Before we can generate our models, we need to specify the ORM we wish to use. For familiarity and simplicity, we're going to use ActiveRecord, so we need to uncomment the following line in
If you don't already have it installed, you'll need the Merb ActiveRecord adapter, which you can grab via:
gem install merb_activerecord -y
You may have noticed when you looked in the dependencies file that Merb offers you some other ORM choices, in fact, Sequel and DataMapper are supported out of the box, so long as you install the appropriate adapters. If you're tired of the "Railsy" ORM feel, you might give one of these a try, but for now we'll stick with the conventional approach.
Once we have our ORM specified, we can set up our database configuration. By running the
merb executable, a
database.sample.yml file will be created. This serves as a template for your for your
database.yml configuration. To keep things nice and easy configuration-wise, we skip over the default MySQL configuration and use SQLite:
--- :development: &defaults :adapter: sqlite3 :database: db/merb_paste_development.sqlite3 :test: <<: *defaults :database: db/merb_paste_test.sqlite3 :production: <<: *defaults :database: db/merb_paste_production.sqlite3
The YAML here is actually pretty tidy, using the settings for the development environment as defaults and just overriding as needed for the other environments.
Because Merb doesn't autogenerate a
db folder for us, we'll need to create one manually:
$ mkdir db
A Simple Paste Model
Once we have all our configuration stuff set up, we can generate our model files:
$ script/generate model paste ** Ruby version is not up-to-date; loading cgi_multipart_eof_fix Started merb_init.rb ... Connecting to database... Mon, 07 Jan 2008 01:45:20 GMT: loading gem 'merb_activerecord' from config/dependencies.rb:16 ... Loading Application... Compiling routes.. Loaded DEVELOPMENT Environment... exists app/models create app/models/paste.rb dependency merb_model_test exists spec/models create spec/models/paste_spec.rb dependency migration create schema/migrations create schema/migrations/001_add_model_pastes.rb
You'll notice that, like Rails, Merb generates test files for your models. However, the defaults are a bit different. Merb will use RSpec by default instead of Test::Unit and it also does not automatically generate fixtures. You can of course tweak these defaults if you'd like, but avoiding fixtures and using RSpec are two things that'd generally make a lot of Rubyists happy, so they tend to be decent defaults. At any rate, we won't be focusing on testing in this article, so we leave further tweaking with this as an exercise to the reader.
Now that our
Paste model is generated, we need to populate our migration with the appropriate field definitions. As you can see above, this file was generated for us as
schema/migrations/001_add_model_pastes.rb. Below is the migration we're actually using:
class AddModelPastes < ActiveRecord::Migration def self.up create_table :pastes do |t| t.string :title t.string :text t.timestamps end end def self.down drop_table :pastes end end
Luckily, pastebins are incredibly simple at their core, and our migration reflects this. We can now run the migration and check to see that everything is wired up correctly.
$ rake db:migrate (in /Users/sandal/merb_paste) ** Ruby version is not up-to-date; loading cgi_multipart_eof_fix Connecting to database... Mon, 07 Jan 2008 02:09:30 GMT: loading gem 'merb_activerecord' from config/dependencies.rb:16 ... == 1 AddModelPastes: migrating ================================================ -- create_table(:pastes) -> 0.0027s == 1 AddModelPastes: migrated (0.0030s) =======================================
With the migrations running fine, we'll just do a quick sanity check in the Merb console:
$ merb -i ** Ruby version is not up-to-date; loading cgi_multipart_eof_fix # startup info truncated >> Paste => Paste(id: integer, title: string, text: string, created_at: datetime, updated_at: datetime)
Great! This tells us we can access our model and that it is wired up to the database. We can now begin on the real work, which is implementing a controller that exposes a simple web API that our shoes client will interact with.