• If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • Social distancing? Try a better way to work remotely on your online files. Dokkio, a new product from PBworks, can help your team find, organize, and collaborate on your Drive, Gmail, Dropbox, Box, and Slack files. Sign up for free.

View
 

Todos 06-Building with Merb

Page history last edited by Jim Tobin 10 years, 6 months ago

About Merb

 

Merb is a lightweight MVC-style Ruby framework for building web applications. Merb was originally conceived as an alternative to Ruby on Rails and improves on many aspects of Rails 2.0. As of December 23, 2008, the Merb project has begun to be merged with Rails, and a version with the best features of both will be released in late 2009 under the name Rails 3.0.

 

Merb is considerably more efficient (and thus faster) than Rails and is well-suited to serving a RESTful web service such as the one we will need for this tutorial.

 

Before You Get Started

 

You need to have Merb version 1.0.8 or newer installed. You can install it by running:

 

$ sudo gem install merb

 

If you already have it installed, you can update Merb by running

 

$ sudo gem update merb

 

This tutorial assumes that you will use Merb’s default database (sqlite3) and database ORM (DataMapper). Using the defaults eliminates the need for any configuration.

Sqlite3 comes preinstalled on OSX and many flavors of Unix. You can check whether it’s installed by typing sqlite3 on the command line, which will bring you to a sqlite3 prompt if it is installed.

 

Make sure you have installed the database driver that allows DataMapper to connect to sqlite3:

 

$ sudo gem install do_sqlite3

 

Create your Merb App

 

On the command line:

 

$ merb-gen app todos

 

This will set up a basic Merb app. You will need to keep your Merb server running in addition to your sc-server instance, so it’s best to open a new terminal window and change to the directory of the new Merb app you just created and run:

 

$ merb

 

If everything goes as planned, you should now be able to visit http://localhost:4000 to see your app running.

 

Setting Up the Database

 

If you are using Merb’s default database (sqlite3) and database ORM (DataMapper) there is no need for any configuration.

Merb’s generator has already set up a database.yml file which defaults to SQLite and a database named sample_development.db.

 

Creating a Merb Resource

 

Merb has a command, merb-gen resource, which creates a model, controller, and views all at once. We are building a web service for our SproutCore app so we don’t need views. Consequently, instead of using the merb-gen resource command, we’ll create a model and controller separately.

 

Create Your Model

 

Next, we need to create our model objects. Models are easy to create in DataMapper, very much like ActiveRecord in Rails (if you are familiar with that), except that you can describe your schema directly in your classes.

 

Create a new Task model using the generator command:

 

$ merb-gen model task

 

Inside of this model we need to define the properties we want to store for the task. (Note how this will be the same as we have in the client, but in a more complex app it often won’t be. Your client and server are isolated; that’s why we design it like this…)

 

In app/models/task.rb add:

 

class Task   

  include DataMapper::Resource    

    property  :id,            Serial   

    property  :description,   String,     :nullable => false   

    property  :is_done,       Boolean,    :default => false    

    property  :order,         Integer  

end 

 

You now have your basic model setup. Let’s go ahead and get the DB configured. DataMapper has an auto-configuration system that will reset your database with the proper schema. Go back to the command line and run:

 

$ rake db:automigrate

 

Your database should now be configured. You can try it out. On the command line type:

 

$ merb -i

 

To get the interactive console in Merb.

 

NOTE: If you get an error that refers to “uninitialized constant Nokogiri,” you’ll need to update the “webrat” gem (sudo gem update webrat)

 

Now create a new task:

 

>> t = Task.new :description => "My Task", :order => 1  

=> #<Task description="My Task" is_done=false order=1 id=1>  

>> t.save  

=> true

 

Your task should now be added to the DB. Try to find it again:

 

>> Task.first :description => "My Task"  

=> #<Task description="My Task" is_done=false order=1 id=1> 

 

OK, your model is all set. Exit the shell so you can continue. Let’s get the controller written.

 

>> exit

 

Create Your Controller

 

Next, you need a tasks controller. This controller will actually respond to requests from your client.  Create a new Task model using the generator command:

 

$ merb-gen controller tasks

 

Open app/controllers/tasks.rb it will have the following content:

 

class Tasks < Application      

  def 

    index "Hello World"   

  end

end

 

For this controller to receive requests, you will need to register it as a resource in the router. Open config/router.rb and add:

 

Merb::Router.prepare 

do    

  resources :tasks 

end

 

Let’s see if that did the trick. Kill your Merb server, if you still have it running, and restart it. Then visit:

 

http://localhost:4000/tasks

 

If everything went as planned, you should see “Hello World.”

 

Merb has the ability to format your responses in several different formats, but we only want to provide JSON in our response for this tutorial. Add this to the top of the controller, above the index action:

 

only_provides :json

 

If you want to support XML as well, you will need to tell the controller that we support both YAML, JSON and XML. Just replace only_provides with the following line. (You will have to add .json, .xml to your request urls)

 

