Você está na página 1de 19

RAV E N OU S D E V E LOP E RS G U I D E

Just enough to get you started and leave you confused

Based on Ravenous v0.7.2


March 17, 2006

Ravenous Developers Guide


1
RAV E N OU S D E V E LOP E RS G U I D E

Written by Kasper J. Jeppesen


Solido Systems A/S

Introduction
Ravenous is a Java based webserver which does not in any way attempt to implement any
parts of the Java Enterprise Edition standard. Instead it aims to provide you, the devel-
oper, with a simple easy to grasp way of developing solid high performance web sites in
Java.

This short guide, will introduce you to the basic stuff you need to know before you can
start pumping out great Ravenous based websites. It is assumed that you are somewhat
familiar with UNIX, Java and web development in general.

Installation
Ravenous should be able to run fine on most modern UNIX flavors with good Java sup-
port. The following 8 steps will show you how to install and start Ravenous.

1. Download Ravenous from http://sourceforge.net/projects/ravenous/

2. Expand the distribution archive. ( tar -zxvf )

3. Change your working folder to that of the distribution. ( cd ravenous )

4. Run install.sh as root. (sudo will do fine)

5. Start Ravenous by running “/usr/local/ravenous/bin/ravenousctl.sh start” as root. (


sudo will do fine )

6. Point your browser at http://localhost/ and check that Ravenous is running.

7. Click on the compile test link to verify that Ravenous can compile and run dynamic
content.

8. Pat yourself on the back for a job well done.

If Ravenous won’t start, it is most likely due to a missing java binary in /usr/local/bin/. Use
your favorite editor to fix the problem in /usr/local/ravenous/bin/ravenousctl.sh

If Ravenous started, but can’t compile dynamic content, it is most likely due to a missing
javac binary in /usr/local/bin/. Use your favorite editor to fix the problem in
/usr/local/ravenous/etc/ravenous.conf

Ravenous Developers Guide


2
If are still experiencing problems, have a look in the log file called
/usr/local/ravenous/log/ravenous.dump. If that still doesn’t help you to get Ravenous to
work, start pestering me at kjj@solidosystems.com or through ichat/aim where my ac-
count handle is kasperjjeppesen.

First Contact
Lets start out by creating the simplest page possible... in other words, lets do a Hello
World! example. Call your page hello.rvp and place it in the document folder of your
Ravenous site. If you have not setup a specific site, but simple installed Ravenous as de-
scribed in the previous section, simple place your hello.rvp file in /usr/local/ravenous/
docs/. That is the document folder for the default site you just saw when testing your
Ravenous installation. Type in the following code in hello.rvp using your favorite text edi-
tor.
<html>
<head>
<title>Test page</title>
</head>
<body>
<p>
<?rav
println(“Hello World!”);
?>
</p>
</body>
</html>

Now open your browser, and point it at http://localhost/hello.rvp (or some other location
if you are not using the default site). This will make Ravenous compile our hello world
code into a class, instantiate the class, and use it to generate output for your browser. If
all went well, you should now see the hello world text in your browser.

MVC
In theory, you could write all your Ravenous based websites using only .rvp files as dem-
onstrated in the previous section. This would give you a development framework much
like PHP, but with Java as programming language. However, much of Ravenous has been
designed with the MVC (model view controller) architecture in mind, so to get the full
advantage of working in Ravenous, you really should be developing MVC based sites.

Lets forget the model part for a second and focus on the controller and view parts. Con-
trollers in Ravenous are written in files with .rvc as suffix. In-order for Ravenous to rec-
ognize them, they must be placed in a folder with the same name as the controller. As an
example, a controller called foo would be written in a file called foo.rvc which is placed in
a folder called foo. Confused? Lets create an example to illustrate this. Create a folder

Ravenous Developers Guide


3
called test in your sites documents folder (the same location you placed the hello.rvp file
in). Now create a file called test.rvc with the following content and place it in the test
folder.

public void index(){


}

Ravenous will now recognize this controller in the subpath of your site called /test/. This
means that all requests for http://localhost/test/ will be interpreted as requests which
should be handled by your test controller. If you pointed your browser to this location
right now, Ravenous would recognize that you intended to call the method index on your
test controller. You could try this right now (ok admit it... you already did it, right?) but
all you would get, is an error message telling you that Ravenous can’t find the template
for the index method.

