Introducing Lego

For a while now Mathias and I have been hacking away on our very own web framework called Lego.
But why? I hear you cry, why yet another one, what’s wrong with Rails, Merb, Ramaze, Sinatra, Camping, Rango, Waves, Mack, and so on and so on.
Well, they’re all great but we believe they aren’t dumb enough!
Lego however is about as dumb as it gets, all it knows is how to handle the request/response cycle and leaves the rest up to plugins.
The downside of this approach is that it takes a little bit more work to get it up and running the upside is that you get exactly what you want, nothing more, nothing less.

I’m intrigued, tell me more

So at it’s core there’s lego-core which contains only the bare essentials, a way to set up a controller that rack can run, and a way to extend the various parts of lego.

Future plans include a lego-more gem already setup with commonly used plugins so you easily can get up and running quickly and a central repository of available lego plugins.

A simple application

Provided that you have lego-core installed (gem install lego-core) put the following in a config.ru file:

1
2
3
4
5
6
require 'rubygems'
require 'lego-core'

class MyApp < Lego::Controller; end

run MyApp

Run it with rackup and point your browser to http://localhost:9292/ and you should be met by a plain old ‘404 – Not found’, not terribly exiting right? But that’s pretty much all lego-core gives you :)

So what about those plugins?

The lego extension system allows you to hook into various parts of the execution chain, namely:

View – For action and view related stuff
Controller – For defining controller dsl’s
Router – Rules for how lego should match routes, must have a self.match_route method which takes a route and an env parameter and return either a two element array containing the env and a hash of match data (or an empty hash if no match data is available) or false

At it’s core, a plugin is a module that responds to register who’s responsibility is to register with lego, following is a simple plugin example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
module SamplePlugin

  # Register is called by lego when plugin is loaded

  def self.register(lego)
    lego.add_plugin :view, View
    lego.add_plugin :router, Matcher
    lego.add_plugin :controller, Routes
  end

  # A plugin module we load as a view helper

  module View
    def h1(content)
      "<h1>#{content}</h1>"
    end
  end

  # A very simply route helper

  module Routes
    def get(path, &block)
      add_route(:get, {:path => path, :action_block => block})
    end
  end

  # A very simple route matcher

  module Matcher
    def self.match_route(route, env)
      (route[:path] == env['PATH_INFO']) ? [env, {}] : false
    end
  end
end

It’s about time for a real example!

As we showed you earlier, lego-core gives you a way to set up an application but it don’t know how to map routes to action so let’s expand on the previous application with our newly gained knowledge of lego’s extension system:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require 'rubygems'
require 'lego-core'

module Routes
  def self.register(lego)
    lego.add_plugin :controller, SinatraController
    lego.add_plugin :router,     SimpleRouter
  end

  # implements a basic Sinatra inspiered dsl
  module SinatraController
    def get(path, &block)
      add_route(:get, {:path => path, :action_block => block})
    end
  end

  # Implements a very basic mather rule
  module SimpleRouter
    def self.match_route(route, env)
      (route[:path] == env['PATH_INFO']) ? true : false
    end
  end
end

# define an application
class MyApp < Lego::Controller
  plugin Routes
 
  get "/" do
    "Hello from /"
  end
end

run MyApp

As before, run it with rackup and point your browser to http://localhost:9292/ and this time you should be greeted by “Hello from /”.

Don’t repeat yourself

If you have a plugin you want to use in every application, then consider defining it globally:

1
2
3
4
5
6
7
8
9
  Lego.plugin MyPlugin

  # or group multiple plugin calls in a config block

  Lego.config do
    plugin MyViewPlugin
    plugin MyControllerPlugin
    plugin MyRouteMatcherPlugin
  end

Configuration options

Lego also comes with a very simple configuration handler, it basically provides two methods, set and options, lets look at them in action:

1
2
3
4
5
6
7
8
9
class MyApp < Lego::Controller
  plugin Routes

  set :foo => "bar", :baz => "quux"

  get("/foo") { options :foo }
 
  get("/bar") { options :bar }
end

As with plugins you can use set globally:

1
2
3
4
5
6
7
8
  Lego.set :foo => "bar"

  # or group multiple set calls in a config block

  Lego.config do
    set :foo => "bar"
    set :baz => "quux"
  end

Wrapping up

In conclusion Lego is a ‘Use what you want’ framework that tries to stay out of your way as much as possible and thank’s to it’s modular design, you are free to use your favourite technologies and tools.

Please note that it’s still in a highly experimental stage!

Posted in Programming, Projects, Ruby, gems at January 13th, 2010. No Comments
Tagged with , , , , . Written by: Patrik Hedman

pastbedti.me is using WP-Gravatar