Imagine you're developing a web application with a typical web framework. You've noticed a bug on some page of your app... how do you fix it? Maybe you'll look for some distinctive text on the page, then search for that text in your project using your favorite IDE. Say the text turns up a template which was used to generate the page.

Now you have to track down the data that was used to render the template, which means you need to find the code that retrieved the data, and maybe an XML configuration file which specifies how that data was mapped to an identifier used in the template. If you're not intimately familiar with the inner workings of the app, and of the framework you're using, you've got a lot of searching to do.

With Ronin, everything is out in the open - there's almost no reflective magic or arcane XML files. The URL in your browser tells you which method on which class was called to generate the current page. That method contains a call to render the template for the page, and an explicit set of parameters providing the data for the template. If you're using IntelliJ, you can navigate between all these different pieces with a single click.

And best of all, should you make a mistake - misspell a file name, or leave out a parameter - there will be a compilation error, so you'll know right away instead of having to exhaustively walk through your app (or maintain 100% unit test coverage).

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

First, if you are on Windows, make sure your current directory doesn't contain any spaces - the H2 database that Ronin uses for development has problems with spaces in the path. With aardvark on your PATH, you can run the remote Ronin installation script:

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

When prompted, enter a group ID (e.g. "com.your-company-name") and a name for your application (e.g. "my_app"). You will see output like this:

      [get] Getting: http://gosu-lang.org/nexus/content/repositories/releases/org/gosu-lang/ronin/ronin/maven-metadata.xml
      [get] To: /var/folders/zk/q0vbtth11kbcmbzvwkfpz6_w0000gp/T/maven-metadata661392782909272406.xml
      [get] Getting: http://gosu-lang.org/nexus/content/repositories/releases/org/gosu-lang/ronin/ronin-archetype/0.9.5/ronin-archetype-0.9.5.jar
      [get] To: /var/folders/zk/q0vbtth11kbcmbzvwkfpz6_w0000gp/T/ronin-archetype1860242163037573960.jar
    [unzip] Expanding: /var/folders/zk/q0vbtth11kbcmbzvwkfpz6_w0000gp/T/ronin-archetype1860242163037573960.jar into ./my_app
     [echo] Created a new ronin application at /private/tmp/./my_app.
     [echo]   To start your ronin app, cd my_app; vark server

BUILD SUCCESSFUL
Total time: 0 seconds

Great, you've got a Ronin application set up. Let's look at how it is laid out:

Ronin applications are laid out in a very simple manner:

/build.vark
This is the Aardvark file for your project, and can be used for build scripting.
/pom.xml
This is the dependency file for your Ronin application. Ronin does not use Maven for building, but leverages it for dependencies and IDE support.
/src
This is where all your source will go.
/src/config/RoninConfig.gs
This Gosu class allows you to configure the Ronin application.
/src/controller
This is where your Ronin controllers will go.
/src/view
This is where your Gosu templates will go (if you are using templates for your UI).
/src/db
This is where your .ddl and .sql files will go (if you are using Tosa to connect to a SQL database).
/test
This is where all your tests will go.
/html
This is the root of the Ronin WAR file.
/html/WEB-INF/web.xml
This is a thin web.xml file that bootstraps Ronin and Gosu in standard server environments.
/html/public
This folder can be used for static resources (e.g. images, CSS files, and Javascript).

Note that there is very little configuration, besides dependencies in the pom.xml file. Ronin biases towards source code and away from configuration files, leveraging Gosu's flexible syntax and Open Type System. This makes things obvious and debuggable.

