Merb blog tutorial

I started using merb a few days ago. After searching through the net, I couldn't find any working blog example for merb 1.0.6.1 (that also uses datamapper) that I'm using. Yes, I know that the new documentation is underway, but I'd rather get this out quickly. (And, yes, I know that merb 2 = rails 3.) This tutorial just tries to be an shorter updated version of this great tutorial "merb + datamapper + noob: quick start" (from a slightly different viewpoint). It also assumes that you have some knowledge of rails. First you have to install merb. The easiest way is to just
$ sudo gem install merb
Because we'll use datamapper and sqlite3:
$ sudo gem install datamapper merb_datamapper do_sqlite3
Start by generating merb application:
$ merb-gen app blog
Using this merb-gen app command generates an empty merb application that uses datamapper. In blog/, you'll find a familiar directory structure. As in rails, configuration files are in config/. The ones that you want to look at are dependencies.rb (that basically lists all the components you're using), init.rb (that specifies your choice of ORM, test framework, and template framework), and database.yml (that, as in rails, provides database config). Right now, everything merb generates for us looks fine, so we'll stick with it.

Creating the model

It's time to create a model. Issue the command (inside blog/),
$ merb-gen model post
to create model Post. It'll be in app/models/post.rb. In rails, you'll only have to write migration and model specification (e.g., all the columns) will be automatically read from the database. With DataMapper, we have to specify the model's properties inside its class definition. Out Post model looks like this:
class Post
  include DataMapper::Resource
  
  property :id, Serial
  property :title, String
  property :body, String
end
After that, we have to work with the database to create the table for it. For simple property changes (e.g., adding a new model or adding properties), you don't have to write any migration as merb is clever enough to do that for you: just call
$ rake db:autoupgrade
There's another option for migrating table structures, rake db:automigrate, but this rake task also deletes all your old data (it is destructive), so I prefer db:autoupgrade. Merb has an interactive console that we can play will models, so let's try to add some post to our blog before moving on.
$ merb -i
irb(main):001:0> p = Post.new :title => 'Hello!', :body => 'This is my first post'
=> #<Post id=nil title="Hello!" body="This is my first post">
irb(main):002:0> p.save
 ~ INSERT INTO "posts" ("title", "body") VALUES ('Hello!', 'This is my first post')
=> true
irb(main):003:0> p2 = Post.create :title => 'Second post', :body => 'what??'
 ~ INSERT INTO "posts" ("title", "body") VALUES ('Second post', 'what??')
=> #<Post id=2 title="Second post" body="what??">

Creating the controller

Okay, we have something to display now. Let's create a controller. Call
$ merb-gen controller posts
This command generates app/controllers/posts.rb. Note that there's no Controller suffix to the name. Let's add something to method index.
class Posts < Application
  def index
    @posts = Post.all
    render
  end  
end
In merb, you have to call render explicitly. This would render a default view for this action. Another point to note is that instead of using Post.find(:all) with Active Record, in Datamapper you only have to call Post.all. Let's create a view for this action. We can edit views/posts/index.html.erb like this:
<h1>My posts</h1>
<% for post in @posts %>
  <h2><%= post.title %></h2>
  <p><%= post.body %></p>
<% end %>
It's time to see the posts! Run
$ merb
then point your browser to http://localhost:4000/posts or http://127.0.0.1:4000/posts.

New posts

Let's create a new post! We'll add a link "add new post" at the bottom of the index page.
<%= link_to 'add new post', url(:controller => :posts, :action => :new) %>
Method url, that returns a url for an action, can be really fancy, but for now we go with a simple rails-like version. Then create methods new in Posts controller that render the view.
class Posts < Application
  # ...
  def new
    @post = Post.new
    render
  end
end
Also, the view new.html.erb in app/views/posts:
<h1>Creating new post</h1>
<%= form_for @post, :action => 'create' do %>
  <%= text_field :title, :label => 'Title' %><br/>
  <%= text_area :body, :label => 'Body' %><br/>
  <%= submit 'New post' %>
<% end =%>
Note that we use form_for and methods text_field and text_area know that it is inside this form automatically. (In rails you'd need the block parameter.) Finally, we add method create to Posts controller:
class Posts < Application
  # ...
  def create
    @post = Post.new params[:post]
    @post.save
    redirect url(:action => 'index')
  end
end
Try it to see if you can create new posts.

Comments

What kind of blogs without comments? So we'll create the comment model. Call
$ merb-gen model comment
and edit app/models/comment.rb:
class Comment
  include DataMapper::Resource
  
  property :id, Serial
  property :body, String
end
We have to tell a Post that it has many comments. We'll do this by adding something like has_many in Active Record to the model:
class Post
  # ...
  has n, :comments
end
This is how you specify one-to-many association in Datamapper. If you want one, just use has 1, :comment. Now let's turn to view and controller. Edit app/views/posts/index.html.erb by adding comment listing and new comment form.
<h1>My posts</h1>
<% for post in @posts %>
  <h2><%= post.title %></h2>
  <p><%= post.body %></p>
  <ul>
    <% post.comments.each do |comment| %>
      <li><%= comment.body %></li>
    <% end %>
    <li>
      <%= form_for Comment.new, :action => url(:controller => 'posts', 
                                               :action => 'create_comment', 
                                               :id => post.id) do %>
        <%= text_field :body %>
        <%= submit 'New comment' %>
      <% end =%>
    </li>
  </ul>
<% end %>

<%= link_to 'add new post', url(:controller => :posts, :action => :new) %>
I believe that with merb router, we can write a better the action url. I'll update this entry after I find out how. Also, note that to get the list display correctly, I have to delete the merb stylesheet link line in app/views/layout/application.html.erb because it hides my list items. To handle this action, we create method create_comment in Posts controller.
class Posts < Application
  # ...
  def create_comment
    post = Post[params[:id]]
    comment = Comment.new params[:comment]
    post.comments << comment
    post.save
    redirect url(:action => 'index')
  end
end
Note the way we find a post by id (Post[params[:id]]). Hope this blog post help getting you started with merb and Datamapper. Any comments are welcome! ฉบับภาษาไทย

Popular posts from this blog

กฎแห่งการมีจำนวนมาก (1)

ทฤษฎีบทขีดจำกัดกลาง (1)

ชื่อซ้ำ ชื่อต่าง