How To Use - plecong/gimlet GitHub Wiki
The goal of Gimlet is to be lightweight and magic-free. It should also be extremely pluggable meaning that the routing pattern, template engine, response handlers, etc. should all be pluggable. This should be scalable from small single-file scripts to web applications with multiple modules.
Route Definitions
Here are samples of ways to define the routes and their logic:
Sinatra Style
- Single file
- AST-based
- Utilizes closures to reference dependencies
@Gimlet.Script
get('/') {
'Hello World'
}
Less Sinatra Style
- Still single file
- Doesn't do any AST Transformation
- Utilizes closures to reference dependencies
def gimlet = new Gimlet(port: 8081)
gimlet.route {
get('/') {
'Hello World'
}
}
gimlet.serve()
Less Sinatra Fluent Style
- Same as above, but the API should be fluent
new Gimlet().route {
get('/') {
'Hello World'
}
}.serve()
Flask/Graffiti Style
- Annotations define routes
- Dependencies can be injected
class Hello {
@Get('/')
def index() {
'Hello World'
}
}
Configuration
There will be a standard set of configuration options including but not limited to the following:
- Port
- Path to static files
- Response handling and templating engine
- Routing engine
- Helper methods and binding variables available
The same options should be available as a Map passed to a standalone Gimlet
instance or as annotation parameters on the @Gimlet.Script
annotation.
Modularization
For medium to large application it will be necessary to split the route definitions into separate modules. There should be an equivalent method for each of the route definition
Script Files
Groovy scripts that are defined in the Sinatra style and have the @Gimlet.Script
annotation can be included from another file using the module()
method:
@Gimlet.Script
module('FooRoutes.groovy')
// or
module(new File('FooRoutes.groovy'))
// or (since the class will be annotated)
module(FooRoutes.class)
Routes as Closures
If the routes are defined as a closure as in the Less Sinatra Style, then the closures can just be passed to the module()
method which is really a method on the Gimlet
object:
class SomeRoutes {
static routes = {
get('/nothing') { 'Something' }
}
}
def gimlet = new Gimlet()
gimlet.module(SomeRoutes.routes)
// or
gimlet.route {
get('/') { 'Hello World' }
module(SomeRoutes.routes)
}
If the module is a class and that class does not have the @Gimlet.Script
annotation, then the class' methods will be scanned for routing annotations (e.g. @Get
):
class SomeRoutes {
@Gimlet.Get('/nothing')
def nothing() { 'Something' }
}
def gimlet = new Gimlet()
gimlet.module(SomeRoutes)
Module-specific Configuration
Modules can define their own configuration either as parameters on the @Gimlet.Script
annotation or as a Map
, Closure
or method marked with the @Gimlet.Config
annotation that returns a Map
. That configuration is overridden by an optional second parameter to the module()
method that is used to load it. The combined configuration Map
overrides the global Gimlet configuration.