As the installation script says, you can change into your application's main directory and start up the built-in webserver (Jetty):

  $ cd my_app
  $ vark server
  1 [main] INFO Ronin - Environment properties are: {}
	1 [main] WARN Ronin - The DCEVM is not available, Ronin will use classloaders for hotswapping
  182 [main] INFO Ronin - H2 DB started at jdbc:h2:file:runtime/h2/devdb
  2455 [main] INFO Ronin - H2 web console started at http://localhost:8082/frame.jsp?jsessionid=9bca2a3194f8e6a7
  2455 [main] INFO Ronin - 
  You can connect to your database using "jdbc:h2:file:runtime/h2/devdb" as your url, and a blank username/password
  2492 [main] INFO org.eclipse.jetty.util.log - jetty-8.0.0.M2
  2612 [main] INFO org.eclipse.jetty.util.log - NO JSP Support for /
  11324 [main] INFO org.eclipse.jetty.util.log - Started SelectChannelConnector@0.0.0.0:8080
  11325 [main] INFO Ronin - 
  11325 [main] INFO Ronin - Your Ronin App is listening at http://localhost:8080
        

At this point, you should be able to see your application at http://localhost:8080/.

So now you've got a server running, but it's not necessarily doing anything interesting. If you try to access it from your browser, you'll get a placeholder page.

In Ronin, there are generally two components involved in responding to a user's request: a controller and a view. The controller will perform any necessary actions, and then will delegate to a view, which will create the HTML (or other response) that is sent back to the user's browser.

Creating A Controller

Let's create the simplest possible controller. A Ronin controller is defined via a Gosu class. Classes in Gosu are very similar to classes in Java; they are defined in their own file, and the name of the file (plus the directory in which it lives) determines the name of the class. Ronin has a special rule that controller classes must live in the "controller" package (my_app/src/controller). Since our goal is to create a blogging application, let's create a controller class which will eventually contain all of the code for viewing and manipulating blog posts.

Add create and add the following code in the src/controller/PostCx.gs file:

  package controller

  uses ronin.RoninController

  class PostCx extends RoninController {

  }

Let's take a moment to examine the anatomy of this class. The package statement simply restates which package this class lives in. The uses statement identifies a class in another package which we want to reference in this class; this is the equivalent of import in Java. Finally, the class statement restates the name of the class, and identifies its superclass.

Note that unlike a Java import statement, the uses statement doesn't end in a semicolon. Ending statements with a semicolon is not required in Gosu (though it is permitted).

When I tell you later on to add a uses statement to this class, add it after the existing uses statement and before the class statement.

Throughout this tutorial, we'll be using a convention where controller classes have names ending in "Cx" (short for "Controller"). In general you may name a controller class however you like.

Now let's add a function to this class. Each function on the controller class will be responsible for responding to requests from a single URL.

Add this code to your class:

  function viewPost() : String {
	  return "<html><body>This is a post</body></html>"
  }

Gosu functions are defined using the function keyword. They have public access by default, and are assumed to have a void return value unless specified otherwise. This method returns a java.lang.String.

This is the simplest possible controller method: it does not even delegate to a view. Instead, it simply returns a string value directly. Now that you have created a controller function, you can invoke it by simply navigating to:

http://localhost:8080/PostCx/viewPost

While returning strings directly like this can be handy, it is much more common to use a Gosu Template as a view. Let's look at how to do that.

Creating a View

Create the following file in src/view/ViewPost.gst:

<%@ extends ronin.RoninTemplate %>
<html>
  <body>
    This is a post.
  </body>
</html>

A Gosu template is a special kind of Gosu file (with the extension .gst) which mixes plain text with Gosu code to produce the desired output (somewhat like a JSP). Ronin does not require that views live in the "view" package, but it is the conventional place to put them.

Note that templates are a built-in part of the Gosu language, and are not specific to Ronin.

The first line of this template is a template directive, as it is surrounded by <%@ and %>. The extends directive defines a "superclass" for this template; this isn't a true superclass in terms of inheritance, but it allows the template to call static methods on the given class without qualifying them. In this case, we're extending RoninTemplate, which contains some useful methods for Ronin templates.

Now let's use this view template, rather than returning a string. Change the code in PostCx.gs to this:

  function viewPost() {
    view.ViewPost.render(Writer)
  }

and reload the page. You should see the same output.

Note that the method now does not declare a return type, making it void. Rather than returning a value, all response writing is delegated to the ViewPost template, by passing the Writer Property along to it. (Writer is a property available on all controllers.)

Using Gosu code in templates

Let's make this template slightly more interesting. Modify ViewPost.gst as follows:

<%@ extends ronin.RoninTemplate %>
<% uses java.util.Date %>
<html>
  <body>
    The current time is ${new Date()}.
  </body>
</html>

There are two new types of template tags here. The first is surrounded by <% and %>; note that there is no @ like in the directive tags. This type of tag contains Gosu code which is executed as soon as the template encounters it; here, we're using it to add a uses statement. The tag will not be present in the output text.

The second is surrounded by ${ and }. This type of tag contains Gosu code which evaluates to a value; that value is then inserted into the output text.

Reload the page. This time, you should see the current date and time. That's because the expression between ${ and } was evaluated as Gosu code, converted to a String, and inserted into the template.

Our application is still rather uninteresting in that it doesn't really accept any input from the user. Let's address that by parameterizing our controller and view.

Modify the viewPost() method in PostCx.gs as follows:

  function viewPost(post : String) {
    view.ViewPost.render(Writer, post)
  }

and ViewPost.gst as follows:

<%@ extends ronin.RoninTemplate %>
<%@ params (post : String)  %>
<html>
  <body>
    ${post}
  </body>
</html>

We've added a parameter to the viewPost method; its name is post, and its type is String. (Note that unlike in Java, the name comes first and the type second.) We've also added a similar parameter to the ViewPost template, using the params directive. When Gosu sees this, it modifies the template type's render method to take an additional parameter - if we had added the directive but not modified viewPost() to pass in an additional argument, that method call would have produced a compilation error.

When Ronin handles a URL which calls a method with one or more parameters, the values given to those parameters are derived from the arguments in the URL. For instance:

http://localhost:8080/PostCx/viewPost?post=Hello+world

will assign the string "Hello world" to the post parameter of viewPost(). If you access the URL without a post parameter, as we did before, the value of the post parameter will be null.

In a web application, it's important to ensure that you never render user input directly to your HTML (as we have done above), since a user could insert malicious code into your page. The RoninTemplate class provides a method called h() to help with this. Change the line that says ${post} to ${h(post)}, and the user's input will be properly escaped for inclusion in an HTML page.

Before we go crazy adding controllers and views to our application, here's an observation: the <html> and <body> tags aren't likely to change too much between views. It would be nice if there was an easy way to extract the content that's common to all views to a single place, so that it's easier to change and maintain.

Fortunately, there is. Create a file in your view directory called Layout.gst with the following contents:

<%@ extends ronin.RoninTemplate %>
<%@ params(content()) %>
<html>
 <head>
   <title>Blog</title>
 </head>
 <body>
   <div id="content"><% content() %></div>
  </body>
</html>

The parameter we've defined in this template looks a bit different from our previous template. That's because it's a block (also known as a "closure", "first-class function", or "lambda"). The content parameter is itself a function, taking no parameters and returning nothing. As you can see in the body of the template, a block can be invoked like any other function. Here, we're invoking it in order to render the content of the page within the structure we've defined. We can now remove the <html> and <body> tags from ViewPost.gst.

If you're a Java programmer, it may help to think of a block as being like an instance of Runnable, and invoking the block being like calling the run() method. Unlike a Runnable, however, a block can take parameters and return a value, and it can access variables from the scope where it's declared.

Let's modify the viewPost() function to make use of our layout template. Change the contents of the function to:

  Layout.render(Writer, \ -> {
    ViewPost.render(Writer, post)
  })

and add the following uses statements:

uses view.Layout
uses view.ViewPost

The second argument to Layout.render() is a block, which is initiated with the backslash character. As the block takes no arguments, it's followed immediately by an arrow (->), then the contents of the block surrounded by curly brackets. When this block is invoked, the ViewPost template will be rendered.

Now remove the layout elements from ViewPost.gst so that it simply reads:

<%@ extends ronin.RoninTemplate %>
<%@ params (post : String)  %>
{post}

Reload the page and you should see that the output has not changed, but you now have a reusable layout template.

A blogging application needs a way to store and retrieve data - specifically, posts and comments. Ronin allows you to use any mechanism you like for this purpose, but it is particularly well-suited for use with Tosa, a data persistence layer that takes advantage of some powerful features of Gosu to make simple database operations very convenient.