So how do we give Ravenous a template for the index method? Simply create a file called
index.rvp and place it in the same folder as your test controller. To speed things along,
simply copy the contents of your hello.rvp page into your new index.rvp page. If you re-
load http://localhost/test/ you should now see the same output you did when running the
hello world test. You won’t be able to see it, but this time, Ravenous actually started out
with a call to the index method before executing the view page. All we need now, is a way
to transport data from the controller to the view right? There are two ways to do this, the
first is to directly access the controller object, and the second is to transport the data to
the view through a hashtable called ENV.

Lets have a look at the direct method first. We will first modify our controller to leave
some data behind for us:

protected String data=”No calls yet...”;

public void index(){


data=”You called the index method!”;
}

And now lets modify our view to access the data variable on our controller. Because I’m
really really lazy, I won’t repeat the entire view code here, but just the Java code.
<?rav
println(controller.data);
?>

Simple right? We could also call methods on the controller if we needed to. Now lets look
at the other method. We will be placing the data in a hashtable called ENV. This is not a
regular hashtable as found in the java.util package, but a custom class called Flexible-
Hash. It works almost like the regular hashtable, but always assumes that you will be
working with strings if you don’t specifically tell it otherwise. Lets look at the controller
again.

Ravenous Developers Guide


4
public void index(){
ENV.put(“data”,”You called the index method”);
}

And now for the view.

<?rav
println(ENV.get(“data”));
?>

The direct way of accessing the controller might seem prettier, however, this second way
is actually the preferred way of doing it. The reason is, that it completely isolates the
view from controller. The view does not need to know how the controller works, it can
simply stick to its job... to present data to the browser. In-order to keep the view as sim-
ple and readable as possible, we have added a set of tags which allow you to access the
data in the ENV hashtable without any Java code. Lets look at an example which does
exactly the same as the last view example.

<?rve “data” ?>

Thats basically how controllers and views work in Ravenous. If you need to know more,
skim through the lists of advanced view and controller wisdom in the end of this section.
We still need to talk about the model part of the whole MVC architecture. Models are
supposed to hold the actual data model which the controller manipulates. We will have a
very deep look into this in the section called Relational Mapping, but for now lets have a
short look at Ravenous model files.

Models are contained in files named with .rvm as suffix. The base of the name should
match the name of a Java class contained in the file. We could for example have a file
called User.rvm, which contained a class called User. This class would be available for use
by controllers located in the same folder as the model file. If you need to create models
which are shared between all controllers in your site, you can create a folder in the root
of your site called model and place all your .rvm files in that folder. That will automati-
cally make them available to all views and controllers.

