Friday, 11 January 2008

MERB: Rendering JPEG's from actions

While working on my MERB app, I wanted to pull an image from the database and render it inline as if it were a static jpg. I knew how to do this in Rails but not in MERB. It turns out to be really easy, although quite different from how it is done in Rails.

This blog post was very useful to figuring it out.

Let's suppose you have a database model Image and an Images controller and action 'show'. Firstly at the end of your merb_init.rb file, put the following line:
Merb.add_mime_type(:jpg, :to_jpg, %w[image/jpeg], {})

then in the controller add a provides parameter for jpg and

In your model add a method to_jpg like so.
def to_jpeg
data
end

Where data is the name of the column in your images table that holds the binary data.

Make sure you restart your server after all these changes.

Next your controller should look something like this:

class Image < Application
provides :xml, :js, :yaml, :jpg

def show
image = Image.find(params[:id])
render image
end
end


And thats it! If you go to /images/1.jpg (assuming RESTful routing...), the controller will retrieve the image with id 1 and MERB will call the to_jpg method on that object. It will then output that binary image data and send with the correct http header Content-Type parameter.

Thursday, 10 January 2008

Merb Gotchas

I'm gonna to keep this post up to date with any gotchas or "mistakes of understanding" that I make as I work on MERB.

First up. Controllers are called the same as models! Example:

$ script/generate controller monkey
Started merb_init.rb ...
Loading Application...
Compiling routes..
Loaded DEVELOPMENT Environment...
exists app/controllers
create app/controllers/monkey.rb

and
$ script/generate model monkey
Started merb_init.rb ...
Loading Application...
Compiling routes..
Loaded DEVELOPMENT Environment...
exists app/models
create app/models/monkey.rb


So if you create a controller for monkeys and a model for monkeys, the two class names will both be called Monkey, except one will inherit from controller and the other from model.

In fairness to MERB the generators do warn you about this.

"The name 'Monkey' is reserved."


But it doesn't explain why it is reserved and maybe I'm just thick but it stumped me for longer than it should have.

You never have a chance to make that mistake in Rails because controllers are always like "MonkeyController". (I don't know why merb didn't follow suit... DRYer? simpler?)

What I did to get around that was to make the controller plural. monkeys.rb

Wednesday, 9 January 2008

Enabling Sessions in MERB

Sessions are disabled by default in MERB (because it makes the performance numbers look great! ;) ). To enable sessions, just set the variable :session_store: in merb.yml to one of activerecord, sequel, datamapper, memory, cookie or mem_cache.

Because I want to use memory for my development sessions and mem_cache for my production environment, I set up config/environments/development.yml and put in:

:session_store: memory
:memory_session_ttl: 3600


and config/environments/production.yml and put in:

:session_store: mem_cache


If you're using memcached with MERB, you need to put


require 'memcache'
CACHE = MemCache.new('127.0.0.1:11211', { :namespace => 'my_app' })


in your merb_init.rb (http://merb.rubyforge.org/classes/Merb/MemCacheSession.html)

Tuesday, 8 January 2008

HTTP Authentication for MERB

I've just started working on a new web application. It's going to be quite a simple app but speed and responsiveness is important so I decided to try Merb. I'm going to blog about some of the difficulties and (hopefully) solutions I discover as I go through the process. One of the main problems with MERB is the lack of documentation so hopefully with these blog posts, I can help with that.

First up, HTTP authentication. In the merb.yml it says the following:

# If you want to protect some or all of your app with HTTP basic auth then
# uncomment the following and fill in your credentials you want it to use.
# You will then need to set a 'before' filter in a controller. For example:
# before :basic_authentication
#:basic_auth:
# :username: ezra
# :password: password
# :domain: localhost

All well and good, but what if you want to validate the authentication against a database of users?

Well the answer is to overwrite the authenticated? method in BasicAuthenticationMixin

This is easy to do, in your application.rb, do something like this:

class Application < Merb::Controller
before :basic_authentication

private
def authenticated?
# if you want get the username and password that was inputted via http use:
# username, password = *credentials
# then validate the user by authenticating against a datbase or whatever
# make sure the return value is true or false
end
end


Note: You still have to uncomment the basic_auth configuration in merb.yml. But it doesn't matter what the username/password is there since you'll be overriding them anyways.