Tosa plugs in to Gosu's type system to generate types based on a pre-defined database schema; unlike with many other similar frameworks, you don't explicitly define classes for the entities stored in your database. Before you can start using Tosa in your Gosu code, then, you need to define your database schema.

Roninit created a file called model.ddl in src/db, which is meant to contain the data definition for your database. Paste the following SQL in to that file, replacing the sample schema that's currently there:

  CREATE TABLE "Post"(
      "id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
      "Title" TEXT,
      "Body" TEXT,
      "Posted" TIMESTAMP
 );
  CREATE TABLE "Comment"(
      "id" BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
      "Post_id" BIGINT,
      "Name" VARCHAR(255),
      "Text" TEXT,
      "Posted" TIMESTAMP
 );

then stop your server and run:

$ vark reset-db

Each entity (Post and Comment) is represented by a table in the database. Each table has one primary key column named "id", and some number of other columns containing information about the entity. A Comment has a foreign key to the Post on which it was made, as denoted by the column named "Post_id".

The connection string for your database is managed in the RoninConfig class in src/config. Here is the default configuration code:

  if(m == DEVELOPMENT) {
    db.model.Database.JdbcUrl = "jdbc:h2:file:runtime/h2/devdb"
  } else if(m == TESTING) {
    db.model.Database.JdbcUrl = "jdbc:h2:file:runtime/h2/testdb"
  } else if(m == STAGING) {
    db.model.Database.JdbcUrl = "jdbc:h2:file:runtime/h2/stagingdb"
  } else if(m == PRODUCTION) {
    db.model.Database.JdbcUrl = "jdbc:h2:file:runtime/h2/proddb"
  }

As you can see, depending on the mode that Ronin is in, it will use various different database connection strings. You can always tell which database you are connecting to because, like all Gosu code, this is debuggable.

Our database isn't very interesting without any data in it, so let's create a page where the user can enter a new post. Add these uses statements to PostCx:

uses db.model.Post
uses view.EditPost

and these methods:

  function create() {
    Layout.render(Writer, \ -> {
      EditPost.render(Writer, new Post())
    })
  }

  function save(post : Post) {
    // we will fill this in later.
  }

The create() method will render a template named EditPost (as we will be using the same template for creating a new post and editing an existing post), passing in a new instance of Post. Note that we never defined a Gosu class called db.model.Post; that type is generated for us by Tosa because it is a table in the database. When we create a new instance here, we are not yet performing any database operations - we're simply creating an object in memory which can be read from or persisted to the Post table.

(If the Gosu editor shows an error on db.model.Post, you may have to restart it in order for it to find the database types.)

Create the EditPost.gst template in the view directory with the following contents:

<%@ extends ronin.RoninTemplate %>
<%@ params(aPost : db.model.Post) %>
<% uses controller.PostCx %>
<% uses db.model.Post %>