A D VA N C E D C O N T R O L L E R U S A G E
• If you need to create a controller for the root of your site (http://localhost/), simply call
the controller index.rvc and place it in the root of the site. This will allow Ravenous to
recognize it as a controller even though it might not have the same name as the folder
it lies in.

• You can create controllers within controllers, simply by creating subfolders containing a
controller in the same way as other controllers. If a method on a controller is called the
same as a direct sub controller, any requests to the URL which matches both the
method and the sub controller, will always go to the sub controller.

Ravenous Developers Guide


5
• Your controller will automatically extend a class called ControlEngine.... look it up in
the documentation, it contains lots of useful methods!

• If you need shared code to be run before each method on your controller is run, you
can override the method called void preAction() on your controller.

• If you redirect using the setRedirectAction method in the preAction method, your ac-
tion method on the controller will never be run. Use this to create controller wide user
access protection.

• If you need shared code to be run after each method on your controller is run, you can
override the method called void postAction() on your controller.

• If you need shared code to be run after the view has been executed, you can override
the method called void postView() on you controller.

• If you need maintenance code to be run daily weekly or monthly on your controller, you
can override the methods called executeDaily(), executeWeekly() and executeMonthly().

• Need to bypass the view and parent template? Just create your method with String as
return type instead of void.

• Need to return binary content? Just create your method with byte[] as return type in-
stead of void. Remember to set your mime type!

• Time to stop repeating yourself? You can create a folder called control in you documen-
troot and place controllers in it. These will be available for inheritance in all your other
controllers.

A D VA N C E D V I E W U S A G E
• If you don’t want to repeat yourself in every view, you can create a shared template in a
file called parent_template.rvp, located in the same folder as your controller. In this file,
you will find the output of your view as a string called BODY.

• You can create a site wide parent template, by placing a file called parent_template.rvp
in the root of your site. If any of your controllers has its own parent_template.rvp, the
site wide template will be ignored.

Interacting with the user


Now that you know how to structure your site, lets have a look at how data is passed to
your controllers through query strings and form posts. The simple answer is that these
are transfered to you in the form of two different hastables: GET and POST. Just as the
ENV hashtable, these are not java.util hashtables, but of the custom type called Flexi-
bleHash. Any changes that you make to these hashtables, will persist throughout the
handling of the request. This means that you can add or remove data to these hashes in

Ravenous Developers Guide


6
the controller to modify what the view sees (you can also modify them in the preAction
method to modify what the controller action sees).

Transferring data to the query string is a simple matter of adding query parameters to the
links presented to the user. Sending post data is likewise a simple matter of creating a
normal HTML form with the controller set as its action, and letting the user submit it.

In the last couple of years, it has become very trendy to avoid setting query parameters as
arguments to the query string but instead to transfer these parameters as a part of the
resource path.... lets look at an example of the traditional query string:

http://localhost/show.rvp?id=2384

The parameter here is id and the value 2384. Now lets look at the über trendy way of do-
ing it.

http://localhost/show/2384/

The idea here, is that the simpler URL is easier for users to remember, write down or
dictate to their mother over the phone. In most web development frameworks, these
kinds of URLs are actually translated into the old kind of URL before the actual script
receives the request. Sometimes this is done automatically (as is the case for Ruby on
Rails), at other times, it is done through a separate service such as mod_rewrite under the
Apache webserver.

In Ravenous, we decided that wanted to support these kind of URLs, but that there
shouldn’t be any translation or configuration necessary. This is done by having Ravenous
automatically figure out which parts of the query path are used to specify the controller
and method, and which parts represent parameters. In the last URL, Ravenous would
have assumed that show was the name of the method to call on the index controller, and
that 2348 was a parameter. This parameter will be transfered to the code in your control-
ler through a list object called ACTION. Just as we have seen with previously used hash-
tables, this is not a standard java list, but instead a class called FlexibleList. In the last
URL, the string “2348” would be transfered to your controller as the item at index 0 in
this list. You can have as many parameters as you like separated by the / symbol in your
URLs.

If you have a method on your controller, which must always be passed a fixed set of pa-
rameters, you can specify this directly in your code, and have Ravenous automatically
transfer the correct parameters to your method. Lets look at an example for the show
method.

public void show(String id){


}

If this was the code being executed for the last URL, then the string “2348” would have
been given to the method as the parameter id. You can add as many parameters as you

Ravenous Developers Guide


7
need. Ravenous will look for the method which fits the exact number of parameters. Fail-
ing that, it will look for a method without parameters.

Cookies and Sessions


Cookies can be set both in your controllers and views using the setCookie method. You
can find documentation for this method by looking up BasicEngine in the online refer-
ence. Lets have a look at the most basic usage of the setCookie method:

public void index(){


setCookie(“cookiename”,”cookievalue”);
}

For most uses, you would probably rather use sessions instead of manually setting cook-
ies. The session system in Ravenous allows you to store any kind of Java object you like in
a hashtable for each user. Before you can use the session system, you must initialize it.
This must be done for every request in which you intend to use session data. Fortunately
it is extremely simple to do. The following example shows how to initialize the session
system in the preAction method. This is the natural place to do it, so you won’t have to
think about it in the individual methods.

public void preAction(){


startSession();
}

After this initialization, you can access session data for the user in a FlexibleHash called
SESSION.

A D VA N C E D S E S S I O N U S A G E
• In theory you can place any kind of data you like in the session hash, but if you intend
to use disk based or clustered sessions, you should stick to simple non object types,
Strings, FlexibleHash and FlexibleList objects. This will ensure that Ravenous is able to
detect changes in the session data and that it is able to serialize the data correctly.

• The default behavior of the session system, is to recognize the user through the user of
a cookie called rvn_session_id. If you wish to transfer the session id in some other way,
you can manually specify the session id when starting the session.

Database Connections
Ravenous includes functionality to automatically manage a pool of JDBC connections for
your sites. This however, does not mean that you absolutely have to use it, you can still
open and close JDBC connections manually when needed or use a non JDBC based data-
base system. However, if you are going to use a JDBC connection to handle your database
interaction, you might as well have a look at the build in features.

Ravenous Developers Guide


8
The class called BasicEngine, includes two methods for opening JDBC connections:
openJDBCConnection and openExclusiveJDBCConnection. They will both automati-
cally maintain a connection pool, but openExclusiveJDBCConnection, will ensure that
no other thread will be able to access the given JDBC connection while you are using it.
Use this call when you need to use transactions. (Transactions are handled on a per con-
nection basis in the JDBC api) At all other times, you should use the non exclusive con-
nections, since they will ensure the highest possible performance.

You can open and use JDBC connections both in your controllers and view, but it is gen-
erally recommended that you leave all database usage to the controller. Lets look at an
example which shows how to open and use a JDBC connection to a PostgreSQL database
in your controller.
private Connection dbcon;
private Statement dbst;

public void preAction(){


try{
String driver=”org.postgresql.Driver”;
String db=”jdbc:postgresql:testdb”;
String user=”testuser”;
String pass=”foobar”;
dbcon=openJDBCConnection(driver,db,user,pass);
dbst=dbcon.createStatement();
}catch(Exception e){
setRedirectAction(“/dberror/”);
}
}

public void index(){


ResultSet rs=dbst.executeQuery(“SELECT id,username FROM tbl_user”);
}

public void dberror(){


}

There are a couple of things to notice here. The first is that you typically open your con-
nection in the preAction method, since it will typically be used for most of the methods
on the controller. Because Ravenous uses a connection pool, the overhead for doing this
is extremely low. The next thing to notice, is that we have wrapped our connection at-
tempt in a try catch section, which redirects the page if an exception occurs. It might
seem like this would cause an endless redirection loop, but it doesn’t. Ravenous auto-
matically checks for this, so that redirects will only take place if they actually redirect the
user to another location.

When you are done using a connection, simply leave it... Ravenous will automatically re-
turn it to the connection pool after the page request has been executed.

Ravenous Developers Guide


9
If you are primarily opening connections to a single database, you can leave the configu-
ration in a file called db.conf placed in the document root of your site. The following ex-
ample shows the typical contents of this file.

username = testuser
password = foobar
database = jdbc:postgresql:testdb
jdbcdriver = org.postgresql.Driver

By using the file, the previous example could be written as follows:

private Connection dbcon;


private Statement dbst;

public void preAction(){


try{
dbcon=openJDBCConnection();
dbst=dbcon.createStatement();
}catch(Exception e){
setRedirectAction(“/dberror/”);
}
}

public void index(){


ResultSet rs=dbst.executeQuery(“SELECT id,username FROM tbl_user”);
}

public void dberror(){


}

You don’t need to worry about people being able to access your database configuration.
Any file called db.conf located in the documentroot of your site cannot be accessed
through HTTP.

Relational Mapping
Ravenous includes a system for automatically mapping an object oriented datamodel
onto a relational database through a JDBC connection. The system requires that you fol-
low some of our naming conventions, but other than that, its quite easy to use, so don’t
let the size of this section scare you... you don’t need to know everything to start using
the system.

Lets go through a small example for a multiuser weblog. We will create three tables for
our weblog: users, blog entries and comments. Lets first look at the table design for the
user table.

CREATE TABLE tbl_user(


id CHAR(14),
username VARCHAR(64),

Ravenous Developers Guide


10
password VARCHAR(32),
tmodified TIMESTAMP,
tcreated TIMESTAMP
)

The first naming convention you need to learn is that the names of your tables must start
with tbl_. The next is that each table must have column named id. This can be either an
auto incrementing integer, a 14 or 36 characters string based id. Be aware that some data-
bases (PostgreSQL is one example) won’t return the id of the last row created. This pre-
vents Ravenous from using auto incremented integers as id’s in some databases. In this
example, I have used the 14 character string option. In Ravenous we call this type of id
for LUID (Locally Unique ID)... the 36 character type is called GUID (Globally Unique
ID). Lets have a look at the model which will map onto this table. The model should be
named so it matches the table name, excluding the tbl_ prefix and with the first character
upper cased. So for our user table we will name the model User and place it in a file called
User.rvm.

public class User extends JDBCModel{


persistent LUID id;
persistent String username;
persistent String password;
persistent Timestamp tmodified;
persistent Timestamp tcreated;
}

The first thing you should notice about this class is that it extends JDBCModel. This is
what lets Ravenous know that you need it to generate relational mapping code for your
class. The second thing is that all the instance variables match the names and types of the
database table and that they are declared as persistent. This lets Ravenous know that it
should map these variables onto the tbl_user table. If you need additional instance vari-
ables on your class to support your business logic, simply declare them as you normally
would using private, protected or public.

Now that we have created our model, lets look at how we can use it. The first task is ob-
viously to create new data. We do this by using the static method createNew to obtain an
instance, then we fill in the needed data and call storeData to save it to the database.

User usr=User.createNew();
usr.setUsername(“root”);
usr.setPassword(md5(“root”+”password”));
usr.storeData(dbst);

Ravenous will automatically create set and get methods for all your persistent variables.
You will need to use these to access them in-order for Ravenous to do its magic. There is
one more bit of magic that happens here. If you create a persistent variable called tcre-
ated of the Timestamp type, then Ravenous will automatically fill it in when the object is
created. Likewise, if you create a persistent variable called tmodified, also of the type

Ravenous Developers Guide


11
Timestamp, Ravenous will automatically update it whenever you change the data on the
object.

The next task we need to know how to do, is to fetch data from the database again. We
do this through static methods on the User class. The first method we will look at, is used
to fetch a single User.

User usr=User.fetchSingle(dbst,id);

The type of the id argument should match the type of the id. That is, if its an autoincre-
menting integer id, then the value should be an integer, if its a GUID or LUID, it should
be a String. If the database does not hold an entry with a matching id, then null will be
returned.

So now we have our entry back. If you need to change any persistent data held by the ob-
ject, simple use the set methods and then call storeData as when we created the object.

User usr=User.fetchSingle(dbst,id);
usr.setUsername(“looser”);
usr.storeData(dbst);

If you need to delete an object again, just call delete on the object.

User usr=User.fetchSingle(dbst,id);
usr.delete();

And... should you need to delete all objects... just call deleteAll.

User.deleteAll(dbst);

Now lets move on to querying for more than one object at a time. The first most simple
way to do it, is simply to ask for all objects belonging to that model.

List lst=User.fetchAll(dbst);

The returned list is an ordinary java.util list. If we need to query for a specific set of ob-
jects, we can add a query string to the fetchAll method. This query string will be injected
into the where clause of the SQL query used to fetch the objects. You can use any kind of
SQL statement which your chosen database would normally accept in the where clause.
List lst=User.fetchAll(dbst,”username like ‘John%’”);

Need to sort the output? Simply call fetchAllOrdered instead of fetchAll, and add an or-
der statement at the end. Again, any kind of SQL statement that your database would
normaly accept in the order clause will do.

List lst=User.fetchAllOrdered(dbst,”username”);

The next thing we need to look at is relations. Lets add a new table for blog entries called
tbl_blog.

CREATE TABLE tbl_blog(


id CHAR(14),

Ravenous Developers Guide


12
user_id CHAR(14),
title VARCHAR(256),
body VARCHAR(32768),
tmodified TIMESTAMP,
tcreated TIMESTAMP
)

Notice the column called user_id, this is our relation to the user table. In-order for Rav-
enous to use this, this column must be named with the name of the foreign model and
_id. Lets look at the class for this table.

public class Blog extends JDBCModel{


persistent LUID id;
persistent relation belongsTo LUID user_id;
persistent String title;
persistent String body;
persistent Timestamp tmodified;
persistent Timestamp tcreated;
}

The new thing here is the variable which is declared as a persistent relation instead of
just persistent. There are currently only two kinds of relations that are recognized by
JDBCModel: belongsTo and hasMany. Lets revisit the code for the User model and add a
hasMany relation to the new Blog model.

public class User extends JDBCModel{


persistent LUID id;
persistent relation hasMany blog_id;
persistent String username;
persistent String password;
persistent Timestamp tmodified;
persistent Timestamp tcreated;
}

This time the instance variable blog_id, does not refer to an actual value on the tbl_user
table, but is simply a naming convention to let Ravenous know that it needs to go look
for a table called tbl_blog, and that the id column in that table is called id.

Lets first take a look at the way we can use this relation to fetch all blog entries for a
given user in our code.

User usr=User.fetchSingle(dbst,id);
List lst=usr.fetchBlogList();

We could actually add a query to the fetchBlogList method. We could also have used
fetchBlogListOrdered, just like the regular fetchAll. Lets have a quick look from the
other side. When have a blog entry, and need to find the related user.
Blog blog=Blog.fetchSingle(dbst,id);
User usr=blog.fetchUser();

Ravenous Developers Guide


13
Simple right? Now we just have one thing left to look at. How to create a new blog object
with the user relation already set. That is, we have a user, and need to create a new blog
object for the user in hand.

User usr=User.fetchSingle(dbst,id);
Blog blog=usr.createNewBlog();
blog.setTitle(“A new blog entry”);
blog.setBody(“Talking about irrelevant stuff...”);
blog.storeData(dbst);

A D VA N C E D J D B C M O D E L U S A G E
• If you place your model files in a folder called model in the document root of your site,
they will automatically be available for all controllers.

• BasicUtil which is a shared ancestor for all controllers and views, contain two functions
createLUID() and createGUID() for creating these two types of ids.

• Once you have obtained an instance from your model, you don’t need to keep passing it
the database connection. The instances will keep an internal reference to the connec-
tion used to fetch them and use that if you omit it.

• Ravenous won’t create an id for a new record before you call storeData.

Working with SubViews


Once you start building larger sites, you will most likely end up repeating large sections
of view code. Ravenous includes a feature called SubViews, which allows you to create
reusable and extendable view components.... think of them as user interface plugins. Lets
start out by looking at the most basic SubView usage.

Create a folder in your document root called subview. Place a text file in it called
time.rvp. The contents of this file can be anything valid in an ordinary Ravenous rvp file.
In this example, I will use the following piece of code.

<h1>Current Time</h1>
<p><?rav
println(new java.util.Date().toString());
?></p>

Now lets look at the file using the subview... this can be any view template in your site
(.rvp files). The first way of usage we are going to look at, is the simple step by step code
for getting a subview, and asking it to render its content.

<?rav
SubView v=getSubView(“time”);
println(v.render());
?>

Ravenous Developers Guide


14
Simple right? but not very convenient. This way of using subviews actually has some very
cool possibilities, but for now, lets look at an easier way, which requires a bit less typing
=).

