<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Hobo Blog &#187; Documentation</title>
	<atom:link href="http://hobocentral.net/blog/category/documentation/feed/" rel="self" type="application/rss+xml" />
	<link>http://hobocentral.net/blog</link>
	<description>Hobo - the web app builder for Rails</description>
	<lastBuildDate>Tue, 24 Apr 2012 15:48:31 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>What is Hobo?</title>
		<link>http://hobocentral.net/blog/2007/01/25/what-is-hobo-2/</link>
		<comments>http://hobocentral.net/blog/2007/01/25/what-is-hobo-2/#comments</comments>
		<pubDate>Thu, 25 Jan 2007 09:03:14 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://hobocentral.net/blog/2007/01/25/what-is-hobo-2/</guid>
		<description><![CDATA[There&#8217;s a fair few reactions to Hobo out there on the big wide Internets by now, and it&#8217;s interesting to see people&#8217;s interpretations. I suppose it&#8217;s pretty obvious that people are going to jump to conclusions when I&#8217;ve only given a brief glimpse of what Hobo is about. I just wanted to take a quick [...]]]></description>
			<content:encoded><![CDATA[<p>There&#8217;s a fair few reactions to Hobo out there on the big wide Internets by now, and it&#8217;s interesting to see people&#8217;s interpretations. I suppose it&#8217;s pretty obvious that people are going to jump to conclusions when I&#8217;ve only given a brief glimpse of what Hobo is about. I just wanted to take a quick moment to clear a few things up. [Update: this turned from a "quick moment" into a fairly comprehensive overview of what Hobo can do :-)]</p>

<p><span id="more-30"></span></p>

<p>The main point here is this &#8211; Hobo is nothing but extensions to Rails! There&#8217;s nothing that Rails can do that Hobo can&#8217;t. I&#8217;m not saying that Hobo will be to everyone&#8217;s taste &#8211; of course you may prefer Rails as it is. I&#8217;m just making the point that Hobo only <em>adds</em> to Rails, it doesn&#8217;t take anything away. Perhaps it&#8217;s the slightly &#8220;magic&#8221; feel to the POD screencast &#8212; I can see that the screencast might leave some people thinking they&#8217;d rather build the app themselves and have control over everything. So I guess I&#8217;m really just making the point that you <em>do</em> have control over everything, in just as direct a manner as you do in Rails without Hobo.</p>

<p>Hobo does do more than your average plugin though &#8212; it&#8217;s kind of like several plugins rolled into one. Inevitably people are going to be interested in having those features as separate plugins &#8212; a few people have asked for DRYML by itself already. But there are a bunch of dependencies between the different features, and to be honest I won&#8217;t know exactly where things can be separated until Hobo matures a bit.</p>

<p>So, for now at least, I&#8217;ve taken a different approach &#8211; you <em>install</em> all these features at once by installing Hobo, but you <em>use</em> only those features you want to. If you want the whole &#8220;web app builder&#8221; thing, then you&#8217;ll end up using most or all of them, but if you just want DRYML? Not a problem, the rest of Hobo won&#8217;t get in your way. If you later see that you could also benefit from, say, the ajax-mechanism, fine &#8212; just start using it.</p>

<p>So perhaps a quick tour of what these features are would be in order :-)</p>

<h3>DRYML</h3>

<p>This is really the heart of Hobo &#8212; it all started with DRYML. There&#8217;s a few posts on this already of course &#8211; have a look in the <a href="/blog/category/documentation/">documentation category</a>. DRYML consists of the templating mechanism itself, as well as a &#8220;core&#8221; tag library, with the basic things like loops and conditionals.</p>

<h3>Ajax Rendering</h3>

<p>Building on DRYML, this is the ability to mark a section of your page as a &#8220;part&#8221;. Having done this, Hobo will track the object that was in context when the part was rendered, and can re-render the part using the new state of that object when requested. This mechanism can be used by itself, but works best in combination with the Hobo Rapid tag library, which lets you write very simple stuff like</p>

<pre><code>&lt;delete_button update="number_of_users"/&gt;
</code></pre>

<p>and the number of users will automatically change to reflect the deletion.</p>

<p>Right now using the Ajax mechanism <em>without</em> also buying into Hobo Rapid is kind of a pain. Some extra work to smooth that out is in order. Perhaps some regular Rails helpers.</p>

<h3>Hobo Rapid Tag Library</h3>

<p>This is where things get groovy. A tag library is basically just like a load of Rails helpers, but as DRYML tags instead of methods. Hobo Rapid contains the Hobo equivalent of link helpers, form helpers, ajax helpers, and pagination helpers. There&#8217;s also some &#8220;scaffold-like&#8221; tags for default pages, and support for navigation bars.</p>

<p>This library is expected to grow!</p>

<h3>ActiveRecord Permission System</h3>

<p>This appears prominently in the POD screencast. At it&#8217;s heart it&#8217;s just a simple convention for declaring who&#8217;s allowed to do what to your models (create, update, delete and view). The permissions come into effect in two places: in customising the views, and in validating http requests.</p>

<p>View customisation is provided in the tag library. The output of various tags is dependent on permissions. For example the <code>&lt;edit&gt;</code> tag will automatically become a read-only view if the current user does not have edit permission.</p>

<p>Validation of web requests is done in Hobo&#8217;s generic model controller (see below). If the request, be it a view, a create, an update or a delete (http get/post/put/delete) is not permitted for the current user, it will be rejected.</p>

<h3>Cross-Model Search</h3>

<p>Fairly simple but useful. Models can declare which columns are searchable (there are sensible defaults of course!) and Hobo provides both server and client-side support for a nice ajaxified search that finds many types of model from a single search page.</p>

<h3>Switchable Themes</h3>

<p>A Hobo theme consists of public assets such as a stylesheet and images, and some tag definitions. The tags define the basic HTML structure of the various theme elements &#8211; the overall page, header and footer, navigation bar, sidebars, panels (boxed sections within the page) etc. Themes are switchable by changing a configuration variable. All the variable does is determine the paths used to find the tag definitions (app/views/hobolib/themes/<em>theme-name</em>) and the public assets (public/hobothemes/<em>theme-name</em>).</p>

<h3>User Management (Acts As Authenticated)</h3>

<p>In order for the permission system to work, a user model must be present. For that reason we decided to go ahead and include AAA in Hobo. It&#8217;s been tweaked a bit &#8212; the controller has been merged with Hobo&#8217;s &#8220;front&#8221; controller (so called because it gives you your front page and related pages like search, login and signup). The user model methods that are not commonly changed have been moved out into a module to keep your user model clean.</p>

<p>Hobo also adds the concept of a guest user, this is a pseudo model that is not stored in the database. It&#8217;s just a place to define the permissions for someone who has not logged in. One implication of this change this is that instead of testing if <code>current_user</code> is nil, you test for <code>current_user.guest?</code>.</p>

<h3>ActiveRecord Composable Query Mechanism</h3>

<p>It&#8217;s common practice with ActiveRecord to write query methods to capture standard queries your application needs beyond the magic <code>find_by_thingumybob</code> methods. A problem I ran into was that I wanted to be able to <em>compose</em> queries: find everything that matches this query <em>or</em> that one; find the first record that matches some query <em>and</em> some other one.</p>

<p>I found I needed such a feature in Hobo so I implemented the following:</p>

<pre><code>Post.find(:all) { (title_contains("Hobo") | content_contains("Hobo")) &amp;
                   is_in(news_category.posts) }
User.find(:first) { name_is("Tom") | email_is("foo@bar.com") }
</code></pre>

<p>Note that in the first example, the collection <code>my_category.posts</code> doesn&#8217;t need to be loaded &#8211; the query generates SQL and runs entirely on the database.</p>

<p>The query operations in those examples &#8212; <code>*_contains</code>, <code>*_is</code> and <code>in_collection</code> &#8212; are all built in. If you write class methods on your model that return SQL expressions you can use those in the query too.</p>

<h3>Generic Model Controller (CRUD Support)</h3>

<p>Hmmm &#8211; this does a fair few things. Maybe I need another cup of tea&#8230; Slurp, ahhh. Sorry where was I? Oh yeah &#8211; the model controller. To get this functionality, add this to your controller</p>

<pre><code>hobo_model_controller
</code></pre>

<p>It&#8217;s just a short-hand for including <code>Hobo::ModelController</code>. You can also generate a controller which already contains this declaration (hey &#8211; every little helps!) with:</p>

<pre><code>$ ./script/generate hobo_model_controller &lt;model-name&gt;
</code></pre>

<p><strong>CRUD support</strong></p>

<p>Hobo derives the model class from the controller class name, so you should follow the Rails convention of <code>PostsController</code> for model <code>Post</code> (the generator will do this for you of course). You then get the basic CRUD methods familiar from scaffolding: index, show, new, create, edit and update.</p>

<p>The controller also has support for your <code>has_many</code> and <code>belongs_to</code> associations. Firstly, your <code>has_many</code> associations get published automatically, so for example the URL</p>

<pre><code>/posts/12/comments
</code></pre>

<p>Would map to the action <code>show_comments</code>, which is implemented for you. Also</p>

<pre><code>/posts/12/comments/new
</code></pre>

<p>Will invoke the <code>new_comment</code> action &#8211; again it&#8217;s implemented for you. Note that the form on that page doesn&#8217;t post to <code>/posts/12/comments</code> but to <code>/comments</code> as usual. I decided it was best to have a single URL for creating a given model.</p>

<p>There&#8217;s also support for your associations when creating / updating models. Without going into too much detail in this overview, when you post (create) or put (update), you can set both <code>has_many</code> associations and <code>belongs_to</code> associations. You can either pass the id of an existing record, or pass entirely new records in nested parameter hashes. There&#8217;s support for all this in DRYML and Hobo Rapid, so for example if a person has an address as a separate, associated model, you can easily build a single form with fields for both the person and the associated address.</p>

<p><strong>Data filters</strong></p>

<p>The composable query mechanism for ActiveRecord has been described above. Data filters allow you to expose these queries to the web. This is easiest to explain with an example. Say we have people that are members of groups, and we want a page with all the people not in a particular group. Inside the people controller we define a data filter:</p>

<pre><code>def_data_filter :not_in do |collection|
  not_in(collection)
end
</code></pre>

<p>A page with all the people not in the group with id 7 would then be available at</p>

<pre><code>/people?where_not_in=group_17
</code></pre>

<p><strong>Enhanced autocomplete</strong></p>

<p>Rails already has support for working with the Scriptaculous auto-completer through the helper <code>auto_complete_for</code> (although that&#8217;s on its way out in 2.0). Hobo&#8217;s version is enhanced to work with the data filter mechanism. Say for example you wanted an auto-completer to allow you to add people to a list &#8211; you don&#8217;t want the people already in the list to be included in the completions. You would add the following to your controller:</p>

<pre><code>def_data_filter :not_in do |collection|
  not_in(collection)
end

autocomplete_for :name
</code></pre>

<p>You would then point your auto-completer at (e.g.) <code>/people/completions?for=name&amp;where_not_in=list_12</code></p>

<p>Hobo Rapid has an auto-completer tag so the client side is nice and easy too.</p>

<p><strong>Remote procedure calls</strong></p>

<p>When DHH introduced the Simply Restful stuff, he described CRUD as an aspiration rather than a hard rule (IIRC). In other words, you shouldn&#8217;t go crazy trying to model absolutely everything as resources &#8212; there will still be &#8220;remote procedure calls&#8221; (RPC) that we post to. I hit this recently with a need for a &#8220;reset password&#8221; service on an app I&#8217;m building. (BTW &#8211; this is basically how Hobo moves forward &#8211; if I hit a need for a feature that I feel will crop up again and again, I build it into Hobo, not the app. In this case I didn&#8217;t want to build password reset into Hobo, but I did build the RPC mechanism).</p>

<p>The basic procedure is: write an action in your controller as normal. Add <code>web_method &lt;method-name&gt;</code>. Hobo will add a route for your method, and apply a before filter so the object in question is already available in <code>@this</code>. Hobo&#8217;s ajax mechanism supports updating the page with results from your method, and there&#8217;s integration into the permission system too.</p>

<p>Let&#8217;s see an example &#8211; the reset password method. In the controller we add:</p>

<pre><code>web_method :reset_password
def reset_password
  new_password = @this.reset_password
  hobo_ajax_response(@this, :password =&gt; new_password)
end
</code></pre>

<p>On the model we can add a method <code>can_call_reset_password?(user)</code>. In my case I only wanted the super-user to have access to this method so I didn&#8217;t need <code>can_call_reset_password?</code>.</p>

<p>The method will be available to HTTP posts at, e.g.</p>

<pre><code>/users/21/reset_password
</code></pre>

<p>For the client side there&#8217;s a <code>&lt;remote_method_button&gt;</code> tag (note to self: should be called <code>&lt;web_method_button&gt;</code>?)</p>

<p><strong>Alternative show methods</strong></p>

<p>Simply Restful introduced the semicolon in the URL to access alternate views of the same object. So</p>

<pre><code>/posts/12
</code></pre>

<p>Would be a &#8216;normal&#8217; view of the post, while</p>

<pre><code>/post/12;edit
</code></pre>

<p>Would give you a form to edit the post &#8212; still a view of the post, just a different one. You&#8217;ll probably find the need for additional &#8220;alternative views&#8221;, e.g. you might have a main view of a user, plus a separate &#8220;My Profile&#8221; page where users can edit details that don&#8217;t change so often, like their email address. Hobo allows you to simply say</p>

<pre><code>show_method :profile
</code></pre>

<p>In your controller. You then create <code>app/views/users/profile.dryml</code>. You also get a named route <code>person_profile</code> so you can call <code>person_profile_url(fred)</code> and get the URL back.</p>

<h3>Migration Enhancements</h3>

<p>This is a little one, but very useful. In <code>create_table</code> calls, you can add columns like this</p>

<pre><code>t.string :name, :subject, :limit =&gt; 50
</code></pre>

<p>instead of</p>

<pre><code>t.column :name, :string, :limit =&gt; 50
t.column :subject, :string, :limit =&gt; 50
</code></pre>

<p>There&#8217;s also <code>t.auto_dates</code> that gives you <code>updated_at</code> and <code>created_at</code>, and</p>

<pre><code>t.fkey :zip, :zap
</code></pre>

<p>That will give you integer columns zip<em>id and zap</em>id. </p>

<h3>ID Names</h3>

<p>In ActiveRecord everything has a numeric ID. It&#8217;s common however, that models also have an identifying name (e.g. a name column where every row is unique). If a model has an ID name like this, it&#8217;s nice to be able to sometimes use it instead of the numeric ID, for example in readable URLs. If your model declares <code>hobo_model</code>, you can also declare</p>

<pre><code>id_name &lt;column-name&gt;
</code></pre>

<p>If you don&#8217;t specify a column it defaults to <code>name</code>. This gives you a read/write attribute <code>id_name</code> which is just an alias for the column in question. You also get <code>find_by_id_name</code> on the class.</p>

<p>If you are using Hobo&#8217;s <code>model_controller</code> you can then use names instead of IDs in your URLs, e.g. (using ID names in combination with the associations support):</p>

<pre><code>/categories/News/posts
</code></pre>

<p>The helper <code>object_url</code> will generate this kind of URL too &#8211; if the object you pass supports ID names.</p>

<h3>That&#8217;s All Folks!</h3>

<p>That&#8217;s pretty much everything, although of course lots of detail is lacking. Oh and there&#8217;s a <em>ton</em> of great new stuff just around the corner of course!</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2007/01/25/what-is-hobo-2/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Customising a Page</title>
		<link>http://hobocentral.net/blog/2006/12/28/cusomtising-a-page/</link>
		<comments>http://hobocentral.net/blog/2006/12/28/cusomtising-a-page/#comments</comments>
		<pubDate>Thu, 28 Dec 2006 10:43:00 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://hobocentral.net/blog/2006/12/28/cusomtising-a-page/</guid>
		<description><![CDATA[I owe you a screencast on customising the whole UI of the POD demo, but strictly speaking I&#8217;m on holiday at the moment :-) I&#8217;m just stealing a few moments on the laptop here and there to check the site stats and respond to any comments.

So to keep you going, I thought I&#8217;d better rattle [...]]]></description>
			<content:encoded><![CDATA[<p>I owe you a screencast on customising the whole UI of the POD demo, but strictly speaking I&#8217;m on holiday at the moment :-) I&#8217;m just stealing a few moments on the laptop here and there to check the site stats and respond to any comments.</p>

<p>So to keep you going, I thought I&#8217;d better rattle off a few quick tips on customising a page, seeing as it&#8217;s a total mystery at the moment. We&#8217;ll customise the view of a category page in the POD demo.</p>

<p><span id="more-20"></span></p>

<p>If you wanted to customise the front page, that would be a relatively familiar process &#8211; look in app/views/front and you&#8217;ll find index.dryml, as well as a few others. But app/views/categories is empty &#8212; so where is the current page coming from?</p>

<p>Hobo&#8217;s model controller (i.e. any controller that declares <code>hobo_model_controller</code>) provides the following pages (actions) just like a regular Rails scaffold:</p>

<ul>
<li><p>index &#8211; A list of all the records</p></li>
<li><p>show &#8211; A view of a single record</p></li>
<li><p>edit &#8211; an editable view of a single record (the default UI doesn&#8217;t use this,
as the show page supports in-place editing)</p></li>
<li><p>new &#8211; A blank form for a new record</p></li>
</ul>

<p>If Hobo doesn&#8217;t find a template for a given page, it will use a tag instead &#8211; these are defined by the theme:</p>

<ul>
<li><code>&lt;index_page&gt;</code></li>
<li><code>&lt;show_page&gt;</code></li>
<li><code>&lt;edit_page&gt;</code></li>
<li><code>&lt;new_page&gt;</code></li>
</ul>

<p>So there are two ways to override the look of a page. If you override one of these tags, say by defining <code>&lt;show_page&gt;</code> in app/views/hobolib/application.dryml, <em>all</em> the show pages will change. If you create a file, say app/views/categories/show.dryml, then just that page will change.</p>

<p>Try creating this file:</p>

<h4>app/views/categories/show.dryml</h4>

<pre><code>My category page
</code></pre>

<p>If you browse to a category page, you should see just that text &#8211; all trace of the theme is gone. To get the theme back, we need to use the <code>&lt;page&gt;</code> tag which gives us a generic page (this tag is used in turn by <code>&lt;index_page&gt;</code>, <code>&lt;show_page&gt;</code> etc.). E.g.:</p>

<h4>app/views/categories/show.dryml</h4>

<pre><code>&lt;page&gt;
  &lt;h1&gt;My category page&lt;/h1&gt;
&lt;/page&gt;
</code></pre>

<p>Now lets start to build out the page</p>

<h4>app/views/categories/show.dryml</h4>

<pre><code>&lt;page&gt;
  &lt;h1&gt;&lt;show attr="name"/&gt;&lt;/h1&gt;

  &lt;panel&gt;
    &lt;h1&gt;Adverts&lt;/h1&gt;
    &lt;repeat attr="adverts"&gt;
      &lt;section&gt;
        &lt;h2&gt;&lt;show attr="title"/&gt;&lt;/h2&gt;
      &lt;/section&gt;
    &lt;/repeat&gt;
  &lt;/panel&gt;
&lt;/page&gt;
</code></pre>

<p>Note the use of <code>&lt;panel&gt;</code> and <code>&lt;section&gt;</code> tags. These are theme tags and will be rendered differently by each theme.</p>

<p>One problem with this page is that we&#8217;ve lost the ability to edit in-place. If we change those <code>&lt;show&gt;</code> tags to <code>&lt;edit&gt;</code> tags, Hobo will provide an in-place editor to any user with the right permissions. Lets include the advert body while we&#8217;re at it:</p>

<h4>app/views/categories/show.dryml</h4>

<pre><code>&lt;page&gt;
  &lt;h1&gt;&lt;edit attr="name"/&gt;&lt;/h1&gt;

  &lt;panel&gt;
    &lt;h1&gt;Adverts&lt;/h1&gt;
    &lt;repeat attr="adverts"&gt;
      &lt;section&gt;
        &lt;h2&gt;&lt;edit attr="title"/&gt;&lt;/h2&gt;
        &lt;p&gt;&lt;edit attr="body"/&gt;&lt;/p&gt;
      &lt;/section&gt;
    &lt;/repeat&gt;
  &lt;/panel&gt;
&lt;/page&gt;
</code></pre>

<p>And there you have it. An obvious feature we&#8217;re missing is the ability to paginate. What&#8217;s needed I think is something like <code>&lt;paginated_repeat&gt;</code> which would only be allowed once on a page. Hobo doesn&#8217;t have this yet. There&#8217;s no shortage of things on the to-do list!</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2006/12/28/cusomtising-a-page/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Ajax, Hobo Style</title>
		<link>http://hobocentral.net/blog/2006/12/05/ajax-hobo-style/</link>
		<comments>http://hobocentral.net/blog/2006/12/05/ajax-hobo-style/#comments</comments>
		<pubDate>Tue, 05 Dec 2006 17:44:22 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://hobotek.net/blog/2006/12/05/ajax-hobo-style/</guid>
		<description><![CDATA[OK, next up we&#8217;re going to see what Hobo brings to the Ajax table. In a nutshell &#8212; the ability to refresh fragments of a page without pushing them out into separate partial files. To see how that works, let&#8217;s knock up a quick demo application.



We&#8217;re going to build on the todo-list demo app from [...]]]></description>
			<content:encoded><![CDATA[<p>OK, next up we&#8217;re going to see what Hobo brings to the Ajax table. In a nutshell &#8212; the ability to refresh fragments of a page without pushing them out into separate partial files. To see how that works, let&#8217;s knock up a quick demo application.</p>

<p><span id="more-14"></span></p>

<p>We&#8217;re going to build on the <a href="/blog/2006/11/11/dryml-meet-activerecord/">todo-list demo app</a> from an earlier post, so if you want to follow along you should start with that post. Just to recap, the app is trivially simple, consisting of a <code>TodoList</code> model which <code>has_many :tasks</code> and a <code>Task</code> model which <code>belongs_to :todo_list</code>. We created a controller for to-do lists with just a <code>show</code> action, and we implemented a DRYML view for that action.</p>

<p>We&#8217;re going to add an ajaxified &#8220;New Task&#8221; form on that same page. Let&#8217;s do the back-end first, so that it&#8217;s possible to create tasks. We&#8217;ll start by being good modern Rails citizens, and switch to RESTful routing. Add this line to <code>routes.rb</code>:</p>

<pre><code>map.resources :todo_lists, :tasks
</code></pre>

<p>Now create our controller</p>

<pre><code>$ ./script/generate controller tasks
</code></pre>

<h4>app/controllers/tasks_controller.rb</h4>

<pre><code>class TasksController &lt; ApplicationController

  def create
    Task.create(params[:task])
  end

end
</code></pre>

<p>Now we&#8217;ll add a simple ajax form to <code>todo_lists/show.dryml</code>. We&#8217;ll use the familiar <code>remote_form_tag</code> helper. We&#8217;ll use raw HTML for the form controls, just to be clear about exactly what&#8217;s going on, What we won&#8217;t do, for now, is deal with refreshing the page.</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;head&gt;
  &lt;%= javascript_include_tag :defaults %&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

  &lt;ul_for attr="tasks"&gt;&lt;name_link/&gt;&lt;/ul_for&gt;

  &lt;% form_remote_tag :url =&gt; "/tasks", :method =&gt; :post do %&gt;
    &lt;input type="hidden" name="task[todo_list_id]"
                         value="&lt;%= this.id %&gt;"/&gt;
    &lt;input type="text" name="task[name]"/&gt;
    &lt;input type="submit" value="New Task"/&gt;
  &lt;% end %&gt;
&lt;/body&gt;
</code></pre>

<p>That should work as is, although you&#8217;ll have to manually reload the page to see any new tasks. Let&#8217;s fix that, Hobo style.</p>

<p>With DRYML, any fragment of the page can be marked as a <em>part</em>. A part can be rendered separately from the rest of the page, just like a partial. To create a part, just give any element a <code>part_id</code>. For this demo we&#8217;ll add it to the <code>ul_for</code> tag.</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;head&gt;
  &lt;%= javascript_include_tag :defaults %&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

  &lt;ul_for attr="tasks" part_id="task_list"&gt;&lt;name_link/&gt;&lt;/ul_for&gt;

  &lt;% form_remote_tag :url =&gt; "/tasks", :method =&gt; :post do %&gt;
    &lt;input type="hidden" name="task[todo_list_id]"
                         value="&lt;%= this.id %&gt;"/&gt;
    &lt;input type="text" name="task[name]"/&gt;
    &lt;input type="submit" value="New Task"/&gt;
  &lt;% end %&gt;
&lt;/body&gt;
</code></pre>

<p>Having done that, reload the page in the browser and have a look at the source. You should see something like (with bits cut out for the sake of clarity):</p>

<h4>Generated HTML Source</h4>

<pre><code>&lt;head&gt;
  &lt;!-- A BUNCH OF JAVASCRIPT INCLUDES --&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Todo List: Launch Hobo!&lt;/h1&gt;

  &lt;span id='task_list'&gt;
  &lt;ul&gt;
    &lt;!-- A BUNCH OF LIST ITEMS --&gt;
  &lt;/ul&gt;
  &lt;/span&gt;

&lt;!-- THE FORM HERE --&gt;
&lt;/body&gt;

&lt;script&gt;
var hoboParts = {}
hoboParts.task_list = 'todo_list_1'
&lt;/script&gt;
</code></pre>

<p>The important bits to note are the <code>&lt;span&gt;</code> with the same ID as our part, and the JavaScript snippet at the end. The JavaScript was generated by Hobo to keep track of which model objects are displayed in which parts.</p>

<p>We can ask for a part to be updated, simply by adding a few parameters to the request. Hobo provides tags to make this blissfully easy, and we&#8217;ll have a look at those shortly. For now though, just to show there&#8217;s no magic going on, we&#8217;ll add them by hand using hidden fields.</p>

<p>The parameters we need are:</p>

<ul>
<li><p><code>part_page</code>: The path of the current page template, e.g. &#8220;todo_lists/show&#8221;. (Future development: maybe we can do away with this and use the HTTP referrer instead)</p></li>
<li><p><code>render[][part]</code>: The name of the part we&#8217;d like to refresh. e.g. <code>task_list</code></p></li>
<li><p><code>render[][object]</code>: The &#8220;DOM ID&#8221; of the &#8220;context object&#8221; for that part. e.g. <code>todo_list_1</code></p></li>
</ul>

<p>Update the form as follows:</p>

<h4>app/views/todo_lists/show.dryml (fragment)</h4>

<pre><code>&lt;% form_remote_tag :url =&gt; "/tasks", :method =&gt; :post do %&gt;
  &lt;input type="hidden" name="task[todo_list_id]"
                       value="&lt;%= this.id %&gt;"/&gt;
  &lt;input type="text" name="task[name]"/&gt;
  &lt;input type="submit" value="New Task"/&gt;

  &lt;input type="hidden" name="part_page" value="todo_lists/show"/&gt;
  &lt;input type="hidden" name="render[][part]" value="task_list"/&gt;
  &lt;input type="hidden" name="render[][object]"
                       value="todo_list_&lt;% this.id %&gt;"/&gt;
&lt;% end %&gt;
</code></pre>

<p>We now need to upgrade our controller to recognize this &#8220;render request&#8221;. That just requires including a module, and calling one method:</p>

<h4>app/controllers/tasks_controller.rb</h4>

<pre><code>class TasksController &lt; ApplicationController

  include Hobo::AjaxController

  def create
    this = Task.create(params[:task])
    hobo_ajax_response(this)
  end

end
</code></pre>

<p>The <code>hobo_ajax_response</code> method needs a page context. As you can see we&#8217;re passing in the object just created.</p>

<p>You should now have a working ajax &#8220;New Task&#8221; feature.</p>

<p>The code might work but it&#8217;s pretty ugly (heh). Let&#8217;s clean it up using the appropriate Hobo tags. The tags we need come from a tag library that&#8217;s provided with Hobo &#8212; Hobo Rapid. Hobo Rapid is a very general purpose tag library that makes it extremely quick and easy to do the bread-and-butter stuff: links, forms, ajax&#8230;</p>

<p>We&#8217;ll look at Hobo Rapid in more detail in another post. For now we&#8217;ll see how to pretty-up our ajax demo.</p>

<p>To include Hobo Rapid in your application:</p>

<pre><code>$ ./script/generate hobo_rapid
</code></pre>

<p>Then edit your view to look as follows:</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;head&gt;
  &lt;%= javascript_include_tag :defaults %&gt;
  &lt;hobo_rapid_javascripts/&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

  &lt;ul_for attr="tasks" part_id="task_list"&gt;&lt;name_link/&gt;&lt;/ul_for&gt;

  &lt;create_form attr="tasks" update="task_list"&gt;
    &lt;edit attr="name"/&gt;
    &lt;submit label="New Task"/&gt;
  &lt;/create_form&gt;
&lt;/body&gt;
</code></pre>

<p>Yep &#8211; that&#8217;s it :-) Try it &#8212; it should be working.</p>

<p>The <code>&lt;create_form&gt;</code> tag can be read as: Include a form which will first create an object in the collection &#8220;tasks&#8221; of the current context (the <code>TodoList</code>), and then update the &#8220;<code>task_list</code>&#8221; part. Note that you don&#8217;t need to say anything about <em>how</em> to update that part. Hobo knows.</p>

<p><code>&lt;booming-voice&gt;</code>AND NOW [drum-roll] THE GRAND FINALE&#8230;<code>&lt;/booming-voice&gt;</code></p>

<p>How easy is it to update multiple parts in one go? For example, suppose the page had a count of the number of tasks &#8212; that would need updating too. We&#8217;ll use another handy little tag from Hobo Rapid: <code>&lt;count/&gt;</code> (Note this tag doesn&#8217;t have any special ajax support. We could have used a regular ERB scriptlet and the ajax would still work). And for bonus marks, we&#8217;ll DRY up all those <code>attr='tasks'</code> using a <code>&lt;with&gt;</code> tag, which just changes the context.</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;head&gt;
  &lt;%= javascript_include_tag :defaults %&gt;
  &lt;hobo_rapid_javascripts/&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

  &lt;with attr="tasks"&gt;
    &lt;ul_for part_id="task_list"&gt;&lt;name_link/&gt;&lt;/ul_for&gt;
    &lt;p&gt;&lt;count part_id="task_count"/&gt;&lt;/p&gt;

    &lt;create_form update="task_list, task_count"&gt;
      &lt;edit attr="name"/&gt;
      &lt;submit label="New Task"/&gt;
    &lt;/create_form&gt;
  &lt;/with&gt;
&lt;/body&gt;
</code></pre>

<p>To update multiple parts, just list the part names in the update attribute.</p>

<p>What more could you ask for? :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2006/12/05/ajax-hobo-style/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>DRYML, meet ActiveRecord</title>
		<link>http://hobocentral.net/blog/2006/11/11/dryml-meet-activerecord/</link>
		<comments>http://hobocentral.net/blog/2006/11/11/dryml-meet-activerecord/#comments</comments>
		<pubDate>Sat, 11 Nov 2006 10:42:36 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://hobotek.net/blog/2006/11/11/dryml-meet-activerecord/</guid>
		<description><![CDATA[DRYML has a nifty little feature called implicit context. Despite the title of this post, this feature has got nothing directly to do with ActiveRecord. Implicit context makes it a lot easier to populate your pages with data, and in Rails that means ActiveRecord models. So this post is about getting your models into your [...]]]></description>
			<content:encoded><![CDATA[<p>DRYML has a nifty little feature called <em>implicit context</em>. Despite the title of this post, this feature has got nothing directly to do with ActiveRecord. Implicit context makes it a lot easier to populate your pages with data, and in Rails that means ActiveRecord models. So this post is about getting your models into your views.</p>

<p><span id="more-10"></span></p>

<p>For those that want all the details, we&#8217;ll do the whole thing from scratch: create the Rails app, install Hobo, create some models&#8230; For those who don&#8217;t, you can skip the stuff between the two rules</p>

<hr />

<h3><code>&lt;skippable&gt;</code></h3>

<p>Let&#8217;s get started with a fresh Rails app and get Hobo installed:</p>

<pre><code>$ rails hobo_demo
$ mysqladmin create hobo_demo_development
$ cd hobo_demo
$ ./script/plugin install svn://hobocentral.net/hobo/trunk
$ ./script/generate hobo
</code></pre>

<p>&#8230;then create some models, populate with some data. Hmm, I guess we need to pick some kind of domain to model. How about a simple to-do list app? [Groan] OK but it&#8217;s a clichÃ© for a reason &#8211; simple, familiar&#8230; Let&#8217;s move on :-)</p>

<p>To start we&#8217;ll just have TodoLists and Tasks, where a TodoList has many Tasks.</p>

<pre><code>$ ./script/generate model todo_list
$ ./script/generate model task
</code></pre>

<p>Now let&#8217;s get those tables created &#8212; edit the two migrations so the create tables look like these:</p>

<pre><code>create_table :todo_lists do |t|
  t.column :name, :string
end

create_table :tasks do |t|
  t.column :name, :string
  t.column :todo_list_id, :integer
end
</code></pre>

<p>Declare the relationships in the models:</p>

<h4>app/models/task.rb</h4>

<pre><code>class Task &lt; ActiveRecord::Base
  belongs_to :todo_list
end
</code></pre>

<h4>app/models/todo_list.rb</h4>

<pre><code>class TodoList &lt; ActiveRecord::Base
  has_many :tasks
end
</code></pre>

<p>Then just a quick</p>

<pre><code>$ rake migrate
</code></pre>

<p>&#8230;and we&#8217;re good to go. Gotta love Rails eh?</p>

<p>If you&#8217;re following along you might want to pause here to get some data in there. Personally I&#8217;d use <code>./script/console</code> but you might prefer scaffolding, your MySQL GUI&#8230;</p>

<h3><code>&lt;/skippable&gt;</code></h3>

<hr />

<p>(Welcome back to the skimmers :-) What you missed: we&#8217;re working with a skeleton app with a <code>TodoList</code> model that has a name and <code>has_many :tasks</code> and a <code>Task</code> model that has a name and <code>belongs_to :todo_list</code>)</p>

<p>OK lets create a view of a single list</p>

<pre><code>$ ./script/generate controller todo_lists
</code></pre>

<h4><code>app/controllers/todo_lists_controller.rb</code></h4>

<pre><code>class TodoListsController &lt; ApplicationController

  def show
    @this = TodoList.find(params[:id])
  end

end
</code></pre>

<p><code>@this</code>?? That&#8217;s kind of unconventional isn&#8217;t it? The implicit context in a DRYML template is held in a variable <code>this</code> (actually it&#8217;s not a variable, it&#8217;s a method, but don&#8217;t worry about it). To set the context for the whole page, we assign to <code>@this</code> in the controller.</p>

<p>Now the view. We can access the context as <code>this</code> in a regular ERB scriptlet.</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;h1&gt;Todo List: &lt;%= this.name %&gt;&lt;/h1&gt;

&lt;ul&gt;
  &lt;% this.tasks.each do |task| %&gt;
    &lt;li&gt;&lt;%= task.name %&gt;&lt;/li&gt;
  &lt;% end %&gt;
&lt;/ul&gt;
</code></pre>

<p>Now lets clean that up with some DRYML goodness. First we&#8217;ll use <code>&lt;repeat&gt;</code>, which is part of the core taglib (available everywhere) instead of that loop. Let&#8217;s see the code first, and then go through how it works</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;h1&gt;Todo List: &lt;%= this.name %&gt;&lt;/h1&gt;

&lt;ul&gt;
  &lt;repeat attr="tasks"&gt;
    &lt;li&gt;&lt;%= this.name %&gt;&lt;/li&gt;
  &lt;/repeat&gt;
&lt;/ul&gt;
</code></pre>

<p>Starting to look cleaner! Two things to notice. First: we gave <code>repeat</code> just the name of the attribute we were interested in (<code>tasks</code>). It implicitly found that collection in the current context (the current value of <code>this</code>). We are therefore iterating over <code>this.tasks</code>. In other words, you just say &#8220;how to get there from here&#8221;: the page is displaying a <code>TodoList</code>, so iterate over the <code>tasks</code>. Nice and easy.</p>

<p>(Aside: We use the term &#8216;attribute&#8217; here in the Ruby sense, as in, say, <code>attr_reader</code>. Unfortunately, in a mark-up setting that term conflicts confusingly with XML attributes. We might change this name. What else could you call the attributes of a model object? Properties? Fields?)</p>

<p>The second thing to notice is that <em>inside</em> the repeat, we&#8217;re using <code>this</code> again, only now it refers to the individual task. <code>repeat</code> sets the context to each item in the collection in turn. If you&#8217;ve ever worked with XSL-T, this idea may be familiar.</p>

<p>Notice now that both the scriptlets that display the name are identical. Bad programmer! <em>Don&#8217;t</em> repeat yourself! No problemo:</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;def tag="name"&gt;&lt;%= this.name %&gt;&lt;/def&gt;

&lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

&lt;ul&gt;
  &lt;repeat attr="tasks"&gt;
    &lt;li&gt;&lt;name/&gt;&lt;/li&gt;
  &lt;/repeat&gt;
&lt;/ul&gt;
</code></pre>

<p>Note that the <code>name</code> tag declares <code>this</code> as an attribute. Any tag that&#8217;s going to use <code>this</code> should declare so in this way, even though we never actually give <code>this</code>
 as an attribute when we use the tag.</p>

<p>Next we should move that <code>&lt;def&gt;</code> to the global taglib (application.dryml, which lives in app/views/hobolib). While we&#8217;re at it, <code>&lt;ul&gt;/&lt;li&gt;</code> lists tend to crop up all over the place, right? Bad programmer! <em>Don&#8217;t</em> repeat yourself! Let&#8217;s make a tag for those lists.</p>

<h4>app/views/hobolib/application.dryml</h4>

<pre><code>&lt;def tag="name"&gt;&lt;%= this.name %&gt;&lt;/def&gt;

&lt;def tag="ul_for"&gt;
  &lt;ul&gt;
    &lt;repeat&gt;
      &lt;li&gt;&lt;tagbody/&gt;&lt;/li&gt;
    &lt;/repeat&gt;
  &lt;/ul&gt;
&lt;/def&gt;
</code></pre>

<p>The <code>ul_for</code> tag expects the context to be a collection. Here&#8217;s how we use it:</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

&lt;ul_for attr="tasks"&gt;&lt;name/&gt;&lt;/ul_for&gt;
</code></pre>

<p>You can&#8217;t ask for much more concise than that! Let&#8217;s now improve things a bit. Suppose the individual tasks were each going to have their own page in this app, perhaps displaying notes and such-like. In that case, we&#8217;d like that list to be a list of links. Here&#8217;s a handy tag that makes it easy to create a link to anything with a name:</p>

<h4>app/views/hobolib/application.dryml (fragment)</h4>

<pre><code>&lt;def tag="name_link"&gt;
  &lt;%= link_to this.name,
              :controller =&gt; this.class.name.underscore.pluralize,
              :action =&gt; 'show',
              :id =&gt; this %&gt;
&lt;/def&gt;
</code></pre>

<p>There&#8217;s a little reflective name-mangling magic going there, but the beauty is that we can forget about that and just use the <code>name_link</code> tag. The change to our page to add our links is trivial:</p>

<h4>app/views/todo_lists/show.dryml</h4>

<pre><code>&lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

&lt;ul_for attr="tasks"&gt;&lt;name_link/&gt;&lt;/ul_for&gt;
</code></pre>

<p>And we&#8217;re done. For today at least. Now &#8212; bearing in mind that this page is dramatically simpler than a typical page in a production app &#8212; let&#8217;s just compare:</p>

<h4>Traditional ERB</h4>

<pre><code>&lt;h1&gt;Todo List: &lt;%= @todo_list.name %&gt;&lt;/h1&gt;

&lt;ul&gt;
  &lt;% @todo_list.tasks.each do |task| %&gt;
    &lt;li&gt;
      &lt;%= link_to task.name, :controller =&gt; "tasks",
                             :action =&gt; "show",
                             :id =&gt; task %&gt;
    &lt;/li&gt;
  &lt;% end %&gt;
&lt;/ul&gt;
</code></pre>

<p>&#8230;to&#8230;</p>

<h4>DRYML</h4>

<pre><code>&lt;h1&gt;Todo List: &lt;name/&gt;&lt;/h1&gt;

&lt;ul_for attr="tasks"&gt;&lt;name_link/&gt;&lt;/ul_for&gt;
</code></pre>

<p>I don&#8217;t know about you but to me that&#8217;s like a breath of fresh air. Now imagine that clarity in the context of a vastly more complex production app, and you might see what Hobo is all about.</p>

<p>(And we <em>still</em> haven&#8217;t shown you the best bit&#8230;)</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2006/11/11/dryml-meet-activerecord/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>A Quick Guide to DRYML</title>
		<link>http://hobocentral.net/blog/2006/11/10/guide-to-dryml/</link>
		<comments>http://hobocentral.net/blog/2006/11/10/guide-to-dryml/#comments</comments>
		<pubDate>Fri, 10 Nov 2006 16:45:49 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://hobotek.net/blog/?p=7</guid>
		<description><![CDATA[

UPDATE: This stuff is very out of date now, you might prefer to look here

Gosh &#8211; there&#8217;s quite a lot in here &#8211; where to start? Guess I&#8217;ll just dive right in.

To follow along, create yourself a new app, install Hobo, and throw in a demo controller. (for help with that, see Hello World)

Tags can [...]]]></description>
			<content:encoded><![CDATA[<p><span id="more-7"></span></p>

<p><em>UPDATE: This stuff is very out of date now, you might prefer to look <a href="/blog/2007/08/15/dryml-tour-06-not-quite-ready-yet/">here</a></em></p>

<p>Gosh &#8211; there&#8217;s quite a lot in here &#8211; where to start? Guess I&#8217;ll just dive right in.</p>

<p>To follow along, create yourself a new app, install Hobo, and throw in a <code>demo</code> controller. (for help with that, see <a href="/blog/2006/11/10/hello-world-2/">Hello World</a>)</p>

<p>Tags can be defined inside your views, making them local to that view. Useful for quick prototyping:</p>

<h4>app/views/demo/my_page.dryml</h4>

<pre><code>&lt;def tag="time"&gt;&lt;%= Time.now %&gt;&lt;/def&gt;

&lt;p&gt;The time is &lt;time/&gt;&lt;/p&gt;
</code></pre>

<p>More commonly you&#8217;d define tags in app/views/hobolib/application.dryml, making them available to your whole app, but for the purposes of exploring we&#8217;ll stick with local definitions.</p>

<p>Tags can have attributes. They are available as local variables inside the definition:</p>

<pre><code>&lt;def tag="time" attrs='format'&gt;
  &lt;%= Time.now.strftime(format) %&gt;
&lt;/def&gt;

&lt;p&gt;Today is &lt;time format='%A'/&gt;&lt;/p&gt;
</code></pre>

<p>Tags can call other tags, as you&#8217;d expect:</p>

<pre><code>&lt;def tag="time" attrs='format'&gt;
  &lt;%= Time.now.strftime(format) %&gt;
&lt;/def&gt;

&lt;def tag='today'&gt;&lt;time format='%A'/&gt;&lt;/def&gt;

&lt;p&gt;Today is &lt;today/&gt;&lt;/p&gt;
</code></pre>

<p>Tags can have a body. Use DRYML&#8217;s <code>&lt;tagbody/&gt;</code> to insert the body where you need it:</p>

<pre><code>&lt;def tag="flower_box"&gt;
  &lt;div class='flower_box'&gt;
    &lt;img src='flower.gif'/&gt;&lt;tagbody/&gt;
  &lt;/div&gt;
&lt;/def&gt;

&lt;flower_box&gt;Nice flower eh?&lt;/flower_box&gt;
</code></pre>

<p>(Aside: yes, you could have done something similar with CSS, but there plenty you <em>can&#8217;t</em> do with CSS, like adding drop shadows in a way that copes with resizing. The drop-shadow technique I prefer needs <em>8 nested DIVs</em>. Boy was I ever glad to wrap <em>that</em> in a tag)</p>

<p>Watch out for the XHTML compliant image tag (<code>&lt;img ... /&gt;</code>). DRYML templates must be valid XML, except for two relaxations: they may contain ERB scriplets (in content or attribute values), and they need not have a single root tag.</p>

<p>OK let&#8217;s define a tag that lays out a whole page &#8212; yes you can use DRYML tags instead of layouts. This has the advantage of letting the choice of page-layout be made in the view, where it belongs (regular Rails layouts are selected in the controller). It also makes it easier for the page to pass bits-and pieces to the layout, such as a title and an <code>onload</code> event.</p>

<p>Here&#8217;s a simple page layout that separates the header and footer with a <em>horizontal rule</em> no less! Notice how we define multiple attributes using commas in <code>attrs</code> attribute.</p>

<pre><code>&lt;def tag='page' attrs='title,onload,header,footer'&gt;
  &lt;html&gt;
    &lt;head&gt;&lt;title&gt;&lt;%= title %&gt;&lt;/title&gt;&lt;/head&gt;
    &lt;body onload="&lt;%= onload %&gt;"&gt;

      &lt;div class='header'&gt;
        &lt;%= header %&gt;
      &lt;/div&gt;
      &lt;hr/&gt;

      &lt;tagbody/&gt;

      &lt;hr/&gt;
      &lt;div class='footer'&gt;
        &lt;%= footer %&gt;
      &lt;/div&gt;

    &lt;/body&gt; 
  &lt;/html&gt;
&lt;/def&gt;


&lt;page title="My Page"
      onload="alert('NO PLEASE! Not an onload alert!')"&gt;
  My wonderful page
&lt;/page&gt;
</code></pre>

<p>Note the absence of a header and footer in that case. We&#8217;d rather not define those in attributes, as they&#8217;ll probably contain mark-up. So we&#8217;ll use an alternate syntax for supplying parameters to tags. Lets quickly get rid of that horrible alert too &#8211; if we don&#8217;t supply the <code>onload</code>, the result in the rendered page will simply be a blank <code>onload</code> on the <code>&lt;body&gt;</code> tag.</p>

<pre><code>&lt;def tag='page' attrs='title,onload,header,footer'&gt;
  &lt;html&gt;
    &lt;head&gt;&lt;title&gt;&lt;%= title %&gt;&lt;/title&gt;&lt;/head&gt;
    &lt;body onload="&lt;%= onload %&gt;"&gt;

      &lt;div class='header'&gt;
        &lt;%= header %&gt;
      &lt;/div&gt;
      &lt;hr/&gt;

      &lt;tagbody/&gt;

      &lt;hr/&gt;
      &lt;div class='footer'&gt;
        &lt;%= footer %&gt;
      &lt;/div&gt;

    &lt;/body&gt; 
  &lt;/html&gt;
&lt;/def&gt;


&lt;page title="My Page"&gt;
  &lt;:header&gt;
    Welcome to my site
  &lt;/:header&gt;

  My wonderful page

  &lt;:footer&gt;
    My site. &amp;copy; Me. Some Rights Reserved
  &lt;/:footer&gt;
&lt;/page&gt;
</code></pre>

<p>Those tags that start with a colon, e.g. <code>&lt;:footer&gt;</code>, are not defined tags. They are simply parameters to the <code>&lt;page&gt;</code> tag. They can appear anywhere <em>directly</em> inside the call (i.e. the <code>&lt;page&gt;</code> tag in this case). They could even have been given the other way around, the result would be the same. </p>

<p>Obviously the <code>&lt;page&gt;</code> tag isn&#8217;t much use unless it&#8217;s defined somewhere where it can be used by multiple pages. The simplest thing is to move it into app/views/hobolib/application.dryml. That library &#8212; or taglib as we call them &#8212; is implicitly imported by every page in your app. Alternatively here&#8217;s how you could put it into your own taglib instead. Move just the <code>&lt;def&gt;</code> into a new file e.g.:</p>

<h4>app/views/shared/my_tags.dryml</h4>

<pre><code>&lt;def tag='page' attrs='title,onload,header,footer'&gt;
  &lt;html&gt;
    &lt;head&gt;&lt;title&gt;&lt;%= title %&gt;&lt;/title&gt;&lt;/head&gt;
    &lt;body onload="&lt;%= onload %&gt;"&gt;

      &lt;div class='header'&gt;
        &lt;%= header %&gt;
      &lt;/div&gt;
      &lt;hr/&gt;

      &lt;tagbody/&gt;

      &lt;hr/&gt;
      &lt;div class='footer'&gt;
        &lt;%= footer %&gt;
      &lt;/div&gt;

    &lt;/body&gt; 
  &lt;/html&gt;
&lt;/def&gt;
</code></pre>

<p>In the page, instead of the full definition of <code>&lt;page&gt;</code>, you now just need to import that taglib using, um, <code>&lt;taglib&gt;</code>. Like this:</p>

<pre><code>&lt;taglib src="shared/my_tags"/&gt;

&lt;page title="My Page"&gt;
  &lt;:header&gt;
    &lt;h1&gt;Welcome to my site&lt;/h1&gt;
  &lt;/:header&gt;
  &lt;:footer&gt;
    &lt;i&gt;My site. &amp;copy; Me. Some Rights Reserved&lt;/i&gt;
  &lt;/:footer&gt;

  My wonderful page

&lt;/page&gt;
</code></pre>

<p>If you define your tags in app/views/hobolib/application.dryml, you don&#8217;t need to use <code>&lt;taglib&gt;</code> &#8212; this is the global taglib and is always imported. Convention over configuration, man!</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2006/11/10/guide-to-dryml/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Getting Started: Hello World in DRYML</title>
		<link>http://hobocentral.net/blog/2006/11/10/hello-world-2/</link>
		<comments>http://hobocentral.net/blog/2006/11/10/hello-world-2/#comments</comments>
		<pubDate>Fri, 10 Nov 2006 16:00:04 +0000</pubDate>
		<dc:creator>Tom</dc:creator>
				<category><![CDATA[Documentation]]></category>

		<guid isPermaLink="false">http://hobotek.net/blog/?p=5</guid>
		<description><![CDATA[Hello World will never get old for me. What more could you need to
get yourself oriented in a new technology? I remember the first time
I managed to string the whole J2EE stack together. All that technology
&#8211; JNDI, RMI, EJB&#8230; All just to get &#8220;Hello World&#8221; on the
screen. Hmmm. Can&#8217;t say I miss that too much.

Where [...]]]></description>
			<content:encoded><![CDATA[<p>Hello World will never get old for me. What more could you need to
get yourself oriented in a new technology? I remember the first time
I managed to string the whole J2EE stack together. All that technology
&#8211; JNDI, RMI, EJB&#8230; All just to get &#8220;Hello World&#8221; on the
screen. Hmmm. Can&#8217;t say I miss <em>that</em> too much.</p>

<p>Where was I?</p>

<p><span id="more-5"></span></p>

<p>Oh yes. One of the main features of Hobo is DRYML &#8211; an new template
engine that extends ERB with user-defined tags. Thats right: <em>extends</em>.
 DRYML templates can use erb scriptlets just like regular RHTML
templates.</p>

<p>We&#8217;re going to create a custom tag <code>&lt;hello&gt;</code> which will render &#8211; you
guessed it &#8211; &#8220;Hello World&#8221; on the page. Obviously, the real goal here is to get
Hobo installed and verify that it&#8217;s running.</p>

<p>To run Hobo you need Rails 1.2, which at the time of writing is available as release-candidate 1. Install it like this:</p>

<pre><code>gem install rails --source http://gems.rubyonrails.org -y
</code></pre>

<p>To install Hobo into a new Rails app, simply:</p>

<pre><code>$ rails hobo_hello_world
$ cd hobo_hello_world
$ ./script/plugin install svn://hobocentral.net/hobo/trunk
</code></pre>

<p>Next, there are one or two directories and files that Hobo expects to
find. Create these with the handy generator:</p>

<pre><code>$ ./script/generate hobo
  create  app/views/hobolib
  create  app/views/hobolib/themes
  create  app/views/hobolib/application.dryml
</code></pre>

<p>OK, Hobo is now at your service! Before we can have a Hello World view, of
course, we&#8217;ll need a controller:</p>

<pre><code>$ ./script/generate controller hello
</code></pre>

<p>No need to edit that. We&#8217;ll just go straight for the view.</p>

<h4>File: app/views/hello/hello_world.dryml</h4>

<pre><code>&lt;def tag="hello"&gt;Hello World!&lt;/def&gt;

&lt;p&gt;Here it comes... Can you stand it??!&lt;/p&gt;

&lt;p style="font-size: 500%"&gt; &lt;hello/&gt; &lt;/p&gt;
</code></pre>

<p>That&#8217;s it. Start your engines:</p>

<pre><code>$ ./script/server
</code></pre>

<p>And browse yourself over to <code>localhost:3000/hello/hello_world</code></p>

<p>Outstanding work people. If that has whet your appetite and you want
more, I suggest you haul your digital assets over to either <a href="/blog/2006/11/10/why-dryml/">Why
DRYML?</a> or <a href="/blog/2006/11/10/guide-to-dryml/">A Quick Guide to DRYML</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://hobocentral.net/blog/2006/11/10/hello-world-2/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

