Wednesday, September 8, 2010

Ramaze and Mongo(DB and mapper): Day 1

Edit ** I forgot to mention here how you would write a configuration file but you can check out my next post which mentions a small config for mongoDB and also for Sequel.

Okay. So day 1. I've chosen to use MongoMapper for now over the other two I've provided last post. MongoMapper is quite good, I haven't read/delved into their code but he said it was readable so looking forward to it. I just hope they don't make MongoMapper too rails-specific.

Okay, so moving on. I've started with the basics of most apps that is "Inserting data" on a "Collection". And having a "One to Many" relationship. This is just a basic app where you could post and comment to demonstrate. I assume here that you already have working applications and just want to start out using MongoDB.

The model would look like this.
Message Model
class Message
  include MongoMapper::Document

  key :name, String, :require => true
  key :body, String, :require => true
  timestamps! # This would create a "created_at" and "updated_at" keys on create

  many :comments
end

Take note that I have defined keys in the model. The keys can have typecast but it doesn't require you to have one. Also, you need not to define every key in the model as documents are schema-less, that's also one of the reasons of using MongoDB (unkown unknowns).

So for example you have a key :blah.
Test Model
class Test
  include MongoMapper::Document

  key :blah
end
Test.create :blah => 42
# => { "_id" : ObjectId("..."), "blah" : 42 }

Test.create :blah => "the answer"
# => { "_id" : ObjectId("..."), "blah" : "the answer" }

Test.create :blah => 42, :answer => "ultimate question"
# => { "_id" : ObjectId("..."), "blah" : 42, "answer" => "ultimate question" }

Okay. Going back to inserting records (but I think I've shown that already in the test). I only have basic information for now but here is a snippet of posting.
  def post
    if request.post?
      Message.create :name => request[:name], :body => request[:body]
      redirect r(:/)
    end
  end

Upon posting a message here, the data would look something like this.
{ "_id" : ObjectId("..."), "name" : "Post 2", "created_at" : "Tue Sep 07 2010 18:31:45 GMT 0800 (PHT)", "body" : "Test Body!", "updated_at" : "Tue Sep 07 2010 18:31:45 GMT 0800 (PHT)" }
Now I'd like to step on to relations, embedded documents.
Did you notice the last line before ending the MessageModel? It is a many relationship to comments. A comment model would look like this:
Comment Model
class Comment
  include MongoMapper::EmbeddedDocument

  key :name, String, :require => true
  key :body, String, :require => true
end

Note on the line 2 EmbeddedDocument would embed this document on the Message document. To try it out:
Per post snippet
  def posts(id = nil)
    id = id || request[:id]
    redirect r(:/) unless @msg = Message.find(id)

    if request.post?
      @msg.comments << Comment.new(:name => request[:name], :body => request[:body])
      @msg.save
    end
  end

This would produce a record like this
{ "_id" : ObjectId("4c8618b6be9294085d000001"), "name" : "Post 3", "created_at" : "Tue Sep 07 2010 18:49:26 GMT 0800 (PHT)", "comments" : [
    {
        "name" : "Tester",
        "body" : "Comment Yah!",
        "_id" : ObjectId("4c862781be929409d100000a")
    }
], "body" : "Another test body!", "updated_at" : "Tue Sep 07 2010 19:52:33 GMT 0800 (PHT)" }

Then when you pull the data from the "Per post snippet". You could iterate on the comments like so:
  <% @msg.comments.each do |c| %>
    <%= c.name %><br />
    <%= c.body %><br />
  <% end %>

This app and idea is far from perfect as it would still require validations and some form of filter to remove scripts but you get the gist of it. Also, I didn't include the views as I haven't written them completely as well, they are just basic forms with inputs and textareas and iterations for display but I think you all get what they would look like. If you need to see it, post a comment and I'll email it to you.

I don't know if I have written it awesome for now but I would like to hear how others are doing with Ramaze with MongoDB and MongoMapper.

Also, here is a complete code of the controller:
class MessagesController < Ramaze::Controller
  map '/'

  def index
    @msgs = Message.all
  end

  def post
    if request.post?
      Message.create :name => request[:name], :body => request[:body]
      redirect r(:/)
    end
  end

  def posts(id = nil)
    id = id || request[:id]
    redirect r(:/) unless @msg = Message.find(id)

    if request.post?
      @msg.comments << Comment.new(:name => request[:name], :body => request[:body])
      @msg.save
    end
  end
end

No comments:

Post a Comment