Playing around with Merb
(An italian translation can be found here)
I’m curious. I’ve always been curious in my life, so now I’d like to play a little with Merb, a project by Ezra Zygmuntowicz of EngineYard.
After all I’m starting to think that Rails (to be clear: more and more I find it really nice and impressive under different standpoints) is a little over-hyped. It’s a nice framework, but sometimes I find it a little “fat”: a lot of extension, gem, module are preloaded.
So I’ve “met” Merb: claimed as “lightweight” I was curious to play with it a little. One of he most nice thing I have learnt was the “other ORM: DataMapper. It has a really clean and short syntax, though it seems to be enough compatible with AtiveRecord showing a lot of method names in common.
Other thing I liked a lot to find in Merb was the RSpec approach to testing, though “classical” Test::Unit is still present.
Installing all the required components
Ok, first of all you’ve to install the required gems:
$ sudo gem install merb
$ sudo gem install mongrel json json_pure erubis mime-types rspec
hpricot mocha rubigen haml markaby mailfactory Ruby2Ruby
Merb can use the well know ActiveRecord ORM (like Rails) to manage object persistence, but now I’d like to show you an interesting alternative: DataMapper. So install it:
$ sudo gem install datamapper merb_datamapper merb_helpers
And now we need a specific driver: do_mysql, do_sqlite3, do_postgres. I’d like to use mysql, so:
$ sudo gem install do_mysql
..and a little problem appear, but don’t worry: let fix it!
Reach the do_mysql gem directory (on my system is: /usr/local/lib/ruby/gems/1.8/gems/do\_mysql-0.2.3/), open the file ext/mysql\_c.i and change line #41 to include the correct header file:
%include "/usr/local/mysql-5.0.41-osx10.4-i686/include/mysql.h"
Then edit the file ext/mysql\_c.c and comment the line #16079:
/* rb\_define\_const(mMysql\_c, "MYSQL\_OPT\_SSL\_VERIFY\_SERVER\_CERT",
SWIG\_From\_int((int)(MYSQL\_OPT\_SSL\_VERIFY\_SERVER\_CERT))); */
Now we’re ready to recompile and reinstall the gem package:
$ sudo make clean
$ cd ..
$ rake repackage
$ cd pkg
$ sudo gem install ./do_mysql-0.2.3.gem
Environment setup
After installed all the required components (gem) the first step is to create a brand new application:
$ merb blog
As you can see merb command create the structure of our blog application, and if you are a rails developer there isn’t nothing to worry too much about here: skeleton and directory are almost the same, with little variations.
Then we need some databases: for development environment, for testing and (hopely) for production. So let’s create them (here I’m using MySQL but it’s not mandatory):
$ echo "create database blog\_dev" | mysql -u root
$ echo "create database blog\_test" | mysql -u root
$ echo "create database blog\_prod" | mysql -u root
Now the real dive into Merb begin: configuration!
In the file config/dependencies.rb (approximatively around line 13) we need to uncomment the following line in order to enable the +DataMapper+ ORM usage:
use_orm :datamapper
The smart reader have noticed (didn’t you?) the absence of a database configuration file, in fact it still doesn’t exists. Lets create it issuing a:
$ rake -T
No database.yml file found in ~/blog/config.
A sample file was created called database.sample.yml for you to copy and edit.
After having enabled the usage of an ORM, Merb noticed the absence of such file and created a stub for us in config/database.sample.yaml.
We have to rename it in config/database.yml and fill in the correct values:
# This is a sample database file for the DataMapper ORM
:development: &defaults
:adapter: mysql
:database: blog_dev
:username: root
:password:
:host: localhost
:test:
<<: *defaults
:database: blog_test
:production:
<<: *defaults
:database: blog_prod
Now issuing again a rake -T command we obtain the task list:
rake aok # Run all tests, specs and finish with rcov
rake clobber_rcov # Remove rcov products for rcov
rake controller_specs # Run all controller specs
rake dm:db:automigrate # Perform automigration
rake dm:sessions:clear # Clears sessions
rake dm:sessions:create # Creates session migration
rake haml:compile_sass # Compiles all sass files into CSS
rake merb:freeze # freeze the merb framework into merb for portab...
rake merb:freeze_from_svn # freeze the merb framework from svn, use REVISI...
rake merb:unfreeze # unfreeze this app from the framework and use s...
rake merb_env # Setup the Merb Environment by requiring merb a...
rake merb_init # load merb_init.rb
rake model_specs # Run all model specs
rake rcov # RCov
rake spec # Run a specific spec with TASK=xxxx
rake specs # Run all specs
rake specs_html # Run all specs output html
rake svn_add # Add new files to subversion
rake test # Run tests for test
rake test_functional # Run tests for test_functional
rake test_unit # Run tests for test_unit
Ok, now we are ready to start building our blog application.
Model creation
Every blog has a series of post, so the first model we need is a “post” model. As in rails we have generators here:
$ script/generate model Post title:string body:text created_at:datetime
Connecting to database...
exists app/models
create app/models/post.rb
dependency merb_model_test
exists spec/models
create spec/models/post_spec.rb
Without any surprise the file app/models/post.rb was generated by merb:
class Post < DataMapper::Base
property :title, :string
property :body, :text
property :created_at, :datetime
end
The first thing to note is that DataMapper describe model in unique file, that is: you don’t need to jump your eyes from a migration file to a model file to understand (or simply remember) all the fields and validations and associations you made on your models.
I really like this approach: it’s really DRY.
So we’ve defined three properties (fields) in the ruby class, now we’ve to tell merb to generate the corresponding table in databases:
$ rake dm:db:automigrate
Connecting to database...
Started merb_init.rb ...
Loading Application...
Compiling routes..
Loaded DEVELOPMENT Environment...
$ echo "desc posts" | mysql -u root --database blog_dev -t
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(50) | YES | | NULL | |
| body | text | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
+------------+-------------+------+-----+---------+----------------+
Umm… DataMapper created a primary key column for us, and inflected correctly the table name (posts). Let’s play a little with it starting an interactive merb session (console in rails world):
# hit that command in blog directory, not elsewhere!
$ merb -i
Started merb_init.rb ...
Connecting to database...
Loading Application...
Compiling routes..
Loaded DEVELOPMENT Environment...
Not Using Sessions
irb(main):002:0> p = Post.new
=> #<post @new_record="true," @title="nil," @created_at="nil," @body="nil," @id="nil"%gt;
irb(main):003:0> p.comments << Comment.new
=> [#<comment @new_record="true," @content="nil," @post_id="nil,"@created_at="nil," @id="nil">]
irb(main):004:0> p.comments.first.content = 'really nice your post!'
=> "really nice your post!"
irb(main):005:0> p.valid?
=> false
irb(main):006:0> p.title = 'my first post (again)'
=> "my first post (again)"
irb(main):007:0> p.body = '....'
=> "...."
irb(main):008:0> p.valid?
=> true
irb(main):009:0> p.save
=> true
irb(main):010:0> Post.count
=> 1
irb(main):011:0> Comment.count
=> 1
irb(main):012:0>
As you can see everything is usual.
Ok, now we need a comment to attach on post (otherwise what kind of blog it will be?). So generate another model:
$ script/generate model Comment
Connecting to database...
exists app/models
create app/models/comment.rb
dependency merb_model_test
exists spec/models
create spec/models/comment_spec.rb
and see the content of the generated app/model/comment.rb file:
class Comment < DataMapper::Base
end
As we have not declared any fields, the model is empty. It’s up to us now to insert appropriate property declarations:
class Comment < DataMapper::Base
property :content, :text
property :created_at, :datetime
belongs_to :post
validates_presence_of :content
end
Also we need to say that a post has many comments. No migrations involved (for now): simply modify the post model file (app/model/post.rb):
class Post < DataMapper::Base
property :title, :string
property :body, :text
property :created_at, :datetime
has_many :comments
validates_presence_of :title
validates_presence_of :body
end
and try to issue again the “automigrate” task:
$ rake dm:db:automigrate
and see directly what happened in data base:
$ echo "show tables" | mysql -u root --database blog_dev -t
+------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| content | text | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| post_id | int(11) | YES | | NULL | |
+------------+----------+------+-----+---------+----------------+
And play directly with the interactive session:
$ merb -i
irb(main):001:0> Post.all
=> []
As we can notice each time we issue the rake “dm:db:automigrate” task it erases the database content. That is normal because, actually, DataMapper doesn’t use migrations: it simply destroy and recreate the tables structure.
Let’s continue to play:
irb(main):002:0> p = Post.new
=> #
<post @new_record="true," @title="nil," @created_at="nil," @body="nil," @id="nil">
irb(main):003:0> p.comments < < Comment.new
=> [#<comment @new_record="true," @content="nil," @post_id="nil," @created_at="nil," @id="nil">]
irb(main):004:0> p.comments.first.content = 'really nice your post!'
=> "really nice your post!"
irb(main):005:0> p.valid?
=> false
irb(main):006:0> p.title = 'my first post (again)'
=> "my first post (again)"
irb(main):007:0> p.body = '....'
=> "...."
irb(main):008:0> p.valid?
=> true
irb(main):009:0> p.save
=> true
irb(main):010:0> Post.count
=> 1
irb(main):011:0> Comment.count
=> 1
irb(main):012:0>
The only thing to note is DataMapper has (little) different methods from ActiveRecord to fetch things (method all rather than find(:all)), more in this on a future post ;-).
Controllers
Now it’s time to add some “logic” in our application, lets generate a controller:
$ script/generate controller posts
Connecting to database...
exists app/controllers
create app/controllers/posts.rb
create app/views/posts
create app/views/posts/index.html.erb
exists app/helpers/
create app/helpers/posts_helper.rb
dependency merb_controller_test
exists spec/controllers
create spec/controllers/posts_spec.rb
create spec/views/posts
create spec/views/posts/index_html_spec.rb
create spec/helpers
create spec/helpers/posts_helper_spec.rb
that generated the file app/controllers/posts.rb
class Posts < Application
def index
render
end
end
Despite what rails do in controllers, we have to explicitly call the render method.
Well, we want that our index action shows all post, in termporal reverse order:
class Posts < Application
def index
@posts = Post.all(:order => 'created_at DESC')
render
end
end
Views
It’s time to create and fill some views in our application, we find them in app/views/posts/ directory. Let’s modify the stub created in index.html.erb:
<h1>All posts</h1>
<% for post in @posts %>
<p class="post">
<h2> <%= post.title %> </h2>
<span class="time" > <%= post.created_at %> </span>
<p class="body">
<%= post.body %>
<% end %>
<%= link_to 'add new post', "posts/new" %>
If we’d like to spice up the views through CSS the file is public/stylesheets/master.css (but maybe is better to add another one). As you can see merb provides us a layout for the application (app/views/layout/application.html.erb).
Finally it’s time to make our eyes happy admiring what we’ve built, lets start the merb server and point our browser to http://localhost:4000.

Managing forms and routes
Ok, now let’s add a methods and a view to insert a new post.
In controller we put:
class Posts < Application
def index
@posts = Post.all(:order => 'created_at DESC')
render
end
def new
@post = Post.new
render
end
end
And the fresh new view (app/views/posts/new.html.erb):
<h1>New post</h1>
<%= error_messages_for @post %>
<% form_for @post, :action => url(:post) do %>
<%= text_control :title, :label => 'Title' %>
<%= text_area_control :body, :label => 'Body' %>
<%= submit_button 'save' %>
<% end %>
Now pointing our browser on http://localhost:4000/posts/new we obtain:

Ouch! What happen? Simple answer: we used some helper in recently added view without tell merb to load it before. In fact merb aims to be as thin as possible, so every piece of functionality we need have to be explicitly included. Open and modify the file app/config/dependencies.rb adding the following line:
dependency 'merb_helpers'
The clever reader probably noticed that we used the mehod url(:post) that is supposed to build a url given some model….or resource. In fact Merb is restful too, we simply have to declare which resources we want in the file config/router.rb:
puts "Compiling routes.."
Merb::Router.prepare do |r|
r.resources :posts
r.default_routes
end
(notice that the order matters).
Now restart the merb server and reload the previous (error) page:

If we try to submit the form, that points to /posts/ action (so post controller), we receive an error because the method create doesn’t exist: it’s the REST tongue!
So add some methods to post controller:
def create
@post = Post.new(params[:post])
if @post.save
# it's a GET request on post resource (=> show action)
redirect url(:post, @post)
else
render :action =>'new'
end
end
def show
@post = Post.find(params[:id])
render
end
when we are simply say “after a successful creation, show the content of new post”.
The last piece we need is the “show view” (appt/views/posts/show.html.erb):
<h1>Post</h1>
<p class="post">
<h2> <%= @post.title %></h2>
<span class="time"> <%= @post.created_at %> </span>
<p class="body">
<%= @post.body %>
<%= link_to 'full post list', url(:posts) %>
and enjoy the result:

I leave to readers the joy to finish inserting all other methods to implement the REST verbs (that is: index, show, new, create, edit, update, destroy). But before terminating this introductory article, let me show you how use partial in Merb.
Every partial is stored in a file with leading underscore, app/view/posts/\_comment.html.erb is the partial for comment:
<span class="time"> <%= comment.created_at %> </span>
<%= comment.content %>
and we can use it in other views with:
<%= partial :comment, :with => @a_comment %>
So, what to say now..? Enjoy with merb! :)