provides :xml, :json, :yaml

 

Add an index action

 

OK, we have our basic app ready. Let’s get some actions working. The first thing we need to get going is the index action. This action should return JSON with the tasks inside. To get started, we need a helper method that can return the JSON data structure for a task. Add this to the bottom of the controller:

 

protected      

def json_for_task(task)     

  { :guid  => "/tasks/#{task.id}",

    :description => task.description,

    :order => task.order, 

    :isDone => task.is_done 

  }   

end

 

Finally, we need to update the index action to do the right thing. Replace the current index action with the following:

 

def index     

  tasks = Task.all.map { |task| json_for_task(task) }     

  ret = { :content => tasks, :self => '/tasks' }     

  display ret    

end 

 

The code above constructs the JSON hash we would like to return (a hash with a content property that contains an array of records.)

Give it a try in the browser:

 

http://localhost:4000/tasks

 

If it works, you will download a file containing this:

 

{"content":[{ "order":1,"description":"My Task", "guid":"\/tasks\/1", "isDone":false}], "self":"\/tasks"}

 

Let’s move onto the next action… returning a single task.

 

GET task

 

When we want to retrieve a single task, we need to get just the JSON for that task. This should be easy. Just add a new action to the controller:

 

def show     

  task_id = params[:id]     

  task = Task.get(task_id) rescue nil     

  raise NotFound if task.nil?     

  ret = { 

    :content => json_for_task(task), 

    :self => "/tasks/#{task_id}" 

  }     

  display ret   

end

 

Try it in the browser:

 

http://localhost:4000/tasks/1

 

Creating a Task

 

We will accept JSON from the client. We are going to need a new helper method here. Add this method below the json_for_task:

 

def apply_json_to_task(task, json_hash)   

  task.description = json_hash['description'] unless json_hash['description'].nil?

  task.order = json_hash['order'] unless json_hash['order'].nil?

  task.is_done = json_hash['isDone'] unless json_hash['isDone'].nil? 

end

 

Now we can start creating some tasks. Add the following action just before the “protected” line:

 

def create      

  json = JSON.parse(request.raw_post) rescue nil?     

  json = json['content'] if json     

  raise NotFound if json.nil?          

  task = Task.new  

  apply_json_to_task(task, json)     

  task.save     

  

  # Return the location header with the new URL          

  url = headers['Location'] = "/tasks/#{task.id}"     

  ret = { :content => json_for_task(task), :self => url }            

  status = 201     

  display ret   

end 

 

Updating a Task

 

Updating is nearly as easy. Just add this action:

 

def update      

  json = JSON.parse(request.raw_post) rescue nil?     

  json = json['content'] if json     

  raise BadRequest if !json          

  task_id = params[:id]     

  task = Task.get(task_id) rescue nil     

  raise NotFound if task.nil?     

  

  # Update task     

  apply_json_to_task(task, json)     

  task.save     

  

  # Return the updated JSON     

  ret = { 

    :content => json_for_task(task), 

    :self => "/tasks/#{task_id}" }     

  display ret   

end

 

Deleting a Task

 

This one is easiest. Just find the task and destroy it.

 

def destroy     

  task_id = params[:id]     

  task = Task.get(task_id) rescue nil          

  

  # if task was found destroy it.  If it was not found, do nothing     

  task.destroy unless task.nil?          

 

  "200 Destroyed"   

end

 

Setup Your Proxy

 

SproutCore applications can only communicate with the specific host/port they were loaded from. Since you are running the sc-server tool for development purposes, this is a problem because you load your app on http://localhost:4020 but your Merb app is running on http://localhost:4000. How do we fix this?

 

You’ll find that sc-server includes a handy proxy tool that solves this problem. We will want to proxy all requests for http://localhost:4020/tasks to http://localhost:4000/tasks. This is easy to setup. Just open the Buildfile file in your SproutCore project and add the following line to the bottom:

 

proxy '/tasks', :to => 'localhost:4000'

 

Now restart your sc-server. Remember you need to keep both the sc-server and your Merb app running. Now visit

 

http://localhost:4020/tasks

 

You’re Done!

 

You should see a page from Merb. You’ve just created your first SproutCore-friendly web service. It’s worth noting now that you’ve not only done that, but you’ve just built a very nice API you can let others use to access your Todo’s service as well. Not bad for a few minutes of work.

 

This concludes the server-technology specific section of the tutorial. Please continue the tutorial with Step 7 below.

 

Continue to next step: Step 7: Hooking Up to the Backend »

 

History

 

  • The Building With Merb instructions were originally written by Charles Jolley for Merb version 0.9.3.
  • This version of the Building With Merb instructions (suitable for Merb 1.0.8) was written by Daniel Kehoe on 17 January 2009.
  • This was updated to work with SC 1.0 on April 17 2009 by Juan Pinzon 

 

Comments (0)

You don't have permission to comment on this page.