Here's what you need to get started with Ronin:

With the Aardvark binary vark on your PATH, you can run the remote Ronin installation script:

  $ vark -url http://ronin-web.org/init.vark init -name my_app

Where my_app is whatever name you would like for your Ronin application. You will see output like this:

Buildfile: /private/var/folders/yy/1h6qk8t52rjczystgf79s5nm0000gn/T/build6584956297061779085.vark
Done parsing Aardvark buildfile in 1839 ms

init:
      [get] Getting: http://ronin-web.org/ronin-template.zip
      [get] To: /var/folders/yy/1h6qk8t52/T/ronin-template8153128738706862212zip
    [unzip] Expanding: /var/folders/yy/1h6qk8t52/T/ronin-template8153128738706862212zip into ./my_app
     [echo] Created a new ronin application at ./my_app.
     [echo]   To start your ronin app, cd my_app; vark server

BUILD SUCCESSFUL
Total time: 0 seconds

Ronin applications are very self-contained. All dependencies are managed via the Maven POM dependency model, so there are no global libraries to worry about, nor is it typically necessary to check in jars.

Configuration of a Ronin server is done by creating a class called config.RoninConfig. Ronin finds this class reflectively at startup if it exists. This class must implement IRoninConfig; more commonly, you will want to subclass DefaultRoninConfig, which provides standard implementations of the methods on IRoninConfig.

Most common configuration should be performed by overriding DefaultRoninConfig's constructor, calling super(), and then setting the following properties:

A controller in Ronin is simply a class in the "controller" package containing one or more methods. There's nothing magic about these methods; they can do anything a normal method would do. They can even call each other, for example, to render subcomponents of a page.

Each controller class should correspond loosely to a broad division of your application, whether it be controller methods operating on a particular object in your data model, rendering the pages of a particular subsection of your site, etc. The name of the controller class will be the first part of the URL used to call a controller method in the class. For instance, the URL "http://localhost:8080/Main/view" will call the view() method on controller.Main.

