19.6. Web Development with IOWAIOWA (Interpreted Objects for Web Applications) is a web framework written by Kirk Haines. IOWA enables development with reusable, encapsulated web components for site generation. 19.6.1. Basic IOWA ConceptsAn IOWA application runs in a persistent Ruby process that listens on a socket for requests. IOWA includes various adapters so that the source of that request may be CGI, Mongrel, WEBrick, and so on. The IOWA home page includes a good explanation of the underlying architecture plus a tutorial, so here we'll just take a look at some key features. IOWA is available for downloading from rubyforge.org. A gem version may be ready when version 1.0 is released. The examples here were built with a preview release of version 1.0, available as a zip or tgz file. The installation includes some examples and test cases demonstrating a variety of uses. Here we'll look at a simple application derived from those examples. An IOWA application needs some code to start off the persistent server process, and we'll use the built-in WEBrick server as the front end. This script, app.rb, handles both: require 'iowa_webrick' class HWApplication < Iowa::Application self.daemonize = true attr_accessor :dbpool def initialize(*args) super Iowa.config[Iowa::Capplication][Iowa::Croot_url] = 'http://127.0.0.1:2000' end end Iowa.run By default it will try to read a configuration file, app.cnf, in the same directory. Here's our app.cnf: socket: hostname: localhost path: .. logging: basedir: ../log minlevel: 0 maxsize: 10000000 maxage: 86400 application: daemonize: false sessioncache: class: LRUCache maxsize: 20 ttl: 3600 dispatcher: class: StandardDispatcher policy: class: iowa/Policy The preceding file is a YAML file with assorted information describing how IOWA should behave. One more configuration file is needed as well (mapfile.cnf). It tells IOWA how to map page requests to components. Here is a one-line mapfile.cnf file: /main.html: Main IOWA requests are typically handled by a combination of HTML template files and IOWA components. Template and component source files share the same base name but use different file extensions. The default template/object is Main, so our sample application has Main.html and Main.iwa files. Files ending with .iwa are just Ruby files; IOWA uses that extension to distinguish them from other Ruby code that may be part of the application. These files act much like controller classes in Nitro or Rails. Methods defined in a component class are available in the corresponding HTML file. Our demo Main.html file is as follows: <html> <head><title>The Time Is...</title></head> <body> <p>The time is @now.</p> <p>The count is @count.</p> <a o>RELOAD</a> </body> </html> IOWA templates allow the mixing of plain HTML and component instance variables. Note that those variables do not need to be "interpolated" in the usual way; you just drop them into your markup. There is also the special oid variable; IOWA uses this to dynamically alter the template during rendering. In our example, it is used to create a link back to the reload method in the component class defined in Main.iwa. If you hover your mouse over that link in the rendered page (or view the generated source), you see something like this: http://127.0.0.1:2000/main.html/6b38f6fb-4f087af7-ab6JaqUM9KyWE.a.1.7 IOWA uses such URLs to track session state; if you click that link a few times, you'll see how it changes. If you manually edit it to a prior value, you'll retrieve the session state for that URL. In this application, that state is in the @count instance variable. Here's Main.iwa: class Main < Iowa::Component attr_accessor :count def awake @count = 0 end def setup @count += 1 end def now Time.now.asctime end end 19.6.2. Templating in IOWAAlthough most web applications will benefit from a separation of application code and presentation templates, IOWA, somewhat like Nitro, allows you to skip components entirely and put Ruby code in the view. Here's PureView.html, containing both class and HTML: <% class PureView < Iowa::Component def right_now Time.now end end %> <html> <head><title>A Self-contained View</title></head> <body> <p>The time is @right_now.</p> </body> </html> Unlike Nitro, though, this only works if there is no corresponding component for the view. When you decide to have both a separate component file and a template, IOWA will not parse the embedded code in the HTML file. The template file may still contain looping and conditional instructions. Suppose we add a new method to the Main.iwa file: def after_dinner? Time.now.hour > 19 end We can then add some conditional rendering to Main.html using IOWA's if element: <if oid='after_dinner?'> <p>It's after dinner. What's for dessert?</p> </if> Good question! What is for dessert? Let's have IOWA tell us. We'll have Main.iwa produce a dessert menu in the form of an array: def desserts %w{ Cake Brownies Fruit Gelato } end We'll then have Main.html display it. We update the conditional content to include the list of desserts: <p>It's after dinner. Here's what we have for dessert:</p> <ul o> <li>@dessert_item</li> </ul> </if> <p> We also have to tell IOWA how to populate the iteration, so at the end of Main.iwa after the class definition we add this binding definition section: <? dessert_list { item = dessert_item list = desserts } ?> This creates an IOWA binding for the template list dessert_items. During the iteration, each list item is available from a variable named dessert_item, with list data coming from the desserts component method. 19.6.3. Component Control TransferIt can be useful to partition application logic among multiple component classes. We've seen how URLs may be mapped to components. You may also transfer control without changing the base URL path. We'll add a new method to Main.iwa to handle the link from the dessert selection: def dessert_choice new_page = page_named('DessertChoice') new_page.choice = @dessert_item yield new_page end We'll also change the dessert choice loop in Main.html: <ul o> <li><a oid='dessert_choice'>@dessert_item</a></li> </ul> There's a fair amount of magic occurring here; the oid in the ul element guides the loop content, whereas the oid in the anchor element will create a special link to our new dessert_choice method. To top things off, the text value of the link will be passed (albeit cryptically) in that page request as well. The dessert_choice method is short; it uses the IOWA method page_named to create an instance of another component class, DessertChoice. The choice= method is called to pass along the dessert choice. The call to yield then transfers control to this new component. The new component is defined like any other, using a pair of .iwa and .html files. Here's the class code: class DessertChoice < Iowa::Component attr_accessor :choice def details "Details about #{@choice} should really come from a database." end end DessertChoice.html renders the details: <html> <head><title>Your Dessert Choice</title></head> <body> <h1>Dessert!</h1> <p>@details</p> </body> </html> There is still more to IOWA than can be shown here. To learn more, visit the IOWA home page (http://enigo.com/projects/iowa/) and IOWA's project page on RubyForge (http://rubyforge.org/projects/iowa). |