<?rvs time ?>

That will do exactly the same as the previous example. Now lets look at how we can con-
figure our subviews. Lets first modify our time subview so it can handle a background
color for the time data.

<h1>Current Time</h1>
<p style=”background:<?rav
print(CONFIG.get(“background”));
?>”>
<?rav
println(new java.util.Date().toString());
?>
</p>

<?rav
public void setDefaults(){
CONFIG.put(“background”,”#FFFFFF”);
}
?>

As you can see, we store all configuration data for our subview in a FlexibleHash called
configuration. If you create a method called setDefaults, this will be called to fill in the
configuration with default values for the subview. After the defaults have been set, Rav-
enous will look for a file called time.plist which, if it exists, must contain XML data in
Apple Property List style for a FlexibleHash (FlexibleHash has a method called
writeToFile, which will automatically write down this file for you from data in the hash).
If this file exists, the values present in this hash will override those set by setDefaults.
And finally, they can once again be overridden when the subview is used. The idea is that
the subview is distributed with a set of defaults, if you generally need to change these for
all pages in your site, set the sitewide configuration in the plist file, and finally, if you have
specific needs on the current page, set them when you use the subview. Lets look at how
we change the background of the time paragraph to a green color. First in the step by
step java code.

<?rav
SubView v=getSubView(“time”);
v.put(“background”,”#00FF00”);
println(v.render());
?>

And then in the short tag way of using subviews.

<?rvs time:{background=”#00FF00”} ?>

Ravenous Developers Guide


15
If you need to add further configuration parameters, simply add them separated by com-
mas.

Without some way of providing these subviews with specific data to render, they will not
be very useful. So lets look at how we pass them some data.... once again, we will first
start looking at the step by step java code.

<?rav
SubView v=getSubView(“time”);
v.put(“background”,”#00FF00”);
FlexibleHash data=new FlexibleHash();
data.put(“time”,System.currentTimeMillis());
println(v.render(data));
?>

Inside the SubView code, you will now find the FlexibleHash passed to the render
method as a FlexibleHash called DATA. (It will be called data no matter what you call it
when you pass it to render). Its a bit different for the tag style, there you must first have
the data set as a value in the ENV hash. Typically you would do this in the controller.... so
lets look at the controller code.

public void index(){


FlexibleHash data=new FlexibleHash();
data.put(“time”,System.currentTimeMillis());
ENV.put(“timedata”,data);
}

And now for the page template.

<?rvs time:timedata:{background=”#00FF00”} ?>

Notice how timedata is a reference to the key in the ENV hash. Remember that the
value must be a FlexibleHash.

This might all seem a bit weird... why would we demand that the type FlexibleHash is
used for passing data to the subview? Well, the FlexibleHash and FlexibleList data types,
are advancing into a central role in many forthcoming Ravenouse features. Furthermore,
they allow for very compact and readable Java code when writing pages and controllers.
So we find that they would be the natural choice for a data structure, which would com-
pletely isolate the subview from the controller and the datamodel.

Before you start moaning about all the work it will take for you to convert your model
data into these kinds of data structures, lets revisit the User model from the previous sec-
tion. All the fetch methods used in the examples actually have an alternative variant
which has the same method name, but with the suffix hash. These fetch the objects as
FlexibleLists of FlexibleHashes. Lets look at an example. A controller and view, which
intend to show a list of all users, using the SubView called tableView. First the code for
the controller.

Ravenous Developers Guide


16
public void index(){
FlexibleHash users=new FlexibleHash();
users.put(“data”,User.fetchAllHash(dbst));
ENV.put(“userlist”,users);
}

And now the view page.


<?rvs tableView:userlist ?>

Thats all there is to it. The tableView subview is actually one of the standard subviews
distributed with Ravenous. It expects to find a FlexibleList for the key “data” in the hash
given as parameter. It will automatically build a table, with a column for each key in the
data set.

A D VA N C E D S U B V I E W U S A G E
• SubViews are actually executed in the context of the controller or page calling it. Fur-
thermore, the SubView class actually extend the PageEngine class. This means that you
can actually do everything in a SubView you can in an ordinary page.... however, it is
really recommended that you keep the SubView isolated and ignorant about the inter-
nals of the page or controller calling it. This will make it easier to reuse the SubView for
other purposes.

• It is possible to build a subview from several internal subviews. This allows developers
to extend the functionality of a subview, by injecting new internal subviews into an ex-
isting subview. Check the online documentation for SubView for further information
about this.

Shared Plugins
Ravenous includes a type of shared objects we call Plugins. These are objects, which will
be instantiated once for each host and shared for all requests on that host. They are usu-
ally used to cache data, but can be used as a Plugin for any type of functionality.

Create a folder in your document root called plugin. Place a text file in it called
counter.rvplg. The contents of this file can be anything valid in an ordinary Ravenous rvc
file. In this example, I will use the following piece of code.

private int counter;


public String get(String method){
if(method.equals(“count”)){
synchronized(this){
return “”+(counter++);
}
}
return “”;
}

Ravenous Developers Guide


17
To use this from a controller, i could use the following code.

public void index(){


Plugin plug=getPlugin(“counter”);
String current=plug.get(“count”);
}

A typical usage for the Plugin system, is to cache data which takes a long time to reload.
It might be really nasty database joins, or perhaps a search into a very large data file. In
any case, it might be desirable to store the state of the plugin when Ravenous is restarted
or when the specific plugin is recompiled and reinstantiated. This can be done by overrid-
ing the following methods.

public void restoreAfterRestart(FlexibleHash hash){


}
public void restoreAfterCompile(FlexibleHash hash){
}
public FlexibleHash dumpForRestart(){
return new FlexibleHash();
}
public FlexibleHash dumpForCompile(){
return new FlexibleHash();
}

You can use any data types you like for dumpForCompile, since it will be kept in memory,
but dumpForRestart needs to store you data to disk. This limits you data structure to
simple types (int, boolean, float, string and so forth) as well as FlexibleHash and Flexi-
bleList.

A D VA N C E D P L U G I N U S A G E
• Plugins have the same features for providing meta data about themselves as SubViews.
If you implement those you will be able to submit you plugins to our online repository.

Where to go from here...


Thats all for the basic developers guide. There is a lot of functionality in Ravenous we
haven’t discussed yet, such as sending mails, handling multi language sites and working
with rss feeds. These features will hopefully soon be covered in other developers guides,
but in the mean time, check the online API documentation available at
http://ravenous.solidosystems.com/

Ravenous Developers Guide


18
If you have questions which has not been covered in the online documentation, don’t
hesitate to contact me at kjj@solidosystems.com or through AIM/iChat at kasperjjeppe-
sen.

And now for a final plea... once you get started with Ravenous, please help us out by add-
ing your knowledge to the documentation system. It works much like a wiki, and all de-
velopers are very welcome to add information. Don’t be shy and let your current knowl-
edge level or language skills hold you back! Every little drop helps!

Ravenous Developers Guide


19

Você também pode gostar