mojito – Next level Ruby webframework
Mojito is a lean and simple webframework.
As the name implies mojito derives from cuba (https://github.com/soveran/cuba). Many thanks to Michel and his cuba project for giving me some major insights on web-application-simplicity! See also https://github.com/mtrense/mojito-examples for more examples.
Installing
$ gem install mojito
Your first application
The easiest way to start with mojito is to use a plain config.ru
script to develop and configure your application.
$ cat config.ru require 'mojito'
class FirstApp include Mojito controller :runtime rendering :all
on GET(), PATH('/hello/:name') do |name| write "Hello #{name}! How are you?" content_type :plain ok! end end
run FirstApp
This installs a handler for GET requests to a path of /hello/ followed by a name (actually this notation catches everything but a slash ‘/’). You can start this application by running rackup
from the same directory:
$ rackup
This starts a http-server on port 9292. Now try to call your handler with curl:
$ curl http://localhost:9292/hello/Fred
Hello Fred! How are you?
Controllers
Controllers are the common building blocks of Mojito Applications. For each successful request at least one controller is instantiated. The call to controller :runtime
actually defines the calling class as runtime controller and includes the corresponding module – Mojito::Controllers::Runtime in this case. For a specific class one can choose from many controller types, each of which is specialized to a specific use-case.
Controller classes are actually Rack-Applications, so they must provide a (class-)method call(env)
. The common sequence of processing a request includes:
-
call(env)
is called from the surrounding application/the framework - a new instance of the controller-class is instantiated and configured with the given environment
- a kind of dispatch method (
__dispatch
for build-in controllers) is called to do the actual work
This ensures that the controller instance which handles a specific request is used solely for that request, so that any instance variables created during request-processing stay request-local. At the same time this keeps application stacking and nesting as simple as call
-ing the next application.
Structure of a real-world application
This is not up-to-date
The above call to Mojito.application
actually created a class and included most of Mojitos foundation methods, so that you could start writing your application code without wondering what features you want to include in your application. As a tribute to simplicity (one of the most important I think), another helper method Mojito.base_application
can be used to create a blank application class, which only includes modules that are strictly necessary. Instead of using those methods you can also write out all of the generated classes code (here Mojito
is included, which in turn includes Mojito::Base
, the basic module which is used by Mojito.base_application
):
class FirstApp
include Mojito
routes do
on GET(), PATH('/hello/:name') do |name|
...
end
end
end
which is not only perfectly legal but also favoured, when you need any special behaviour on your application class.
Although you can actually use any structure you want (including a single-file application), some of them are more useful than others. My preferred way of developing Mojito applications is to reuse most of the concepts found in product development with rubygems and git.
Behind the scenes
Understanding how Mojito dispatches requests and selects handlers, which in turn use common functionality to create the response is vital to an efficient use of Mojito as web application framework. First of all Mojito applications (classes which include Mojito::Base
or Mojito
) are simply Ruby classes, which are instantiated with the Rack-environment hash for each request. This is automatically triggered by a rack-enabled webserver through the call of run ApplicationClass
, which tells rack to call call(env)
on that class. The Mojito implementation will then instantiate the Application (the instance is called a controller from now on) and call dispatch
on that controller. The dispatch method then executes any block given to the routes
-method in the class definition. As in Cuba the routes-definition is executed once for each request.
Matching
A matcher is simply a Proc, instantiated with matcher-specific configuration, which returns whether or not a given request matches its criteria. The request-method matcher for example is implemented as follows:
def METHOD(method)
proc { request.request_method == method.to_s.upcase }
end
This method configures a Proc to return true when the requests method matches the given parameter. The returned Proc is evaluated in the context of the current controller. This is also where the request
method comes from. Like request
, a matcher can access any method from the controller, within which it is executed. The naming convention is to name all matchers uppercase.
Rendering
Helpers
Deployment/Hosting
tbd
Writing extensions
tbd
Contributing to mojito
- Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet.
- Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it.
- Fork the project.
- Start a feature/bugfix branch.
- Commit and push until you are happy with your contribution.
- Make sure to add tests for it. This is important so I don’t break it in a future version unintentionally.
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
Copyright
Copyright © 2012 Max Trense. See LICENSE.txt for further details.