<% using(target(PostCx#save(Post))) { %>
<form method="post" action="${TargetURL}">
  <% if(not aPost.New) { %>
  <input type="hidden" name="${n(Post)}" value="${aPost.id}">
  <% } %>
  <input type="text" name="${n(Post#Title)}" value="${h(aPost.Title)}"><br>
  <textarea name="${n(Post#Body)}" rows=20 columns=80>${h(aPost.Body)}</textarea><br>
  <input type="submit">
</form>
<% } %>

Let's walk through this template. The first two lines should appear familiar to you. Note that the type of the parameter is our entity type, since that's what we're passing in from the controller.

The first code snippet after the uses statements opens a using statement. This is a Gosu construct which allows you to safely initialize and dispose of resources used within the contained code. Here, we are calling a method on RoninTemplate called target(), and using the context object that it returns.

The parameter we're passing in to target() is a method literal, indicated by the use of the # operator (instead of a dot operator). Whereas a dot operator would cause the method to be called, the # operator simply represents a theoretical future call to the method, and it returns a MethodReference object. target() takes the method literal to mean that you are defining some part of your template in the context of a call to the save() method.

(Method literals come in two flavors - those with bound arguments and those with unbound arguments. If specific values are provided for the method's arguments, they are said to be bound; if only the types of the arguments are specified, as is the case here, they are unbound. target() does not require bound arguments.)

The action attribute of the form tag is being set to the string returned by the TargetURL property. Since we're inside the using statement defined on the previous line, the TargetURL will be the URL to which our form should be posted in order to invoke the save() method: http://localhost:8080/PostCx/save.

So why not just put that URL in the action attribute by hand? Say you change the name of the save method for some reason. If the URL were hard-coded in your HTML, you wouldn't know anything was wrong until you ran your application, tried to submit the form, and got an error. Using the target() method, on the other hand, means that PostCx#save(Post) will not compile if the method has been renamed (or its signature has been changed, etc.), so you'll see instantly in the editor that something is wrong.

After the form tag, we have an if statement. If the post passed in to this template is not a new post, we want to store its ID in a hidden input, so that we know which post to edit when the form is posted to the server. We determine whether the post is new by accessing the New property, which is created automatically on all Tosa entity types, and returns true as long as the entity has not yet been saved to the database.

Since we know our post will be a new post for now, let's skip ahead. After we've closed the if statement, we have a text field for the post's title. The value of the text field is set to the title of the existing post object, aPost.Title (after being escaped by the h() function). The name of the field is set by a call to a helper method named n() (for "name"). The argument to n() is a property literal, which is like a method literal, but for a property instead of a method - here, for the Title property on Post. n() finds the parameter to save() which takes a Post, and generates the appropriate input name to set the Title property (here, post.Title). The text area on the next line performs the same task for the Body of the post.

Let's go over what will happen when we submit this form, say with the title "Hi" and the body text "Hello world". The browser will post to the URL http://localhost:8080/PostCx/save, with the form data post.Title=Hi&post.Body=Hello+world. Ronin will find the save function and see that it requires a db.model.Post parameter; since we haven't told it a specific one to use, it will create a new one. It will then examine the form data, and set the Title property of the post to "Hi" and the Body property to "Hello world". This will all happen automatically, so most of the time you won't have to worry about it.

Now we'll go back and implement the save() method on PostCx:

uses java.lang.System
uses java.sql.Timestamp
function save(post : Post) {
    if(post.New) {
      post.Posted = new Timestamp(System.currentTimeMillis())
    }
    post.update()
    redirect(#viewPost(post))
  }

If the post is a new post (as it is in our case), we will set its Posted property to the current time. (Note that the type of the Posted property is java.sql.Timestamp, since the Posted column in the database is a TIMESTAMP column.) After we've done this, we save the post to the database by calling its update() method (which is generated for us by Tosa). Finally, we redirect the user to a page where they can view the newly saved post.

This redirection bears further examination.

redirect(#viewPost(post))

redirect() is a method we've inherited from RoninController. It takes a single argument, which is a method literal with bound arguments. Note that since the method in question is on the same class, we don't need anything before the # operator. redirect() examines the method reference and determines what URL would invoke it, then sends the browser a response telling it to redirect to that URL.

(By sending a redirect to the browser, we're ensuring that if the user hits the back button, they are returned to the edit screen, instead of to the URL which saved the post - that would result in a duplicate post being saved to the database. It's generally a good idea to redirect after any action that changes data or is otherwise not idempotent.)

Let's quickly modify viewPost() so that it actually views a post:

  function viewPost(post : Post) {
    Layout.render(Writer,
      \ -> ViewPost.render(Writer, post))
  }

And the ViewPost.gst template:

  <%@ extends ronin.RoninTemplate %>
  <%@ params(post : db.model.Post) %>

  <div class="header">${h(post.Title)}</div>
  <div class="body">${h(post.Body)}</div>
  <div class="posted">Posted on ${post.Posted}</div>

All the pieces are in place. Restart the server, go to http://localhost:8080/PostCx/create, and create a new post. After clicking the submit button, you'll be redirect to the ViewPost page for that post. Keep this open in a browser tab for later.

Now let's say we want to edit the post we've created. As we noted above, we can reuse the same template. Let's go back and look at the if clause that we skipped before:

<% if(not post.New) { %>
  <input type="hidden" name="${n(Post)}" value="${post.id}">
<% } %> 

If the template receives an existing post, it will create a hidden input on the form whose value is the post's unique ID (primary key). The name of the input is generated by calling n() with the Post type, which finds the parameter of that type expected by save(), so when Ronin calls save(), it will use the ID to look up the existing post in the database. The Title and Body properties of that post will then be set using the values of those inputs, just as they were before, and the save() method will update the entity in the database.

So how do we get an existing post into the EditPost template? Create the following function in PostCx:

  function edit(post : Post) {
    Layout.render(Writer, \-> 
      EditPost.render(Writer, post))
  }

That's all there is to it. When http://localhost:8080/PostCx/edit is accessed, Ronin will use the URL parameters to look up the post and pass it in to the edit() method, which passes it in to the template.

Now let's take a look at the other table in our database - comments. Ideally we want to show all of the comments for a post on the page where we view the post. Add the following code to the bottom of ViewPost.gst:

  <% for (comment in post.Comments) { %>
    <div class="comment">
      <div class="commentAuthor">${comment.Name} - ${comment.Posted}</div>
      <div class="commentBody">${comment.Text}</div>
    </div>
  <% } %>

This is a Gosu for loop, which is similar to a Java for loop, but with the in keyword in place of the : character. The collection we're looping over - post.Comments - is of particular interest here. You'll notice that it doesn't correspond to a column on the Post table in our schema above. Instead, it represents the Post_id foreign key on the Comment table. post.Comments will return all of the Comments whose Post_id matches the ID of the post.

Also note that, unlike in Java, we haven't explicitly stated the type of the comment variable. Gosu infers the correct type for this variable because it knows that post.Comments contains objects of that type.

Let's add one final piece to our application - a page which lists all of the posts we've created, in reverse chronological order.

Add the following to PostCx:

uses view.AllPosts
  function all() {
    var posts = Post.selectAll().orderBy(Post#Posted, DESC).toList()
    Layout.render(Writer, \ -> AllPosts.render(Writer, posts))
  }

The first line of the function declares a local variable. As in our for loop, it is not necessary to specify the type of the variable - Gosu will infer the correct type from the value you assign to it. Instead, you use the var keyword to declare the variable.

The selectAll() method is one of several static methods present on all types generated by Tosa. It returns a QueryResult object that will return all Posts in the database (we could have used select(), selectWhere(), or selectLike() if we wanted to add a WHERE clause to the query). We then apply an ordering to the query before it's issued, using the orderBy() method, which takes two parameters: the property we want to sort on and the sort direction. We specify the Post#Posted property, so the posts will be sorted chronologically, and DESC as the sort direction so that more recent posts will appear first. (The second parameter is optional and defaults to ASC.) If we needed a more complicated sort, we could have used orderBySql() instead, which takes a direct SQL statement to use as the order by clause. Lastly, we materialize the results as a List to pass off to our template by using toList(). (We could have also simply passed the QueryResult itself off to a template that was expecting to get an Iterable instead of a List.)

We then pass the list of posts to a new template, AllPosts.gst. Create this file and give it the following contents:

<%@ extends ronin.RoninTemplate %>
<%@ params(posts : List<db.model.Post>) %>
<% uses controller.PostCx %>

<div class="header">All Posts</div>

<% for(post in posts) { %>
  <div class="postListEntry">
    <a href="${urlFor(PostCx#viewPost(post))}">${post.Title}</a>
  </div>
<% } %>

By this point, everything here should be familiar. Go to http://localhost:8080/PostCx/all to see all the posts in the database.

In this tutorial, I've shown you the basics of working with Gosu and Ronin. To learn more about Gosu, visit the Gosu website. For more details on Ronin and Tosa, dig into the documentation.

Here are some further exercises for extending our blog application, from easiest to most challenging:

If you need some help with these exercises, or just want to see more examples of how to use Ronin, download and examine the full sample RoBlog application.