<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:cookbook.hobocentral.net,2005:/recipes/atom</id>
  <link type="text/html" href="http://cookbook.hobocentral.net" rel="alternate"/>
  <link type="application/atom+xml" href="http://cookbook.hobocentral.net/recipes/atom.xml" rel="self"/>
  <title>HoboCookbook - recipes</title>
  <updated>2012-08-21T03:53:44Z</updated>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/1804</id>
    <published>2012-08-21T03:53:44Z</published>
    <updated>2012-08-21T03:54:22Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/1804-mobile-ipad-friendly-sortable-collections" rel="alternate"/>
    <title>Mobile &amp; iPad friendly sortable-collections</title>
    <content type="html">I could not get [sortable-collection](http://cookbook-1.0.hobocentral.net/api\_tag\_defs/sortable-collection) drag-and-drop to function on mobile devices and iPads and had to come up with the following solution.

For the purpose of this recipe, we are going to build on the [Task re-ordering](http://cookbook-1.0.hobocentral.net/tutorials/agility#task_reordering) in the Agility Tutorial (stories have many tasks that can be re-ordered).

To app/controllers/tasks_controller.rb file, add the following:

    web_method :move_higher
    web_method :move_lower

To the app/models/task.rb file, add the following:

    def move_higher_permitted?
      editable_by?(acting_user, :position)
    end
    
    def move_lower_permitted?
      editable_by?(acting_user, :position)
    end

To the app/views/taglibs/application.dryml file, add the following:

    
      
        
          &lt;div class="ordering-buttons"&gt;
           
             
          &lt;/div&gt;
        
      
    

Aside:  please suggest a better tag name than "button-sortable-collection".

To the public/stylesheets/application.css file, add the following:

    div.ordering-buttons { float: left; color: white; margin-left: -10px; padding: 0; }

Change the app/views/stories/show.dryml file to:

    
      
      
        
      
    

It would be nice if this functionality could be added to the Hobo core, but it requires a better understanding of Hobo that I have.
</content>
    <updated>2012-08-21 03:54:22 UTC</updated>
    <author>
      <name>Henry Baragar</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/1803</id>
    <published>2012-06-23T10:27:19Z</published>
    <updated>2012-06-23T10:30:45Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/1803-use-your-own-hobo-fork" rel="alternate"/>
    <title>Use your own hobo fork</title>
    <content type="html">If you want to use your own hobo-version and also help fixing problems, there are some easy steps to do this.

**Fork hobo:**

  - you need a [github account]
  - fork [hobo] - see also [fork-a-repo]

&gt;If you have push problems:
&gt;
&gt; - git config -l # to see your config
&gt;  - change git config line to use ssh:

&gt;          remote.origin.url=ssh://git@github.com/YOUR_NAME/hobo.git

**Add this line to your Gemfile:**

       gem "hobo", "=VERSION", :git =&gt; 'git://github.com/YOURNAME/hobo.git'
        -&gt; e.g. gem "hobo", "=1.4.0.pre6", :git =&gt; 'git://github.com/kredmer/hobo.git'
(and remove your old gem "hobo" line !)

**run in console to update your gemfile.lock:**

       bundle update hobo


**YOU ARE DONE !**


Check issues and send a [pull-request], if you solved a problem.


[github account]: https://github.com/
[hobo]: https://github.com/tablatom/hobo
[fork-a-repo]: https://help.github.com/articles/fork-a-repo
[pull-request]: https://help.github.com/articles/using-pull-requests

</content>
    <updated>2012-06-23 10:30:45 UTC</updated>
    <author>
      <name>kredmer</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/1802</id>
    <published>2012-02-11T18:23:29Z</published>
    <updated>2012-02-11T18:52:47Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/1802-appcelerator-titanium-mobile-app-and-hobo" rel="alternate"/>
    <title>Appcelerator Titanium (Mobile App) and Hobo</title>
    <content type="html">Hi all,

I've tried searching for documentation on using Titanium (or anything) to make a mobile app (iOS/Android) from a Hobo app.  I wanted to use Hobo both as a front-end and a back-end.  While, I have seen this documented well outside of the Hobo world, I had not seen much on the combo of the two.  (As always throughout, please correct me if I am wrong.)

An example of a combo Rails/iPhone app is [here](https://github.com/clarkware/budgets-iphone)

However, given I don't know Objective-C, and I like the Hobo-style...so I set out with [Appcelerator](http://www.appcelerator.com/).

-----

The first issue was authentication.  I knew I did not want to get deep into how Hobo authenticates, so I decided to use "Basic Authentication" - passing the username and login in the [HTTP header.](http://en.wikipedia.org/wiki/Basic_authentication)  NOTE that this is wildly not secure unless you use HTTPS, which I am planning to use in production (unless someone comes up with a better idea).

In general, I followed this really amazing [tutorial.](http://mobile.tutsplus.com/tutorials/appcelerator/titanium-user-authentication/)  Also, [here](http://timneill.net/2011/06/simple-remote-requests-with-titanium-appcelerator/) is another guy that wrote a function to make all his data connections.

I just made a new controller method that just returns some basic user information, in a controller that is protected by login (by adding the below to one of my controllers).  

     before_filter :login_required


This way Appcelerator etiher gets JSON information or redirected to the login page, which results in an error.  From then on, in the header of the communication, I just pass what was given to be as a "Set-Cooke" back as a "Cookie."  (Header fields are described [here](http://en.wikipedia.org/wiki/).)

There are a few important nuances at this point:

1) Appcelerator errors out whenever you have an HTTP error on the Rails side (mostly manifests as "406 Not Acceptable." You must set the [Content-Type header.](http://developer.appcelerator.com/question/116980/iphone--rails--xhr--undefined-method-tosym-for-nilnilclass#203901)


     xhr.open('GET', url, false);
     xhr.setRequestHeader('Content-Type', 'application/json');
     xhr.send(); 


2) Pass authentication details [encoded in Base64](http://developer.appcelerator.com/question/120731/xhr-authentication-with-restful-api)

3) Store the cookie details in an [Appcelerator "property"](http://developer.appcelerator.com/question/117952/remember-me--session-implementation)

4) Appcelerator has what I think is a bug, and sometimes does not like the cookies that Hobo produces.  When you pass the header back to Hobo, you have to use ["cookie"](http://developer.appcelerator.com/question/118509/can-i-set-cookie-whose-value-include-equal-on-http-request) instead of "Cookie" (otherwise it errors out).


Creating methods in the controller (in Hobo) that send JSON is really easy.  It is quite awesome actually.  More on that [here.](http://stackoverflow.com/questions/2566759/how-can-i-generate-json-from-respond-to-method-in-rails)

In general you can simply do something like this:


     show_action :new_show

     def new_show
         hobo_show do
             render :json =&gt; @this.to_json
         end
     end

I mainly created new methods just so I didn't mess up the old ones, and so I did not have to deal with the respond_to and content-type issues.  There are some create tutorials on what to do with the data in Appcelerator once you have it.  [This one!](http://developer.appcelerator.com/blog/2011/08/handling-remote-data-with-httpclient-and-json.html)

Also, one more thought on this - get the [KitchenSink app](https://github.com/appcelerator/KitchenSink)!  You can copy and paste components practically.

I am about to embark on writing the part that allows me to create data on the mobile side, and pass it back up.  I'm not sure how this will pan out, but I think it will probably involve turning off [protect from forgery](http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html#method-i-protect\_from\_forgery) (that token on the web-side forms....or possibly, this might not be a problem with POST requests??)

-----

Please punch holes in this - I want to hear what has worked for others, and what might be wrong with what I am doing.  It also took me a while to find these resources, so I thought it would be important to share.

Also given the text java-script field definitions, and the general simplicity, I could see writing generators to make Appcelerator components from Hobo models!!  Anyone interested??

Cheers!

</content>
    <updated>2012-02-11 18:52:47 UTC</updated>
    <author>
      <name>mdkarp</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/75</id>
    <published>2011-09-20T15:50:31Z</published>
    <updated>2011-09-20T16:18:02Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/75-quick-and-dirty-required-field-highlighter" rel="alternate"/>
    <title>Quick-and-Dirty Required Field Highlighter</title>
    <content type="html">After almost a year of beta testing, the REAL users requested that the required/mandatory fields be highlighted.  (I had thought of this last year at the beginning of development, but could not figure out how to do it, then).

I recently found a thread from January 2011 that discussed this capability\.  In this thread, the use of &amp;lt;feckless-fieldset&gt; was recommended.  I tried this and got it to work (functionally, that is; it would highlight the required fields), but it took much too much time to style.  Unless I missed something, each page needed to be styled independently which would have taken more time than I had available.

After examining the &amp;lt;feckless-fieldset&gt; code, it appeared that the highlighting capability could be merged with the normal &amp;lt;field-list&gt; tag to give me the effect that I wanted.

I modified &amp;lt;field-list&gt; to produce &amp;lt;field-list-star-required&gt; (star-required being the imperative, meaning put a star (\*) on each required field).

Using this, I could change the form code from:

    

to:

    

I put the following code in application.dryml (I should have put it in a separate file, but as the title says, "quick and dirty"):

    
      
      
      
        
         no_edit} if tag == "input"
        -%&gt;
          
            
            
            
              &lt;span class="required"&gt;*&lt;/span&gt;
            
            
            
              
                 
              
              &lt;div&gt;&lt;/div&gt;
            
          
        
      
    

and the following code in application.css:

    .required { color : red; }

The resulting form, when being edited, shows each of the required field's captions with a red asterisk appended.  Just what I needed, no more, no less.

If anyone would like to extend this such that the required fields in the model replace the static list in the tag's invocation, please do so (and let me know!).</content>
    <updated>2011-09-20 16:18:02 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/74</id>
    <published>2011-09-07T19:33:02Z</published>
    <updated>2011-09-07T20:13:05Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/74-cut-and-paste-almost-recipe-to" rel="alternate"/>
    <title>Cut and paste (almost) recipe to add the tiny-mce editor to hobo apps.</title>
    <content type="html">In my RoR days, I used the tiny-mce editor when ever I wanted to edit html fields.

I searched the web for hobo versions of html editors and found Hoboyui in another recipe in this Cookbook.  I followed the recipe and got an editor that worked, but all of the buttons were blank.  Further investigation showed that the Yui folks were rationalizing their directory structure and even some of their own members were complaining about blank buttons.  I could not find the button image data anywhere on their website or on the rest of the web for that matter.  After hours of searching, I gave up.  Remembering the success I had with tiny-mce in the past, I decided to hoboize it.

If you don't already have it:

     [sudo] gem install tiny_mce

edit your Gemfile, adding the line:

     gem 'tiny_mce'

then:

     bundle install

edit config/application.rb, adding the line:

     config.gem 'tiny_mce' 

edit taglibs/application.dryml, adding the lines:

    
        
            
                
                
                   
            
        
    

Add the file public/javascripts/hoboTinyMce.js (see comments about file name):

    // This file is hoboTinyMce.js
    //
    // I had extreme agony when the file was named "hobo_tiny_mce.js"
    // the editor would not initialize and the log showed many unresolved
    // javascript files.  After renaming the file, everything just worked.
    //
    function tinymce_page_loaded()
    {
        var elements=document.getElementsByTagName("textarea");
        for(i = 0;i &amp;lt; elements.length;i++)
        {
            className = elements[i].className
    // Only diddle with hobo :html fields, ignore :text, etc.    
            if(className.indexOf('html') &gt; -1 )
            {
                elements[i].className = className + " mceEditor";
            }
        }
    }

Now modify the controllers of those pages that need the html editor.  This is where you can customize the appearance of tiny-mce on a page-by-page basis.  If you do not include a "uses\_tiny\_mce" line, the page will not invoke tiny-mce even if the model has an :html field.

controller 1:

    uses_tiny_mce # shows a very simple editor (with controls on the bottom as the default).

controller 2:

    uses_tiny_mce(:options =&gt; {  :theme =&gt; 'advanced', :theme_advanced_toolbar_align =&gt; "left"} ) # fancier editor

Note that the :options hash allows you to customize all of the pages associated with any particular controller.  It is the equivalent of the javascript tiny_mce:init( .... ) except that it works on a controller-by-controller basis whereas the init works on every editor on the site.  See http://tinymce.moxiecode.com/documentation.php for details about options and themes.  There are so many configuration items that it would be inappropriate to show them all here.  Besides the doc site does a much better job of explaining them than I can.



</content>
    <updated>2011-09-07 20:13:05 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/73</id>
    <published>2011-09-06T18:21:37Z</published>
    <updated>2011-09-06T18:26:50Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/73-i18n-internationalization-for-beginners" rel="alternate"/>
    <title>I18n Internationalization for beginners</title>
    <content type="html">#Step 1:

Create a new Hobo app (we are using Hobo 1.3 RC for this tutorial)

    &gt; hobo new i18ntest


(usual options in wizard, assuming you don't rename the user model name)
When defining locales, add the needed ones: en es fr de

    &gt; cd i18ntest

#Step 2:

Create a UserLocale class to store the available locales

    &gt; hobo g resource UserLocale
    ...
    &gt; edit app/models/user_locale.rb

    fields do
        +name :string, :required, :unique, :limit =&gt; 5+
        timestamps
    end
    +has_many :users+

    &gt; edit app/models/user.rb

    +belongs_to :user_locale+

    &gt; hobo g migration
    ...
    &gt; rails server

#Step 3:

Signup as administrator and add some user locales: en es de fr

    &gt; edit app/controllers/user_locales_controller.rb


      -auto_actions :all-
      +auto_actions :all, :except =&gt; :index+


#Step 4:

Edit your user and select the correct locale

(note that the field list will show something like `"	&lt;a href="/user_locales/1-en" class="user_locale-link"&gt;&lt;span class="view user-locale-name "&gt;en&lt;/span&gt;&lt;/a&gt;". ` Is it probably a bug?

#Step 5:

Add some information in the page footer to help you understanding the process.

    &gt; edit app/views/taglibs/application.dryml


    
      
    

    
      
        
          &lt;br /&gt;User: 
          &lt;br /&gt;Locale: 
          &lt;br /&gt;Params Locale: 
          &lt;br /&gt;User Locale: 
          &lt;br /&gt;Default Locale: 
        
      
    

This will add the information about the locale variables.  Our precedence will be: params &gt; user &gt; default.  That is: if there exist a ?param=xx URL parameter, it will be the locale of the page to be shown.  If not, then the page will take the current_user.user_locale as locale.  If the current_user is not logged or has no locale set, then the default locale of the application will be used.

#Step 6:

Configure the I18n for the application
    
    &gt; edit config/application.rb


uncomment and tune:

     -# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]-
     -# config.i18n.default_locale = :de-

     +config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]+
     +config.i18n.default_locale = :en+
    
    &gt; edit apps/controllers/application_controller.rb

      before_filter :set_locale

      def set_locale
        if (params[:locale])
          I18n.locale = params[:locale]
        else if (current_user.signed_up?)
            I18n.locale = current_user.user_locale.to_s
          else
            I18n.locale=I18n.default_locale
          end
        end 
      end

    =begin
      def default_url_options(options={})
        logger.debug "default_url_options is passed options: #{options.inspect}\n"
        { :locale =&gt; I18n.locale }
      end
    =end  

Please note that the =begin and =end code will only work fine in the production mode.  The development fast user switcher will fail if you activate this, so we will not uncomment this until the problem is fixed.

#Step 7:

The application must be restarted in order to allow this configuration to take effect.

#Step 8:

Add the user_locale field to the signup page.


    &gt; edit apps/models/users


        create :signup, :available_to =&gt; "Guest",
               -:params =&gt; [:name, :email_address, :password, :password_confirmation],-
               +:params =&gt; [:name, :email_address, :password, :password_confirmation, :user_locale],+
               :become =&gt; :active

      def update_permitted?
        acting_user.administrator? ||
          (acting_user == self &amp;&amp; only_changed?(:email_address, :crypted_password,
                                                -:current_password, :password, :password_confirmation))-
                                               +:current_password, :password, :password_confirmation,:user_locale))+

      end



#Step 9:

Test the locale preferences are working
Create a new user and set a different locale for him (f.i. 'en' for admin and 'es' for the new user).

Use the development fast user switcher to switch between the useres and see how this change the language the Hobo app is using.

#Step 10:

Activate the default\_url\_options to test the URL param method.

    &gt; edit apps/controllers/application_controller.rb

     -=begin-
     +#=begin+
      def default_url_options(options={})
        logger.debug "default_url_options is passed options: #{options.inspect}\n"
        { :locale =&gt; I18n.locale }
      end
     -=end-  
     +#=end+

#Step 11:

Test the URL params work.

Go to admin user, and go to home.  Add ?locale=fr to the URL, and you see the changes.  Try adding ?locale=de, and see how it changes.

Please note:

* If you use the "Login" "Signup" links, the locale is correctly keeped.

* If you use the "Home" link, then the locale is missing.

* If you try to change the user using the development fast switcher, it will fail.  

This is because it forces urls like

instead of 

Try to use it, and change the ? to &amp; to see it works.

#Step 12:

Let's fix these problems:

Copy the apps/views/taglibs/auto/rapid/pages.dryml definition of main-nav to the apps/views/taglibs/application.dryml.

In apps/views/taglibs/application.dryml file, add the '?locale=#{I18n.locale}' text to the href of the Home item:

    &gt;edit apps/views/taglibs/application.dryml


    
      
        -Home-
       +Home+
      
    

#Step 13:
Let's add some flag icons in the footer to change the param[:locale], to allow the Guest user to change the locale of the web.

    &gt; edit app/views/taglibs/application.dryml

    
        0)+" ES", "?locale=es" %&gt;
        0)+" EN", "?locale=en" %&gt;
        0)+" FR", "?locale=fr" %&gt;
        0)+" DE", "?locale=de" %&gt;
    

    
      
        &lt;li&gt;&lt;/li&gt;
      
    

    &gt; edit app/views/taglibs/application.dryml


    
      
        
          ++
          &lt;br /&gt;User: 
          &lt;br /&gt;Locale: 
          &lt;br /&gt;Params Locale: 
          &lt;br /&gt;User Locale: 
          &lt;br /&gt;Default Locale: 
        
      
    

#Step 15:

The system is almost complete.  A user can specify the locale to use in the web session by clicking on the flag.  If not specified, the locale will be his/her preference in his her profile.  If not, the locale will be set from I18n.default_locale

#TO DO:

* Solve the problem with the development fast user switcher.
* Populate automatically the UserLocale class with the locales found in the config/locales directory, or find a solution that does not require a model.
* When a user has a locale1 preference and uses the flag to select the locale2 preference, some links will ignore the default\_url\_options functions: the users-&gt;show action and the users-&gt;account action.
* In the user-&gt;show action, solve the way the User Locale is shown (User locale 	`&lt;a href="/user_locales/1-en" class="user_locale-link"&gt;&lt;span class="view user-locale-name "&gt;en&lt;/span&gt;&lt;/a&gt;)`

</content>
    <updated>2011-09-06 18:26:50 UTC</updated>
    <author>
      <name>txinto</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/72</id>
    <published>2011-06-12T05:39:03Z</published>
    <updated>2011-06-12T15:42:53Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/72-upload-progress-bar-with-hobo-and" rel="alternate"/>
    <title>Upload Progress  Bar with Hobo and Paperclip</title>
    <content type="html">This recipe has been tested with Hobo 1.0.1 and Rails 2.3.8. 

It uses Phusion Passenger, Ajax and a iframe hack to get a nice ajax progress bar for the uploads.

Before we start, you can take a look at [the demo video I recorded](http://ihuerta.net/lang/en-us/2011/06/new-hobo-recipe-using-a-progress-bar-with-hobo/) and the [complete demo in .tar.gz](http://ihuerta.net/wp-content/uploads/2011/06/demo\_progress\_bar.tar.gz).

Step One: Create two tables and install paperclip
=================================================

We create the application and two tables: clients and attachments
&lt;pre&gt;
hobo demo\_progress\_bar
cd demo\_progress\_bar
script/generate hobo\_model\_resource client name:string
script/generate hobo\_model\_resource attachment
&lt;/pre&gt;

Every *client* has_many *attachments*:

&lt;pre class="ruby"&gt;
&lt;code&gt;
# app/models/client.rb
has_many :attachments
&lt;/code&gt;
&lt;/pre&gt;


&lt;pre class="ruby"&gt;
&lt;code&gt;
# app/models/attachment.rb
belongs_to :client
&lt;/code&gt;
&lt;/pre&gt;

Create and run the migrations:
&lt;pre&gt;
script/generate hobo_migration
&lt;/pre&gt;



Now we add the Hobo magic to connect the clients and the attachments:

&lt;pre class="ruby"&gt;
&lt;code&gt;
# app/viewhints/client_hints.rb:
children :attachments
&lt;/code&gt;
&lt;/pre&gt;

&lt;pre class="ruby"&gt;
&lt;code&gt;
# app/controllers/attachments_controller.rb:
  auto_actions :write_only
  auto\_actions\_for :client, :create
end
&lt;/code&gt;
&lt;/pre&gt;


Next we install Paperclip and Paperclip with Hobo. 
&lt;pre&gt;
script/plugin install git://github.com/thoughtbot/paperclip.git
script/plugin install git://github.com/tablatom/paperclip\_with\_hobo.git
&lt;/pre&gt;

I found a bug between the latest paperclip and paperclip\_with\_hobo, so you can try this slightly older paperclip revision (untar it in vendor/plugins): http://ihuerta.net/wp-content/uploads/2011/06/Paperclip.tar.gz

&lt;pre&gt;
cd vendor/plugins
wget http://ihuerta.net/wp-content/uploads/2011/06/Paperclip.tar.gz
tar xzfv Paperclip.tar.gz
&lt;/pre&gt;


Now we prepare the model, the views and the migrations

&lt;pre class="ruby"&gt;
&lt;code&gt;
      has_attached_file :file, 
          :whiny =&gt; false, 
          :path =&gt; "#{RAILS_ROOT}/files/:id.:extension"
      validates_attachment_size :file, :less_than =&gt; 5.megabytes
      validates_attachment_presence :file


      def name
        file.original_filename
      end
      
      def size
        if (file_file_size / 1024) &amp;lt; 1024
          (file_file_size / 1024).to_s + ' KB'
        else
          (file_file_size / 1048576).to_s + ' MB'
        end
      end
&lt;/code&gt;
&lt;/pre&gt;








app/views/clients/show.dryml

    
      
        
      
    



app/views/taglibs/application.dryml

    &amp;lt;!-- Paperclip support --&gt;
    
     
       
    
    
    &amp;lt;!-- Card for every attachment --&gt;
    
      
        
          &lt;h4&gt;&lt;a href="/attachments/download/#{this.id}"&gt;&lt;/a&gt;&lt;/h4&gt;
          &lt;div&gt;
            
          &lt;/div&gt;
        
        
          &lt;p&gt;Size: &lt;/p&gt;
          &lt;p&gt;Date: &lt;/p&gt;
        
      
    





app/controllers/attachments_controller.rb

      def download
        attachment = Attachment.find(params[:id])
        send_file 'files/' + attachment.name
      end


&lt;pre&gt;
script/generate hobo_migration
&lt;/pre&gt;




Step 2: Start tracking the uploads with Phusion Passenger
=========================================================

In order to track the progress of uploads, we need the server to give us that information. "Apache Upload Progress Module" is a nice extension that solves our problem.


Download https://github.com/drogus/apache-upload-progress-module

Extract it

Install it
&lt;pre&gt;
sudo apxs2 -c -i -a mod\_upload\_progress.c
&lt;/pre&gt;

Prepare the virtualhost to track uploads:

    
      ServerName demo_progress_bar.localhost
      DocumentRoot '/home/ignacio/Trabajos/2_Proyectillos/Hobo/demo_progress_bar/public'
      RailsEnv development
      
         AllowOverride all
         Options -MultiViews
      
    
       
         # enable tracking uploads in /
         TrackUploads On
       
    
       
         # enable upload progress reports in /progress
         ReportUploads On
       
    
    


And a nice tip if you are working with Ubuntu. While you are testing in localhost, it's very nice to simulate a slow upload speed so you can actually see the bar moving. I use this iprelay command and work through http://demo\_progress\_bar.localhost:8002
&lt;pre&gt;
iprelay -b350000 8002:demo\_progress\_bar.localhost:80
&lt;/pre&gt;


Before we start with the upload bar, let's check if the tracking is working. First we add a test UUID to the file upload request:

app/views/clients/show.dryml:

    
    
      
        
        
          &lt;a href="/progress/?X-Progress-ID=#{uuid}"&gt;Testing the upload URL&lt;/a&gt;
        
      
    



When you reload the client page, you will see a link that gives you a JSON answer, something like:

&lt;pre&gt;
{ "state" : "starting", "uuid" : "659102713689307356231103628731" }
&lt;/pre&gt;

This means the download hasn't yet started. If you are uploading the file, the JSON should be:

&lt;pre&gt;
{ "state" : "uploading", "received" : 47104, "size" : 173966, "speed" : 47104, 
"started_at": 1307856054, "uuid" : "659102713689307356231103628731" }
&lt;/pre&gt;

With this info we are going to create the progress bar :)



Step 3: Build the progress bar
==============================

Now let's get to the fun part. First we need a JS function to take care of the progress-bar. Basically it gets the JSON info every couple of seconds, and based on the answer it decided what to do:

public/stylesheets/application.js:

    function hoboprogress(uuid){
      // Make the progress bar appear
      $('progress-bar').setStyle('display: block;');
      
      // Reload the Progress Bar every 2 seconds
      new PeriodicalExecuter(
        function(pe){
          new Ajax.Request("/progress",{
            method: 'get',
            parameters: 'X-Progress-ID='+uuid,
            onSuccess: function(xhr){
              /* When we get the upload info in JSON, we evaluate it */
              var upload = xhr.responseText.evalJSON();
              if(upload.state == 'uploading'){
                /* Calculate the percentage */
                upload.percent = Math.floor((upload.received / upload.size) * 100);
                $('progress-bar').setStyle({width: upload.percent + "%"});
                $('progress-bar').update(upload.percent + "%" + upload.speed);
              };
              /* Once we are in the 100%, we trigger some stuff */
              if(upload.state == 'done' || upload.percent == 100){
                // Stop the PeriodicalExecuter
                pe.stop();
                
                // Change the message
                $('progress-bar').update('Saving...');
                
                // Update the file list (we wait a second so the server writes the new file to the DB)
                Hobo.ajaxRequest.delay(0.8,'/clients/show/3', ['collection'],{
                  onSuccess: function(response){
                    // Hide the progress bar once the update is complete
                    $('progress-bar').fade();
                  },
                  message:false
                });
                
                // Empty the file input box
                // This is a bit complicated to do cross browser
                // Apparently, replacing the HTML works best
                $('file-input').update("");
              };
              
            }
          })
        },2
      );
    };



Ok, too much JS, I know. But the result is worth it! Now let's add the progress-bar div to the show.dryml, plus an iframe for the upload to work:


app/views/clients/show.dryml

    
    
      
        
          
        
        
        
          &amp;lt;!-- Progress Bar --&gt;
          &lt;div style="background-color: green; color: white; font-size: 20px; padding: 10px; display: none;"&gt;Starting upload&lt;/div&gt;
          &amp;lt;!-- Iframe for the Ajax upload --&gt;
          
           
        
      
      
      
        
      
      
    

Last step: make the controller answer to Ajax requests:

&lt;pre class="ruby"&gt;
&lt;code&gt;
# app/controllers/clients_controller.rb
  def show
    hobo_show do
      hobo_ajax_response if request.xhr?
    end
  end
&lt;/code&gt;
&lt;/pre&gt;
</content>
    <updated>2011-06-12 15:42:53 UTC</updated>
    <author>
      <name>ignacio</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/70</id>
    <published>2011-05-11T02:27:01Z</published>
    <updated>2011-05-12T20:11:47Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/70-permissions-through-a-nested-has-many" rel="alternate"/>
    <title>Permissions through a nested has_many relationship.</title>
    <content type="html">I have a app where the ability to assign many different users to review a project is needed. Each project should only be viewable by either the administrator or the assigned reviewers.

What I did was discover a handy gem named "nested\_has\_many\_through" which allowed the hobo permissions access to the nested data.

See [https://github.com/romanvbabenko/nested\_has\_many\_through] ("https://github.com/romanvbabenko/nested\_has\_many\_through") for more information about the gem in the recipe.
  
  
Be sure to add the gem. Put this line in your Gemfile:

    gem "nested_has_many_through"
  
and run:

    bundle install
  
  
  

The models:

Generate the project model:

    $ hobo g resource project.rb name:string

app/models/project.rb

    class Project &amp;lt; ActiveRecord::Base

    hobo_model # Don't put anything above this

    fields do
      name        :string
      description :text
      timestamps
    end

    has_many :reviews
    has_many :users, :through =&gt; :reviews

    # --- Permissions --- #

    def create_permitted?
      acting_user.administrator?
    end

    def update_permitted?
      acting_user.administrator? || acting_user.in?(users)
    end

    def destroy_permitted?
      acting_user.administrator?
    end

    def view_permitted?(field)
      acting_user.administrator? || acting_user.in?(users)
    end

Now generate your review model:

    $ hobo g resource review.rb name:string

app/models/review.rb

    class Review &amp;lt; ActiveRecord::Base
    
    hobo_model # Don't put anything above this
    
    fields do
      name :string
      timestamps
    end
    
    belongs_to :project
    has_many :review_assignments, :dependent =&gt; :destroy
    has_many :users, :through =&gt; :review_assignments, :accessible =&gt; true
    
    # Get a nice view of the relationships  
    children :review_assignments
    children :users
    
    # --- Permissions --- #


Then create your review assignment model:

    $ hobo g model review_assignment


app/models/review_assignment.rb

    class ReviewAssignment &amp;lt; ActiveRecord::Base
    
    hobo_model # Don't put anything above this

    fields do
      timestamps
    end
    
    has_many :users
    has_many :reviews
    
    # --- Permissions --- #

This is very handy if you want only admins to have the ability to create reviews and assign users. All that is needed is to move the reviews controller to the admin directory *(app/controllers/admin/)* and replace the first line of the file with this:

    class Admin::ReviewsController &amp;lt; Admin::AdminSiteController

  
or when creating your model run the following command (*I have not tried this so YMMV*)

    hobo g resource admin::reviews name:string

  
Now we're all set, run the following from the command line.

    $ hobo g migration


    $ rails s

  
Add some users and programs then go to reviews and assign users to their programs. This application has been posted on Github, so feel free to check it out. [https://github.com/jdowning/Has-Many-Reviewers] ("https://github.com/jdowning/Has-Many-Reviewers")</content>
    <updated>2011-05-12 20:11:47 UTC</updated>
    <author>
      <name>jarad</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/69</id>
    <published>2011-04-19T16:23:29Z</published>
    <updated>2011-04-21T17:06:03Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/69-dynamic-role-based-main-menu" rel="alternate"/>
    <title>Dynamic, Role-based Main Menu</title>
    <content type="html">I have a demo site for this recipe that I posted to github.  Keep in mind that I have never used github before and am a relative new-comer to git itself.

https://github.com/dziesig/Hobo-Dynamic--Role-based-Main-Menu


This recipe implements a Dynamic Main Menu whose contents are a function of the 
site's models and the User's Role(s), with each User potentially having both Primary and Secondary Roles.

&amp;lt;bs&gt;
Background:  My daughter is a successful Real Estate Broker here in Florida. 
&amp;lt;brag&gt;She has degrees in Russian, French, Marketing and Computer Science.&amp;lt;/brag&gt; 
Together we collaborated on a Back Office Intranet originally implemented in 
Ruby-on-Rails (1.2 if I recall correctly).  The menu system on that website
was hard-coded to allow any given user to choose between two potential modes.
For example, the Broker is also an Agent with different needs for each role.
We had multiple roles:

       *   Administrator (me)
       *   Broker (my daughter)
       *   Reviewer (new for the Hobo version)
       *   Agent (my daughter plus many others)
       *   InactiveAgent (few)
       *   Unassigned (needed as a place holder).
  
The design called for a main menu with the following tabs:

       *   Home
       *   ModeSelect (caption is based on user's alternate role)
       *   Yada 1 (Required place-holder, never visible)
       *   Yada 2 (Broker-specific)
          *
       *   Yada 10 (Agent-specific)
          *
       *   Yada 20 (Reviewer-specific)
          *
       *   Yada 32 (it's a big site with many functions/data).

After a really helpful tip from kevinpfromnm, which eliminated a lot of redundant editing between migrations, and which serendipitously improved the appearance of
menus whose owners had primary and secondary roles, the menu design became:

       *   ModeSelect (caption is based on user's alternate role, if any)
           *
           
           *
       *   Home
       *   Yada 1 (Required place-holder, never visible)
       *   Yada 2 (Broker-specific)
           *
       *   Yada 10 (Agent-specific)
           *
       *   Yada 20 (Reviewer-specific)
           *
       *   Yada 32 (it's a big site with many functions/data).
  
The admin menu was:

       *   Home
       *   Permissions
       *   Roles
       *   Users
  
The formats of these menus can be seen in the attached images (once someone tells me where the link is [hint, it is not the words "Upload image" on the left aside]).
In the meanwhile, the images are in the github repository noted above under the doc directory.

If all of the tabs on the main menu were visible simultaneously, 
there would be no room "above the fold" for application data.  
Also, there are things that the Broker/Administrator can do that 
we didn't want Agents know was possible.

My original Hobo design (or lack thereof) used a hard-coded main menu
that (like its Rails predecessor) was getting very hard to maintain.

I took a step back (figuratively) and looked at what Hobo supplied
in a "plain-vanilla" site with the intent of using dryml to extend it and map
the main menu to the permission/role system.
&amp;lt;/bs&gt;

The setup wizard choices need invite-only/email-invitation so the 
admin can set user's roles in this recipe.  There may be other
ways for the administrator to setup the restricted attributes of
users (specifically the user's role that only admin can change) but
I haven't tried to find them.

        hobo new DynamicMainMenu

        cd DynamicMainMenu
        
I found this permission/role implementation somewhere but I can't
remember where.  With a few mods to the original it works fine here.

        hobo g model permission_role
        hobo g resource admin::permission name:string description:string
        hobo g resource admin::role name:string description:string
        
This will populate the menu with some items.

        hobo g resource test_with_space name:string
        hobo g resource yada1  name:string
        hobo g resource yada2  name:string
        hobo g resource yada3  name:string
        hobo g resource yada4  name:string
        hobo g resource yada5  name:string
        hobo g resource yada6  name:string
        hobo g resource yada7  name:string
        hobo g resource yada8  name:string
        hobo g resource yada9  name:string
        hobo g resource yada10 name:string
        hobo g resource yada11 name:string
        hobo g resource yada12 name:string
        hobo g resource yada13 name:string
        hobo g resource yada14 name:string
        hobo g resource yada15 name:string
        hobo g resource yada16 name:string
        hobo g resource yada17 name:string
        hobo g resource yada18 name:string
        hobo g resource yada19 name:string
        hobo g resource yada20 name:string
        
        hobo g migration
        
At this point, you can start the app and see what happens when you have
a lot of menu items.  Note that the White *Current* tab impacts those below it.

Edit application.css

        /* Fixes the over-sized current tab */
        
        .page-header .navigation.main-nav li.current a {
          color: #222;
          background: url(../images/30-DBE1E5-FCFEF5.png) repeat-x #FCFEF5;
          border:0;
        }
        
        /* reduces size of the development-mode dryml file names */
        /* Note, this comes from a mod I made to the hobo templates */
        /* It is not necessary without my mods, but is present in the demo */
        .dryml_file
        {
          font-size: 9px;
        }

        
Refresh the page and the White current tab height returns to the default of the
other tabs and the menu behaves normally.


edit role.rb

        fields do
          name        :string, :unique, :index
          description :string
          timestamps
        end
      
        has_many :users;
      
        has_many :permissions, :through =&gt; :permission_roles, :accessible =&gt; true
        has_many :permission_roles, :dependent =&gt; :destroy
        
        # --- Permissions --- #
        
edit permission.rb

        fields do
          name        :string, :unique, :index
          description :string
          timestamps
        end
        
        has_many :roles, :through =&gt; :permission_roles, :accessible =&gt; true
        has_many :permission_roles, :dependent =&gt; :destroy
        
        # --- Permissions --- #


edit permission\_role.rb

        fields do
          timestamps
        end
      
        belongs_to :role
        belongs_to :permission
      
        def name
          if !role.nil? &amp;&amp; !permission.nil?
            role.name + ' ' + permission.name
          else
            '' # just in case
          end
        end
        
        # --- Permissions --- #

hobo g migration

edit user.rb

        fields do
          name          :string, :required, :unique
          email_address :email_address, :login =&gt; true, :required =&gt; true, :unique =&gt; true
          administrator :boolean, :default =&gt; false
          use_secondary_role 	:boolean, :default =&gt; false #when true, user assumes secondary role
          timestamps
        end
      
        belongs_to  :primary_role, :class_name =&gt; "Role"
        belongs_to  :secondary_role, :class_name =&gt; "Role"
        delegate    :permissions, :to =&gt; :role
        
        def role
          if self.use_secondary_role?
            self.secondary_role
          else
            self.primary_role
          end
        end
        
        def alternate_role
          if self.use_secondary_role?
            self.primary_role
          else
            self.secondary_role
          end
        end    
      
        def toggle_mode
          self.use_secondary_role = !self.use_secondary_role
          self.save!
        end
        
        # This gives admin rights and an :active state to the first sign-up.

&amp;lt; ..... snip ..... &gt;

        alias_method_chain :new_password_required?, :invite_only
      
        # set default roles to Unassigned
        before_save do |user|
          self.primary_role_id = 6 if self.primary_role.nil?
          self.secondary_role_id = 6 if self.secondary_role.nil?
        end
        
        # --- Signup lifecycle --- #

&amp;lt; ..... snip ..... &gt;

        #--------------------------------------------------------------------
        # Roles, Permissions, has_multiple_roles
        #--------------------------------------------------------------------
        
          def method_missing(method_id, *args)
            if match = matches_dynamic_role_check?(method_id)
              tokenize_roles(match.captures.first).each do |check|
                if self.role.name.downcase == check
                  return true
                end
              end
              return false
            elsif match = matches_dynamic_perm_check?(method_id)
              if !permissions.nil? 
                result = permissions.find_by_name(match.captures.first)
                return result
              end
            else
              super
            end
          end
        
          def has_multiple_roles?
            if self.secondary_role != nil
              return self.secondary_role != self.primary_role unless self.secondary_role.name == 'Unassigned'
            end
            false
          end
         
          private
          
          def matches_dynamic_role_check?(method_id)
            /^is_an?_([a-zA-Z]\w*)\?$/.match(method_id.to_s)
          end
         
          def tokenize_roles(string_to_split)
            string_to_split.split(/_or_/)
          end
        
          def matches_dynamic_perm_check?(method_id)
            result = /^can([a-zA-Z]\w*)\?$/.match(method_id.to_s)
            result
          end   
        end
        
hobo g migration

edit users\_controller.rb

          &amp;lt; ..... snip ..... &gt;
        
          def toggle
            current_user.toggle_mode
            redirect_to "#{base_url}/"
          end
          
        end


edit routes.rb

          match 'search' =&gt; 'front#search', :as =&gt; 'site_search'
        
          match 'user/toggle' =&gt; 'users#toggle'
          
          # The priority is based upon order of creation:
          
          &amp;lt; ..... snip ..... &gt;

          # Sample resource route within a namespace:
          #   namespace :admin do
          #     # Directs /admin/products/* to Admin::ProductsController
          #     # (app/controllers/admin/products_controller.rb)
          #     resources :products
          #   end
          namespace :admin do
            resources :roles
            resources :permissions
          end
  
Now for some DRYML magic (there may be a DRYer way for some of this, but
it works for me). Create the file:

app/views/taglibs/nav\_item.dryml

        &amp;lt;!-- 
          The extension of nav-item implements an interface to the
          Permission/Role subsystem such that a positive permission in the
          form of AccessMenuItem will show the &amp;lt;nav-item&gt; associated with 
          MenuItem (Use caution, the rails convention implies singular names,
          I've already been burnt using the permission "AccessStreetTypes" 
          which should have been "AccessStreetType") --&gt;
        
        
          /, '').strip
             show_it = !(current_user.class == Guest)
            if show_it
              show_it = (name == 'Home') || 
                        (name == 'Permissions') || 
                        (name == 'Roles') || 
                        (name == 'Users') || 
                        (name.include?(' Mode'))
              if !show_it
                access = "current_user.canAccess" + this.to_s + "?"
                show_it = !(eval access).nil?
              end
            end
          -%&gt;
          
          
            
          
            
          
        

        
           
            
          
        
        
The following is only necessary if you absolutely need to re-arrange the menu.  kevinpfromnm's tip populates the menu automatically by &amp;lt;extending...&gt; the &amp;lt;main-nav&gt; tag.       

Create the file app/views/taglibs/main\_navigation.dryml

Copy the head of app/views/tablibs/auto/rapid/pages.dryml that contains the
main menu into it, then make the noted change:

        &amp;lt;!-- ====== Main Navigation ====== --&gt;
        
        
          
            Home
             &amp;lt;!-- ADD THIS IF YOU WANT TO HAVE MULTI-ROLE USERS --&gt;
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
          
        
        

Notice that I re-arranged the Yada's so they were in numeric order, not alphanumeric order.
I did this only to  be sure that we were NOT using the rapid-generated menu.  Using the extended &amp;lt;main-nav&gt; tag will return them to the original alphanumeric order.

Edit application.dryml

        
        
         &amp;lt;!-- only if you used the 
        
        &amp;lt;set-theme name="clean"/&gt;

End of the "don't do this unless..." section

kevinpfromnm's tip.  Create the file app/views/taglib/front\_site.dryml:

        &amp;lt;include src="rapid" plugin="hobo"/&gt;

        &amp;lt;include src="taglibs/auto/rapid/cards"/&gt;
        &amp;lt;include src="taglibs/auto/rapid/pages"/&gt;
        &amp;lt;include src="taglibs/auto/rapid/forms"/&gt;

        &amp;lt;set-theme name="clean"/&gt;

        &amp;lt;extend tag="main-nav"&gt;
          &amp;lt;navigation class="main-nav" merge-attrs param="default"&gt;
          &amp;lt;mode-nav-item/&gt;
          &amp;lt;old-main-nav merge-attrs/&gt;
          &amp;lt;/navigation&gt;
        &amp;lt;/extend&gt;
        
At this time, if you restart the app and refresh your browser, you
will see a very empty main menu

Now to populate the roles and permissions.

Login as the administrator (first user).  In the gitHub demo site, the admin email is a@a.com with 'Password' as the password (all users of the demo site have 'Password' as their passwords).

Select the Admin link to get to the Admin sub-site.

Select the Roles tab on the menu and start adding Roles.
In the demo site they are (in descending capability order):

*  Administrator
*  Broker
*  Reviewer
*  Agent
*  InactiveAgent
*  Unassigned &amp;lt;&amp;lt; note this is hard-coded in user.rb as the 6th item.

You do not have to add permissions at this time.  You do not have
to order the entries except, for now, the 6th entry is the initialization
value when users are created (I'll fix that soon, I hope).

Select the Permissions tab on the menu and start adding
menu permissions.  Note that menu permissions start with
Access and contain the SINGULAR name of the associated model.

Thus in the demo site,

       *  AccessYada1
       *      .
       *      .
       *      .
       *  AccessYada20

map to each of the dummy model menu tabs, even though the tabs are
captioned Yada1s, ..., Yada20s.

In my real-estate app, I use the locales to re-caption the menu tabs (this works
even if you only use a single language, I implement en and es so it is not much 
of an additional hardship) just because the model names don't necessarily map to concise labels.

Let's add some permissions to the roles.

Select the Roles tab on the menu and map permissions to each role as you like.

I entered some pseudo-random associations in the demo site.

Now add some users with various roles.  Since I have not setup the email
for the demo site, you will have to look in the terminal output for the email message
and manually copy the link for the invitation acceptance, as in:

        Sent mail to betty@broker.org (711ms)
        Date: Sun, 17 Apr 2011 21:18:20 -0400
        
        From: no-reply@0.0.0.0
        
        To: betty@broker.org
        Message-ID: &amp;lt;4dab915cceea9_546d25615c84186cc@development.mail&gt;
        Subject: Invitation to Dynamic Main Menu
        Mime-Version: 1.0
        Content-Type: text/html;
         charset=UTF-8
        Content-Transfer-Encoding: 7bit
         
        Betty Broker,
         
        You have been invited to join Dynamic Main Menu. If you wish to accept, please click on the following link
         
           http://0.0.0.0:3001/users/6-betty-broker/accept_invitation?key=48607c3d25d861ca00cb5062ae6662a3afcd58bd
        
        Thank you,
        
        The Dynamic Main Menu team.

As soon as you issue the invitation (in the real world), edit the new user and assign appropriate roles.  If you don't, and the new user is fast enough, she will encounter the situation where her roles are unassigned and she can't do anything.

Play with the demo site and you can see how the dynamics work.  The Broker and Reviewer both have alternate roles as Agents so the tab immediately to the left of Home allows them to toggle to the mode displayed on the tab.

Note also that the permission system is not limited to the AccessMenuItem function.  In my real-estate app, I have fine-grained
permissions that enable/limit each role's ability to (for example) view or modify other user's data.  The broker,
in broker mode, can edit everyone's profiles; in agent mode, she can only edit her own profile.  The permissions,
in this case are:

EditAllProfiles -- associated code: current\_user.canEditAllProfiles?

EditOwnProfile  -- associated code: current\_user.canEditOwnProfile?

in the absence of either of these permissions, the user can not even edit her own profile.

Many thanks to kevinpfromnm for tip on extending the main-nav tag.

One last comment.  If I were starting over, I would change the name of the Administrator role to something different to avoid confusion with the hobo user's administrator boolean.  For me this is more of a political issue; the name could be easily changed by editing the Role, but the rest of the staff is accustomed to it, (backwards compatibility with the rails app where it did not conflict), so I'm stuck with it (unless I just do it).



</content>
    <updated>2011-04-21 17:06:03 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/68</id>
    <published>2011-03-20T22:49:40Z</published>
    <updated>2011-03-21T12:32:55Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/68-a-simple-way-to-do-location" rel="alternate"/>
    <title>A Simple Way to Do Location-Based Breadcrumbs</title>
    <content type="html">For ease of navigation, our team decided to incorporate breadcrumbs into our Hobo site. 

In this method below, the breadcrumbs are based not on history, but on the current location of the user, the url. Since historical navigation can easily get quite deep and complex, it seems the wiser choice to maintain our adherence to the KISS (keep-it-simple-stupid) principle. If you're curious as to a discussion of the pros/cons of historical versus hierarchical breadcrumbs, i rather enjoyed this: http://derivadow.com/2010/02/18/the-problem-with-breadcrumb-trails/.

Basically, I placed the following get\_bread\_crumb() function in my application's FrontHelper: 

	def get_bread_crumb(url, root_model=nil, separator = " &amp;raquo; ")
		root_model = root_model.pluralize
		breadcrumb = []
		so_far = []
		elements = url.split('/')
		last_element = elements.last

		# remove the last element, let dryml specify 
		if last_element == 'edit'
			elements.pop(2)
		else
			elements.pop
		end

		elements.each_with_index do |element, index|
			so_far &amp;lt;&amp;lt; element
			url = so_far.join('/')

			breadcrumb &amp;lt;&amp;lt; if element =~ /^[0-9]*$/
				link_to_if(element != last_element, elements[i-1].constantize.find(element).name.humanize, url) rescue element
			else
				link_to_if(element != last_element, element.titleize, url)
			end
		end

		# prepend the root_model IF we're not already in it!
		if root_model != elements[1]
			breadcrumb.insert(1,link_to(root_model.titleize, '/' + root_model))
		end

		# return the breadcrumb
		breadcrumb.join(separator) + separator
	rescue
		'Not available'
	end

Then, simply place in application.dryml:

	&amp;lt;!-- ====== Breadcrumb Navigation ====== --&gt;
	
	
	   
	    
	       New&lt;br /&gt;
	    
	  
	
	 
	
	   
	    
	      &lt;br /&gt;
	     
	  
	 
	
	
	   
	    
	      &lt;br /&gt;
	    
	  
	 
	
	
	   
	    
	      &lt;br /&gt;
	      
	    
	  
	 

I chose NOT to include the name of the current page in the function for flexibility, leveraging Hobo's nice rendering of the name based on the context. For instance, on our Eteachables index page, we have the following DRYML:

      &lt;br /&gt;

which nicely generates the count of Eteachables in the breadcrumb: 

	» Eteachables » 3 Eteachables 

We also chose to have a root model, in this case Eteachables, so the user can always quickly return to this primary location. For instance, one might navigate to the Math learning category, and while the url simply states 

	/learning_categories/1-math 

the breadcrumb shows Eteachable as the root:

	» Eteachables » Learning Categories » Math

In sum, the breadcrumbs, being just below the tabs, are a quick way to navigate while keeping it all very Hobo-feeling!

Now if anyone can help me get the Edit button back on the Show page, I think that's the primary deficiency I've noticed at this juncture. I'll update the recipe with that edit once resolved. In the meantime, enjoy!

*Special thanks for the considerable head start via https://gist.github.com/446655.*
</content>
    <updated>2011-03-21 12:32:55 UTC</updated>
    <author>
      <name>hobokoop</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/67</id>
    <published>2011-02-22T16:04:37Z</published>
    <updated>2011-02-22T16:08:34Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/67-dropdown-tab-menus" rel="alternate"/>
    <title>Dropdown tab menus</title>
    <content type="html">One of my clients insisted on having drop-down menus on her website.  Since "The Client is Always Right" (tm), I had to find a quick way of doing this.

1) put main-nav in its own file. I used app/views/taglibs/main-nav.dryml. Once you do this, the automatic updating of menus stops, any future changes to the displayed menu must be done manually to this file.

2) add the following code to main-nav.dryml

    &amp;lt;!-- ====== Main Navigation ====== --&gt;

    
      &lt;div class="navigation main-nav"&gt; &amp;lt;!-- NEW --&gt;
        

    *
    *
    *
          &amp;lt;!-- note the '#' is used to dis-allow clicking on the root menu item --&gt;
          &amp;lt;!-- It is perfectly allowable to insert a link at this point, too    --&gt;
          &lt;li class=""&gt;
	    &lt;a href="#"&gt;&lt;/a&gt;
	  &lt;/li&gt;
    *
    *
    *

          &lt;li class=""&gt;
	    &lt;a href="#"&gt;&lt;/a&gt;
	  &lt;/li&gt;

    *
    *
    *
        &amp;lt;!--/navigation&gt; &amp;lt;!-- original closing tag --&gt;
      &lt;/div&gt; &amp;lt;!-- NEW --&gt;

      &amp;lt;!-- Add your dropdown menus as needed --&gt;
      &lt;div class="my_drop_down_div"&gt;		
        &lt;a href="#{base_url}/dropdown_1_1"&gt;&lt;/a&gt;
	&lt;a href="#{base_url}/dropdown_1_2"&gt;&lt;/a&gt;
	&lt;a href="#{base_url}/dropdown_1_3"&gt;&lt;/a&gt;
	&lt;a href="#{base_url}/dropdown_1_4"&gt;&lt;/a&gt;
      &lt;/div&gt;

      &lt;div class="my_drop_down_div"&gt;		
        &lt;a href="#{base_url}/dropdown_2_1"&gt;&lt;/a&gt;
	&lt;a href="#{base_url}/dropdown_2_2"&gt;&lt;/a&gt;
	&lt;a href="#{base_url}/dropdown_2_3"&gt;&lt;/a&gt;
	&lt;a href="#{base_url}/dropdown_2_4"&gt;&lt;/a&gt;
      &lt;/div&gt;

    /***********************************************

    * Drop Down Tabs Menu- (c) Dynamic Drive DHTML code library (www.dynamicdrive.com)

    * This notice MUST stay intact for legal use

    * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code

    ***********************************************/



      &amp;lt;!-- initialize the supporting js --&gt;

      

     &amp;lt;!-- The original end tag --&gt;

3) Insert the following code in application.js (This should go in its own file, but I couldn't get it to work that way, at least in any reasonable time).

    //Drop Down Tabs Menu- Author: Dynamic Drive (http://www.dynamicdrive.com)
    //Created: May 16th, 07'
    /***********************************************

    * Drop Down Tabs Menu- (c) Dynamic Drive DHTML code library (www.dynamicdrive.com)

    * This notice MUST stay intact for legal use

    * Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code

    ***********************************************/
    
    var tabdropdown={
    	disappeardelay: 200, //set delay in miliseconds before menu disappears onmouseout
    	disablemenuclick: false, //when user clicks on a menu item with a drop down menu, disable menu item's link?
    	enableiframeshim: 1, //1 or 0, for true or false

    	//No need to edit beyond here////////////////////////
    	dropmenuobj: null, ie: document.all, firefox: document.getElementById&amp;&amp;!document.all, previousmenuitem:null,
    	currentpageurl: window.location.href.replace("http://"+window.location.hostname, "").replace(/^\//, ""), //get current page url (minus hostname, ie: http://www.dynamicdrive.com/)

    	getposOffset:function(what, offsettype){
    		var totaloffset=(offsettype=="left")? what.offsetLeft : what.offsetTop;
    		var parentEl=what.offsetParent;
    			while (parentEl!=null){
    				totaloffset=(offsettype=="left")? totaloffset+parentEl.offsetLeft : totaloffset+parentEl.offsetTop;
				parentEl=parentEl.offsetParent;
    			}
    		return totaloffset;
    	},

    	showhide:function(obj, e, obj2){ //obj refers to drop down menu, obj2 refers to tab menu item mouse is currently over
    		if (this.ie || this.firefox)
    			this.dropmenuobj.style.left=this.dropmenuobj.style.top="-500px"
    		if (e.type=="click" &amp;&amp; obj.visibility==hidden || e.type=="mouseover"){
    			if (obj2.parentNode.className.indexOf("default")==-1) //if tab isn't a default selected one
    				obj2.parentNode.className="selected"
    			obj.visibility="visible"
    			}
    		else if (e.type=="click")
    			obj.visibility="hidden"
    	},

    	iecompattest:function(){
    		return (document.compatMode &amp;&amp; document.compatMode!="BackCompat")? document.documentElement : document.body
    	},

    	clearbrowseredge:function(obj, whichedge){
    		var edgeoffset=0
    		if (whichedge=="rightedge"){
    			var windowedge=this.ie &amp;&amp; !window.opera? this.standardbody.scrollLeft+this.standardbody.clientWidth-15 : window.pageXOffset+window.innerWidth-15
    			this.dropmenuobj.contentmeasure=this.dropmenuobj.offsetWidth
    		if (windowedge-this.dropmenuobj.x &amp;lt; this.dropmenuobj.contentmeasure)  //move menu to the left?
    			edgeoffset=this.dropmenuobj.contentmeasure-obj.offsetWidth
    		}
    		else{
    			var topedge=this.ie &amp;&amp; !window.opera? this.standardbody.scrollTop : window.pageYOffset
    			var windowedge=this.ie &amp;&amp; !window.opera? this.standardbody.scrollTop+this.standardbody.clientHeight-15 : window.pageYOffset+window.innerHeight-18
    			this.dropmenuobj.contentmeasure=this.dropmenuobj.offsetHeight
    			if (windowedge-this.dropmenuobj.y &amp;lt; this.dropmenuobj.contentmeasure){ //move up?
				   edgeoffset=this.dropmenuobj.contentmeasure+obj.offsetHeight
    				if ((this.dropmenuobj.y-topedge)div a:hover /*THEME CHANGE HERE*/
{
  color: #242E42;
  background-color: #ffffff; 
}


I fiddled with the css until the drop down looked like the main tabs.  You will probably want to adjust the width to suit your drop down menu width, but it seems to adjust itself appropriately for most cases.

</content>
    <updated>2011-02-22 16:08:34 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/66</id>
    <published>2011-01-24T12:59:59Z</published>
    <updated>2011-01-24T13:08:57Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/66-save-context-for-table-and-table" rel="alternate"/>
    <title>Save Context for table and table-plus</title>
    <content type="html">One of my clients has tables with thousands of rows which are related positionally (adjacency is relevant) and have searchable values.  Once a row is found it is often used in many subsequent operations. Consequently using the page navigator for rows in the middle of the current sort order is cumbersome.

It was requested that I save the context between visits to the index pages to avoid the "page chase" that occurred when the user needed to return to the most recent row (or adjacent rows).

Inspired by Dean's saving of filter_parameters in the session, but needing a more general solution, I put together a pair of subprograms to save and restore the context from within the controllers of the various tables.

New file lib/table\_plus\_support.rb

    module TablePlusSupport
      
      def self.save_page(params, per_page, session, ignore=nil)
        field_list = ''
    # If we have a field to ignore (usually one with much data),
    # get a list of all columns excluding that one.
    # TODO Generalize this to allow ignore to be both a string and an array
        if !ignore.nil?
          index_fields = column_names.select{ |col| col != ignore }
          for i in index_fields do
            field_list = field_list + i + ', '
          end
          field_list = field_list[0..-3]
        end
    # Default to the first page.
        page = 1
    # Generate a session key from the name of the table.
        controller = params[:controller]
        key = controller+'-page'
    
        if params[:page]
    # If we have a page parameter, save it in the session
          page = params[:page].to_i      
          session[key] = page
        else
    # If we don't have a page parameter get the page from the session if it exists
          page = session[key] if session[key]
        end
    # Return a series of tAssoc to satisfy hobo_index, here I presume that
    # pagination is desired since this wouldn't be called without it.
        return :per_page =&gt; per_page, :page =&gt; page, :paginate=&gt; true if ignore.nil?
        return :per_page =&gt; per_page, :page =&gt; page, :paginate=&gt; true, :select =&gt; field_list
      end
  
      def self.save_param(params,param,session)
        controller = params[:controller]
        key = controller + '-' + param.to_s
        sort = nil
        if params[param]
    # If we have a value in params[param], save it in the session
          value = params[param]
          session[key] = value
        else
    # The following line is weird.  I tried using session[key].class == 'String',
    # but it always evaluated FALSE no matter what version of 'String', "String", :String
    # I used, even though session[key].class and session[key].class.inspect both printed "String"
    # Weirder still, session[key].class == session[key].class.inspect also evaluated FALSE
    # This version works, so ...

    # If we don't have a value in params[param], but we do have a string in session[key]
    # then use the value in the key
          value = session[key] if session[key].class.inspect == 'String'
        end
    # set params[param] if we have a value for sort.
        params[param] = value if value    
      end
    end

This does not mess up the controller too badly.  An example, based on a smaller table which saves U.S. States and Canadian Provinces is:

    require "table_plus_support"
    
    class StateProvsController &amp;lt; ApplicationController
    
      hobo_model_controller
    
      auto_actions :all
      
      def index
        TablePlusSupport::save_param(params,:sort,session)
        TablePlusSupport::save_param(params,:search,session)
        hobo_index StateProv.apply_scopes(:search =&gt; [params[:search],:full_name,:name],:order_by =&gt; parse_sort_param(:name, :full_name)), TablePlusSupport::save_page(params,10,session)
      end

    end

One somewhat obvious note:  The search parameter will be saved until it is reset by issuing "Go" with a blank search box.  Fortunately, the search box is re-populated so it is obvious that the filter is still in place.
</content>
    <updated>2011-01-24 13:08:57 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/65</id>
    <published>2011-01-16T16:44:27Z</published>
    <updated>2011-11-26T16:46:12Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/65-ajax-file-uploads" rel="alternate"/>
    <title>Ajax file uploads</title>
    <content type="html">


Note: actually there's one or two steps missing here:  you also need the formSubmit function in hobo-jquery.   If you have questions, ping me on the list.   Even better, this is built into Hobo 1.4.

With the [paperclip plugin](http://cookbook.hobocentral.net/plugins/paperclip\_with\_hobo) installed, Hobo supports file uploads, but does not support Ajax file uploads.   It seems like it should work, but the file doesn't get uploaded.   The problem is that browsers do not allow Javascript access to the local file system as a security measure.

To get around this, most sites either use a Flash plugin or use an ugly IFrame hack.   We'll use the [JQuery form plugin](http://malsup.com/jquery/form/), which packages up the IFrame hack.

You'll also need [this enhancement to Hobo](https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/861-need-to-wrap-ajax-response-in-textarea).   You can either install the patch, or use 1.1.0.pre3 or 1.3.0.pre26 (?).

The first step is to install [JQuery form plugin](http://malsup.com/jquery/form/), using something like this:

    
      
        
          
          
        
      
    

This assumes you're using the [hobo-jquery plugin](http://cookbook.hobocentral.net/plugins/hobo-jquery).   See [its instructions](http://cookbook.hobocentral.net/plugins/hobo-jquery) for more details.

On our application, we have a package that has-many uploads.

    class Package &amp;lt; ActiveRecord::Base
      hobo_model
      has_many :uploads, :dependent =&gt; :destroy
    end

    class Upload &amp;lt; ActiveRecord::Base
      hobo_model
      fields do
        name :string
        timestamps
      end
      has_attached_file :attachment
      belongs_to :owner, :class_name =&gt; "User", :creator =&gt; true
    end

Our view is pretty standard Hobo Ajax:

    
      
        
        &lt;h3&gt;Attachments&lt;/h3&gt;
        &lt;div&gt;
          
          
        &lt;/div&gt;
        
        &lt;h3&gt;Add an Attachment&lt;/h3&gt;
        
          
        
       
     

But we need some extra stuff in the controller to correspond to the requirements listed [here for the jQuery form plugin](http://malsup.com/jquery/form/#file-upload).

    class UploadsController &amp;lt; ApplicationController
      hobo_model_controller
      auto_actions  :all, :except =&gt; [:index]
      auto_actions_for :package, [:create, :new]

      def create_for_package
        hobo_create_for :package do
          if valid?
            # we have to ignore the requested file type because the workarounds
            # necessary to send files via ajax don't allow us to set the
            # headers.  So we just assume that we need to send an Ajax
            # response.  We'll also Wrap that response in a textarea so
            # the browser keeps its hands off of it.
            ajax_update_response(params[:page_path], params[:render].values, {}, {:preamble =&gt; "&lt;textarea&gt;\nvar _update = Hobo.updateElement;", :postamble =&gt; "&lt;/textarea&gt;"})
          else
            render(:status =&gt; 500,
                  :js =&gt; "alert(\"#{this.errors.full_messages.join(". ").gsub('\n', '')}\");")
          end
        end
      end
    end
</content>
    <updated>2011-11-26 16:46:12 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/64</id>
    <published>2011-01-14T22:50:50Z</published>
    <updated>2011-01-15T13:51:10Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/64-single-table-inheritance-with-children" rel="alternate"/>
    <title>Single Table Inheritance with Children</title>
    <content type="html">I have used the design pattern which consists of STI with child tables in many situations before (RoR, Delphi, ... ).  I looked for existing recipes but the existing one didn't exactly fit the design pattern (and didn't seem to work with Hobo 1.3 either).

My current instantiation of the design pattern is as follows:

The primary class is a Profile - it has children in the form of a list of Phone Numbers (and other lists, all of which are implemented the same way) - it is not directly manipulated (it could be, but I haven't tried. YMMV).

There are three sub-classes of Profile:  Agents, Clients, CooperatingAgents - (yes my client is a real-estate agency).

Supporting tables:  PhoneKind (e.g., Home, Office, Home FAX, Cell, ... ).

I have simplified the contents of the various tables below by eliminating a lot of the real-estate-specific columns (which don't add to understanding the hobo problem/solution anyway) and permissions (which are also application-specific).

The following assumes an hobo app named sti-with-children

    hobo new sti-with-children
    cd sti-with-children

    hobo generate resource phone_kind kind:string
    hobo generate resource phone_number number:string
    hobo generate resource profile name:string street:string #snip...
    hobo generate resource client
    hobo generate resource agent
    hobo generate resource cooperating_agent

Edit the models:

**phone\_number.rb**

      snip ...

      fields do
        number :string, :required =&gt; true # Hobo bug? see bottom of recipe
        timestamps
      end

      belongs_to :phone_kind
      belongs_to :profile
      belongs_to :agent
      belongs_to :client
      belongs_to :cooperating_agent
  
      validates_presence_of :phone_kind # Hobo bug? see bottom of recipe
  
      # The never_show line gets rid of extraneous fields that could otherwise
      # result in erroneous behavior.  There is a residual bug here.  I tried
      # to add :profile to the list.  When I did, attempting to display the
      # phone number resulted in a Hobo error message decrying the attempt to
      # display a non-readable field.  I will look at this further and if
      # necessary I will post a ticket and fix this recipe when possible.
      # Editing application.dryml as shown below gets rid of the profile field.

      never_show :agent, :client, :cooperating_agent

      def name
        number + ' ' + phone_kind.kind # minimal formatting - this could be fancier
      end
  
      # --- Permissions --- #

      snip ...

**phone\_kind.rb**

      snip ...

      fields do
        kind :string
        timestamps
      end

      has_many :phone_numbers
  
      # --- Permissions --- #

      snip ...

**profile.rb**

      snip...

      fields do
        name :string
        street :string
        sti_type :string       # Add this
        timestamps
      end
  
      set_inheritance_column :sti_type # Add this
    
      has_many :phone_numbers    # This is normal relationship
      children :phone_numbers    # This was needed to make the phone_numbers appear.
  
      # --- Permissions --- #

      snip ...

**agent.rb**

    class Agent &amp;lt; Profile # For STI

      # Leave out the hobo_model line.  Without it, there is no tab for the class (e.g. Agent tab).
      # With it, the ^(*&amp;% thing overflows the stack when creating, editing, etc.
       
      has_many :phone_numbers  # The other recipe(s) say leave the sub-class models empty, but
      children :phone_numbers  # I needed both these lines to make the app work.

    end

Similarly for **client.rb** and **cooperating\_agent.rb**:

    class Client &amp;lt; Profile 

      # Leave out the hobo_model line.  Without it, there is no tab for the class (e.g. Agent tab).
      # With it, the ^(*&amp;% thing overflows the stack when creating, editing, etc.
  
      has_many :phone_numbers
      children :phone_numbers

    end

    class CooperatingAgent &amp;lt; Profile 

      # Leave out the hobo_model line.  Without it, there is no tab for the class (e.g. Agent tab).
      # With it, the ^(*&amp;% thing overflows the stack when creating, editing, etc.
  
      has_many :phone_numbers
      children :phone_numbers

    end

Edit the controllers:

**phone\_numbers\_controller.rb**

    class PhoneNumbersController &amp;lt; ApplicationController

      hobo_model_controller

      auto_actions :all, :except =&gt; :index  # I didn't want the Phone Numbers tab to be visible
  
      auto_actions_for :profile, :create			# These were required to get the
      auto_actions_for :agent, :create			# Add Phone Number link to
      auto_actions_for :client, :create			# appear on all of the show pages
      auto_actions_for :cooperating_agent, :create

    end

**profiles\_controller.rb**

    class ProfilesController &amp;lt; ApplicationController

      hobo_model_controller

      auto_actions :all, :except =&gt; :index # I didn't want the Profile tab to be visible

    end

Edit **application.dryml

    # Add this at an appropriate place (I put it at the end).

    
      
        
         &amp;lt;!-- NOTE: ", profile" has been removed --&gt;
        &lt;div&gt;
          
        &lt;/div&gt;
      
    

    &amp;lt;!-- ====== Main Navigation ====== --&gt;

    
      
        Home
    &amp;lt;!-- Manual addition of sub-class tabs --&gt;
        
        
        
    &amp;lt;!-- =============================== --&gt;
        
      
    

    
      

        

        
              
    &amp;lt;!-- #######################################################################################
         Removing the following line eliminated a (Not Available) link-substitute from the page.
         It could be replaced by a series of &amp;lt; if= ... &gt;tags based on the sti_type field to link
         back to the appropriate sub-class if desired.
         ####################################################################################### --&gt;
           &amp;lt;!--     &amp;lt;a:agent param="parent-link"&gt;&amp;laquo; &amp;lt;ht key="phone_number.actions.back_to_parent" parent="Agent" name="&amp;this"&gt;Back to &amp;lt;name/&gt;&amp;lt;/ht&gt;&amp;lt;/a:agent&gt; --&gt;
                &lt;h2&gt;
                  
                    
                  
                &lt;/h2&gt;

                

                &lt;a&gt;
                  
                    Edit Phone number
                  
                &lt;/a&gt;
              
    
              
    &amp;lt;! ##################################################################################
       Removing the following line eliminated another (not Available) link-substitute.
       I don't know where it came from since profile doesn't have a description field.
       ################################################################################## --&gt;
           &amp;lt;!--     &amp;lt;view:profile param="description"/&gt;  --&gt;
                
              
        
    
      
    


This edit was necessary (1) to remove the "profile" field which could have caused erroneous behavior.  Removing it via the never\_show method failed with a Hobo error; (2) to add back the missing menu tabs, and; (3) To remove extraneous (Not available) link-substitutes associated with profile.

Once you have done the above, then:

    hobo generate migration
    rails server

I have not been able to get the appropriate tabs for the sub-classes to appear without causing a stack overflow when performing db operations on the sub-classes.  With the exception of that, everything works if you use the appropriate route (e.g. localhost/clients, or localhost/agents).  In my app, I just manually modified the main menu to include tabs for the sub-classes as shown in application.dryml above.

There is minor exception to it working as expected:  when the validation for the phone number fails during create or edit, there is **NO notification** to the user.  It just silently re-loads the failing page.  I think this is a bug in Hobo, but I haven't investigated any further yet.


</content>
    <updated>2011-01-15 13:51:10 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/63</id>
    <published>2011-01-05T17:40:32Z</published>
    <updated>2011-01-11T16:19:23Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/63-hobo-with-paperclip-storage-to-db" rel="alternate"/>
    <title>Hobo with Paperclip - storage to db column.</title>
    <content type="html">After installing **Paperclip**, getting it to work, and searching for the uploaded data I decided to RTM and found that I had two choices, storage on the file system (the default) or storage to s3.  For business reasons (and the desire to eventually host on Heroku) I needed the data storage in the appropriate db tables.

Kevin found **http://patshaughnessy.net/2009/2/19/database-storage-for-paperclip** for me (I never thought to Google for it).  Pat Shaughnessy did a really good implementation for Rails 3.0 which I used in place of the original paperclip.  After a few issues, I got it working on Ralis 3.0, Hobo 1.3, as follows:

**First install the plain-vanilla paperclip and get it working (use the default file system storage for simple installation) so you are starting from a known good condition.**

Then

**Edit Gemfile**


    gem 'paperclip',  :git =&gt; 'git://github.com/patshaughnessy/paperclip.git'
 

I put this just after the hobo line:

    gem "hobo", "&gt;= 1.3.0.pre25"

Then

**sudo bundle install**

In my case, this overwrote the existing plain-vanilla paperclip with Pat's version.

**Edit the model** (in my case agent\_documents.rb) adding/changing only two lines:

    document_file	:binary # in the field definition block

    has_attached_file :document, :storage =&gt; :database #THIS DID NOT WORK AT FIRST!

**hobo g migration ... THIS FAILED.**

There is a chicken vs. egg problem here.  The *has\_attached\_file* line is processed before the *document\_file :binary* field declaration which leads to an undefined method error.  The solution is to **first comment out the , :storage =&gt; :database**,
then

**hobo g migration**

This time it works.  Now **un-comment ", :storage =&gt; :database"**, restart your server and have fun uploading attachments.

Now for the download:

**Edit the controller** (in my case agent\_documents\_controller.rb), adding:

    downloads_files_for :agent_document, :document

the first argument is the name of the model, the second is the attachment name as generated by paperclip.

**Edit routes.rb**, adding:

    match 'agent_documents/documents/:id' =&gt; 'agent_documents#documents' 

at the appropriate priority level (mine was low so I put it at the end).

Lastly, **insert the link** in your appropriate dryml file.  In my case, I added it to views/agent_documents/show.dryml:

    View Document

or

    View Document


My memory is going.  I created (plagiarized and modified I think) the nav-item-external tag, but forgot about it, so anyone who implements this will get an error. The definition (and the similar a-external tag) is:

    
      /, '').strip
       -%&gt;
      &lt;li class="#{'current' if (c = scope.current_navigation) &amp;amp;&amp;amp; c.downcase == name.downcase}"&gt;
      &lt;a&gt;&lt;/a&gt;
      &lt;/li&gt;
    


    
      /, '').strip
      -%&gt;
        &lt;a&gt;&lt;/a&gt;
    


In my case, I put both of these in app/views/taglibs/internet_nav.dryml
</content>
    <updated>2011-01-11 16:19:23 UTC</updated>
    <author>
      <name>dziesig</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/62</id>
    <published>2010-12-04T02:53:08Z</published>
    <updated>2010-12-04T02:55:02Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/62-full-stack-testing-a-hobo-application" rel="alternate"/>
    <title>Full stack testing a Hobo Application</title>
    <content type="html">Reuse of DRYML and controller logic in Hobo allows us to apply the same test pattern for all the controllers in the project.
Roll your application complete with functional and acceptance tests. RSpec, Steak, Capybara, FactoryGirl and Shoulda are hands-on demonstrated on a project: 

__[Full stack testing a Hobo Application](http://conceptspace.wikidot.com/blog:all-about-testing-hobo-organizations-revisited)__
</content>
    <updated>2010-12-04 02:55:02 UTC</updated>
    <author>
      <name>umuro</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/61</id>
    <published>2010-11-05T18:32:27Z</published>
    <updated>2010-11-05T19:02:33Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/61-hobo-on-amazon-ec2" rel="alternate"/>
    <title>Hobo on Amazon EC2</title>
    <content type="html">A minimal setup guide to getting Hobo running on a Tiny 32 bit Linux EC2 instance.


     sudo yum erase ruby
     sudo yum groupinstall 'Development Tools'
     sudo yum install readline-devel

     bash &amp;lt; &amp;lt;( curl http://rvm.beginrescueend.com/releases/rvm-install-head )

     rvm package install openssl
     rvm install 1.9.2 --with-openssl-dir=$HOME/.rvm/usr

     gem install sqlite3
     gem install rails -v 2.3.5
     gem install hobo


Enjoy!</content>
    <updated>2010-11-05 19:02:33 UTC</updated>
    <author>
      <name>brett</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/60</id>
    <published>2010-11-02T05:22:56Z</published>
    <updated>2010-11-02T05:22:56Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/60-incompatibilti-help-me" rel="alternate"/>
    <title>incompatibilti ç, help me</title>
    <content type="html">after install "hobo" 
I tried ran exmaple  "hobo agility"

but shown thats,.....



lord@lord-desktop:~/projects$ hobo agility

Generating Rails app...
/bin/bash: -S: invalid option
Usage:	/bin/bash [GNU long option] [option] ...
	/bin/bash [GNU long option] [option] script-file ...
GNU long options:
	--debug
	--debugger
	--dump-po-strings
	--dump-strings
	--help
	--init-file
	--login
	--noediting
	--noprofile
	--norc
	--posix
	--protected
	--rcfile
	--restricted
	--verbose
	--version
Shell options:
	-irsD or -c command or -O shopt_option		(invocation only)
	-abefhkmnptuvxBCHP or -o option
/usr/lib/ruby/gems/1.8/gems/hobo-1.0.1/bin/hobo:96: private method `split' called for nil:NilClass (NoMethodError)
	from /usr/bin/hobo:19:in `load'
	from /usr/bin/hobo:19
lord@lord-desktop:~/projects$ 



What it is=?
</content>
    <updated>2010-11-02 05:22:56 UTC</updated>
    <author>
      <name>martinszutner</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/59</id>
    <published>2010-10-26T09:58:55Z</published>
    <updated>2010-10-26T19:30:10Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/59-instant-front-end-just-add-schema" rel="alternate"/>
    <title>Instant Front End: Just Add Schema to Hobo</title>
    <content type="html">My personal holy grail of web programming is to take any database and then convert it into a CRUD complete webstie at the click of a button. Yesterday I realized I could get half way there with little effort using tools I already have. I used my old friends sed and awk to parse a rails schema into hobo generator format.

Here is the ruby script I came up with (you have to manually set the rails version for now):

    #!/usr/bin/ruby

    require 'rubygems'
    require 'active_support/inflector'
    
    RAILS_VERSION = 2

    schema = `cat #{ARGV[0]} | sed '/#/d' | sed '/ActiveRecord/d' | sed '//d' | sed '/add_index/d' | awk '{print $2":"$1" "}'`
    

    schema.gsub!(/t\./,'')
    schema.gsub!(/,/,'')
    schema.gsub!(/\"/,'')

    xs = schema.split(/\n/)

    xs.delete_if {|line| line[0] == 58}

    xs.map! do |line|
      if(line[/create_table/])
        line.gsub!(/.create_table/,'').chop!
        line = line.pluralize.singularize
        if(RAILS_VERSION &amp;lt; 3)
          $/ + "script/generate hobo_resource " + line + ' '
        else
          $/ + "rails generate hobo:resource " + line + ' '
        end
      else
        line 
      end
    end

    puts xs.to_s


Run the above script on a schema file dumped by rake (rake db:schema:dump while connected to the target database):

    ActiveRecord::Schema.define(:version =&gt; 20101025160107) do
      create_table "addresses", :primary_key =&gt; "addressid", :force =&gt; true do |t|
          t.string "street",         :limit =&gt; 100
          t.string "postcode",        :limit =&gt; 100
          t.string "townname",        :limit =&gt; 100
          t.string "provincename",    :limit =&gt; 100
          t.string "homephonenumber", :limit =&gt; 100
       end
    end

Output:

Rails 2.X

    script/generate hobo_resource address street:string postcode:string townname:string provincename:string homephonenumber:string

Rails 3.X

    rails generate hobo:resource address street:string postcode:string townname:string provincename:string homephonenumber:string



</content>
    <updated>2010-10-26 19:30:10 UTC</updated>
    <author>
      <name>allen13</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/58</id>
    <published>2010-08-10T21:36:02Z</published>
    <updated>2010-08-10T21:36:02Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/58-setting-up-unix-like-permissions-with" rel="alternate"/>
    <title>Setting up unix like permissions with hobo</title>
    <content type="html">For any unfamiliar with unix permissions, the basic concept is that each file, or in our case model instance, has an owner and group.  There are independent permissions for owner, group and everyone on read, write, and execute.

Because of the way hobo and rails work, I used view, write (update) and destroy.  Now, most of the work for this actually has to exist in the model instance.  So, to keep this DRY, we'll setup a module to handle the basic logic of permission and count on overriding for any exceptions.  http://gist.github.com/517863 has the module I ended up writing.  Save it in a file called unix_permissions.rb in app/models/

For it to work, it has to be included for one and there has to be a group model and user instances can have many groups.

    script/generate hobo_model_resource Group name:string
    script/generate hobo_model_resource GroupUser

In `app/models/user.rb` add

    has_many :group_users
    has_many :groups, :through =&gt; :group_users, :accessible =&gt; true

`app/models/group_user.rb`

    belongs_to :user
    belongs_to :group

`app/models/group.rb`

    has_many :group_users
    has_many :users, :through =&gt; :group_users, :accessible =&gt; true

`app/controllers/group\_users\_controller.rb`

    auto_actions :write_only

Note: only one of the group or user needs ability to add links technically.

Add these new tables and columns to the db

`script/generate hobo_migration`

At this point, you could add `include UnixPermissions` to a model(s), remove the default permission methods and have it work (mostly).  You'll want to clean up the views as by default it will show each of the permissions that's true on the model show page.  And likely move group/user administration to a subsite.

Remember, any of the methods defined here can be overridden just by redefining the method after the include UnixPermissions call.  Say for instance, you don't want anyone able to destroy a particular model:

    include UnixPermissions
    def destroy_permitted?
      false
    end

Obviously, this is not a perfect solution for all situations but should give you a starting point or some ideas for writing your own.</content>
    <updated>2010-08-10 21:36:02 UTC</updated>
    <author>
      <name>kevinpfromnm</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/57</id>
    <published>2010-08-06T19:25:12Z</published>
    <updated>2010-08-07T17:41:31Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/57-how-to-get-full-text-search" rel="alternate"/>
    <title>How to get full text search working with Sunspot</title>
    <content type="html">Basically we'll be following [sunspots quickstart guide](http://wiki.github.com/outoftime/sunspot/adding-sunspot-search-to-rails-in-5-minutes-or-less) with a couple of tweaks for hobo.

## Setup a hobo app

For this, I'll make the, at this point, stereotypical blog application.

    hobo search_demo
    cd search_demo
    ./script/generate hobo_model_resource Post title:string body:text
    script/generate hobo_migration

## Install Sunspot and dependencies

&gt; First, you’ll need to install Sunspot::Rails, which integrates Sunspot search seamlessly into your Rails app. This will install Sunspot 
&gt; and RSolr as dependencies.
&gt; 
&gt; `$ sudo gem install sunspot_rails`
&gt; 
&gt; Add it to your config/environment.rb:
&gt; 
&gt; `config.gem 'sunspot_rails', :lib =&gt; 'sunspot/rails'`
&gt; 
&gt; Run the generator to create the config/sunspot.yml file:
&gt; 
&gt; `$ script/generate sunspot`
&gt; 
&gt; To get the Sunspot rake tasks, you’ll need to require the tasks from your app’s Rakefile:
&gt; 
&gt; `require 'sunspot/rails/tasks'`
&gt; 
&gt; Now it’s time to start Solr. Solr is a standalone HTTP server, but Sunspot comes packaged with a copy of it that’s already configured to  
&gt; work with Sunspot; use the rake task to fire it up:

Here's where I diverge from their path and add in that you need to copy the solr/conf and solr/lib directories from the gem installation directory if you have run `acts\_as\_solr` in the past.  That or delete the solr/ directory from your app before running this next command.

&gt; `$ rake sunspot:solr:start`
&gt; 
&gt; The first time you run this command, it will create a solr/ directory in your Rails root. This contains Solr’s configuration, as well as
&gt; the actual data files for the Solr index. You’ll probably want to add solr/data to your .gitignore. If you later would like to play 
&gt; with Solr’s configuration (e.g., customizing the filter chain for text fields), you’ll find the configuration files in solr/conf.

## Tell sunspot what to index

Add this to your Post model

      searchable do
        text :title, :default_boost =&gt; 2
        text :body
      end

This tells sunspot to index the post model, with full text searching on the title and body fields with title matching being twice as relevant by default.

Now, tell sunspot to index any existing post data.  You can skip this step if you have a blank application and instead add a few posts.

`$ rake sunspot:reindex`

## Adding search to your controller

Here, we'll diverge a little because Hobo makes this easier for us.  We're going to add the search to the normal index method if a query parameter is supplied.

    class PostsController &amp;lt; ApplicationController

      hobo_model_controller

      auto_actions :all

      def index
        if params[:query]
          @posts = Post.search do
            keywords(params[:query])
            paginate(:page =&gt; (params[:page] or 1), :per_page =&gt; 10)
          end.results
          @posts.member_class = Post
        else
          hobo_index
        end
      end
    end

A quick explanation is in order, Post.search is sunspots search method.  The block describes how to search and return results.  Right after the block, the .results is not a typo.  Sunspots search method returns a search type object with search scores and objects but for a quick and dirty search, we're just interested in the, well, results.

Now, we can actually test this out at this point by going to `http://localhost:3000/posts?query=someword`.  Be sure to pick a word you know is in at least 1 but not all posts.

Obviously we can't leave it like that for the end user but we're most of the way there.

## Add search to view

We need to tweak the index page to show a search box.

`app/views/posts/index.dryml`

    
      
        
          &lt;label&gt;Search Posts&lt;/label&gt;
          
          
        
      
    


And have the controller save the query parameter for the view by changing the if line as follows.

`app/controllers/posts_controller.rb`

    + if @query = params[:query]
    - if params[:query]

And now search is easily accessible.

Exercises left for the reader: 
* possibly change live-search box to use sunspot or remove entirely from view
* explore other Post.search options to increase usefulness
* perhaps each post has an owner and/or comments that you want to search to include.  add these to the searchable method in the Post model
* this isn't production ready as you'll want a separate solr server that isn't the one bundled with sunspot</content>
    <updated>2010-08-07 17:41:31 UTC</updated>
    <author>
      <name>kevinpfromnm</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/56</id>
    <published>2010-08-03T18:45:57Z</published>
    <updated>2010-08-03T19:11:35Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/56-setup-ruby-debug" rel="alternate"/>
    <title>Setup Ruby-debug</title>
    <content type="html">A good practice for getting acquainted with code, and pinning down issues in the code you write, is to use a debugger.  In many languages, we would have to setup an IDE to accomplish this.  In Ruby, however, there exists a good gem for accomplishing the same task.

## Setup ##

* Install the ruby-debug gem:
&gt;        gem install ruby-debug
   * Note: the ruby-debug gem is a native gem, and won't yet compile on Ruby 1.9.1.  There isn't any really good reason to be running Hobo on 1.9.1 (other than speed), so I'm going to assume this isn't an issue for most.
* Add the gem to your Rails environment:
    * Open project_name/config/environment.rb
    * Right under Rails::Initializer.run do |config| enter "config.gem 'ruby-debug'" (without the double-quotes)
* Open project_name/config/environments/development.rb and add to the bottom of the file:
&gt;        require 'rubygems'
&gt;        require 'ruby-debug'
&gt;        Debugger.start
* Reboot your Rails server and debug mode is enabled.  To use it, just add "debugger" (again, without the quotes) to anyplace you would normally put ruby code.  If you are trying to look at something in the view layer, enter the following:
&gt;        
Be sure not to use the ll update any confusing sections.</content>
    <updated>2010-08-03 19:11:35 UTC</updated>
    <author>
      <name>apolzon</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/55</id>
    <published>2010-07-28T13:40:30Z</published>
    <updated>2010-07-28T15:51:12Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/55-children-viewhint-and-paginate" rel="alternate"/>
    <title>Children Viewhint and Paginate</title>
    <content type="html">&lt;h1&gt;Intro&lt;/h1&gt;
If you have models with belongs\_to and has\_many relationships and have used the children view hint on the parent model, you will get a collection of cards on the bottom of the main page or on the sidebar depending on how many child models you specify and in what order.  See () for more information on this behavior.

You may have noticed, however, that automatic pagination does not happen.  :-(

It would be nice if the children viewhint supported parameters that would enable this.  Maybe the author of the generator spitting out the Rapid show page is reading this...heck, I might try a hack of it myself.

But until then, there are two ways to support this behavior.  This recipe documents them.

&lt;h1&gt;Prerequisites&lt;/h1&gt;
Create a demo app.

    $ hobo paginate_children

&lt;h2&gt;Create models&lt;/h2&gt;
Let's create models that support parent/children relationships.  How about Post and Comment?

    $ cd paginate_children
    $ ruby script/generate hobo_model_resource Post name:string
    $ ruby script/generate hobo_model_resource Comment name:string

Edit the models to define the parent/child relationship.
&lt;pre&gt;paginate_children/app/models/post.rb&lt;/pre&gt;

    class Post &amp;lt; ActiveRecord::Base
        hobo_model # Don't put anything above this

        fields do
            name :string
            timestamps
        end

    +   has_many :comments

        # --- Permissions --- #

        def create_permitted?
            acting_user.administrator?
        end

        def update_permitted?
            acting_user.administrator?
        end

        def destroy_permitted?
            acting_user.administrator?
        end

        def view_permitted?(field)
            true
        end
    end

&lt;pre&gt;paginate_children/app/models/comment.rb&lt;/pre&gt;
    class Comment &amp;lt; ActiveRecord::Base

        hobo_model # Don't put anything above this

        fields do
            name :string
            timestamps
        end

    +   belongs_to :post

        # --- Permissions --- #

        def create_permitted?
            acting_user.administrator?
        end

        def update_permitted?
            acting_user.administrator?
        end

        def destroy_permitted?
            acting_user.administrator?
        end

        def view_permitted?(field)
            true
        end
    end

&lt;h2&gt;Add children viewhint&lt;/h2&gt;
&lt;pre&gt;paginate_children/app/viewhints/post_hints.rb&lt;/pre&gt;
Add the viewhint for comments to the post model.

    class PostHints &amp;lt; Hobo::ViewHints
        # model_name "My Model"
        # field_names :field1 =&gt; "First Field", :field2 =&gt; "Second Field"
        # field_help :field1 =&gt; "Enter what you want in this field"
        # children :primary_collection1, :aside_collection1, :aside_collection2
    +   children :comments
    end

&lt;h2&gt;Overriding will_paginate default per_page value&lt;/h2&gt;
In order for us to be able to see pagination work with only a few child entries, use this monkey patch to change the default per\_page value from 30 to 3.  Add this code to the end of paginate\_children/config/environment.rb:

    # Set will_paginate default per_page to 15 instead of the default 30
    ActiveRecord::Base.instance_eval do
        def per_page; 3; end
    end

&lt;h2&gt;Startup&lt;/h2&gt;
Migrate your models and startup the server.

    $ ruby script/generate hobo_migration
    ...
    $ ruby script/server

At this point, you should be able to add the administrative user.  Go ahead also and add a post and add 4 comments attached to that post.

&lt;h1&gt;Two techniques&lt;/h1&gt;
There are two techniques to achieve this.
1.  You create a paginated collection instance variable in the post controller and then override the show page to use this variable.  You also have to add the paginate tags so the pager controls show.
2.  Use the index owner action on the comment controller.

&lt;h2&gt;Collection instance variable&lt;/h2&gt;
We need to set up pagination for the comments collection.  Add a comments instance variable to the posts\_controller.
&lt;pre&gt;paginate_children/app/controllers/posts_controller.rb&lt;/pre&gt;

    class PostsController &amp;lt; ApplicationController

        hobo_model_controller

        auto_actions :all

    +   def show
    +       hobo_show do
    +           @comments = this.comments.paginate(:page =&gt; params[:page])
    +       end
    +   end
    end

Next, override the comments collection in the Post show page to use the instance variable just created and add the paginate tags.
&lt;pre&gt;paginate_children/app/views/taglibs/application.dryml&lt;/pre&gt;

    

    
    
    

    

    Paginate Children

    +
    +    
    +        
    +            
    +            
    +            
    +        
    +    
    +

&lt;h3&gt;Test it out&lt;/h3&gt;
If you added 4 or more comments to a post, that post's show page should show those comments paginated with 3 entries per page.

&lt;h2&gt;Index owner action&lt;/h2&gt;
You can get pagination behavior out of the box by using the index owner action on the comment controller as defined below.
&lt;pre&gt;paginate_children/app/controllers/comments_controller.rb&lt;/pre&gt;

    class CommentsController &amp;lt; ApplicationController

        hobo_model_controller

        auto_actions :all

    +   auto_actions_for :post, [:index]
    end

You can the use the url http://localhost:3000/posts/:id/comments to access the show page for the post id referenced by :id.  The comments collection will be paginated (and note that this is not because of the code from technique 1...see app/views/taglibs/auto/rapid/pages.dryml and look for index-for-post-page).  The problem with this technique (unless I am missing something) is that the wiring up of the navigation to this url is not done by the framework automatically.</content>
    <updated>2010-07-28 15:51:12 UTC</updated>
    <author>
      <name>cbenedict</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/54</id>
    <published>2010-07-19T18:00:14Z</published>
    <updated>2010-07-26T10:30:36Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/54-jqgrid-on-hobo" rel="alternate"/>
    <title>jqGrid on Hobo</title>
    <content type="html">About a year ago I started working with hobo and really liked what I saw. I could do some amazing things really fast. The only piece that felt like it was missing something was the table-plus tag. So I started working on an alternative and decided to start from the well known jqGrid widget. I went on to install every rails jqGrid plugin in existance and found that none of them were quite what I wanted.

The main features of my incarnation of the jqGrid plugin are:
 - Uses a rails model directly instead of the limiting auto-detect method
 - Uses a randomly generated id name so you can put as many grids of the same or different types as you want on the same page
 - Direct editing of the jqGrid properties through hash properties e.g. :title =&gt; "Title"
 - Easy to edit default jqGrid properties (plan to take it a step further and implement them as yaml later)
 - Uses the # symbol as the first character to identify a function property (I implemented the ondblclickrow property this way)
 - Double clicking on rows and anything else that is possible with the jqGrid widget...

##[rails-jqgrid @ Github](http://github.com/allen13/rails-jqgrid)

This is a rails plugin dedicated to the great jqGrid javascript widget. I had two rails concepts in mind when I created 
it, "DRY it up" and "Convention over configuration". I managed to get jqGrid to work with two lines of code ( one if you 
don't count the styling). A standard set of grid options has already been defined, so all you really need to do is give 
it a model. Enjoy :-)

Usage Instructions:

    #For the jQuery grid theme
    
    #For the grid
    

Example:

    
       
             
       
       
             "exercise_type,duration_in_mins,date") %&gt;
       
    


Copyright (c) 2010 [name of plugin creator], released under the MIT license</content>
    <updated>2010-07-26 10:30:36 UTC</updated>
    <author>
      <name>allen13</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/53</id>
    <published>2010-07-08T05:43:08Z</published>
    <updated>2010-07-08T07:37:25Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/53-remembering-table-plus-filter-parameters" rel="alternate"/>
    <title>Remembering &lt;table-plus&gt; Filter Parameters</title>
    <content type="html">When you are using a `` on an index page, it is often nice to be able to remember the filter parameters that the user had set.  This allows the user to wander off to other pages and have their filter parameters restored when they subsequently return to the index page.

For example, in a work tasking application, users use filters to select the subset of tasks from the task list that they wish to work on.  Working on each task takes the user away from the index screen.  When they have completed the task and return to the index screen, their filter parameters would normally have been reset and they now need to reselect their subset of tasks they are working on. It is much more user friendly to have the filter parameters to remain in use in the current session until they explicitly change them.

There are two parts to the solution.

First, you need to save the filter parameters.  In the `index` action in the controller, add the lines:

     filter_parameters = request.parameters 
     filter_parameters.delete(:controller) 
     filter_parameters.delete(:action) 
     session[:filter_parameters] = filter_parameters 
 
This gets the parameters from the request, strips out the action and controller, and then stores them in the session hash. When ever the filter parameters are changed, they are stored in the session.  This also works with pagination, so the user will return to the page of results they were last on - just strip out the `page` parameter if you don't want this.

Secondly, we need to restore them when the user returns to the index page.  We do this using the assumption that there is only one path to the index page, namely through the navigation bar.  The filter parameters are restored by appending them to the url of the index page with:

     
        Surveys 
      

The `params` attribute of the `` tag converts the hash to a http parameter string and attaches it to the end of the url. 

Now when the user clicks on the nav item in the nav bar, they will return to the index page just how they had left it.</content>
    <updated>2010-07-08 07:37:25 UTC</updated>
    <author>
      <name>Dean</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/52</id>
    <published>2010-06-27T23:33:02Z</published>
    <updated>2010-06-28T01:54:59Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/52-hierarchies-using-acts-as-tree" rel="alternate"/>
    <title>Hierarchies - Using acts_as_tree</title>
    <content type="html">&lt;h1&gt;Intro&lt;/h1&gt;
The acts\_as\_tree gem () provides support for organizing rows in a table into a parent/child hierarchy.  This is useful for structures where entries have subentries and so on.  Category listings often have this structure.  Hobo does not have native support for this gem, so this is my attempt to add some support.  I welcome feedback on how to make this more flexible/usable.

&lt;h1&gt;Prerequisites&lt;/h1&gt;
First, you need to get the acts\_as\_tree gem.

    $ sudo gem install acts_as_tree

Now create a demo app.

    $ hobo tree_demo

Add the gem to your environment.rb so that the gem autoloads into your application at startup.
&lt;pre&gt;tree_demo/config/environment.rb&lt;/pre&gt;

    # Be sure to restart your server when you modify this file

    # Specifies gem version of Rails to use when vendor/rails is not present
    RAILS_GEM_VERSION = '2.3.8' unless defined? RAILS_GEM_VERSION

    # Bootstrap the Rails environment, frameworks, and default configuration
    require File.join(File.dirname(__FILE__), 'boot')

    Rails::Initializer.run do |config|
        config.gem 'hobo'
    +   config.gem 'acts_as_tree'
    ...

&lt;h1&gt;Create a model&lt;/h1&gt;
Let's create a model of Categories that support's parent/child relationships.

    $ ruby script/generate hobo_model_resource Category name:string parent_id:integer

Edit the new model to define the hierarchical relationship.
&lt;pre&gt;tree_demo/app/models/category.rb&lt;/pre&gt;

    class Category &amp;lt; ActiveRecord::Base
        hobo_model # Don't put anything above this

        fields do
            name      :string
            parent_id :integer
            timestamps
        end

    +   acts_as_tree :order =&gt; :name

        # --- Permissions --- #

        def create_permitted?
            acting_user.administrator?
        end

        def update_permitted?
            acting_user.administrator?
        end

        def destroy_permitted?
            acting_user.administrator?
        end

        def view_permitted?(field)
            true
        end
    end

Migrate the model modification and start the server.

    $ ruby script/generate hobo_migration
    ...
    $ ruby script/server

&lt;h1&gt;Add new tag definition&lt;/h1&gt;
 
Add the following tag definition to a new tag library.  This new tag displays a hierarchical unordered list of a model that has acts\_as\_tree defined.  Credit to  for the original code snippet to do this.  I adapted it to work with hobo. 
&lt;pre&gt;tree_demo/app/views/taglibs/tree.dryml&lt;/pre&gt;

    &amp;lt;!-- My hack to display an unordered list of links for acts-as-tree hierarchy.
         If you pass to="collection" attribute, the links will change to 
         index_for_collection view of the parent --&gt;
    
         0
                    %&gt;&lt;ul&gt;&lt;li&gt; 0
                        %&gt;&lt;/li&gt;&lt;/ul&gt;(none)&lt;a&gt;&lt;/a&gt; 
        
    

&lt;h1&gt;Modify view to display hierarchy&lt;/h1&gt;
Open up the applications.dryml file.  We are going to override the default categories index page and replace the collection of Categories with the new links-for-tree tag.  You also have to add an include for the new tag library file created that contains the links-for-tree tag.
&lt;pre&gt;tree_demo\app\views\taglibs\application.dryml&lt;/pre&gt;

    

    
    
    
    +

    

    Tree Demo

    +
    +    
    +        
    +            
    +        
    +    
    +

&lt;h1&gt;Test it out&lt;/h1&gt;
Go to the Categories link and add some new categories.  Be sure to add subcategories and set up their parent.  When you go back to the Categories index page, you should see a hierarchy list of links to categories instead of a flat collection.

&lt;h1&gt;Further steps&lt;/h1&gt;
&lt;h2&gt;Relationships&lt;/h2&gt;
The links-for-tree tag supports index\_for\_\* relationships, meaning that if you pass the 'to=' attribute, where 'to' represents the name of a has\_many relationship on the passed in context, then the links that will be rendered will be links to the related object that belongs to the context.

For example, if Tasks belong to a Category, and you wanted to create a Category side bar on the Tasks index page that would link to index\_for\_category to narrow the scope to show only those tasks that belong to the clicked category, you would code:

    
&lt;h2&gt;New collection tag&lt;/h2&gt;
After writing this tag, it occurred to me that the existing collection tag (or perhaps a new one) could be modified to show categorized cards that belong to a model that has a parent/child relationship.  I could envision some jQuery magic that would roll down/up sections that contain cards by clicking on the model names of the hierarchical relationship.  For next time...</content>
    <updated>2010-06-28 01:54:59 UTC</updated>
    <author>
      <name>cbenedict</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/50</id>
    <published>2010-05-25T01:34:04Z</published>
    <updated>2010-06-10T11:53:38Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/50-integrating-google-maps-with-hobo" rel="alternate"/>
    <title>Integrating Google Maps with Hobo</title>
    <content type="html">Integrating Google Maps into a Hobo application is very simple.  The [YM4R](http://ym4r.rubyforge.org/) plugin does all of the heavy lifting of generating the necessary Javascript.

#### Install YM4R/GM

    ruby script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm

#### Set up a Google Map Tag
In application.dryml, define the following tags:

    
       true,:map_type =&gt; true)
          @map.center_zoom_on_points_init([ne_lat,ne_long],[sw_lat,sw_long]) %&gt;
      
       width, :height =&gt; height) %&gt;
      
    

    
       title, :info_window =&gt; info)) %&gt;
    

The attributes for google-map are:
* id - the id assigned to the div element containing the map.  Different maps on the same page should have a different id.
* ne-lat, ne-long, sw-lat, sw-long - the map will be automatically scaled to contain a bounding box defined by these two points
* width, height - the size of the map to display. Any HTML units of measurement are accepted.

The maker tag places markers on the map.  Its attributes are:
* lat, long - the location of the marker
* title - displayed as a tooltip when you mouseover the marker
* info - HTML code displayed in a popup when you click on the marker

#### Insert a Map in a Page
Each page containing a map first needs the Google Maps Javascript library included:

    
      
    

Then to create the map:

      
            
            
            
      

#### A More Complex Example
I have number of sites as defined by the model:

    class Site &amp;lt; ActiveRecord::Base

      hobo_model # Don't put anything above this
 
      fields do
        code        :string, :required, :unique, :name =&gt; true
        site_name   :string, :required
        latitude    :decimal, :precision =&gt; 15, :scale =&gt; 10
        longitude   :decimal, :precision =&gt; 15, :scale =&gt; 10
        timestamps
      end
 
      belongs_to :district
    end

where each site has a code, site name and belongs to a geographical district.

The index.dryml page to display these sites on a map is:

    
      
        
      
      

      
        
          
          
            
              
            
          
        

        
          
            &lt;div class="filter"&gt;
              District: 
            &lt;/div&gt;
          
          No sites match your criteria
        
      
      
    


The index page will display all of the site locations on a map and scale the map to right size to contain them all.  Note - some of my sites don't have location data so I filter them out from the map.

The user is able to filter the list of sites using the table-plus filter and the map will only display those sites.  The corresponding controller is:

    class SitesController &amp;lt; ApplicationController

      hobo_model_controller

      auto_actions :all

      def index
    
        scopes = {:search =&gt; [params[:search], :site_name],
          :district_is =&gt; params[:district],
          :order_by  =&gt; parse_sort_param(:code, :district, :site_name)}

        hobo_index Site.apply_scopes(scopes), :include =&gt; [:district],
          :paginate =&gt; false

      end

   end

One downside is that the map will only show those sites that are displayed on that page, so if you have more than one page of results you will need to turn off the paginator with :paginate =&gt; false.

YM4R has a number of other features including geocoding and getting maps from other providers such as Yahoo and Microsoft.

#### Google API Key
Google allows you to embed a map in your application freely if it is accessed locally at http://localhost.

If your application is available remotely, Google requires you to register at [http://code.google.com/intl/en/apis/maps/signup.html](http://code.google.com/intl/en/apis/maps/signup.html) and obtain an API key for your site.

Your API key for each address needs to be recorded in config/gmaps\_api\_key.yml</content>
    <updated>2010-06-10 11:53:38 UTC</updated>
    <author>
      <name>Dean</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/49</id>
    <published>2010-05-18T15:04:16Z</published>
    <updated>2010-05-23T10:40:12Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/49-using-highslide-with-hobo" rel="alternate"/>
    <title>Using Highslide with Hobo</title>
    <content type="html">[Highslide](http://highslide.com/) is another powerful  image, media and gallery viewer written in JavaScript that is really easy to integrate with Paperclip in Hobo.

Download Highslide from [http://highslide.com/](http://highslide.com/) and unzip the file in the public directory of your application.

In your application.dryml add the following:

    
      
        
    
          
          
          /highslide/graphics/';
            hs.showCredits = false;
          
      
    

    
      
        
          &lt;a href="#{this.image.url}" class="highslide"&gt;
            &lt;img title="Click to enlarge" /&gt;
          &lt;/a&gt;
        
      
    

</content>
    <updated>2010-05-23 10:40:12 UTC</updated>
    <author>
      <name>Dean</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/46</id>
    <published>2010-05-02T23:46:35Z</published>
    <updated>2010-05-23T10:43:00Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/46-using-lightbox-with-hobo" rel="alternate"/>
    <title>Using Lightbox with Hobo</title>
    <content type="html">[Lightbox] (http://www.huddletogether.com/projects/lightbox2/) is a simple, unobtrusive script used to overlay images on the current page that is used on many web sites and integrates well with Paperclip.

Using Lightbox with Hobo is very straight forward.

#### Install Lightbox
Download the code from [http://www.huddletogether.com/projects/lightbox2/](http://www.huddletogether.com/projects/lightbox2/)


Copy &lt;i&gt;builder.js, scriptaculous.js, lightbox.js&lt;/i&gt; to &lt;i&gt;public/javascripts&lt;/i&gt; in your Hobo project.

Copy &lt;i&gt;lightbox.css&lt;/i&gt; to &lt;i&gt;public/stylesheets&lt;/i&gt; in your Hobo project.

Copy the contents of &lt;i&gt;images&lt;/i&gt; to &lt;i&gt;public/images&lt;/i&gt; in your Hobo project.

#### Include the Lightbox Javascript and Stylesheet

In application.dryml, extend the page tag to include the Lightbox javascript and stylesheet.

    
      
        
          
          
          
        
      
    

#### Fix a Couple of Issues

In lightbox.js, change the following line:

    LightboxOptions = Object.extend({
        fileLoadingImage:        '/images/loading.gif',
        fileBottomNavCloseImage: '/images/closelabel.gif',

In lightbox.css, change the following line:

    #prevLink, #nextLink{ width: 49%; height: 100%; background-image: url(data:image/gif;base64,AAAA); 
        /* Trick IE into showing hover */ display: block; background-color: transparent; border: 0}
    

#### Modify the Card Tag for Images
In your application.dryml, extend the Card for images:

    
      
        
          &lt;a href="#{this.image.url}"&gt;
            &lt;img /&gt;
          &lt;/a&gt;
        
      
    


This card will display the thumbnail of images uploaded using Paperclip and display the larger version when the user clicks on it.  When showing a collection of images, clicking on a thumbnail with present a slideshow of the collection that the user can page through.  Images can have a title included by adding a &lt;i&gt;title=&lt;/i&gt; attribute to the &amp;lt; a &gt; tag.


</content>
    <updated>2010-05-23 10:43:00 UTC</updated>
    <author>
      <name>Dean</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/45</id>
    <published>2010-04-09T12:16:57Z</published>
    <updated>2010-04-09T12:32:34Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/45-routing-from-specific-domain-or-host" rel="alternate"/>
    <title>Routing from specific domain or host</title>
    <content type="html">If you need, like me, routing something using specific domain or host, just create a file under &lt;pre&gt;yourapp/config/initializers&lt;/pre&gt; a file like this:

&lt;pre&gt;&lt;code&gt;
module ActionController

  module Routing

    class RouteSet
      def extract_request_environment(request)
        env = { :method =&gt; request.method }
        env[:domain] = request.domain if request.domain
        env[:host] = request.host if request.host          
        env
      end
    end

    class Route
      alias_method :old_recognition_conditions, :recognition_conditions
      def recognition_conditions
        result = old_recognition_conditions
        result &amp;lt;&amp;lt; "conditions[:domain] === env[:domain]" if conditions[:domain]
        result &amp;lt;&amp;lt; "conditions[:host] === env[:host]" if conditions[:host]        
        result
      end
    end

  end

end
&lt;/code&gt;&lt;/pre&gt;


Now, you're be able to use this in your routes:

&lt;pre&gt;map.connect '', :controller =&gt; 'admin/front', :action =&gt; 'index', :conditions=&gt;{ :domain=&gt;'admin.yourdomain.com' }&lt;/pre&gt;

</content>
    <updated>2010-04-09 12:32:34 UTC</updated>
    <author>
      <name>Fernando</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/44</id>
    <published>2010-04-09T04:32:00Z</published>
    <updated>2010-04-09T04:34:57Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/44-using-multiple-sortable-collection-s-for" rel="alternate"/>
    <title>Using multiple &lt;sortable-collection&gt;'s for the same model on the same page</title>
    <content type="html">Suppose you have a list (of requests for example) that are partitioned into a number of sublists that you want to be able to reorder individually on some page.  

Using the requests, example, the Request model might look like this:

    class Request &amp;lt; ActiveRecord::Base
 
      hobo_model # Don't put anything above this
 
      belongs_to :project
      acts_as_list :scope =&gt; %q(project_id = #{project_id} and priority = '#{priority}')
 
      fields do
        title       :string, :required
        priority    enum_string(:high, :medium, :low), :required, :default =&gt; 'medium'
        timestamps
      end
 
      validates_uniqueness_of :title, :scope =&gt; :project_id

    end

Since we said that requests belong to projects, we better add some has_many's to the Project model:

      has_many :requests, :dependent =&gt; :destroy
      has_many :high_priority_requests, :class_name =&gt; 'Request',
        :conditions =&gt; {:priority =&gt; 'high'}
      has_many :medium_priority_requests, :class_name =&gt; 'Request',
        :conditions =&gt; {:priority =&gt; 'medium'}
      has_many :low_priority_requests, :class_name =&gt; 'Request',
        :conditions =&gt; {:priority =&gt; 'low'}

And, to get the requests automatically displayed on the project page, we need the following in the projects viewhints file:

      children :requests

Now, we are ready to replace the whole list of requests with three sublists: one each for the low, medium and high priority requests.  We create a show page for projects that looks like:

    
      
        &lt;h3&gt;High Priority Requests&lt;/h3&gt;
        
        &lt;h3&gt;Medium Priority Requests&lt;/h3&gt;
        
        &lt;h3&gt;Low Priority Requests&lt;/h3&gt;
        
      
    

Note that we had to create a different div id for each of the sublists, because these id's are required to be unique on the page.

However, hobo uses the div id as the parameter to specify how the elements were reordered.  Thus, we add the following method to the Requests controller:

      def reorder
        # We need to (un)distinguish the high, medium &amp; low priority request sections
        request_ordering = params.keys.grep(/request_ordering$/)[0]
        params['request_ordering'] = params[request_ordering]
        hobo_reorder
      end

Now, the application can the multiple &amp;lt;sortable-collection&amp;gt;'s for the same model on the same page.</content>
    <updated>2010-04-09 04:34:57 UTC</updated>
    <author>
      <name>Henry Baragar</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/43</id>
    <published>2010-03-28T06:37:43Z</published>
    <updated>2010-03-28T06:42:11Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/43-using-gmail-for-sending-email" rel="alternate"/>
    <title>Using GMail for Sending Email</title>
    <content type="html">If you enabled **GoogleApps** in your Domain. You might want to take advantage of sending email using **Gmail**. You just need to do the following simple steps. This is very useful specially when your using **--invite-only** option when creating your hobo application.

Let's start rolling. You need to install the following gem. If your using heroku as your staging server or even production server you might want to include this on your **.gems** file:

Install the gem in your local machine: 
    sudo gem install tlsmail

Again if your using heroku, add the following line in your .gems file:
    tlsmail --version '&gt;= 0.0.1' 


Now that you have the gem in place it's time to add other configurations:

Your **environment.rb** should look like this:

    Rails::Initializer.run do |config|
        config.gem "tlsmail"
    ...
    end
    Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)


Add the following lines in your **environments/development.rb** or **environments/production.rb**:

    config.action_mailer.delivery_method = :smtp
        config.action_mailer.smtp_settings = {
        :address =&gt; "smtp.gmail.com",
        :port =&gt; "25",
        :domain =&gt; "your_domain.com",
        :user_name =&gt; "your_login@your_domain.com",
        :password =&gt; "your_password",
        :authentication =&gt; :login
    }



That should be it. If you do:

    git push heroku master 


It will automatically install the gem in your heroku instance, then you should be able to send invites or better yet send email from your hobo application.

Thanks to: **http://codelikezell.com/using-gmail-with-rails/** for this guide.</content>
    <updated>2010-03-28 06:42:11 UTC</updated>
    <author>
      <name>baldrailers</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/42</id>
    <published>2010-03-09T18:27:29Z</published>
    <updated>2010-03-09T18:46:25Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/42-customized-the-select-menus-in-a" rel="alternate"/>
    <title>Customized the Select Menus In A Hobo Form</title>
    <content type="html">Hobo does a nice job of generating a default form for every model you generate.

Especially nice are the "select" boxes that follow ActiveRecord "belongs\_to" associations.

In any event generally speaking the best way to look at how to change the form for a model is to look at the autogenerated tag created by Rapid,

in 

$RAILS_ROOT/app/views/taglibs/auto/rapid/forms.dryml

There you'll see this (basically):

    &amp;lt;!-- AUTOMATICALLY GENERATED FILE - DO NOT EDIT --&gt;

    
      
        
        
        &lt;div&gt;
          
        &lt;/div&gt;
      
    

Now lets monkey with this tag a bit.  For my model I have a belongs\_to association called "group leader" that I want to filter, or put another way I want to restrict the entries that can appear in my dropdown.

Here's the bit from the model code:

    class InviteGroup &amp;lt; ActiveRecord::Base
    
      hobo_model # Don't put anything above this
    
      fields do
        name :string
        group_leader_id :integer
        address :text
        timestamps
      end
    
      has_many :invitees
      belongs_to :group_leader, :class_name =&gt; 'Invitee', :foreign_key =&gt; "group_leader_id"
      .....

In this case I only want to allow a group leader who is actual an invitee of this group.  So if an invite doesn't point to this group, it can't be my leader.  Put generically:

A foreign key to a parent table may have a restricted set of valid values.  

Lets restrict our form, in this case to whatever the 'invitees' method of our current object returns.

We can do this by 'extending' our form tag, and we'll do this by altering the behaviour of the tag in 

$RAILS_ROOT/app/views/taglibs/application.dryml

    
      
        
          
            
          
        
      
    

Explained simply:
 - extend alters the tag auto-generated by rapid
 - old-form merge; see the agility tutorial more on this construct
 - group-leader-view ; we can change the markup generated for any field in field list

This works like a charm!  

How to learn more?  Probably the best thing you can do is work through the DRYML guide; this will help you crack the mystery of these tags and empower to alter the functionality of the Rapid tags, as well as write your own.  Good luck!</content>
    <updated>2010-03-09 18:46:25 UTC</updated>
    <author>
      <name>bretweinraub</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/41</id>
    <published>2010-02-21T21:50:15Z</published>
    <updated>2010-02-21T21:53:30Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/41-ajax-login-form" rel="alternate"/>
    <title>Ajax login form</title>
    <content type="html">(Note that this recipe requires Hobo 1.1 or the latest version from [github](http://github.com/tablatom/hobo))

On a recent site I worked on, we wished to be able to create an order without logging in.  The solution we came up with was to replace the "save" button with an ajax login form.

Typically, AJAX forms are trivially easy to do in Hobo, but there's a few things that made this recipe a little less trivial.

1.  HTML does not allow nested forms, so the login form is actually placed outside of the order form.

2.  We moved the flash messages to ensure that users still received login failure messages.

3.  Hobo only updates a single part after AJAX.  In our scenario, we had two portions that needed updating -- the actions parameter in the form and the div after the form.  Usually you can just update all or most of the page when you have several portions to update, but we didn't want to lose the user's order.   Instead, we used Javascript for the second update.

4. In our application, Orders are created via a lifecycle method called "initiate".   However, guests are not allowed to trigger the method.   So the generated initiate-form does not work.   However, `` does.

Here's the DRYML:

    
      
        
          
        
        &lt;div class="span-5 last"&gt;
          
            
              
              
            
          
        &lt;/div&gt;
      
    

and the Javascript:

    guestOrderLogin: function() {
        jQuery('.actions').removeClass('hidden');
    }
</content>
    <updated>2010-02-21 21:53:30 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/40</id>
    <published>2010-01-24T17:47:30Z</published>
    <updated>2010-01-24T17:47:30Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/40-navigation-menu-with-more-than-5" rel="alternate"/>
    <title>navigation menu with more than 5 items</title>
    <content type="html">For most applications, one of the first things that is done is to replace the navigation menu with a full custom menu.

However for some applications like admin subsites, the standard navigation would be fine, if it wasn't limited to 5 items.

Put this in your `application.dryml` (or `admin_site.dryml`) for a navigation that isn't limited to 5 items.

    
      
        Home
        
        
          
        
      
    
</content>
    <updated>2010-01-24 17:47:30 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/38</id>
    <published>2009-11-27T07:41:17Z</published>
    <updated>2010-04-06T01:23:52Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/38-optimistic-locking-in-hobo" rel="alternate"/>
    <title>Optimistic Locking in Hobo</title>
    <content type="html">A rarely used feature of activerecord is the lock\_version column, which (if present) enables optimistic locking for that table. When activerecord updates that model it will compare the lock\_version of the version being saved against the version in the database, and if they aren't the same then it will throw a StaleObjectError, which prevents concurrent updates from overwriting one-another. If they do match then the lock\_version is incremented (it's an integer) to invalidate any updates that are waiting to happen. This is a great way of increasing the safety and (potential) concurrency of your app (without needing more traditional concurrency schemes like table locking).

To implement this in your hobo app you need to do update all 3 layers of your model (the model, view and controller).

# Model

This part's easy, just add lock\_version as a field in your models (and obviously generate a migration to add it to the database as well).

    class ConcurrentModel &amp;lt; ActiveRecord::Base
    
        hobo_model # Don't put anything above this

        # --- Fields --- #

        fields do
            lock_version :integer, :default =&gt; 0
            timestamps
        end

        never_show :lock_version
    end

Note that I've set the default to 0 (this is important or activerecord won't have a value to compare against when it updates). Also, the never\_show macro is useful because it hides the field from rapid (so it won't appear in any of your views)

# View

Next you'll need to add it to your forms so that when the user submits an update the server is sent the lock\_version that was current when they loaded the model. Thanks to rapid you can extend the form tag itself and this change will be reflected in every form rendered in your app.

    
        
            
                
            
        
    

    
        
            
                
                
            
        
    

Here I've added lock_version to the input-many tag as well as the form tag - this is because input-many doesn't re-use form, but instead just calls field-list on any associated models. (Otherwise models updated through an input-many wouldn't be protected by optimistic locking).

# Controller

At this point optimistic locking will work, but the user will get a nasty error screen if a StaleObjectError is thrown. To correct this, you'll need to update your controller to catch the error, then reload the model (with the updated values) and send them back to the previous page in the same way as a validation error would.

     def update
          hobo_update
          rescue ActiveRecord::StaleObjectError
               flash_notice "This #{model.view_hints.model_name} was changed by someone else while you were editing it. Please try again."
               response_block(&amp;block) or respond_to do |wants|
                    wants.html do
                         # reload the model from the database
                         self.this = find_instance
                         # re-render the form
                         re_render_form(:edit)
                    end
                    wants.js do
                         render(:status =&gt; 500, :text =&gt; ("There was a problem with that change.\n" + @this.errors.full_messages.join("\n")))
                    end
               end
          end
     end

If you like you can override the update method in hobo itself and get this across all of your models.


And that's it! Now your users will get a nice, user-friendly flash notice if they try a concurrent update, whereas before they would have silently overwritten someone else's update.

To test it, try opening the same edit page (for the same model) twice in two different browsers (as you can have a different session in each). Change a few fields in both, then save them both (without reloading either after saving). The second one to be saved will show the warning above and reject the update.

The idea for this recipe came from [Scripted Zen](http://scriptedzen.blogspot.com/2007/08/optimistic-locking-in-rails-with-active.html), which is one of the few places I've seen optimistic locking discussed (besides the pragmatic programmers rails book).</content>
    <updated>2010-04-06 01:23:52 UTC</updated>
    <author>
      <name>iainbeeston</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/36</id>
    <published>2009-08-26T18:03:21Z</published>
    <updated>2009-08-26T18:10:05Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/36-using-a-name-one-one-a" rel="alternate"/>
    <title>using a name-one one a model with a generated field for a name:</title>
    <content type="html">Suppose you have a model that uses a generated field for its name:

    class User
      def name
        "#{first_name} #{last_name}"
      end
    end

Now suppose that you have an `Institution` that `belongs_to :contact, :class_name =&gt; "User"`.  And suppose you want to use a name-one to choose the contact:

    
      
        
          
            
          
        
      
    

The next step is to autocomplete support to the users controller.  I recently added support to `hobo_completions` to allow the completer to search more than one field.   (In other words, this will not work with Hobo 0.8.8, you need 0.8.9 or edge Hobo)  Let's take advantage of this:

    class UsersController &amp;lt; ApplicationController
      autocomplete :name, :query_scope =&gt; [:first_name_contains, :last_name_contains]
    end

Now the autocompleter should work.   However, if you submit the form, you get an error complaining that `find\_by\_name` doesn't exist.   Let's fix that:

    class User &amp;lt; ActiveRecord::Base

      def self.find_by_name(name)
        names = name.split(' ')
        (0..(names.length-2)).inject(nil) do |result, n|
          result ||= self.find_by_first_name_and_last_name(names[0..n].join(' '), names[1..(n+1)].join(' '))
        end
      end

The code is a little tricky simply because we wish to handle both "Vince van Vickel" and "Mary Sue Jones" appropriately.</content>
    <updated>2009-08-26 18:10:05 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/35</id>
    <published>2009-08-25T19:01:30Z</published>
    <updated>2009-09-09T19:19:51Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/35-adding-an-rss-feed" rel="alternate"/>
    <title>Adding an RSS feed.</title>
    <content type="html">This is mostly the same as standard rails.  Hobo does shorten the controller side if you don't need any filtering.

In your controller, add an index_action for your feed.

    index_action :rss

Hobo index actions will paginate for html requests but by default do not for xml requests.

Then add the view file under &lt;code&gt;/app/views/&lt;/code&gt;controller&lt;code&gt;/rss.builder&lt;/code&gt;

    xml.instruct! :xml, :version =&gt; "1.0"
    
    xml.rss "version" =&gt; "2.0" do
     xml.channel do
    
       xml.title       "news"
       xml.link        url_for(:only_path =&gt; false, :controller =&gt; 'posts')
       xml.description "news items"
    
       @posts.each do |post|
         xml.item do
           xml.title       post.title
           xml.link        url_for(:only_path =&gt; false, :controller =&gt; 'posts', :action =&gt; 'show', :id =&gt; post.id)
           xml.description post.body
           xml.updated_at  post.updated_at
           xml.guid        url_for(:only_path =&gt; false, :controller =&gt; 'posts', :action =&gt; 'show', :id =&gt; post.id)
         end
       end
    
     end
    end

The above is from a feed I created for a posts controller.  Substitute your fields as needed.</content>
    <updated>2009-09-09 19:19:51 UTC</updated>
    <author>
      <name>kevinpfromnm</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/34</id>
    <published>2009-08-06T08:43:33Z</published>
    <updated>2009-08-06T09:31:29Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/34-using-recaptcha-in-your-hobo-lifecycle" rel="alternate"/>
    <title>Using recaptcha in your Hobo Lifecycle</title>
    <content type="html">The easiest way avoid having automated spam-bots creating zillions of fake accounts in your application is to put a [captcha](http://en.wikipedia.org/wiki/Captcha) on the signup page. [Recaptcha](http://recaptcha.net/) is a fantastically cool one because it:

 * Is a free service
 * Uses the responses to OCR books
 * Has an easy to use [Rails plugin](http://ambethia.com/recaptcha/files/README_rdoc.html)

This is how to add this functionality to a Hobo lifecycle event (create or transition). We'll use the 'signup' create from the [Agility tutorial](http://cookbook.hobocentral.net/tutorials/agility) as an example.

 - Install the recaptcha plugin:

        script/plugin install git://github.com/ambethia/recaptcha

 - Sign up to [Recaptcha](http://recaptcha.net) and generate the keys for your site.

 - Add the keys you've generated to your environment.rb:

        ENV['RECAPTCHA_PUBLIC_KEY'] = 'REPLACE-WITH-YOUR-PUBLIC-KEY'
        ENV['RECAPTCHA_PRIVATE_KEY'] = 'REPLACE-WITH-YOUR-PRIVATE-KEY'

 - In application.dryml extend the form tag for the lifecycle event that you're protecting. For the signup create event, that would look like:

        
          
            
              
            
          
        

    The `recaptcha_tags` provides all the HTML needed to display the Recaptcha dialog. Here we're adding it  _immediately after_ the other form fields. At this point you should be able to refresh the page and the Captcha should display. The result of the Captcha is ignored though. Nothing is protected.

 - In the controller for the model (eg users_controller.rb), add (or modify) the 'do_' method for the lifecycle action to verify the captcha prior to the actual hobo action being invoked. For signup, this should look something like this:

          def do_signup
           unless verify_recaptcha
              flash[:error] = "The Captcha words you entered weren't right."
              redirect_to :back
              return
            end
            hobo_do_signup
            end
          end


Try it out, your lifecycle event should now be protected by a Captcha.

You may have noticed that we didn't really do anything lifecycle specific in here. You could use the same technique described to Captcha protect any form. I just chose lifecycles because they were a common case.</content>
    <updated>2009-08-06 09:31:29 UTC</updated>
    <author>
      <name>RitchieY</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/33</id>
    <published>2009-08-03T19:00:32Z</published>
    <updated>2009-08-03T19:22:29Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/33-ajax-filtering-on-a-partially-completed" rel="alternate"/>
    <title>Ajax filtering on a partially completed form</title>
    <content type="html">This is a neat trick I picked up reading some of Tom's code.

This is useful when you've got a form that renders differently depending on the state of some of the values in the form.   For example, when the user selects "United States" as the country, you may wish to add a "state" field, rename "Postal Code" to "Zip Code", and modify the list of shipping options in a select box.

You could use lifecycles to make this a two stage form, but this is a neat trick you can use if you want to keep it all in a single form.

Write your view normally using one of the many different options Hobo gives you.  In this example, I'm going to extend the form:

    
      
        
          
            
              
            
          
        
      
    

In this case, I've created an order that varies the options available in the shipping-method drop down depending on the country.  You'll also see that I've wrapped the whole form in a part.

In your controller (replace Order with your model):

    def edit
      self.this = Order.new(params[:order]) if params[:order]
      hobo_show do
        hobo_ajax_response if request.xhr?
      end
    end

    def new
      hobo_create(Order.new(params[:order])) do
        hobo_ajax_response if request.xhr?
      end
    end

In your application.js (replace order-part with your part and order\_country with your select's class):

    Event.addBehavior({
        "select.order_country:change": function(ev) {
             Hobo.ajaxRequest(window.location.href, ['order-part'], {
                 params: Form.serialize(this.up('form')),
                 method: 'get',
                 message: 'Please wait...'
             });
         }
    });

There you go: fancy custom AJAX, without really writing any AJAX code.

### How it works:

Normally, when you hit submit on a new item, it POSTs to a URL like /orders.  We've added a lowpro javascript watcher that triggers on the CSS selector "select.order\_country:change" that submits the form as a ajax request to the current location (ie, /orders/new).

In the edit controller action, instead of looking up the current value in the database, we create it from the parameters passed in.   We don't save it, though!  Then we invoke the standard hobo ajax mechanism that renders our part.   Most standard hobo actions will do this automatically for you, but the standard hobo\_show and hobo_\new do not, so we add it in ourselves.</content>
    <updated>2009-08-03 19:22:29 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/32</id>
    <published>2009-07-24T20:13:02Z</published>
    <updated>2009-07-24T20:19:24Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/32-getting-an-app-working-with-new" rel="alternate"/>
    <title>Getting an app working with new heroku.com (not herokugarden.com)</title>
    <content type="html">## Generate your app ##

There's two possible approaches, either using the hobo gem which is easy with Heroku's .gems file or by installing hobo as a plugin.

Create your app if you haven't already

    hobo appname # or rails appname if you're going to install hobo as a plugin to be able to use edge hobo
    cd appname
    git init
    git add .
    git commit -m "Initial commit"

Make a heroku app to push your changes

    heroku create # enter your credentials if prompted (first use only)
    
As long as you run this within a git initialized directory, it will add a new remote of heroku

    git push heroku master

Note: heroku does not have all versions of rails gem available by default.  Current list as of this posting is rails (2.3.2, 2.2.2, 2.1.0, 2.0.2).  Check http://installed-gems.heroku.com/ for most up to date list.  If your most recent version isn't one of these, make sure to either update the .gems file (see next step) to include your version or change your &lt;code&gt;config/environment.rb&lt;/code&gt; to reference one of those rails versions.

## Gem Installation ##

Just add a file to the root of your app called .gems with the following:

    hobo -v '0.8.8'

The -v '0.8.8' is optional but a good idea to specify the hobo gem version you wish to use so you don't accidentally grab a later version with breaking changes in it.  Any other gems you might need go on separate lines.  

Check this .gems file into git.

    git add .gems
    git commit -m "Added gemspec file"
    git push heroku

Heroku will pick up on the new .gems file and automatically grab appropriate gems.

## Plugin installation ##

This is not much different than the regular hobo edge install but you can't use submodules with heroku.com at this point.

    cd vendor/plugins
    git clone git://github.com/tablatom/hobo.git
    rm hobo/.git -rf
    rm hobo/.gitignore
    cd ../../.. # back to your base app directory
    git add .
    git commit -m "Added hobo plugin."
    git push heroku

## Final notes ##

Heroku does not automatically run migrations so you'll need to run &lt;code&gt;heroku rake db:migrate&lt;/code&gt; if you add any migrations.</content>
    <updated>2009-07-24 20:19:24 UTC</updated>
    <author>
      <name>kevinpfromnm</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/31</id>
    <published>2009-07-07T15:25:23Z</published>
    <updated>2009-07-14T20:47:23Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/31-using-tony-tomov-s-jqgrid-with" rel="alternate"/>
    <title>Using Tony Tomov's jqGrid with Hobo</title>
    <content type="html">The hobo-jqi plugin supports using the jqGrid grid with Hobo.

####Some jqGrid features:####

* resizable columns
* paging controls
* crud functions
* JQuery UI theming

and many more see them at: [http://www.trirand.com/jqgrid35/jqgrid.html](http://www.trirand.com/jqgrid35/jqgrid.html)

See a [screencast install/demo](http://www.screencast.com/t/7nCgbl5L3)

To install the grid

1. go to the plugins directory: cd vendor/plugins
2. load the plugin from github: git clone git://github.com/blizz/hobo-jqi.git
3. go to the root directory of project: cd ../..
4. run the install rake task: rake hobo\_jqi:install
5. add the following line to app/views/taglibs/application.dryml&lt;br /&gt;
&amp;lt;include src="hobo-jqi-all" plugin="hobo-jqi"/&amp;gt;
6. add this line to the header of an index page:&lt;br /&gt;
&amp;lt;jqi-grid-includes theme="smoothness"/&amp;gt;
7. add this line to the content section of an index page:&lt;br /&gt;
&amp;lt;jqi-grid id="jqgrid"/&amp;gt;
8. restart your server




</content>
    <updated>2009-07-14 20:47:23 UTC</updated>
    <author>
      <name>brett</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/30</id>
    <published>2009-07-06T23:02:48Z</published>
    <updated>2009-07-07T17:00:54Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/30-adding-ferret-search-to-a-hobo" rel="alternate"/>
    <title>Adding ferret search to a hobo application</title>
    <content type="html"># Prerequisites #

First, you need the ferret gem.

&lt;code&gt;$ sudo gem install ferret&lt;/code&gt;

Next, let's create a hobo app to use with ferret.

&lt;code&gt;$ hobo ferret_app&lt;/code&gt;

Now, we need the &lt;code&gt;acts\_as\_ferret&lt;/code&gt; plugin.

&lt;code&gt;$ ruby script/plugin install git://github.com/jkraemer/acts\_as\_ferret.git&lt;/code&gt;

# Setup the model #

Let's make a model that will have a ferret index on it.

&lt;code&gt;$ ruby script/generate hobo\_model\_resource Post title:string body:text&lt;/code&gt;

Now we'll tell ferret we want to have an index on the model and what fields we want indexed.

&lt;code&gt;ferret_app/app/models/post.rb&lt;/code&gt;

    class Post &amp;lt; ActiveRecord::Base
    
      hobo_model # Don't put anything above this  
    
      fields do
        title :string
        body  :text
        timestamps
      end
    
    +  acts_as_ferret :fields =&gt; [:name, :body]
    +  
    +  def name
    +    title
    +  end
    
      # --- Permissions --- #
      
      def create_permitted?
        acting_user.administrator?
      end
      
      def update_permitted?
        acting_user.administrator?
      end
      
      def destroy_permitted?
        acting_user.administrator?
      end
      
      def view_permitted?(field)
        true
      end
    
    end

Note: the only reason I used a method for name was to indicate that ferret can index on more than just columns.  You can index any string a method returns so you can do indexes across complex joins or filtered text etc.

Now let's have hobo generate the migration and start up the server.

    $ ruby script/generate hobo_migration
    ...
    $ ruby script/server

Go ahead and create a user and add some sample posts.  Remember, you'll need unique content for at least 1 or 2 of them as we'll be testing out a search function.

# Test Searching #

Start up a console and run a couple of queries.

    $ ruby script/console
    Loading development environment (Rails 2.0.2)
    &gt;&gt; Post.find_with_ferret('test')
    =&gt; [#]
    &gt;&gt; Post.find_with_ferret 'stranger'
    =&gt; [#]

Ferret sorts it's results by relevance according to your search term.  You can do things like set weights on particular fields to make say, posts that match title come up sooner than posts that just match in the body.

# Pagination of Search Results #

So, now we can get results but how do we get them paginated?  This is likely to be a concern as if you're using ferret you most likely have more than just 10 rows (the default limit) in your table.

To make pagination easier, we'll extend the ActsAsFerret module with a paginated search method.  Create the following in a file:

&lt;code&gt;lib/ferret_pagination.rb&lt;/code&gt;

    module ActsAsFerret
      module ClassMethods
        def paginate_search(query, options = {}, find_options = {})
          page     = options[:page] || 1
          per_page = options[:per_page] || 10
          total    = options[:total_entries]
          
          pager = WillPaginate::Collection.new(page, per_page, total)
          options.merge!(:offset =&gt; pager.offset, :limit =&gt; per_page)
          result = find_with_ferret(query, options, find_options)
          returning WillPaginate::Collection.new(page, per_page, result.total_hits) do |p|
            p.replace result
          end
        end
      end
    end

This is modified from a post describing how to do pagination with ferret to work properly with hobo and WillPaginate.

Note: this might not be necessary given the new find\_with\_ferret method that returns results similarly to normal finder methods.

Add a line to your environment.rb file to load in the new file.

&lt;code&gt;config/environment.rb&lt;/code&gt;
    ...
    +require 'ferret_pagination.rb'

Remember, you'll need to restart the server to reload the environment for this to take effect.

# Setting up the controller #

We'll go ahead and modify the posts index method to use our new ferret pagination method.

&lt;code&gt;app/controllers/posts_controller.rb&lt;/code&gt;

    def index
      # return everything if no search parameter
      query = params[:search].blank? ? '*' : params[:search]
      @posts = Post.paginate_search(
            query, 
            {:page =&gt; (params[:page] or 1), 
            :per_page =&gt; 10 } )  # you can also past an additional hash with finder options if needed
      @posts.member_class = Post # allows hobo index page to work as is
    end

If there is no search parameter supplied, it instead uses * to grab everything.

Test this out by adding ?search=searchterm in your address bar.  You should see only items that contain matches to your term come back.

# Finishing Up #

Last step is to add a search field to your index page for easy searching.

Looking up the index-page for Post in app/views/taglibs/auto/pages.dryml show this:

    
      
        
        
        
          
            &lt;h2&gt;Posts&lt;/h2&gt;
    
            &lt;p&gt;There &lt;/p&gt;
          
          
          
    
            &lt;a /&gt;      
    
            
          
            
          
            
          
        
      
    

Adding our search box after the content-header looks like a decent approach.

&lt;code&gt;app/views/posts/index.dryml&lt;/code&gt;

    
      
        
          &lt;label&gt;Search Posts:&lt;/label&gt;
        
      
    

You can also use the search field from a table plus, however sorting either needs to be dropped or redone because ferret sorts on relevance.

# Further Steps #

Obviously, the search form could stand to be integrated better style-wise with the rest of the app.  So adding some css styles to clean that up would be in order.

Ferret has advanced query features, like fuzzy searches, phrase search, field weighting, searching specific fields etc.  It might be useful to have a popup with information on advanced search syntax and/or adjust the weights of each field in the index.  Also, adding the created_at/updated_at fields might be useful as you can search over time ranges as well.  Updating the form or adding an advanced search form that helps build these extended searches could be useful.  Check this page for some more information http://www.railsenvy.com/2007/2/19/acts-as-ferret-tutorial (while it refers to an older version, other than the model calls it is mostly correct) or the project wiki which is up to date but a bit harder to follow http://rm.jkraemer.net/wiki/aaf.

As you develop your models, remember that ferret keeps the index up to date by using the save/destroy hooks.  So, using any ActiveRecord methods which make changes without instantiating the model will cause the index to lose sync with what's in the database.  This can make for a very aggravating bug to track down.  So avoid ActiveRecord::Base.update and delete in ferret indexed models.

Last but probably most important is this... if you're going to have multiple server or even a multi-threaded server hosting your app, you'll want to setup a ferret server.  This hosts your index and handles it with transactions to keep your index current.</content>
    <updated>2009-07-07 17:00:54 UTC</updated>
    <author>
      <name>kevinpfromnm</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/29</id>
    <published>2009-06-18T22:35:55Z</published>
    <updated>2009-06-19T00:43:27Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/29-national-characters-to-ascii-letters-for" rel="alternate"/>
    <title>National characters to ascii letters for user-friendly URLs</title>
    <content type="html">Simple way to have nice URLs:
1. use this lib:  
2. in your Model add 'to\_param' method that looks something like this:

example:

    def to_param
      "#{self.id}-#{self.name.to_textual_id}"
    end

I've made few changes to that lib. My version looks like this:



</content>
    <updated>2009-06-19 00:43:27 UTC</updated>
    <author>
      <name>Adam Hoscilo</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/27</id>
    <published>2009-04-24T12:42:06Z</published>
    <updated>2009-06-03T12:25:57Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/27-how-to-switch-from-the-gem" rel="alternate"/>
    <title>How to switch from the gem to edge as a plugin</title>
    <content type="html">If you're currently running the Hobo gem, and wish to run Hobo as a plugin, here are the steps you should follow.

If you're using git:

    git submodule add git://github.com/tablatom/hobo.git vendor/plugins/hobo

otherwise:

    ./script/plugin install git://github.com/tablatom/hobo.git

Now edit `config/environment.rb` to remove the `config.gem 'hobo'` line.  Edit `Rakefile` to remove the line `require 'hobo/tasks/rails'` if it exists.

Before running the next step, I suggest that you check your project in to change control.

    rake hobo:run_standard_generators

You can now enter 'd' on any conflicts to determine whether you have any changes that will be overwritten, and then enter 'y' or 'n' as appropriate.

Alternatively, if you checked your project into change control as I suggested, answer 'y' to all of the questions.  OK, that's a little extreme -- you can answer 'n' to any of the `application.*` files.  Now use your change control system to determine if any of the changes you had previously made have been overwritten.  Files with changes that may have been overwritten include `user.rb`, `users_controller.rb` and `environment.rb`.</content>
    <updated>2009-06-03 12:25:57 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/25</id>
    <published>2009-03-09T18:07:09Z</published>
    <updated>2010-02-01T02:11:53Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/25-generating-dryml-from-the-command-line" rel="alternate"/>
    <title>Generating DRYML from the command line</title>
    <content type="html">This recipe is obsolete, there's now a much easier way.   See [this hobo-users post](http://groups.google.com/group/hobousers/browse_thread/thread/9501d496add0ad52).


 
After using DRYML for a while, you may want to generate HTML for use outside of rails.  Here's how I did it.  Note that both of these files live inside a working Hobo skeleton.

lib/tasks/render\_test.rake:

    desc "render test.dryml"
    task :render_test =&gt; ["#{RAILS_ROOT}/app/views/taglibs/test.dryml", :environment] do |t|
      src = open(t.prerequisites.first).read
      locals = []
      imports = []
      renderer_class = Hobo::Dryml.make_renderer_class(src, File.dirname(t.prerequisites.first), locals, imports)
      assigns = {}
      view = ActionView::Base.new(ActionController::Base.view_paths, assigns)  
      view.extend(ActionView::Helpers::TagHelper)
      view.extend(Hobo::HoboHelper)
      view.extend(Hobo::RapidHelper)
      renderer = renderer_class.new(File.basename(t.prerequisites.first, ".dryml"), view)
      page_this = nil
      page_locals = []
      puts renderer.render_page(page_this, page_locals).strip
    end

app/views/taglibs/test.dryml:

    
    

    
      &lt;p&gt;Hello!&lt;/p&gt;
    

    
      
    

and then to get the html:

    rake render_test

Note: Most tags work, but the `` tag does not yet -- the functions needed are provided by renderer_class but aren't being found.  If you can help, please comment!</content>
    <updated>2010-02-01 02:11:53 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/23</id>
    <published>2009-03-01T23:20:15Z</published>
    <updated>2009-03-16T14:00:36Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/23-using-single-table-inheritance-sti-models" rel="alternate"/>
    <title>Using Single Table Inheritance (STI) models with Hobo</title>
    <content type="html">Here's what I needed to do to get STI (Single Table Inheritance) working.  It's fairly straightforward, but there are a couple of gotchas.  Thus, this recipe.

The first gotcha is that you have to have your base table fully generated and migrated before you generate the child model.  See [Bug 345](http://hobo.lighthouseapp.com/projects/8324/tickets/345-inheritance-is-broken-again).

In my case:

    script/generate hobo_model_resource DownloadedFile filename:string contents:string
    script/generate hobo_migrations

I chose the "m" option for hobo\_migrations, so the database was migrated.  Then once I had the base class working:

    script/generate hobo_model_resource BatchAcknowledge

I then edited *downloaded\_file.rb* and added `sti_type :string` to the `fields` block and added `set\_inheritance\_column :sti_type` below the `fields` block.

I then edited *batch\_acknowledge.rb* to change its parent from `ActiveRecord::Base` to `DownloadedFile`.  I then removed everything from the file, included the fields definition and the permissions checks -- it gets those from the base class.

    script/generate hobo_migrations

If you want to add additional fields to your sub-models, you have to add them to your base model.  This is how ActiveRecord Single Table Inheritance work.  However, validations and association definitions may be added to the child model.  In my case, I added a field *submission\_id* to the base class, and added:

    belongs_to :submission
    validates_existence_of :submission

to the child class.

Now you should have working inherited models.  You'll notice that forms and pages will be generated for *BatchAcknowledge*.   Cards are not, but that's fairly irrelevant, because the *DownloadedFile* card will display a *BatchAcknowledge* record, and the auto generator would have generated the two cards identically anyways.

In another small gotcha, the name may not propogate correctly for you:  see [Bug 387](http://hobo.lighthouseapp.com/projects/8324/tickets/387-inheritance-sti-models-name-not-propagating#ticket-387-1)

A larger gotcha is that problems have been reported if you drop the database and try to run hobo_migrations:  [Bug 397](http://hobo.lighthouseapp.com/projects/8324-hobo/tickets/397-sti-problems-running-migration-on-dropped-database)</content>
    <updated>2009-03-16 14:00:36 UTC</updated>
    <author>
      <name>Bryan Larsen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/22</id>
    <published>2009-01-18T20:13:02Z</published>
    <updated>2009-01-18T20:13:02Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/22-ria-s-fit-clients-and-hobo" rel="alternate"/>
    <title>RIA's, "Fit Clients", and hobo?</title>
    <content type="html">

Hello there.  I posted a question awhile ago regarding making Rich Internet Applications (RIA's) with Hobo and AJAX.  I was able to get some nice features working with iFRAMEs and a few tricks.  But I think this article below speaks to the complexity and hype of AJAX-ified Desktops.
 
http://ruby.sys-con.com/node/510792

Im curious if anyone has looked at Adobe AIR or Flex for RIAs with hobo?  

Also, has anyone used this framework?
http://ruboss.com/framework/

Thanks!
Bean</content>
    <updated>2009-01-18 20:13:02 UTC</updated>
    <author>
      <name>Bean</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/20</id>
    <published>2008-12-31T15:29:24Z</published>
    <updated>2009-01-02T22:59:12Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/20-auto-complete-for-agility-project-with" rel="alternate"/>
    <title>Auto Complete for Agility Project with Hobo 0.8.5 (It works)</title>
    <content type="html">First, I'm going by: http://cookbook.hobocentral.net/tutorials/agility (12/31/08).  The tutorial on http://hobocentral.net/ needs to be updated.


#How to get Auto-Complete to work:#

Go down to Section 'A form with auto-completion'.  First change "`hobo_completions :username, User.without_joined...`" to "`hobo_completions :name, User.without_joined...`".  This change ends up making auto complete bring up results.  However, once a user is selected an error pops-up.  To fix this, go to '`project_membership.rb`' and add ':accessible =&gt; true' to both 'belongs to' variables.  That should fix Auto-Complete.


#Other Fixes:#

In section 'Part 6 – Project Ownership', when you get down to restricting owner for creation permissions.  In `def create_permitted?`, `owner == acting_user` should be replaced with `owner_is? acting_user`.  Otherwise, no one will be able to create Projects because no one owns a project that hasn't been created yet.

In section 'Part 7 – Granting read access to others', right before 'The view layer' the tutorial should tell the user to run migration.  I understand that it's not hard to see that you need to migrate before you can view anything, but this is a tutorial and step-by-step instructions are nice.

In the next section 'The view layer', there is a slight problem with ``.  When memberships were created, Rapid instantly edited the Project page to have memberships and stories listed.  That means that it edited the page to have an `` section.  The tutorial doesn't take that into consideration and asks the user to modify the entire content.  It's good that the user gets a chance to edit this content, but the user will find it weird when they see two side panels in the end.  CHANGE this section so that the user just replaces the `` that is already on the rapid page.  My code is '`&lt;h3&gt;Project Members&lt;/h3&gt;...`'.

Thank you,
Sean</content>
    <updated>2009-01-02 22:59:12 UTC</updated>
    <author>
      <name>SeanMac</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/19</id>
    <published>2008-11-10T15:23:59Z</published>
    <updated>2009-01-26T16:57:57Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/19-deploy-a-hobo-app-on-heroku" rel="alternate"/>
    <title>Deploy a Hobo app on Heroku</title>
    <content type="html">This recipe will take you through the process of getting a Hobo app deployed on [Heroku Garden](http://herokugarden.com). We'll work through creating a brand new app and getting it deployed, and then discuss how you would use the same approach with an existing app.

**Prerequisites:** you will need git and the herokugarden gem (`gem install herokugarden`)

# Deploying a new Hobo app with Heroku Garden

**1. Log in to Heroku Garden and create the new app.** Rather annoyingly, Heroku doesn't prompt you for the app name, so we'll start by renaming the app. Click on your email address down in the Heroku bar on the right. That will take you back to "My Apps". Then click "Settings" for the new app, and you'll be able to rename it. Keep the browser open on that page.

For the recipe we'll assume you've called it `hoboapp`, but remember that all Heroku apps live in one global namespace, so you'll need to use your own name. Use your name wherever you see `hoboapp`.

**2. Grab a local clone of the app**:

    $ herokugarden clone hoboapp
    $ cd hoboapp

**3. Switch to Rails 2.2.2.** By default a new Heroku Garden app will be configured to use Rails 2.1, but Hobo needs Rails 2.2.2. Edit `config/environment.rb` and change the `` to:

    RAILS_GEM_VERSION = '2.2.2'

(Should be the first line of the file).

**4. Add Hobo as a git submodule.** This adds an extra step to making things work with Heroku, but Hobo is a moving target and being able to keep up to date with changes is critical. Having Hobo as a submodule makes this much easier.
    
    $ git submodule add git://github.com/tablatom/hobo.git vendor/plugins/hobo
    
When we push our changes, Heroku will not grab the Submodule for us. The easiest way to fix that is to write a simple Rake task that will update any submodules in the app.

**5. Create a rake task to do the submodule update.**. Create a file `lib/tasks/git_submodules.rake` containing the following: 

    task :git_submodules do
      puts `git submodule init 2&gt;&amp;1`
      puts `git submodule update 2&gt;&amp;1`
    end
    
**6. "Hoboize" the blank Rails app.** We'll run the standard Hobo generators:

    ruby script/generate hobo --add-routes
    ruby script/generate hobo_rapid --import-tags
    ruby script/generate hobo_user_model user
    ruby script/generate hobo_user_controller user
    ruby script/generate hobo_front_controller front --delete-index --add-routes

**7. Run the migration generator** to create the initial migration, which will just create the `users` table.

    ruby script/generate hobo_migration
    
(Chose the 'g' or 'm' option)

**8. Commit the code into the repo**:

    $ git add .
    $ git commit -am "Hoboize the app"
    
**9. push your code up to Heroku**. Note that you will get an error about the Hobo gem not being available. That's OK.

    $ git push
    
**10. Run the rake task that we defined above.** The heroku gem allows us to run remote rake tasks:

    $ herokugarden rake hoboapp git_submodules
    
It may take a while, but you should eventually see the normal output from the `git submodule init` and `git submodule update` commands.
    
**11. Run migrations.** We can use the `herokugarden` remote rake tasks again:

    herokugarden rake hoboapp db:migrate

**12. run Hobo's taglib generators.** Return to your terminal and run:

    $ herokugarden rake hoboapp hobo:generate_taglibs
    
**13. Install `will_paginate`.** In the file browser on Heroku's edit page, expand the 'vendor' folder and click "Gems and plugins". Select "Available" instead of "Installed" from the menu, and search for `will_paginate`. I found that installing the gem version did not work, so install the plugin version.
    
You should now be able to click the "view" link in the top right of the Heroku editor and see your deployed app up and running.

# Deploying an existing app.

As far as I know, Heroku want to host the git repo for you, so again we'll need to create a blank Heroku app. Perform steps 1 to 4 exactly as above.

Steps 6 and 7 should not be necessary if we already have a running app. We want instead to copy our existing code into this repo, overwriting the files that are already there, but make sure you don't overwrite the `.git` directory. Also, if your app already has Hobo in `vendor/plugins`, don't copy that over, as you've just created a submodule for Hobo.

Having copied the files in we want to commit them to git, push them up to Heroku and so on. In other words, perform steps 7 to 13 exactly as above.</content>
    <updated>2009-01-26 16:57:57 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/17</id>
    <published>2008-11-04T17:06:03Z</published>
    <updated>2008-11-04T17:06:21Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/17-add-a-footer-to-every-page" rel="alternate"/>
    <title>Add a footer to every page</title>
    <content type="html">Nice easy one here. In application.dryml:

    
      
        
          ... your custom footer here ...
        
      
    
{: .dryml}

Note that cusomtising the `` tag like this is common for all sorts of reaons. If you already have an `` in application.dryml, you probably want to just add that footer parameter to it, rather than extending page again (although that is allowed).</content>
    <updated>2008-11-04 17:06:21 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/16</id>
    <published>2008-11-03T13:53:32Z</published>
    <updated>2009-09-20T14:30:07Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/16-uploading-for-hobo-app" rel="alternate"/>
    <title>Uploading for Hobo app</title>
    <content type="html">What if you want to upload a file to your app?  You can use the  &lt;a href="http://github.com/technoweenie/attachment_fu/tree/master"&gt;attachment_fu&lt;/a&gt; plugin.

The model code needs filename and size and perhaps other fields depending on your implementation, surf over to &lt;a href="http://svn.techno-weenie.net/projects/plugins/attachment_fu/README"&gt;techno-weenie&lt;/a&gt; for info on what attachment\_fu supports.  This example assumes the model is named ImportProject.

    fields do
        content_type :string
        filename     :string    
        size         :integer
        timestamps
      end
  
      has_attachment :storage =&gt; :file_system
               , :max_size =&gt; 100.megabytes
               , :path_prefix =&gt; "public/import_projects"
      validates_as_attachment

To get the plugin from the git repository change directory over to the plugins directory clone from git.

git clone git://github.com/technoweenie/attachment\_fu.git


You may need this patch:

create a file appname/lib/attachment\_fu\_patch.rb

    require 'tempfile'
    
    class Tempfile
      def size
        if @tmpfile
          @tmpfile.fsync # added this line
          @tmpfile.flush
          @tmpfile.stat.size
        else
          0
        end
      end
    end

And this needs to be added to: appname/config/environment.rb

    require 'lib/attachment_fu_patch.rb'

environment.rb

    # Be sure to restart your server when you modify this file

    # Uncomment below to force Rails into production mode when
    # you don't control web/app server and can't set it the proper way
    # ENV['RAILS_ENV'] ||= 'production'
    
    # Specifies gem version of Rails to use when vendor/rails is not present
    RAILS_GEM_VERSION = '2.1.0' unless defined? RAILS_GEM_VERSION
    
    # Bootstrap the Rails environment, frameworks, and default configuration
    require File.join(File.dirname(__FILE__), 'boot')
    
    require 'lib/attachment_fu_patch.rb'
    
    Rails::Initializer.run do |config|
    ...

How about some view code? appname/app/views/import\_project/new.dryml

    
    
    
      
        &lt;label&gt;Choose File&lt;/label&gt;
        
        
      
    

When you create a new import project you should have an upload button to select the file for uploading.
</content>
    <updated>2009-09-20 14:30:07 UTC</updated>
    <author>
      <name>brett</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/15</id>
    <published>2008-10-29T18:24:04Z</published>
    <updated>2008-10-31T14:49:26Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/15-dynamically-populated-select-menus" rel="alternate"/>
    <title>Dynamically populated &lt;select&gt; menus</title>
    <content type="html">(Note: this recipe requires Edge Hobo as of 29 Oct '08)

# Get the code

[The code for this recipe is available on github](http://github.com/tablatom/hoborecipes/tree/master/dynamic_menus)

(don't forget to `git submodule update` to get Hobo)

# Introduction

A common requirement in user-interfaces is to have the options available in one menu depend on the selection in another menu. For example, before you can select a city you have to select a country; on selecting the country the city menu is populated with cities from that country. This recipe shows how to accomplish exatly that using Hobo's ajax mechanism and a little custom JavaScript.

This is a "from scratch" recipe - we build an app from scratch that has this feature.

# Create the app

Get started as normal:

    $ hobo dynamic_menus
    
Then add some models. We'll have a project that belongs to both a city and a country:

    $ ./script/generate hobo_model_controller project name:string
    $ ./script/generate hobo_model country name:string
    $ ./script/generate hobo_model city name:string
    
Now define the relationships as follows:
    
    class Project
      belongs_to :country
      belongs_to :city
    end
    
    class City
      belongs_to :country
    end
    
    class Country 
      has_many :cities
    end
{: .ruby}
    
Create the initial database    
    
    ./script/generate hobo_migration
    
# Add some data

Let's use a migration to add some cities and contries
    
    ./script/generate migration populate_countries_and_cities
    
Here's the up migration (there's no need for a down in this case, since the schema doesn't change)
    
    def self.up
      countries = { "UK"     =&gt; %w(London Birmingham Manchester Sheffield Bristol), 
                    "USA"    =&gt; ['Washington D.C.', 'New York', 'Los Angeles', 'San Fransisco'],
                    "France" =&gt; %w(Paris Nice Marseille Lyon Toulouse)
                  }

      countries.each_pair do |country, cities|
        c = Country.create :name =&gt; country
        cities.each { |city| c.cities.create :name =&gt; city  }
      end
    end
{: .ruby}

Don't forget to

    $ rake db:migrate
    
OK - fire her up. Once you've signed up you'll see you have a regular "New Project" page, but *all* of the cities are in the cities menu. Not what we want.

# Customise the Project form

Here's a custom version of the project form that populates the city menu according to the projects country:

    
      
        
          
            
              
            
            
              &lt;select&gt;&lt;option&gt;First choose a country&lt;/option&gt;&lt;/select&gt;
            
          
        
      
    
{: .dryml}
    
Try adding that to application.dryml and then create a project. The new project wont have a country, so the city menu will be disabled. But if you chose a country, create the project and then go to edit it, you should see the cities from that country in the city menu. We're making progress.

# Add the ajax magic

We now need to do three things to add the ajax behaviour and tie it all together:

 - Put the city menu in an ajax 'part' so that it can be updated dynamically

 - Add some JavaScript to application.js to send to the server (via an ajax call) a change to the country whenever the menu changes

 - Customize the `new` and `edit` actions in `ProjectsController` so that they can update the project from parameters (i.e. the parameters sent by the ajax call)
 

## Put the city menu in a part

Add a `` to the cusomtised form (`` is just a do nothin tag that can be used to add parts or params without changing the markup)
 
    
      
        
          
            
              
                
              
              
                &lt;select&gt;&lt;option&gt;First choose a country&lt;/option&gt;&lt;/select&gt;
              
            
          
        
      
        
{: .dryml}

## Add the JavaScript

We'll use JavaScript to notify the server when the country is changed. Rather than clutter up the page we'll do this 'unobtrusively' by adding a call to `Event.addBehavior` to our application.js:

    Event.addBehavior({

        "form.project select.project-country:change": function(ev) {
            Hobo.ajaxRequest(window.location.href, ['city-menu'], 
                             { params: Form.serializeElements([this]), method: 'get',
                               spinnerNextTo: this, message: ""} )
        }

    })
    
Let's go through we did there:

  - Added an `onchange` handler to our select. We used Hobo's default CSS class names to target the right `&lt;select&gt;`
    
  - Used `Hobo.ajaxRequest` to send a request to the server along with the necessary parameters to trigger the part update:
      - We used `window.location.href` to send the request to the same URL where the form lives
      - The second parameter is an array of part-names that we want updated
      - We used `Form.serializeElements([this])` (from Prototype) to get the required parameter to update the model's country (in an addBehavior callback, `this` is the element that generated the event)
      
And finally:

## Customise the controller

The `new` and `edit` actions in `ProjectsController` need to update the project with the change (if present) that the ajax call sent up. We also need to tell them to perform an ajax part update if the request is from javascript (`request.xhr?`).

Add these methods to `ProjectsConrtoller`:

    def new
      hobo_new do
        this.attributes = params[:project] || {}
        hobo_ajax_response if request.xhr?
      end
    end

    def edit
      hobo_show do
        this.attributes = params[:project] || {}
        hobo_ajax_response if request.xhr?
      end
    end
    
It should now all be working :o)


</content>
    <updated>2008-10-31 14:49:26 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/12</id>
    <published>2008-10-28T15:58:40Z</published>
    <updated>2008-10-28T16:07:13Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/12-change-the-way-non-editable-fields" rel="alternate"/>
    <title>Change the way non-editable fields are handled in forms</title>
    <content type="html">(Note: this recipe is about the `no-edit` attribute which was added to Edge Hobo on Oct 28th '08 in commit 8b197a3)

Hobo's default edit and new pages use the `` tag to render the form fields. By default `` skips any fields for which the current user does not have edit permission. This is normally what you want.

Sometimes it's not what you want. Sometimes you would rather have the current value of the field displayed. Sometimes you want the form fields to appear, but disabled (read-only). As of the commit mentioned above, those two requirements are very easily satisfied. If you want something different, of course you can still achieve it. You may have to resort to marking up the form field by field, instead of using `` (nothing wrong with that -- never let the convenience of Rapid become a prison!).

# The `no-edit` attribute

The `no-edit` attribute tells `` what to do if the current user doesn't have edit permission. There are four options:

 - view: render the current value using the `` tag
 - disable: render the input as normal, but add HTML's `disabled` attribute
 - skip: render nothing at all
 - ignore: render the input normally. That is, don't even perform the edit check.
 
The default is `view`
 
`` also takes the `no-edit` attribute. It simply forwards the value to each of the `` tags it renders, with one exception: `no-edit="skip"` will cause `` to skip both the field label *and* the `` (not just the input). This is the default.

# How to have disabled inputs on an automatic form

Using the `no-edit` attribute, here's how to customise a form, say for a `Product` model, so that non-editable fields are still present in the form, but the input fields are all disabled:

    
      
    
{: .dryml}
  </content>
    <updated>2008-10-28 16:07:13 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/11</id>
    <published>2008-10-28T14:33:46Z</published>
    <updated>2008-11-18T09:32:26Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/11-replicate-the-look-and-feel-of" rel="alternate"/>
    <title>Replicate the look-and-feel of an existing site</title>
    <content type="html">Say we want a new Hobo app to have the same look-and-feel of an existing site. The really big win is if we can have this look and feel happen to our new app almost 'automatically'. We want to be able to develop at "Hobo speed", and have the look and feel "just happen". This is not trivial to set up, but once it is the pay-back in terms of development agility will be more than worth it. That is the topic of this how-to.

Given that our current sponsors - Barquin International - find themselves in this exact situation, we'll base this recipe on a look-and-feel that they frequently have to provide. One example is the CSREES website ([www.csrees.usda.gov](http://www.csrees.usda.gov/)):

  ![CSREES Home page][5]
  
Note that, for now at least, this recipe will document how to create a *close approximation* to this theme. In particular, we're going to skip some of the details that cannot be implemented without resorting to images. This is just to keep the from recipe getting to long and complicated.

## Introduction
  
This will be as much a guide to general web-development best-practice, as it will be a lesson in Hobo and DRYML. The mantra when working with themes in Hobo is something already familiar to skilled web developers:

&gt; Separate content from presentation

The vast majority of common mistakes that are made in styling a web-app come under this heading. If this one idea can be understood and applied, you're well on the way to:

 - Having the look-and-feel "just happen" as your site changes and evolves
 
 - Being able to change the theme in the future, without having to modify the app
 
Since CSS has been widely adopted, most web developers are familiar with this principle. So this is probably just a recap, but to remind ourselves how this works:

 - "Content" describes *what is on the page*, but not *what it will look like*. In a Hobo app content comes from tag definitions, page templates and the applications data of course.
 
 -"Presentation" describes *how the page should look*. That is, it describes fonts, colours, margins, borders, images and so on. In a Hobo app the presentation is handled essentially the same way as with any app. With CSS stylesheets and image assets.
 
Having said that, we need to inject a note a pragmatism:

 - Humans being visual animals, information can never truly be separated from the way it is displayed. The line is sometimes blurred and there are often judgement calls to be made. 
 
 - The technologies we've got to work with, in particular cross-browser support for CSS, are far from perfect. Sometimes we have to compromise.
 
There's probably an entire PhD thesis lurking in that first point, but let's move on!


## The current site

We'll start with a look at the elements of the existing site that we'll need to replicate. The main ones are:

A banner image:

![][6]

The main nav bar:

![][7]

A couple of styles of navigation panels:

![][8] ![][9]

And more navigation in the page footer

![][12]

One of the important things to notice at this stage, is that this is *not* just a "theme" in the Hobo sense of the word. Hobo themes are purely about presentation, whereas the "look and feel" of this site is a mixture of content elements and presentation.

That means we're going to be creating three things to capture this look-and-feel: tags definition, a CSS stylesheet, and some image assets.


## The current markup

The existing site makes extensive use of HTML tables for layout, and the various images in the page are present in the markup as `&lt;img&gt;` tags. In other words, the existing markup is very *presentational*. So rather than create tag definitions out of the existing markup, we'll be recreating the site using clean, semantic markup and CSS.

The other advantage of re-creating the markup is that it will be easier to follow Hobo conventions. There's no particular need to do this, but it makes it a great deal easier to jump from one Hobo app to the next.


## Building the new app

Let's do this properly and actually follow along in a blank Hobo app. At the end of the recipe we'll see how we could package this look-and-feel up and re-use it another app. To follow along, you should use Firefox and the Firebug extension.

    $ hobo csreesdemo
    $ cd csreesdemo
    $ ruby script/generate hobo_migration

If you fire up the server, you'll see the default Hobo app of course:

![][14]

Now we can start to make it look like the page we're after. We'll take it step by step.


### Main background and width

My trusty TextMate colour picker tells me that the CSREES background colour is #A8ACB6. Firebug tells me (click the inspect button, then click on the background) that the CSS rule that sets the current background comes from `clean.css` and looks like:

    html, body { background:#193440 }
    
So I'm going to add this rule to `public/stylesheets/application.css`:

  html, body { background:#A8ACB6 }
  
Again, using Firebug (by clicking on the `` tag in the HTML window) I can see that the width is set on the body tag. 
  
    body { ... width: 960px; ... }
    
Back in CSREES, I can right click the banner image and chose "View Image", and Firefox tells me it's width is 766 pixels. So in `application.css` I add

    body { width: 766px; }
    
Note we've not changed any markup yet - that's how we like it.

    
### Account navigation

These are the log-in and sign-up links in the top right. They are not on the CSREES site, but if the app needed them, the place they are in now would be fine, so we'll leave them where they are.


### Search 

The page header has a search-field which we don't want. To get rid of this we'll customise the `` tag. This will then become the place where we make various changes to ``:
  
    
      
      
    
{.dryml}

So now we *have* made a change to the markup, but that makes perfect sense, because here we wanted to change *what's on the page* not *what stuff looks like*.


### The Banner
    
Again, using Firefox's "View Image", it turns out that the existing banner is in fact two images. This one:

![][15]

And this one:

![][16]

Too add these images without changing the markup, we need to use CSS's background-image feature. One major limitation of CSS is that you can only have one background image per element. That won't be a problem, but to understand our approach, first take a look at a simplified view of the page markup that we're working with:

    
      ...
      
        &lt;div class="page-header"&gt;
          &lt;h1 class="app-name"&gt;Csreesdemo&lt;/h1&gt;
        &lt;/div&gt;
        ...
      
    
{.dryml}

Notice that this image

![][15]

Is essentially a graphical version of that `&lt;h1&gt;` tag, so we'll use CSS to make that same `&lt;h1&gt;` be rendered as an image. The existing text will be hidden, by moving it way out of the way with a `text-indent` rule. First we need to save that image into our public/images folder. The CSS to add to `application.css` is:
  
    div.page-header { padding: 0; }

    div.page-header h1.app-name {
        text-indent: -10000px;
        background: url(../images/banner_csrees.gif) no-repeat;
        padding: 0; margin: 0;
        height: 62px;

    }

OK that was a bit of a leap. Why `padding: 0px` for the page-header, for example? The fact of the matter is, that working with CSS is all about trial and error. Using Firebug to figure out what rules are currently in effect, flipping back and forth between the stylesheet in your editor and the browser. Try experimenting by taking some of those rules out and you'll see why each is needed.

Now for the photo part of the banner. Again, save it to public/images, then add some extra properties to the `div.page-header` selector, so it ends up like:

    div.page-header {
        padding: 0; 
        background: url(../images/banner_photo.jpg) no-repeat 0px 62px;
        height: 106px;
    }
    
Taking shape now, except the main nav is splatted on top of the photos.

### Main Navigation

The existing navigation bar is created entirely with images. It's quite common to do this, as it gives total control over fonts, borders, and other visual effects such as colour gradients. The downside is that you have to fire up your image editor every time there's a change to the navigation. This doesn't sit very well with our goal to be able to make changes quickly and easily. So for this recipe we're going to go implement the nav-bar without resorting to images. We'll lose the bevel effect, but some might think the end result is actually better - cleaner, clearer and more professional looking. Bevelled edges are so 1998 :o)

Our app only has a home page right now, so first let's define a fake nav bar to work with. In `application.dryml`:

    
      
        Home
        About Us
        Grants
        Forms
        Newsroom
        Help
        Contact Us
        
    
{.dryml}

Use Firebug's "Inspect" button to find the nav-bar. You'll see that it's rendered as a `&lt;ul&gt;` list, which is generally considered good practice; it is a list of links after all. There's several things wrong with the appearance of the navigation at this point:
  
 - It's in the wrong place - we want to move it down and to the right.
 - Needs to be shorter, and the spacing of the items needs fixing
 - The font needs to be smaller, and not bold
 - The background colour needs to change, as do the colours when you mouse-over a link
 
Now this is not a CSS tutorial, so we're not going to explain every last detail, but we'll build it up in a few steps which will help to illustrate what does what. First update the rules for `div.page-header` so they look like:

    div.page-header {
        padding: 0; 
        background: white url(../images/banner_photo.jpg) no-repeat 0px 62px;
        height: 138px;
    }

And add:

    div.page-header .main-nav {
        position: absolute; bottom: 0; right: 0; 
    }
  
The nav-bar still looks wrong, but it's in the right place (well, nearly). We'll now fix the sizing and placement. Update the new rule (`div.page-header .main-nav`) and add two new ones, like this:

    div.page-header .main-nav {
        position: absolute; bottom: 0; right: 0; 
        height: 21px; width: 100%; line-height: 21px; padding: 0; 
        text-align: right; 
    }

    div.page-header .main-nav li {
        margin: 0; padding: 0 0 0 4px;
        display:inline; float:none;
    }

    div.page-header .main-nav li a {
        padding: 3px 8px; margin: 0;
        font-weight: normal; display:inline; font-size: 12px;
    }

And finally we'll add the colours. Just to avoid confusion, here's the full stylesheet so far:

    html, body { background:#A8ACB6 }
    body { width: 766px; }

    div.page-header {
        padding: 0; 
        background: white url(../images/banner_photo.jpg) no-repeat 0px 62px;
        height: 138px;
    }

    div.page-header h1.app-name {
        text-indent: -10000px;
        background: url(../images/banner_csrees.gif) no-repeat;
        padding: 0; margin: 0;
        height: 62px;
    }

    div.page-header .main-nav {
        position: absolute; bottom: 0; right: 0; 
        height: 21px; width: 100%; line-height: 21px; padding: 0; 
        text-align: right; 
        background: #313367; 
    }

    div.page-header .main-nav li {
        margin: 0; padding: 0 0 0 4px;
        display:inline; float:none;
        border-left: 1px dotted #eee; background: #313367;
    }

    div.page-header .navigation.main-nav li a {
        padding: 3px 8px; margin: 0;
        font-weight: normal; display:inline; font-size: 12px;
        background: transparent;
        color: #eee;
    }

    div.page-header .navigation.main-nav li a:hover {
        background: #A9BACF; color: black;
    }
    
Note that we had to make the last two selectors a bit more specific, in order to ensure that they take precedence over rules in the Clean theme.

The page header should be done at this point:

![][17]


### The sidebars

The existing site has both left and right sidebars. We'll add those now. The first step is to add the three content sections the `` tag. We've already extended ``, so modify the DRYML you already have to look like:
  
    
      
        
          
            
            
            
          
        
      
    
{.dryml}

We've replaced the existing `` with a `` that contains our two `` tags and the main ``.
  
To try this out, we'll insert some dummy content in `app/views/front/index.dryml`. Edit that file as follows:

    
      
      Aside 1
      Main content
      Aside 2
    
{.dryml}

You should see something like:

![][18]

Obviously we've got a bunch of styling to do. First though, let's add the content for the left sidebar. This is the "search and browse" panel, which is on every page of the site, so let's define it as a tag:

    
      &lt;div class="search-and-browse"&gt;
        &lt;div&gt;
          &lt;h3&gt;Search CSREES&lt;/h3&gt;
          
            
            
          
          &lt;p class="help"&gt;&lt;a href=""&gt;Search Help&lt;/a&gt;&lt;/p&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;h3&gt;Browse by Audience&lt;/h3&gt;
            
        &lt;/div&gt;
        &lt;div&gt;
          &lt;h3&gt;Browse by Subject&lt;/h3&gt;
          
            Agricultural &amp; Food Biosecurity
            Agricultural Systems
            Animals &amp; Animal Products
            Biotechnology &amp; Geneomics
            Economy &amp; Commerce
            Education
            Families, Youth &amp; Communities
          
        &lt;/div&gt;
      &lt;/div&gt;        
    
{.dryml}

A few points to note about that markup:

 - We've tried to maker the markup as "semantic" as possible -- it describes what the content *is*, not what it looks like.

 - We've added a few `param`s, so that individual pages can customise the search-and-browse panel. Each `param` also gives us a CSS class of the same name, so we can target those in our stylesheet.
 
 - We've used `` for the browse-by-subject links. This gives us the ability to highlight the current page as the user browses.

Because the search-and-browse panel appears on every page, lets call it from our master page tag (``). Change:
  
    
{.dryml}

To:

    
{.dryml}


Then remove the `Aside 1` parameter from `front/index.dryml`.

Now we need to style this panel. After a good deal of experimentation, we get to the following CSS:

    div.page-content, div.page-content .aside { background: white; }

    .aside1 { width: 173px; padding: 10px;}

    .search-and-browse {
        background: #A9BACF;
        border: 1px solid #313367;
        font-size: 11px;
        margin: 4px;
    }

    .search-and-browse h3 {
        background: #313367; color: white;
        margin: 0; padding: 3px 5px;
        font-weight: normal; font-size: 13px; 
    }

    .search-and-browse a { background: none; color: #000483;}

    .search-and-browse .navigation { list-style-type: circle; }
    .search-and-browse .navigation li { padding: 3px 0; font-size: 11px; line-height: 14px;}
    .search-and-browse .navigation li a { border:none;}

    .search-and-browse .search form { margin: 0 3px 3px 3px;}
    .search-and-browse .search p { margin: 3px;}
    .search-and-browse .search-field { width: 120px;}
    .search-and-browse .submit-button { padding: 2px;}

    .search-and-browse .browse-by-audience select { margin: 5px; 3px; width: 92%;}
    
With that added to `application.css` you should see:

![][19]

OK - let's switch to the right-hand sidebar.

If you click around [the site](http://www.csrees.usda.gov/) you'll see the right sidebar is always used for navigation panels, like this one:

![][9]

You'll also notice it's missing from some pages, which is as easy as:

    
{.dryml}

It seems like a good idea to define a tag that creates one of these panels, say:

    
      Quick Links
      
        A-Z Index
        Local Extension Office
        Jobs and Opportunities
      
    
{.dryml}  

We've re-used the `` tag as it gives us an `&lt;li&gt;` and an `&lt;a&gt;` which is just what we need here.
  
Now add the definition of `` to your `application.dryml`:

    
      &lt;div class="nav-panel"&gt;
        &lt;h3&gt;&lt;/h3&gt;
        &lt;div&gt;
          &lt;ul /&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    
{.dryml}

Notice that we defined two parameters for the body of the panel. Callers can either provide the `` parameter, in which case the `&lt;ul&gt;` wrapper is provided, or, in the situation where the body will not be a single `&lt;ul&gt;`, they can provide the `` parameter.

OK let's throw one of these things into our page. Here's what `front/index.dryml` needs to look like:

    
      
      Main content
      

        
          Grants
          
            National Research Initiative
            Small Business Innovation Research
            More...
          
        

        
          Quick Links
          
            A-Z Index
            Local Extension Office
            Jobs and Opportunities
          
        

      
    
{.dryml}


And here's the associated CSS -- add this to the end of your `application.css`:

    .aside2 { margin: 0; padding: 12px 10px; width: 182px;}
    .nav-panel {border: 1px solid #C9C9C9; margin-bottom: 10px;}
    .nav-panel h3 {background:#A9BACF; color: #313131; font-size: 13px; padding: 3px 8px; margin: 0;}
    .nav-panel .body {background: #DAE4ED; color: #00059A; padding: 5px;}
    .nav-panel .body a {color: #00059A; background: none;}
    .nav-panel ul {list-style-type: circle;}
    .nav-panel ul li { margin: 5px 0 5px 20px;}
    
### Main content

The main content varies a lot from page to page, so let's just make sure that the margins are OK, and leave it at that. First we need some content to work with, so in `front/index.dryml`, replace:

    Main content
{.dryml}

With:

    
      &lt;h2&gt;Cooperative State Research, Education and Extension Service&lt;/h2&gt;
      &lt;p&gt;Main content goes here...&lt;/p&gt;
    
{.dryml}  

On refreshing the browser it seems there's nothing else to do. That looks fine.

### The footer

The footer is the same throughout the site. Let's define it as a tag and add it to our main `` tag. Here's the definition for `application.dryml`:
  
    
      &lt;div class="footer-nav"&gt;
        &lt;ul&gt;
          CSREES
          USDA.gov
          Site Map
          Policies and Links
          Grants.gov
        &lt;/ul&gt;
      &lt;/div&gt;
    
{.dryml}

And add this parameter to the ``:
  
    
  
And finally, the CSS. To get the corner graphic that we've used here, you need to right-click and "Save Image As" on the bottom left corner in the existing site:

    .page-footer {
        background: white url(../images/footer_corner_left.gif) no-repeat bottom left;
        overflow: hidden; height: 100%;
        border-top: 1px solid #B8B8B8;
        font-size: 10px; line-height: 10px;
        padding: 5px 0 15px 20px;
    }

    .page-footer ul { list-style-type: none; }
    .page-footer ul li { float: left; border-right: 1px solid #2A049A; margin: 0; padding: 0 5px;}
    .page-footer ul li a {border:none; color: #2A049A;}
    
There's one CSS trick in there that is work a mention. In the `.page-footer` section, we've specified:
  
    overflow: hidden; height: 100%;

This is the famous "self clearing" trick. Because all the content in the footer is floated, without this trick the footer looses its height. 

That pretty much brings us to the end of the work of reproducing the look and feel. We should now be able to build out our application, and it will look right "automatically". In practice you always run into small problems here and there and need to dive back into CSS to tweak things, but the bulk of the job is done.

The next question is - how could we make several apps look like this without repeating all this code?


# A look-and-feel plugin

To re-use this work across many apps, we'll use the standard Rails technique - create a plugin. The plugin will contain:

 - A DRYML taglib with all of our tag definitions
 
 - A `public` directory, containing our images and stylesheets


## Creating the plugin
 
Somehow the idea of "creating a plugin" seems like a big deal, but it's there's really nothing to it. Pretty much all we're going to do is move a few files into different places. 

    $ mkdir vendor/plugins/csrees
    $ cd vendor/plugins/csrees
    $ mkdir taglibs
    $ mkdir public
    $ mkdir public/csrees
    $ mkdir public/csrees/stylesheets
    $ mkdir public/csrees/images
    $ cd ../../..
    $ cp app/views/taglibs/application.dryml vendor/plugins/csrees/taglibs/csrees.dryml
    $ cp public/stylesheets/application.css vendor/plugins/csrees/public/csrees/stylesheets/csrees.css
    $ cp public/images/* vendor/plugins/csrees/public/csrees/images

(That last command will also copy `rails.png` into the plugin, which you probably want to delete).

We've copied the whole of `application.dryml` into our plugin, because nearly everything in there belongs in the plugin, but it does need some editing:

 - At the top, remove all of the includes, the `` and the definition of ``
 
 - We need to make sure our stylesheet gets included, so add the following parameter to the call to ``:
 
        
          
        


## Installing the plugin

To try out the plugin, create a new blank Hobo app. There are then three steps to install and setup the plugin:

**To install the plugin** copy `vendor/plugins/csrees` from the app we've been working on, into `vendor/plugins` in the new app. To setup the plugin, we just need to include the taglib, and copy the public assets into our `public` directory

**To install the taglib** add 

    
    
to `application.dryml`. It must be added *after* the `` tag.

**To install the public assets**:

    $ cp -R vendor/plugins/csrees/public/* public
    
That should be it - your new app will now look like the CSREES website, and the tags we defined, such as `` will be available in every template.
</content>
    <updated>2008-11-18 09:32:26 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/10</id>
    <published>2008-10-28T12:50:53Z</published>
    <updated>2010-03-22T10:18:59Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/10-make-a-private-hobo-app" rel="alternate"/>
    <title>Make a private Hobo app</title>
    <content type="html"># UPDATE - `--invite-only` added to Hobo

You can now just create your app with

    hobo --invite-only my_app

and you'll get a new Hobo app set up so that only those invited by the admin can have accounts.

That's it : )

---

By a 'private' app, we mean that only logged in users can get access, and there is no public sign-up facility.

This recipe is a work in progress.

# Require all users to be logged in

Very easy - just add the following `before_filter` to `ApplicationController`:

    class ApplicationController &amp;lt; ActionController::Base
      ...
      before_filter :login_required
    end
{: .ruby}

Note that this prevents access to the entire site to users that are not logged in. That sounds like a problem - how will the user even visit the login page? Fear not - Hobo's user controller declared `skip\_before\_filter :login_required` for the login action and a few others

# Prevent signup

Also easy! Your generated user model has the following lifecycle declaration:

    lifecycle do

      initial_state :active

      create :anybody, :signup, 
             :params =&gt; [:username, :email_address, :password, :password_confirmation],
             :become =&gt; :active, :if =&gt; proc {|_, u| u.guest?}

      transition :nobody, :request_password_reset, { :active =&gt; :active }, :new_key =&gt; true do
        UserMailer.deliver_forgot_password(self, lifecycle.key)
      end

      transition :with_key, :reset_password, { :active =&gt; :active }, 
                 :update =&gt; [ :password, :password_confirmation ]

    end
{: .ruby}

So - just delete the `:signup` creator, so you're left with

    lifecycle do

      initial_state :active

      transition :nobody, :request_password_reset, { :active =&gt; :active }, :new_key =&gt; true do
        UserMailer.deliver_forgot_password(self, lifecycle.key)
      end

      transition :with_key, :reset_password, { :active =&gt; :active }, 
                 :update =&gt; [ :password, :password_confirmation ]

    end
{: .ruby}

That's it. The `` tag tests for the presence of the signup route, which is now gone, so the "sign up" link will be gone too.</content>
    <updated>2010-03-22 10:18:59 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/8</id>
    <published>2008-10-24T08:06:28Z</published>
    <updated>2008-11-18T09:39:19Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/8-use-parts-on-repeated-elements" rel="alternate"/>
    <title>Use parts on repeated elements</title>
    <content type="html">## The basics

Hobo's part mechanism makes it very easy to have parts of the page updates "ajax" style. All you do is add the `part` attribute to a tag somewhere:

    &lt;div&gt;
      ... this section can be updated without reloading the page
    &lt;/div&gt;
{: .dryml}

Then use the `update` attribute, which is supported by various Rapid tags, invcluding ``
  
     ... 
{: .dryml}

The presence of the `update` tells Rapid to generate an ajax form. When the use clicks that "Go!" button, the form is submitted in the background, the content "my-part" is re-rendered on the server, and the new content is placed onto the page.

## The problem

What if you want to have a part in a repeated section of the page? The following **will not work**:

    &lt;ul&gt;
      &lt;li&gt;
        &lt;div&gt; ... &lt;/div&gt;
         ... 
      &lt;/li&gt;
    &lt;/ul&gt;
{: .dryml}

That makes sense, after all, which of the `&lt;div&gt;` elements should be updated.
  
Some points to understand about the part mechanism:

  - The part name is a global and static name, much like the name of a Rails partial

  - The string you pass to the `update` attribute is actually *not* a part name, it is a DOM ID.
  
  - By default, a tag with `part="foo"` is automatically given `id="foo"`. (that's why the first example in this recipe works - if you view-source you will see that the `&lt;div&gt;` has been given `id="my-part"`)
    
That should clarify why the second example doesn't work. There are multiple tags `&lt;div&gt;` in the output, which is invalid.
  
## The solution

To override the part-name becoming the tags ID, just give an explicit `id` attribute. The `typed_id` helper is a handy way to generate a unique ID based on the model-name and ID of `this`. The working version of the second example is:

    &lt;ul&gt;
      &lt;li&gt;
        &lt;div&gt; ... &lt;/div&gt;
         ... 
      &lt;/li&gt;
    &lt;/ul&gt;
{: .dryml}

## One more tip

If it makes sense in your page to have the `` *inside* the part, you can just say `update="self"`:
  
    &lt;ul&gt;
      &lt;li&gt;
        &lt;div&gt;
          ...
           ... 
        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
{: .dryml}

This is particularly useful in reusable tags that contain an updatable part</content>
    <updated>2008-11-18 09:39:19 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/7</id>
    <published>2008-10-23T09:50:44Z</published>
    <updated>2008-10-23T09:53:46Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/7-simple-cross-model-comments" rel="alternate"/>
    <title>Simple Cross-Model Comments</title>
    <content type="html">After posting the question, and recently getting into polmorphic stuff with Rails and Hobo, I realised how easy it is to set up cross-model comments.

(this assumes you want to associate a comment with a user)

    ./script/generate hobo_model comment body:text commentable_id:integer commentable_type:string user_id:integer
    ./script/generate hobo_model_controller comment
    ./script/generate hobo_migration 


then in any model you want to be commentable:

    has_many :comments, :as =&gt; :commentable, :dependent =&gt; :destroy

in my application.dryml I added a card for Comments: 

    
	&lt;div class="card linkable comment"&gt;	      
    	    &lt;h5&gt; wrote:&lt;/h5&gt; 
    		&lt;br /&gt;
    				
    		
    			&lt;div class="delete"&gt;			
    				  'links', :action =&gt; 'destroy',
                                       :id =&gt; this.id.to_s }, 
                                       :confirm =&gt; "Are you sure you want to delete this comment?",
                                       :method =&gt; :delete %&gt;				
    			&lt;/div&gt;
    		
    	&lt;/div&gt;  
     

and a tag for adding a new comment to an object:

    
    	
    		
    		  Add a new comment&lt;br /&gt;
    		  &lt;textarea name="comment[body]" class="body-tag comment-body"&gt;
    		  	  	
    		  	  	
    		  	  	
    		  
    		
    	
    	
    		&lt;p&gt;Error (add-comment): Object not given - use add-comment object="&amp;blah"&lt;/p&gt;
    	
    

</content>
    <updated>2008-10-23 09:53:46 UTC</updated>
    <author>
      <name>adamski</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/6</id>
    <published>2008-10-20T21:48:55Z</published>
    <updated>2008-10-20T22:15:48Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/6-running-a-hobo-application-on-heroku" rel="alternate"/>
    <title>Running a Hobo Application on Heroku</title>
    <content type="html">This is link to a draft in PDF format.

This was done a while ago...needs testing! Describes how to upload and run an app developed on your laptop to Heroku, including mirgrating existing data.


</content>
    <updated>2008-10-20 22:15:48 UTC</updated>
    <author>
      <name>Owen</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/5</id>
    <published>2008-10-17T14:18:53Z</published>
    <updated>2008-10-17T14:18:53Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/5-upgrade-an-app-to-the-latest" rel="alternate"/>
    <title>Upgrade an app to the latest version of Hobo</title>
    <content type="html">## Breaking Changes

It's quite common that there are breaking changes from one version of Hobo to the next. These will settle down once we get to 1.0. In the meantime, there are no general instructions for fixing your app. Have a look at the changelog, any announcements on the blog, or ask in the user group.

## Upgrading

Upgrading Hobo is a simple matter of

    $ gem update hobo

Or, if you are tracking edge Hobo using git

    $ cd vendor/plugins/hobo
    $ git pull

You should also re-run the `hobo_rapid` generator, as some of the assets put in `public` by that generator may well have changed.

    $ ruby script/generate hobo_rapid

For minor upgrades to Hobo that should be enough. Head over to the user group if your app is still not working.</content>
    <updated>2008-10-17 14:18:53 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/4</id>
    <published>2008-10-17T11:32:21Z</published>
    <updated>2008-10-29T09:40:47Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/4-create-a-new-hobo-app-on" rel="alternate"/>
    <title>Create a new Hobo app on edge Hobo</title>
    <content type="html"># Install git

First up, you need to have git installed:

 - [git for windows](http://code.google.com/p/msysgit/)
 - [git for Mac OS](http://bc.tech.coop/blog/070827.html)
 - Linux user? I'm sure you don't need any help : )

# Understand the `hobo` command.

The main difference when working with edge Hobo is that you don't use the `hobo` command, as this command comes from the version of Hobo you have installed from the gem. Instead you want to manually replicate what this command does for you. There's not much to it -- it just runs a few generators.

To make this easier, the `hobo` command actually tells you what it's doing, with lines that start `--&gt;`

e.g, if you run the `hobo` command you will see lines like:

    --&gt; ruby script/generate hobo --add-routes

If you run all of these commands (listed below) in a blank Rails app, you will have a Hobo app. The advantage is that you can install edge Hobo first

# How to do it

## 1. Create a Rails app

    $ rails my_app

## 2. Install the Hobo plugin from the github repo

    $ cd vendor/plugins
    $ git clone git://github.com/tablatom/hobo.git

## 3. Run the hobo generators

    ruby script/generate hobo --add-routes
    ruby script/generate hobo_rapid --import-tags
    ruby script/generate hobo_user_model user
    ruby script/generate hobo_user_controller user
    ruby script/generate hobo_front_controller front --delete-index --add-routes
    
(I ommitted the '$' to indicate the prompt to make those copy-paste friendly)
    
And that's it. For bonus marks, make a script that does all this for you!

## 4. How to get updates

    $ cd vendor/plugins/hobo
    $ git pull

It's possible that some of Hobo's generated files have changed so you may need to run Hobo's generators again. Most often the only changes are in Hobo Rapid, so run

    $ ruby script/generate hobo_rapid

(don't forget to cd back to the root of the app)</content>
    <updated>2008-10-29 09:40:47 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/3</id>
    <published>2008-10-16T20:39:18Z</published>
    <updated>2008-11-03T15:32:41Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/3-create-an-admin-subsite" rel="alternate"/>
    <title>Create an admin subsite</title>
    <content type="html">A `hobo_subsite` generator has been added to Hobo since this guide was written. The following should still work but is a bit out of date.
{: .note}

A sub-site is part of a web-app that has the appearance and behavior of a separate site. The most
common example of a sub-site is an "admin" section. This guide shows how to create a typical
admin sub-site in Hobo.

The typical approach to creating a sub-site is to duplicate the required model controllers. So you
might have a public facing view of the users at `/users` and the administrators view of the same
model at `/admin/users`. These would be managed by two distinct controllers, each with their own
set of enabled actions which might not necessarily be the same. For example the admin users controller
might have a "new" action at `/admin/users/new`, while the public facing users controller might
not enable this action at all if it isn't appropriate.

### Step by Step

At the time of writing, Hobo's generators are unable to generate sub-site controllers so it is 
necessary to create the files manually.

Create a new directory `app/controllers/admin/`

Create a base controller for the sub-site in `app/controllers/admin/admin_controller.rb`:

    class Admin::AdminController &amp;lt; ApplicationController
  
      hobo_controller

      # require administrator to access any controller in the sub-site
      before_filter :admin_required
      def admin_required
        redirect_to login_url unless logged_in? &amp;&amp; current_user.administrator?
      end

      # the default page when visiting the sub-site
      def index
        redirect_to '/admin/users'
      end
    end
{: .ruby}

Now create a model controller in the admin sub-site. As an example we will create a controller to manage users. The following goes in `app/controllers/admin/users_controller.rb`:

    class Admin::UsersController &amp;lt; Admin::AdminController

      hobo_model_controller User
  
      auto_actions :index, :edit, :destroy, :update
  
    end
{: .ruby}
  
Notice two things that are slightly different to normal:

* The controller inherits from `Admin::AdminController` instead of `ApplicationController`
* You have to explicitly specify the name of the model as an argument to `hobo\_model\_controller`. In a non-sub-site controller Hobo can usually work this out automatically so it isn't needed.

Add a route for the home page of the sub-site in `config/routes.rb`:

    map.admin  'admin', :controller =&gt; 'admin/admin', :action =&gt; 'index'
{: .ruby}

This will enable the route `/admin` which will redirect automatically to
`/admin/users` as we set up above.

In the base controller we wrote `include_taglib 'admin'`. This means that the tags
in `app/views/taglibs/admin.dryml` will be loaded for every action in the sub-site.
This is a convenient place to re-define ``, set a theme and define any tags that
are specific to the sub-site. As an example, create `app/views/taglibs/admin.dryml`
and add the following:

    
      
      
        
          
          
        
        
          
          
        
        
          
            Users
          
            
      
    
{: .dryml}

Here we've defined an admin specific version of `` that uses an aside layout by
default. We've also added admin specific javascript and stylesheet files which you will
need to create in `public/javascripts/admin.js` and `public/stylesheets/admin.css`. Finally
we have defined our admin specific nav bar containing a link to our users model controller
because Hobo excludes users from the nav bar by default.

We'll want to tweak Rapid's generic pages to be a bit more admin oriented. By adding the following
to `admin.dryml` we can replace the normal list on an index page with an admin style table:

    
      
        
          
        
      
    
{: .dryml}

If we add additional model controllers to the admin sub-site they will all use this modified
version of ``. Similar admin specific tweaks can be made to the other generic pages
and tags.

Notice the `fields` attribute on ``. This attribute is given a list of fields that will appear as columns in the table. Our shared definition of `` can't put anything model-specific in there, so we just use "this" which will give a single column with links to each show-page. The good thing is that it's easy to customise this for specific models. For example, say we'd like to add an `email_address` column for the `User` model, just create `app/views/admin/users/index.dryml` like this:

	
	  
	
{: .dryml}
	
Notice that the call to `` picks up our customised version, since we've overridden it for the entire admin sub-site.
</content>
    <updated>2008-11-03 15:32:41 UTC</updated>
    <author>
      <name>James</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/2</id>
    <published>2008-10-16T20:38:27Z</published>
    <updated>2008-10-16T21:22:16Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/2-fall-back-to-rails-techniques-in" rel="alternate"/>
    <title>Fall back to Rails techniques in your views</title>
    <content type="html"># How To: Fall back to Rails-style views.

Audience: Anyone that knows Rails-style views (ERB + Rails helpers) and is starting to learn DRYML and Rapid.

While learning to write your views using DRYML and Rapid, you might find yourself stuck trying to do something that you'd be perfectly capable of using ERB and Rails helpers. Obviously the ideal solution would be to learn the "Hobo way", but given that documentation is still a work-in-progress, you might need to just "do it the old way".

We'll start with the most extreme example of "doing it the old way" and work forward to more preferable options.


## Fall back completely to an ERB template.

Rails supports multiple template languages, and uses the file suffix to pick the right one. So simply by calling a template, say, `index.html.erb` instead of `index.dryml`, you have created a normal Rails template. Templates like this will co-exist with any DRYML pages without a problem.

The main downside to doing this is that Rails apps typically use a 'layout' to provide parts of the page that are common to all pages, while DRYML pages typically use a `` tag. Your `.html.erb` file won't get the same page structure that the DRYML pages get. You could always create a Rails layout, but that's not a great idea as you'll have to manually keep it in sync with your DRYML `` tag.
  
There is a better way...


## Using the `` tag + ERB
  
You can solve the problem of the missing layout by sticking with the `.dryml` suffix (i.e. the page is a DRYML page), but still mark-up the entire page in ERB. You just have to wrap your ERB in the appropriate parameter of the `` tag. e.g.
  
By convention, the DRYML `` tag will normally provide two parameters (among many others) that will suit this purpose. If you just want to get the `` section from the page tag -- things like JavaScript includes and CSS stylesheets -- and you want to provide the entire HTML body yourself, then use the `` parameter (remember, every DRYML tag that ends with a ':' is a *parameter* to the encolsing tag -- `` in this case):
  
    
      
        ... write a regular Rails-style ERB template here ...
      
    
{: .dryml}

You will probably want more than just the `` section though. You will want the "theme" of the site -- the page header, site title, nav bar etc. In this case, use the `` parameter
    
    
      
        ... write a regular Rails-style ERB template here ...
      
    
{: .dryml}

It's important to understand that if your application defines a custom `` tag, the `` and `` parameters might not exist. These are just conventions. All of Hobo's built-in pages do provide those parameters though, so if you are working from a typical Hobo app, the above examples will work as advertised.
    

## Using small fragments of ERB and helpers

Hopefully your pages will be entirely written using DRYML and Rapid. If you find that there's just one small part you can't figure out, say a link, or a form, you can just drop down to ERB and helpers for that one thing. Here are a couple of examples.


### Links

Say you wanted to add a link to the current user's "My Account" page, which is at `/users/123/account`. Because Hobo uses normal Rails conventions of RESTful routing, you can use the familiar `link_to` helper. Here's how you would add that after the heading on one of Rapid's generated `show` pages:

    
      
         "users", :action =&gt; "account", :id =&gt; current_user %&gt;
      
    
{: .dryml}
    
### Forms

Again, because Hobo doesn't change any Rails conventions for routes or for form parameter names, if you really can't figure out how to get a DRYML/Rapid form working, you can always fall back on a "normal" Rails form. As an example, we'll completely replace the form on one of Rapid's generated 'edit' pages, say for a 'person' model. The generated `` tag exposes the `` tag as a parameter, so we can replace it with ``:

    
      
        
          
          Name : &lt;br /&gt;
          Biography : &lt;br /&gt;
        
      
    
{: .dryml}</content>
    <updated>2008-10-16 21:22:16 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
  <entry>
    <id>tag:cookbook.hobocentral.net,2005:Recipe/1</id>
    <published>2008-10-16T19:46:14Z</published>
    <updated>2008-10-17T11:09:56Z</updated>
    <link type="text/html" href="http://cookbook.hobocentral.net/recipes/1-add-your-own-rich-type" rel="alternate"/>
    <title>Add your own rich type</title>
    <content type="html">Say your application features currency value, and you'd like them all formatted nicely. A great way to implement this in Hobo is by creating a new type, e.g. `Dollars` and customising the way those values are displayed. That way your dollar values will be displayed correctly throughout the site and you won't need to think about that issue again.


## Start with a blank app

To make this how-to very concrete let's start from scratch with a new application:

        $ hobo my_app
        $ cd my_app
        $ ./script/generate hobo_model_resource product name:string

## Create the type

Now we'll dive right in and create the new type. Create `app/models/dollars.rb` like this:

        class Dollars &amp;lt; DelegateClass(BigDecimal)

          COLUMN_TYPE = :decimal

        end
{: .ruby}

Let's talk that through. It's common with Rich types to create a new subclass of the 'normal' type that ActiveRecord would use. For example, Hobo's `HtmlString` is a subclass of `String`. Unfortunately, subclassing `BigDecimal` is problematic for reasons we won't bother going in to. Fortunately Ruby saves us with the very nifty `DelegateClass`. This allows us to walk like a `BigDecimal` and talk like a `BigDecimal`, which is perfectly good enough in Ruby.

We then declare 

        COLUMN_TYPE = :decimal
{: .ruby}

which tells the migration generator what type of column to generate for these things. That's arguably redundant seeing as we already defined these things as being a special kind of `BigDecimal`. This step might go away in the future.


## View layer

To render these dollar values nicely in the app, we need to define a custom `` tag. So in application.dryml we need:

    $
{: .dryml}


## Declare a field with the custom type

Now our type is ready to go. We can edit `app/modes/product.rb` like this:

        class Product &amp;lt; ActiveRecord::Base

          hobo_model

          fields do
            name :string
            price Dollars, :precision =&gt; 12, :scale =&gt; 2
            timestamps
          end
         
          ...
        end
{: .ruby}

Notice the type of the `price` column is given as the class constant `Dollars`. We can pass the same options that we could to a regular `:decimal` column

# Create the database

If we now run the migration geneartor:

        $ ./script/generate hobo_migration

You'll see that the price column is `:decimal` type (the users table has been omitted for brevity):

        ---------- Up Migration ----------
        create_table :products do |t|
          t.string   :name
          t.decimal  :price, :scale =&gt; 2, :precision =&gt; 12
          t.datetime :created_at
          t.datetime :updated_at
        end
        ----------------------------------

        ---------- Down Migration --------
        drop_table :products
        ----------------------------------
{: .ruby}

## Try it out

That's it! You can now fire up the app and sign up to become the administrator. Create a product with a price, and see the nice dollar formatting.

## Bonus - a custom input for dollars

Add this to `app/views/taglibs/application.dryml`

        
          $ 
        
{: .dryml}

You'll now have a dollar sign in front of every dollar field in your forms.</content>
    <updated>2008-10-17 11:09:56 UTC</updated>
    <author>
      <name>Tom</name>
    </author>
  </entry>
</feed>
