My First Blog

Learn how to create a very simple blog using Merb, DataMapper, haml.

Generating the app

From the console, type:

$ merb-gen app my_first_merblog --template-engine=haml

and then:

$ cd my_first_merblog

NOTE: Would you prefer to use erb then just omitt the –template-engine=haml flag.

Time to REST

What's a blog without posts and comments, right, so let's create those the RESTful way.

From the console, type:

$ merb-gen resource post title:string,body:string
$ merb-gen resource comment post_id:integer,name:string,body:string

Now open the project in your texteditor of choice and edit the file, config/router.rb

Change it from this:

... snipp ...
Merb::Router.prepare do
  resources :comments
  resources :posts
 
  ... snipp ...
end

To this:

... snipp ...
Merb::Router.prepare do
  resources :posts do |post|
    post.resources :comments
  end
 
  ... snipp ...
end

Setting up the relationships

In app/models/post.rb add the folowing:

class Post
  ... snipp ...
  has n, :comments
end

and in app/models/comment.rb add:

class Comment
  ... snipp ...
 
  belongs_to :post
end

Make it pretty

''app/views/posts''

Edit new.html.haml to look like this:

= error_messages_for :post
 
= form_for(@post, :action => url(:posts) ) do
  = partial :form
  %p= submit "Create"
 
= link_to 'Back', url(:posts)

Edit edit.html.haml to look like this:

= error_messages_for :post
 
= form_for(@post, :action => url(:post, @post)) do
  = partial :form
  %p= submit "Update"
 
= link_to 'Show', url(:post, @post)
|
= link_to 'Back', url(:post)

Since our new and edit templates contains similar logic we've extracted it to a partial called form in order to keep things nice and DRY, in order for this to work we need to create a file called _form.html.haml to hold the repeated code and edit it to look like this:

%p= text_field :title
%p= text_area  :body

Now edit index.html.haml to look like this:

%table
  %tr
  - for post in @posts
    %td= post.title
    %tr
      %td= link_to 'Show', url(:post, post)
      %td= link_to 'Edit', url(:edit_post,  post)
 
= link_to 'New', url(:new_post)

Finally let's edit the show page, show.html.haml to look like the following:

%p= @post.title
%p= @post.body
 
%br/
%hr/
%h1 Comments
%p= partial "comments/show", :with => @post.comments.reverse, :as => :comment
 
%hr/
%p= partial("comments/comment")
 
= link_to 'Edit', url(:edit_post,  @post)
|
= link_to 'Back', url(:posts)

''app/views/comments''

Our posts show page is dependent upon two partials in order to create and display comments so let's create those now. First create a file called _show.html.haml and edit it to look like this:

%p
  %i= comment.name
%p= comment.body

NOTE: We don't have to do any kind of looping or anything here since we call the partial with a collection of posts from the posts/show template (:with ⇒ @post.comments.reverse, :as ⇒ :comment)

Next let's create the partial for creating the comment, so create a file called _comment.html.haml and edit it to look like this:

%p= error_messages_for :comment
 
= form_for(:comment, :action => "/posts/#{@post.id}/comments", :method => :post) do
  %p= text_field :name, :name => "comment[name]"
  %p= text_area  :body, :name => "comment[body]"
  %p= submit "Comment"

NOTE: For some reason, when using nested routes, we need to include the :name ⇒ model[field_name] option on text_field helpers or it won't get passed along to the controller, but text_area helpers works fine without it.

UPDATE: As of 1.0.15 it appears the :name option is required on text_area as well, otherwise the parameter is named nil_class[body] and is ignored. Also note that in this example, :name, :name is confusing. The generic form is form_widget :key, :name ⇒ model[key]. The :name ⇒ parameter overrides the name of the POST variable.

One last thing

We need to make some minor modifications to our comments controller's create action in order for this to work, so lets edit it to look like this:

  def create(comment)
    @post = Post.get(params[:post_id])
    @comment = Comment.new(comment.merge(:post => @post))
    if @comment.save
      redirect resource(@post), :message => {:notice => "Comment was successfully created"}
    else
      message[:error] = "Comment failed to be created"
      render :new
    end
  end

Let's blog about it

From inside the directory where you app lives, type the following in the console:

$ rake db:automigrate

and then, start up the merb server:

$ merb

Now open up your web browser of choice and point it to http://localhost:4000/posts and start blogging…

 
my_first_blog.txt · Last modified: 2009/12/31 11:34 by jbarciauskas