Controller methods must be public in order to be called by Ronin in response to a user request. (Gosu methods are public by default, so you don't need to add any modifiers to them.) Any helper functions or functions containing common code called from multiple controller methods should be declared "private", "protected", or "internal", to avoid being accessible by a curious or malicious end user via a URL.

While a controller method can contain any arbitrary code, in a typical web application they will tend to follow one of two patterns:

This interaction model is known as the Post/Redirect/Get model. That being said, again, Ronin does not prescribe any particular model - you are free to structure your controller methods in any way you see fit.

If you would like to restrict a particular controller method to certain HTTP methods - for instance, only allow POST requests to the method - use the @Methods annotation:

  @Methods({POST})
  function myControllerMethod() {
    ...
  }

Likewise, if you would like to restrict a controller method to accepting HTTPS requests only, use the @HttpsOnly annotation:

  @HttpsOnly
  function myControllerMethod() {
    ...
  }

If @HttpsOnly is applied to a controller class, then all methods in that class will require HTTPS.

A controller class must extend the RoninController base class. Doing so provides your controller class with access to the following properties and methods:

Method/Property Description
Writer The output writer for the HTTP response. For a normal web request, this will be what is rendered to the user's browser. Writer should be passed to any templates rendered by the controller.
Method The HTTP method specified by the user's request. It is an instance of the HttpMethod enum, which contains the values GET, POST, PUT, and DELETE.
Session A map containing data pertinent to the current user's session. The map's keys are Strings, and its values are untyped Objects, so you can put data in the session by saying e.g. Session["userName"] = "admin" and retrieve session data by saying e.g. print(Session["userName"]).
Referrer The referring URL of the current request (i.e., the page from which the user clicked a link to generate the request) as a String.
Request An object representing the user's HTTP request. This is an HttpServletRequest object, which can be used to access cookies, headers, and other detailed information about the request.
redirect() Sends a redirect response to the user's browser; this is typically done after a POST action from an HTML form. The argument to redirect() is a feature literal of the desired controller method for the target of the redirect; see the Link Targets section below for more information. Generally, you do not want to do anything further after a call to redirect().
bounce() A convenience method to redirect the user to the URL from whence they came. This can be helpful for responding to a failed login attempt, or a form with validation errors - add the relevant errors to the session, then bounce the user to the login prompt or form which can display those errors. bounce() is not always reliable, as the user may have navigated to the URL directly, in which case there is no referring URL to bounce them to, or they may have configured their browser not to send referrer information.
log() Outputs information to Ronin's log. More below.
trace() Allows you to integrate into the Ronin tracing subsystem. More below.
cache()

invalidate()
Allows you to cache values. More below.
urlFor()

postUrlFor()
These methods provide a type-safe way to generate URLs.

If you need to define functionality common to all methods on a controller, you can override the beforeRequest() and afterRequest() methods. beforeRequest() is called before each request to the controller, and returns a boolean value which allows it (if false) to prevent the request from being processed further. Code to ensure that the user is logged in and has permission to perform a given action can go here. afterRequest() is called after the controller method returns, and can perform cleanup on anything done in beforeRequest().

A controller method which takes no arguments, for instance controller.Main#index(), is accessed via a simple URL - in this case, "http://localhost:8080/Main/index". If a controller method takes one or more arguments, however, it will accept URL parameters corresponding to those arguments, either as part of the URL itself (e.g. "http://localhost:8080/Post/view?id=5") or in the body of the request (as with a request generated by submitting an HTML form). The method will be called with the values provided by the request. If a parameter is not included in the request, null will be passed in (or false for boolean parameters).

Simple Parameters

If the method parameter's type is:

or any other type to which Gosu can coerce a String, it will be set directly using the value in the request. So given the following controller method signature:

  controller.Main.index(b : boolean, i : int, s : String, d : Date) 

the following URL:

http://localhost:8080/Main/index?b=true&i=5&s=Hello&d=07-11-1980

will construct a controller object and call this method:

(new controller.Main()).index(true, 5, "Hello", new java.util.Date("07-11-1980"))

Entity Parameters

If a parameter is of some other type, Ronin will attempt to find a static method on that type called fromId() which takes a single parameter and returns an instance of the desired type. (In this documentation we will refer to such types as entity types, though they can be any Gosu type.) If it finds such a method, it will call it, passing in the value from the request, and assign the result to the controller method's parameter.

For instance, given the following class:

class Person {
  static function fromId(id : long) : Person {
    return fetchPersonFromDb(id)
  }
  ...
}

and the following controller method on controller.Main:

  function viewPerson(p : Person) {
    ...
  }
    

the URL

http://localhost:8080/Main/viewPerson?id=5

will call

(new controller.Main()).viewPerson(Person.fromId(5))

Note that you can use a Gosu enhancement to add fromId() to an existing type, such as a Hibernate class, thus turning it into a valid Ronin entity type. You could even enhance a single interface implemented by all of your entity types, though be careful to ensure that fromID() always returns the concrete type, not the interface type (most likely through the use of a type parameter on the enhancement). Consult the Gosu documentation for further details on enhancements.

Complex Parameters

Parameters of a non-primitive type and array parameters support a more advanced syntax. Individual properties of a non-primitive parameter can be set from URL parameters using a dot-path syntax. For instance, given the following method signature:

function updatePerson(p : Person)

where Person is a Gosu class defining the following property:

var _name : String as Name

the URL "http://localhost:8080/Main/updatePerson?p=0&p.Name=Bob" will call Person.fromId(0), then set the Name property on that instance of Person to "Bob".

If a property is specified in the URL, but no ID is given for the instance - e.g. "http://localhost:8080/Main/updatePerson?p.Name=Bob" - a new Person will be instantiated, and its Name set to "Bob". (This assumes that Person defines a constructor with no arguments.)

For array parameters, an array is created automatically. Elements in the array are specified in the URL using standard array notation, e.g. "names[0]=Bob&names[1]=Fred". If the parameter is an array of entity types, each value specified in this way is passed to fromID() to retrieve the appropriate element.

Complex parameters make it very easy to create an HTML form for creating or updating an entity. The controller method to which the form points need only take a single parameter, the entity, and the name of each input in the form is a dot path for the property it edits (e.g. "p.Name", or "p.PhoneNumber").

Restricting properties

Setting properties on a parameter via request parameters could conceivably pose a security risk. For instance, say you have a page where a user can edit their profile. This page posts to the following controller method:

  function saveUser(u : User) {
    [code to save User object to database]
  }

An attacker could trick a user into posting to this method with the parameter u.Password set to some value known to the attacker.

For this reason, Ronin allows you to specify that certain properties should never be set automatically from request parameters. There are two ways to do this. If the property in question is on a Gosu class, you can use the @Restricted annotation:

  @Restricted
  var _password : String as Password

On the other hand, if the property is on another kind of type, or if you don't have control over the class where it's defined (e.g. it's part of a third-party library), you can set the RestrictedProperties from the constructor of your RoninConfig class:

  RestrictedProperties = { User#Password, User#Salt, ... }

Alternative Argument Passing

In addition to passing parameters to a method as part of the URL, you can also pass them as form-encoded data in the body of the HTTP request. This is what will happen when you submit an HTML form, for example.

You can also pass in parameters via JSON in the body of the request. This could be useful if, for instance, your application supports clients other than a web browser, or you're using a Javascript UI framework which uses JSON for AJAX requests. JSON handling is important enough to warrant its own section:

If Ronin receives a request whose Content-Type header is set to "text/json" or "application/json", it will expect the values for the parameters of the target controller method to be encoded as JSON in the request body (instead of form-encoded data in the request body or URL).

This is best shown by example. Say you have the following Gosu class:

  class Person {
    var _name : String as Name
    var _age : int as Age
  }

and the following controller class:

  class PersonCx extends RoninController {
    function friendsNamed(p : Person, names : String[]) {
      ...
    }
  }

The body of a JSON-based request to friendsNamed() might look something like this:

  {
    "p" : {
      "Name": "Bob",
      "Age": 25
    },
    "names": [
      "Sue",
      "Frank",
      "Joey"
    ]
  }

The body of the request is a single JSON object, with a property defined for each of the method's parameters. The first parameter's type is Person, so the value of "p" is another JSON object, with properties corresponding to each of that object's properties. The second parameter is an array of Strings, so the value is a JSON array containing strings.

Controller methods which are meant to return output to a response (e.g. those which don't call redirect()) can do so in one of two ways: returning a String, or using the Writer property. For simple output (e.g. a simple response to an AJAX request), it may be easier to just return the desired output as a String from the controller method. For more complex output, including HTML pages, passing the Writer to a view is recommended.

A view in Ronin is typically just a Gosu template. (See the Gosu documentation for more information on templates.) Unlike controller classes, templates can be in any package, but the convention is to place them in the view pacakge.

For views which render HTML, you should not hard-code link URLs and form targets in the HTML. This could lead to broken links if you remove or rename a controller class or method, or change its parameters. Instead, use the urlFor(), postUrlFor(), and target() methods (see below).

It is recommended that the following directive be inserted at the top of each Ronin template:

<%@ extends ronin.RoninTemplate %>

This allows Gosu code in the template unqualified access to the following convenience methods:

Method/Property Description
h() Takes a String, and returns a copy of that String escaped for HTML.
g() Takes a String, and returns a copy of that String escaped for inclusion in a Gosu string literal.
urlFor() Generates a URL, given a feature literal for the desired controller method. See the Link Targets section below for more information.
target() Can be used in a using block to set up a context for part of the template (such as an HTML form). Pass in a feature literal for a controller method, and you can then use TargetURL and n() within the using block.
TargetURL A property which returns the base URL for the controller method, with no parameters included. Use this as the target of your HTML form, AJAX request, etc.
n() A convenience method which generates parameter names for the controller method, to use e.g. as the name of an HTML input. It can be used in one of the following ways:

  • Pass in a type: n(String). The name of the first parameter of that type is returned.
  • Pass in an object: n("foo"). The name of the first parameter whose type matches that object is returned.
  • Pass in a property literal: n(Post#Author). The dot path to that property on the first matching parameter is returned.
  • Pass in an index: n(2). The name of the parameter at that index in the method's parameter list is returned.
  • If the parameter is an array type, pass in the desired index as a second argument to n().
postUrlFor() Generates the base URL for a controller method, with no parameters included. This is a simpler alternative to TargetURL, as it doesn't require a using(target()) block. The argument to postUrlFor() is a method literal, whose arguments need not be bound.

Note that Writer, Session, Request, and Response are also available, like they are on controllers.

You can log in Controllers and Views by using the log() method:

  package controller

  class MyController {
    function index() {
      log("Rendering index...")
      Main.render(Writer)
    }
  }

The log() method takes advantage of Gosu's named arguments and default parameters, allowing you to better target your log message:

    log("A debug message", :level = DEBUG) // only logs if debug logging is enabled via RoninServlet#LogLevel
    log("A message in the model", :component = "Model") // a log message associated w/ the "Model" component
    try {
      something()
    } catch(e) {
      log("An exception occurred!", :exception = e) // logs the exception
    }

You can combine named parameters for more elaborate logging messages.

The default logging level is INFO.

Lazy Logging

Sometimes your logging message might be expensive to compute, and you only want to perform the computation if the log message is actually going to be written. You can achieve this by using a no-argument block to the log method:

log(\-> aVeryExpensiveMethod(), :level = DEBUG)

Unless the Ronin application is configured to log debugging messages, aVeryExpensiveMethod() will never be called.

Configuring Logging

By default, Ronin uses the SLF4J API for logging. It includes an SLF4J implementation that outputs log messages to the console based on the LogLevel property on RoninConfig. If you'd like to use a different SLF4J implementation (e.g. log4j), remove the ronin-log dependency in your pom and implement a SLF4J initializer.

If you don't want to use SLF4J at all, you can replace the logging implementation wholesale. In your config.RoninConfig class you can set up a new logging adapter like so:

  package config

  uses ronin.*
  uses ronin.config.*

  /**
  * This class gives you a way to programatically configure the ronin servlet
  */
  class RoninConfig implements IRoninConfig {
    construct(m : ApplicationMode, an : RoninServlet) {
      super(m, an)
      DefaultController = controller.Main
      DefaultAction = "index"
      LogHandler = new MyCustomLogHandler() // <-- set up a custom log handler
    }
  }

Where MyCustomLogHandler is a Gosu class that implements the ronin.config.ILogHandler interface.

This class can wire through to whatever you like: StdOut, Log4j, whatever.

Tracing

Ronin provides basic tracing support to help you understand where time is going in requests. While this is not a complete substitute for serious profiling tools like JProfiler, it can give you a good idea of why your application is so slow.

By default, Ronin (and Tosa) log controller calls, template rendering and SQL statements. Here is an example trace from RoBlog:

    [java] INFO Ronin - request for /PostCx/index - 1104.521 ms
    [java] INFO Ronin -   controller.PostCx.beforeRequest() - 0.214 ms
    [java] INFO Ronin -   controller.PostCx.index - 555.263 ms
    [java] INFO Ronin -   db.roblog.BlogInfo.selectAll() - 1.005 ms
    [java] INFO Ronin -     SELECT * FROM BlogInfo ([]) - 0.124 ms
    [java] INFO Ronin -   view.Layout.render() - 239.046 ms
    [java] INFO Ronin -     db.roblog.BlogInfo.selectAll() - 0.519 ms
    [java] INFO Ronin -       SELECT * FROM BlogInfo ([]) - 0.124 ms
    [java] INFO Ronin -     view.All.render() - 135.492 ms
    [java] INFO Ronin -       db.roblog.Post.selectAll() - 0.884 ms
    [java] INFO Ronin -         SELECT * FROM Post ORDER BY Posted DESC LIMIT ? OFFSET ? ([20, 0]) - 0.562 ms
    [java] INFO Ronin -   controller.PostCx.afterRequest() - 0.103 ms 
    

This gives you a good idea of the perf "shape" of your requests, and where optimizations might be necessary.

If you wish to add a custom trace component in a Controller or Template, you can use the trace() method like so:

    function myExpensiveFunction() {
      using(trace("myExpensiveMethod")) {
        // some expensive logic
      }
    }

The "myExpensiveMethod" string will now appear in your trace with a timing and with all elements that occur within the using statement nested below it. This can help you narrow down what, exactly is causing slowness in your app.

Note that trace(), like log(), can take a no-argument block that returns a String, so that expensive trace messages can be constructed only if necessary.

Ronin provides a simple way to cache values in various stores, the cache() method:

  function myExpensiveFunction() : String {
    return cache(\-> computeTheExpensiveString())
  }

The cache() method takes a block and returns the value of the block or, if it has already been computed, returns the cached value.

By default, the cached value will last for the duration of a single request.

Stores

Sometimes caching for a single request isn't good enough. Ronin provides three different stores: REQUEST (the default), SESSION (associated with the users session) and APPLICATION (global). Let's change the code above to store the value in the session, for additional perf benefit:

  function myExpensiveFunction() : String {
    return cache(\-> computeTheExpensiveString(), :store = SESSION)
  }

Now the value produced by computeTheExpensiveString() will be only computed once per user session, rather than on every request.

Once you start storing caches at the session level, you are probably going to have to worry about invalidating them as well. This can be done by giving the cache a name:

  function myExpensiveFunction() : String {
    return cache(\-> computeTheExpensiveString(), :store = SESSION, :name = "MySweetCache")
  }

Allowing you to invalidate the cache with the invalidate() method:

  function updateExpensiveFunctionsValue() {
    updateTheDataThatTheExpensiveStringDependsOn()
    invalidate("MySweetCache")
  }

Note that cache names must be unique within their store.

Caching Strategery

"There are only two hard problems in Computer Science: cache invalidation and naming things." - Phil Karlton

Well, here you've got both of these problems, so humility is the best policy.

Caches should be few, minimal and judicious, striking at the optimal place in the call stack to cut off expensive computations. You should always, always, always have empirical evidence that a cache is needed before you introduce this complexity to your app.

Ronin provides seamless support for files uploaded via HTTP requests (e.g. via a "file" type input in an HTML form).

Uploaded files are treated just like any other controller arguments. The type of the method parameter can be either byte[] or InputStream; in the former case, the entire contents of the file will be read into the array, while the latter case provides stream-based access, which may be more appropriate for larger files.

For example, given the following HTML form:

  <% using(target(FileCx#uploadFile(byte[]))) { %>
  <form enctype="multipart/form-data" action="${TargetURL}" method="POST">
    <input type="file" name="${n(0)}"/>
  </form>
  <% } %>

and the following controller method:

  function uploadFile(myFile : byte[]) {
    ...
  }

the myFile parameter will contain the bytes of the uploaded file.

Ronin provides a convenient framework for handling user authentication in your app. It defines an interface, IAuthManager, and a default implementation using Apache Shiro. All that you need to provide is a basic user model - a way to retrieve a user object by its username, and to determine the appropriate login credentials from that object.

The AuthManager property on IRoninConfig stores the object responsible for handling authentication. To use the default authentication manager, insert the following in the constructor of your RoninConfig class:

    AuthManager = createDefaultAuthManager(\ username -> User.find(new User(){:Name = username})[0],
      null, User#Name, User#Hash, User#Salt)

The actual parameters passed to createDefaultAuthManager() will vary for your particular application. createDefaultAuthManager is a method on DefaultRoninConfig; if your RoninConfig does not extend DefaultRoninConfig, you'll need to write the equivalent method.

The first parameter to createDefaultAuthManager() is a block which takes a username as a String and returns the corresponding user. The second parameter is used for OpenID support (see below); you can pass in null if you're not planning to support OpenID. The third, fourth, and fifth parameters are references to properties on the user object which return, respectively, the user's name, a hash of the user's password, and the salt used to construct that hash. (More on this later.)

The optional fifth parameter is a reference to a property on the user object which returns a list of strings representing the roles of the user. Typically these are used to restrict access to a particular part of the application to a subset of users. There is no need to specify this parameter if you don't plan on using roles.

The optional sixth and seventh parameters specify the hashing algorithm and the number of hash iterations used when computing a password hash. The default values here should be fine; do not pass in values for these parameters unless you're sure you know what you're doing.

Once you've initialized the authentication manager, you can access it in all of your controllers and templates as AuthManager. The properties and methods on IAuthManager are:

Method/Property Description
login() Takes a username and password, and attempts to log in the specified user. It returns true if the user was successfully logged in, and false otherwise.
logout() Logs out the current user.
CurrentUser Returns the currently logged in user (represented by the object returned by the block you provided above), or null if no user is logged in.
CurrentUserName Returns the currently logged in user's username, or null if no user is logged in.
currentUserHasRole() Takes a String representing a role, and returns true if the currently logged in user has that role. If no user is logged in, this method always returns false.
getPasswordHashAndSalt() Takes a plaintext password and returns a pair of Strings. The first String is a hash of the password, and the second is the salt used to construct that hash. This hash/salt combination is guaranteed to be produced using the same mechanism that login() uses to check passwords, so you should always use this method when creating a new user or changing a user's password.

Requiring a User to be Logged In

It is likely that there are many controller methods in your application that shouldn't be accessed by a user who isn't logged in. Usually in such a case, you'd like to redirect the user to a login page, and after they successfully log in, let them go to where they were originally trying to go. Ronin automates this pattern for you if you set the LoginRedirect property of your RoninConfig. This property should be set to the bound method reference for your login page; for example:

LoginRedirect = AdminCx#login()

In the controller method that processes the user's login, you can call the postLoginRedirect() method to send the user back to where they were trying to get; you must provide a default target in case the user navigated directly to the login page. For example:

  @NoAuth
  function doLogin(username : String, password : String) {
    if(AuthManager.login(name, pass)) {
      postLoginRedirect(MyCx#index())
    } else {
      redirect(#login())
    }
  }

Note the @NoAuth annotation; controller methods with this annotation (and those on a controller class with this annotation) bypass the login check and can thus be accessed by non-logged-in users. Needless to say, the controller method for the login page itself should also be @NoAuth; if it isn't, your application will throw an exception on startup.

Using LDAP

Ronin provides a convenience method for integrating with an LDAP server for user authentication. Assuming your RoninConfig class extends DefaultRoninConfig, insert the following in its constructor:

  AuthManager = createLDAPAuthManager("ldap://myldapserver:389", "uid={0},ou=users,dc=mycompany,dc=com")

where the first argument is the URL to your LDAP server, and the second argument is your LDAP server's User DN format. An optional third argument allows you to specify the authentication mechanism to use, with "simple" being the default.

Using OpenID

OpenID is a standard that allows users to identify themselves via a third-party identity provider. Ronin provides turn-key OpenID support, so you can allow users to log in to your application with, for example, their Google account.

To support OpenID, you must first tell the authentication manager how to find a user given the credentials given by their OpenID provider - specifically their identity string and e-mail address. Not every OpenID provider will provide the user's e-mail address, and not every OpenID provider will provide a unique or reliable identity string, so you may have to handle some providers on a case-by-case basis. Here's an example that finds a user in the database based on the provided e-mail address:

    // Not production-ready - do not copy and paste!
    AuthManager = createDefaultAuthManager(\ username -> User.find(new User(){:Name = username})[0],
      \ identity, email, provider -> User.find(new User(){:Email = email})[0],
      User#Name, User#Hash, User#Salt
  )

(This code is not production-ready, as it doesn't correctly handle the case where no user with the given e-mail address is found. If this happens, you can either return null to reject the login, or create a new user and return it. It also doesn't check to make sure the e-mail address comes from a reliable provider; it's possible for a malicious provider to claim an e-mail address that doesn't actually belong to the user.)

Once you've initialized the authenication manager so that it knows how to handle OpenID logins, all you need to do is add a link or form to your application's login screen. For example:

  <% uses controller.OpenID %>
  <div>
    <a href="${urlFor(OpenID#login(OpenID.GOOGLE, urlFor(PostCx#recent(0))))}">
      Log in with your Google account
    </a>
  </div>
  <div>
    <% using(target(OpenID#login(String, String, boolean, String))) { %>
    <form method="post" action="${TargetURL}">
      <input type="hidden" name="${n(0)}" value="${OpenID.VERISIGN}"/>
      <input type="hidden" name="${n(1)}" value="${urlFor(PostCx#recent(0))}"/>
      Log in with your Verisign PIP account: <input type="text" name="${n(3)}"/>
      <input type="submit" value="Go"/>
    </form>
    <% } %>
  </div>

controller.OpenID is provided by Ronin out of the box. OpenID#login is the target method for all OpenID logins. The first parameter is the OpenID provider's XRDS discovery URL; there are constants for several of the most popular providers on the OpenID class, of which we use two here. The second parameter is the default target to redirect the user to after logging them in, which is analogous to the argument to postLoginRedirect() described above.

Some OpenID providers, such as Verisign, have a different discovery URL per user. In this case, you should prompt the user for their username and pass it to login() as the fourth parameter.

Cross-site request forgery is a technique whereby users can be tricked into submitting a request to your app that appears to be authenticated without their knowledge.

Ronin offers a simple mechanism for protecting against CSRF attacks. The XSRFTokenName and XSRFTokenValue properties, available on all controllers and templates, represent a name/value pair which will authenticate any user request as coming from within your app.

By default, once the XSRFTokenValue property has been accessed in the context of a user session, all future non-GET requests will throw an error if a matching name/value pair is not present in the request. To use this facility, then, all you have to do is include the following hidden input in all of the forms in your app:

<input type="hidden" name="${XSRFTokenName}" value="${XSRFTokenValue}">

AJAX POST requests must also include this name/value pair; how you do so will depend on how you're making the request.

If you'd like to change which HTTP methods to apply XSRF protection to, set the XSRFLevel property on your RoninConfig object. (Note that, if you use this property to apply XSRF protection to the GET method, you'll have to manually add the XSRF token to the URL of any GET requests.)

Ronin provides a simple way to respond to JSONP requests. Simply use the @JSONP annotation, and pass it the name of the request parameter which will contain the callback method name:

  class MyCx extends RoninController {
    @JSONP("callback")
    function myAction() {
      Writer.write("{\"foo\": \"bar\"}")
    }
  }

Given this controller method, a request to

http://[server address]/MyCx/myAction?callback=myCallback

will respond with

myCallback({"foo": "bar"})

Ronin provides a powerful admin console to help you perform administrative tasks on a server, or just to poke around your development instance.

The admin console is started by calling AdminConsole.start() from your RoninConfig. The default RoninConfig that was generated for you will start an admin console in development mode only, on port 8022.

Configuring the Admin Console

Simply calling AdminConsole.start(), as is done by the default RoninConfig, will start an admin console on port 8022 which will accept all logins from the local host in development mode, and deny all logins in any other mode. start() takes two optional arguments which allow you to further configure the admin console:

authorizedUsers is a list of usernames indicating which users are allowed to access the admin console. (See the User Authentication section above for details on how to set up a user authentication manager.) If this parameter is provided but no authentication manager has been set up, all logins will be denied. Users will use their regular password when logging in to the console.

port allows you to use a port other than 8022.

Using the Admin Console

The admin console is set up as an SSH server. You have two options for connecting to it:

Use an SSH client. On OS X or Linux, you should be able to run ssh -l [user name] -p 8022 localhost.

Run vark console. See the Aardvark in Ronin section below.

Once you've logged in, you'll see a prompt. The admin console functions mostly the same as the Gosu interactive shell, which means you can enter any Gosu expression or statement, and you can hit TAB for auto-completion of method and property names. The Gosu expressions are run in the context of your Ronin app, so if for instance you're using Tosa as your model layer, all of the database types exposed by Tosa are available, and any queries you run are run on your actual database.

A Word of Warning

The admin console is very powerful. To see just how powerful, type the following:

java.lang.System.exit(0)

This will terminate your Ronin app.

Needless to say, you shouldn't use the admin console in production unless you have a really good reason to, and if you do, you should limit access to the absolute minimum number of users. Any user to whom you wouldn't give root access on the machine your app is running on probably shouldn't have access to the admin console.

Ronin uses Aardvark for its command-line interface. Aardvark is a Gosu-based scripting tool that integrates features from Ant, Ivy and Maven, but allows you to write your build scripts in plain Gosu.

Your application will automatically have an empty build.vark file created in its root. You can add any vark targets you like to this file. Consult http://vark.github.com for more information.

Ronin provides some default targets out of the box:

Aardvark Command Description
vark server This starts up the development server (a Jetty instance) as well as a default H2 instance to develop against. The server will be in development mode, with full refresh, logging and tracing enabled.
vark server -waitForDebugger Will suspend execution of your Ronin app until a debugger is connected. (waitForDebugger can also be used with most of the following targets.)
vark server -dontStartDB Will start the server without starting an H2 web server. When the H2 web server is started, other JVMs won't be able to connect to the database, so use this mode if you have, for example, a utility script that accesses the database while your server is running.
vark verify-app Verifies the resources in your app to ensure that there are no compilation or statically detectable configuration errors.
vark test Runs the tests in the test folder and reports the results.
vark test -trace Enables detailed tracing output during the test, similar to what is logged when running the application in development mode. Use this to help diagnose test failures.
vark test -parallelClasses Will run your test classes in parallel (but the methods within each class serially); vark test -parallelMethods will run the methods in each test class in parallel (but the test classes serially). vark test -parallelClasses -parallelMethods will run everything in parallel. If you are running on a multi-core machine or your tests perform a lot of I/O, running them in parallel may speed up the testing process; it will also ensure that you haven't written your tests to be order-dependent, and that you haven't somehow introduced any concurrency bugs into your application. However, it can be more difficult to write tests that run correctly in a fully parallel environment.
vark reset-db Erases the content of your development database.
vark make-war Creates a war file in the build directory that will allow you to deploy your ronin application to a servlet container, such as Tomcat.
vark console Launches a client to connect to the admin console.

A common need for web applications is to schedule background processes to run at certain intervals. Ronin provides a simple API for this purpose, based on the popular Quartz library.

To schedule a task you can use the schedule() method available in your RoninConfig class when setting up your Ronin application. Here is a simple example:

schedule(:atHour = 1, :task = \-> scrubTempDirectory())

This would schedule the scrubTempDirectory() method to be run every day at 1AM. Of course the body of the block can be any arbitrary expression, so a more complicated job might be encapsulated in a class and invoked:

schedule(:atHour = 1, :task = \-> new TempDirScrubber().run())

Or not.

If you wanted to schedule a job for midnight and 12 pm you could use the :atHours argument instead, which takes a list of hours to run at:

schedule(:atHours = {0, 12}, :task = \-> scrubTempDirectory())

Your scheduling can get quite elaborate, if necessary:

  schedule(:atSecond = 30, 
            :atMinutes = {15, 45}, :atHours = 1..12, 
            :onDays = {MONDAY, LAST_WEDNESDAY}, :task = \-> doIt())

This example would run at the thirty second mark of the fifteenth and fourty-fifth minute of every hour from 1 am to 12 pm, on every Monday and the last Wednesday of the month. Insane, but that's what it would do.

There are some cron-like options that are not available via the simplified arguments of schedule(). You can pass a raw Quartz cron string if you need these more exotic options by using the :cronString parameter:

schedule(:cronString = "0 15 10 ? * 6L 2002-2005", :task = \-> doIt())

According to the Quartz documentation linked to above, this means "Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005"

Why Such An Old Version Of Quartz?

We use version 1.5.2 of Quartz because it doesn't have any dependencies, in stark contrast to the newer versions, and the functionality provided by the newer versions isn't particularly compelling.

Headers/Footers

A web application will often have a static header and footer surrounding a chunk of generated content. You can implement this pattern in Ronin using beforeRequest() and afterRequest() on each controller; however, a more flexible implementation is described here which uses Gosu blocks.

Define a template for your header/footer - let's call it Layout.gst:

    <%@ extends ronin.RoninTemplate %>
    <%@ params(content()) %>
    <html>
    [header content goes here]
    <% content() %>
    [footer content goes here]
    </html>

The parameter specified here, content(), is a block which takes no arguments and has no return value.

Then, in your controller method, render the Layout template, passing in a block which renders your dynamic content:

view.Layout.render(Writer, \ -> view.MyView.render(Writer, args))

Layout will render the header, then invoke your block, rendering the dynamic content, then render the footer.

Consult the Gosu documentation for more information about blocks.

AJAX

Using AJAX with Ronin is quite straightforward - an AJAX request to a Ronin URL is handled like any other request. Ronin view templates can render XML or JSON (or any other data format) instead of HTML, so they can be used for responding to AJAX calls.

If you're using AJAX to dynamically refresh some subsection of a page, here's an elegant way of doing so. Define a controller method which is responsible for rendering the section of the page you'd like to refresh. From the view template for the main page, insert a Gosu snippet which calls that method in the place where that section goes, and it will render the initial contents of the section. Point the AJAX call for the refresh at the URL for the method, and you'll get back the new HTML for the section.