<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -13,25 +13,4 @@ class HostsController &lt; ApplicationController
     config.columns[:resources].association.reverse = :host
   end
 
-  #def show
-  #  unless @host = Host.find(:first, :conditions =&gt; [ 'name LIKE ?', &quot;#{params[:name]}.%&quot; ])
-  #    @host = Host.find(params[:id])
-  #  end
-  #end
-
-  # POST /hosts
-  # POST /hosts.xml
-  #def create
-  #  attrs = {}
-  #  params.each_pair do |k,v|
-  #    k.chomp
-  #    case k 
-  #      when &quot;name&quot; 
-  #        attrs.store(k,v)
-  #      when &quot;ip&quot;
-  #        attrs.store(k,v)
-  #    end
-  #  end
-  #end
-
 end</diff>
      <filename>app/controllers/hosts_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,7 @@ class ResourcesController &lt; ApplicationController
 
   layout &quot;application&quot;, :except =&gt; &quot;collect_exported&quot;
   active_scaffold :resource do |config|
-    config.list.columns = [:name, :param_values, :resource_tags, :source_file, :line]
+    config.list.columns = [:name, :param_values, :resource_tags]
     config.columns[:param_values].label = &quot;Parameters&quot;
     config.columns[:param_values].includes = nil
     config.columns[:param_values].association.reverse = :resource</diff>
      <filename>app/controllers/resources_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,2 +1,12 @@
 module HostsHelper
+
+  def resources_column(record)
+    display = record.resources.count
+    display ||= &quot;0&quot;
+  end   
+
+  def fact_values_column(record)
+    display = record.fact_values.count
+    display ||= &quot;0&quot;
+  end 
 end</diff>
      <filename>app/helpers/hosts_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,2 +1,3 @@
 module ResourceTagsHelper
+
 end</diff>
      <filename>app/helpers/resource_tags_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,2 +1,11 @@
 module ResourcesHelper
+  def resource_tags_column(record)
+    display = record.resource_tags.count
+    display ||= &quot;0&quot;
+  end   
+
+  def param_values_column(record)
+    display = record.param_values.count
+    display ||= &quot;0&quot;
+  end 
 end</diff>
      <filename>app/helpers/resources_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ class Host &lt; Puppet::Rails::Host
   hobo_model
 
 
-  def viewable_by?(viewer, field)
-    !user.guest?
-  end
+#  def viewable_by?(viewer, field)
+#    !user.guest?
+#  end
 end</diff>
      <filename>app/models/host.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,9 @@
 
 &lt;include src=&quot;rapid&quot; plugin=&quot;hobo&quot;/&gt;
 
-&lt;set-theme name=&quot;clean&quot;/&gt;
+&lt;set-theme name=&quot;puppetshow&quot;/&gt;
+
+&lt;def tag=&quot;app-name&quot;&gt;Puppet Show&lt;/def&gt;
 
 &lt;def tag=&quot;page&quot; extend-with=&quot;app&quot;&gt;
     &lt;page-without-app merge&gt;</diff>
      <filename>app/views/taglibs/application.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -14,9 +14,10 @@
 development:
   adapter: mysql
   database: puppet
-  username: root
-  password:
+  username: puppet
+  password: puppet
   host: localhost
+  socket: &quot;/var/run/mysqld/mysqld.sock&quot;
 
 # Warning: The database defined as 'test' will be erased and
 # re-generated from your development database when you run 'rake'.
@@ -30,7 +31,8 @@ test:
 
 production:
   adapter: mysql
-  database: puppetstore_production
-  username: root
-  password: 
+  database: puppet
+  username: puppet
+  password: puppet
   host: localhost
+  socket: &quot;/var/run/mysqld/mysqld.sock&quot;</diff>
      <filename>config/database.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,22 +1,31 @@
-# This file is autogenerated. Instead of editing this file, please use the
-# migrations feature of ActiveRecord to incrementally modify your database, and
+# This file is auto-generated from the current state of the database. Instead of editing this file, 
+# please use the migrations feature of ActiveRecord to incrementally modify your database, and
 # then regenerate this schema definition.
+#
+# Note that this schema.rb definition is the authoritative source for your database schema. If you need
+# to create the application database on another system, you should be using db:schema:load, not running
+# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
+# you'll amass, the slower it'll run and the greater likelihood for issues).
+#
+# It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version =&gt; 9) do
+ActiveRecord::Schema.define(:version =&gt; 3) do
 
   create_table &quot;fact_names&quot;, :force =&gt; true do |t|
-    t.column &quot;name&quot;,       :string
-    t.column &quot;updated_at&quot;, :datetime
+    t.string   &quot;name&quot;,       :null =&gt; false
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;fact_names&quot;, [&quot;id&quot;], :name =&gt; &quot;index_fact_names_on_id&quot;
   add_index &quot;fact_names&quot;, [&quot;name&quot;], :name =&gt; &quot;index_fact_names_on_name&quot;
 
   create_table &quot;fact_values&quot;, :force =&gt; true do |t|
-    t.column &quot;value&quot;,        :text,     :null =&gt; false
-    t.column &quot;fact_name_id&quot;, :integer,  :null =&gt; false
-    t.column &quot;host_id&quot;,      :integer,  :null =&gt; false
-    t.column &quot;updated_at&quot;,   :datetime
+    t.text     &quot;value&quot;,        :null =&gt; false
+    t.integer  &quot;fact_name_id&quot;, :null =&gt; false
+    t.integer  &quot;host_id&quot;,      :null =&gt; false
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;fact_values&quot;, [&quot;id&quot;], :name =&gt; &quot;index_fact_values_on_id&quot;
@@ -24,13 +33,14 @@ ActiveRecord::Schema.define(:version =&gt; 9) do
   add_index &quot;fact_values&quot;, [&quot;host_id&quot;], :name =&gt; &quot;index_fact_values_on_host_id&quot;
 
   create_table &quot;hosts&quot;, :force =&gt; true do |t|
-    t.column &quot;name&quot;,            :string,   :null =&gt; false
-    t.column &quot;ip&quot;,              :string
-    t.column &quot;last_compile&quot;,    :datetime
-    t.column &quot;last_freshcheck&quot;, :datetime
-    t.column &quot;last_report&quot;,     :datetime
-    t.column &quot;updated_at&quot;,      :datetime
-    t.column &quot;source_file_id&quot;,  :integer
+    t.string   &quot;name&quot;,            :null =&gt; false
+    t.string   &quot;ip&quot;
+    t.datetime &quot;last_compile&quot;
+    t.datetime &quot;last_freshcheck&quot;
+    t.datetime &quot;last_report&quot;
+    t.datetime &quot;updated_at&quot;
+    t.integer  &quot;source_file_id&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;hosts&quot;, [&quot;id&quot;], :name =&gt; &quot;index_hosts_on_id&quot;
@@ -38,19 +48,21 @@ ActiveRecord::Schema.define(:version =&gt; 9) do
   add_index &quot;hosts&quot;, [&quot;name&quot;], :name =&gt; &quot;index_hosts_on_name&quot;
 
   create_table &quot;param_names&quot;, :force =&gt; true do |t|
-    t.column &quot;name&quot;,       :string
-    t.column &quot;updated_at&quot;, :datetime
+    t.string   &quot;name&quot;,       :null =&gt; false
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;param_names&quot;, [&quot;id&quot;], :name =&gt; &quot;index_param_names_on_id&quot;
   add_index &quot;param_names&quot;, [&quot;name&quot;], :name =&gt; &quot;index_param_names_on_name&quot;
 
   create_table &quot;param_values&quot;, :force =&gt; true do |t|
-    t.column &quot;value&quot;,         :text,     :null =&gt; false
-    t.column &quot;param_name_id&quot;, :integer,  :null =&gt; false
-    t.column &quot;line&quot;,          :integer
-    t.column &quot;resource_id&quot;,   :integer
-    t.column &quot;updated_at&quot;,    :datetime
+    t.text     &quot;value&quot;,         :null =&gt; false
+    t.integer  &quot;param_name_id&quot;, :null =&gt; false
+    t.integer  &quot;line&quot;
+    t.integer  &quot;resource_id&quot;
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;param_values&quot;, [&quot;id&quot;], :name =&gt; &quot;index_param_values_on_id&quot;
@@ -58,16 +70,18 @@ ActiveRecord::Schema.define(:version =&gt; 9) do
   add_index &quot;param_values&quot;, [&quot;resource_id&quot;], :name =&gt; &quot;index_param_values_on_resource_id&quot;
 
   create_table &quot;puppet_tags&quot;, :force =&gt; true do |t|
-    t.column &quot;name&quot;,       :string
-    t.column &quot;updated_at&quot;, :datetime
+    t.string   &quot;name&quot;
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;puppet_tags&quot;, [&quot;id&quot;], :name =&gt; &quot;index_puppet_tags_on_id&quot;
 
   create_table &quot;resource_tags&quot;, :force =&gt; true do |t|
-    t.column &quot;resource_id&quot;,   :string
-    t.column &quot;puppet_tag_id&quot;, :integer
-    t.column &quot;updated_at&quot;,    :datetime
+    t.integer  &quot;resource_id&quot;
+    t.integer  &quot;puppet_tag_id&quot;
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;resource_tags&quot;, [&quot;id&quot;], :name =&gt; &quot;index_resource_tags_on_id&quot;
@@ -75,13 +89,14 @@ ActiveRecord::Schema.define(:version =&gt; 9) do
   add_index &quot;resource_tags&quot;, [&quot;puppet_tag_id&quot;], :name =&gt; &quot;index_resource_tags_on_puppet_tag_id&quot;
 
   create_table &quot;resources&quot;, :force =&gt; true do |t|
-    t.column &quot;title&quot;,          :text,     :null =&gt; false
-    t.column &quot;restype&quot;,        :string,   :null =&gt; false
-    t.column &quot;host_id&quot;,        :integer
-    t.column &quot;source_file_id&quot;, :integer
-    t.column &quot;exported&quot;,       :boolean
-    t.column &quot;line&quot;,           :integer
-    t.column &quot;updated_at&quot;,     :datetime
+    t.text     &quot;title&quot;,          :null =&gt; false
+    t.string   &quot;restype&quot;,        :null =&gt; false
+    t.integer  &quot;host_id&quot;
+    t.integer  &quot;source_file_id&quot;
+    t.boolean  &quot;exported&quot;
+    t.integer  &quot;line&quot;
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;resources&quot;, [&quot;id&quot;], :name =&gt; &quot;index_resources_on_id&quot;
@@ -89,13 +104,35 @@ ActiveRecord::Schema.define(:version =&gt; 9) do
   add_index &quot;resources&quot;, [&quot;source_file_id&quot;], :name =&gt; &quot;index_resources_on_source_file_id&quot;
   add_index &quot;resources&quot;, [&quot;restype&quot;, &quot;title&quot;], :name =&gt; &quot;typentitle&quot;
 
+  create_table &quot;sessions&quot;, :force =&gt; true do |t|
+    t.string   &quot;session_id&quot;, :null =&gt; false
+    t.text     &quot;data&quot;
+    t.datetime &quot;created_at&quot;
+    t.datetime &quot;updated_at&quot;
+  end
+
+  add_index &quot;sessions&quot;, [&quot;session_id&quot;], :name =&gt; &quot;index_sessions_on_session_id&quot;
+  add_index &quot;sessions&quot;, [&quot;updated_at&quot;], :name =&gt; &quot;index_sessions_on_updated_at&quot;
+
   create_table &quot;source_files&quot;, :force =&gt; true do |t|
-    t.column &quot;filename&quot;,   :string
-    t.column &quot;path&quot;,       :string
-    t.column &quot;updated_at&quot;, :datetime
+    t.string   &quot;filename&quot;
+    t.string   &quot;path&quot;
+    t.datetime &quot;updated_at&quot;
+    t.datetime &quot;created_at&quot;
   end
 
   add_index &quot;source_files&quot;, [&quot;id&quot;], :name =&gt; &quot;index_source_files_on_id&quot;
   add_index &quot;source_files&quot;, [&quot;filename&quot;], :name =&gt; &quot;index_source_files_on_filename&quot;
 
+  create_table &quot;users&quot;, :force =&gt; true do |t|
+    t.string   &quot;crypted_password&quot;,          :limit =&gt; 40
+    t.string   &quot;salt&quot;,                      :limit =&gt; 40
+    t.string   &quot;remember_token&quot;
+    t.datetime &quot;remember_token_expires_at&quot;
+    t.string   &quot;username&quot;
+    t.boolean  &quot;administrator&quot;,                           :default =&gt; false
+    t.datetime &quot;created_at&quot;
+    t.datetime &quot;updated_at&quot;
+  end
+
 end</diff>
      <filename>db/schema.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,11 +17,6 @@ table.new-record textarea, table.new-record input {
 	display: inline;	
 }
 
-.edit-page .content-header {overflow: hidden; height: 100%;}
-.edit-page .content-header h1 {float: left;}
-.edit-page .content-header .delete-button {float: right;}
-form .actions {margin: 30px 0;width: 100%; text-align: center;}
-
 /**** Admin ****/
 
 .admin-banner {
@@ -39,25 +34,6 @@ form .actions {margin: 30px 0;width: 100%; text-align: center;}
 	float: right;
 }
 
-/* rails error message */
-.error-messages {
-	font-family: &quot;Lucida Grande&quot;, arial, sans-serif;
-	background: #9d0018;
-	border: 1px solid #7a0013;
-	padding: 15px 30px;
-	color: white;
-	margin-bottom: 20px;
-}
-.error-messages h2 {
-	text-transform: none;
-	letter-spacing: normal;
-	color: white;
-	margin-bottom: 10px;
-}
-.error-messages li {
-	margin-left: 20px;
-}
-
 /********* everything below here came from hobo_rapid.css, needs looking at ********/
 
 /**** Default styling for Rapid ***/
@@ -66,30 +42,6 @@ form .actions {margin: 30px 0;width: 100%; text-align: center;}
 	float: right; margin: 20px;
 	position: fixed; display: none; z-index: 10;
 }
-/*
-#ajax-progress {
-    color: grey;
-    float: right;
-    margin: 20px;
-    position: fixed;
-    background: white;
-    font-family: Tahoma &quot;sans serif&quot;;
-    display: none;
-	z-index: 10;
-}
-
-#ajax-progress div {
-    border: 1px dashed grey;
-    margin: 10px;
-    padding: 3px;
-    padding-top: -15px;
-}
-
-#ajax-progress img {
-    padding-left: 6px;
-    vertical-align: middle;
-}
-*/
 
 /* Scriptaculous Autocompleter ---*/
 
@@ -126,7 +78,7 @@ div.completions-popup ul li {
     text-align: left; width: 1px; white-space: nowrap; vertical-align: top;
     padding-top: 10px; padding-bottom: 10px;
 }
-.field-list textarea, .field-list input.string, .field-list input.password { width: 100%; margin: 0; }
+.field-list textarea, .field-list input.string, .field-list input.password { width: 99%; margin: 0; }
 
 /*input[type=text].wide { width: 100%; }*/
 textarea { height: 170px; }</diff>
      <filename>public/hobothemes/clean/stylesheets/rapid-ui.css</filename>
    </modified>
    <modified>
      <diff>@@ -37,7 +37,7 @@ var Hobo = {
 
         var opts = Object.merge(options || {}, { params: params})
         Hobo.ajaxRequest(Hobo.putUrl(el),
-                         el.getAttribute(&quot;hobo-ajax-message&quot;) || &quot;Changing...&quot;,
+                         el.getAttribute(&quot;hobo-ajax-message&quot;) || &quot;Saving...&quot;,
                          updates,
                          opts)
     },
@@ -49,8 +49,7 @@ var Hobo = {
             updates.each(function(id_or_el) {
                 var el = $(id_or_el)
                 if (el) { // ignore update of parts that do not exist
-                    var partDomId
-                    partDomId = el.id
+                    var partDomId = el.id
                     if (!hoboParts[partDomId]) { throw &quot;Update of dom-id that is not a part: &quot; + partDomId }
                     params.push(&quot;render[&quot;+i+&quot;][part_context]=&quot; + encodeURIComponent(hoboParts[partDomId]))
                     params.push(&quot;render[&quot;+i+&quot;][id]=&quot; + partDomId)
@@ -73,11 +72,12 @@ var Hobo = {
         return params.join('&amp;')
     },
 
-    ajaxRequest: function(url_or_form, message, updates, options) {
+    ajaxRequest: function(url_or_form, updates, options) {
         options = Object.merge({ asynchronous:true,
                                  evalScripts:true,
                                  resetForm: false,
-                                 refocusForm: false
+                                 refocusForm: false,
+                                 message: &quot;Saving...&quot;
                                }, options)
         if (typeof url_or_form == &quot;string&quot;) {
             var url = url_or_form
@@ -104,7 +104,7 @@ var Hobo = {
             params.push(Form.serialize(form))
         }
 
-        Hobo.showSpinner(message, options.spinnerNextTo)
+        Hobo.showSpinner(options.message, options.spinnerNextTo)
         var complete = function() {
             if (form &amp;&amp; options.resetForm) form.reset();
             Hobo.hideSpinner();
@@ -112,6 +112,7 @@ var Hobo = {
             if (options.onComplete)
                 options.onComplete.apply(this, arguments)
             if (form &amp;&amp; options.refocusForm) Form.focusFirstElement(form)
+            Event.addBehavior.reload()
         }
         if (options.method &amp;&amp; options.method.toLowerCase() == &quot;put&quot;) {
             delete options.method
@@ -184,30 +185,30 @@ var Hobo = {
         var updateParams = Hobo.ajaxUpdateParams(updates, [{id: id,
                                                             result: 'new_field_value',
                                                             func: &quot;Hobo.onFieldEditComplete&quot;}])
-        opts = {okButton: false,
-                cancelLink: false,
-                submitOnBlur: true,
-                evalScripts: true,
-                htmlResponse: false,
-                ajaxOptions: { method: &quot;put&quot; },
-                onEnterHover: null,
-                onLeaveHover: null,
-                callback: function(form, val) {
-                    old = val
-                    return (Hobo.fieldSetParam(el, val) + &quot;&amp;&quot; + updateParams)
-                },
-                onFailure: function(resp) { 
-                    alert(resp.responseText); el.innerHTML = old
-                },
-                onEnterEditMode: function() {
-                    var blank_message = el.getAttribute(&quot;hobo-blank-message&quot;)
-                    if (el.innerHTML.gsub(&quot;&amp;nbsp;&quot;, &quot; &quot;) == blank_message) {
-                        el.innerHTML = &quot;&quot; 
-                    } else {
-                        Hobo.ipeOldValues[el.id] = el.innerHTML
+        var opts = {okButton: false,
+                    cancelLink: false,
+                    submitOnBlur: true,
+                    evalScripts: true,
+                    htmlResponse: false,
+                    ajaxOptions: { method: &quot;put&quot; },
+                    onEnterHover: null,
+                    onLeaveHover: null,
+                    callback: function(form, val) {
+                        old = val
+                        return (Hobo.fieldSetParam(el, val) + &quot;&amp;&quot; + updateParams)
+                    },
+                    onFailure: function(resp) { 
+                        alert(resp.responseText); el.innerHTML = old
+                    },
+                    onEnterEditMode: function() {
+                        var blank_message = el.getAttribute(&quot;hobo-blank-message&quot;)
+                        if (el.innerHTML.gsub(&quot;&amp;nbsp;&quot;, &quot; &quot;) == blank_message) {
+                            el.innerHTML = &quot;&quot; 
+                        } else {
+                            Hobo.ipeOldValues[el.id] = el.innerHTML
+                        }
                     }
-                }
-               }
+                   }
         Object.extend(opts, options)
         return new Ajax.InPlaceEditor(el, Hobo.putUrl(el), opts)
     },
@@ -219,14 +220,14 @@ var Hobo = {
         }
 
         select(&quot;.in-place-textfield-bhv&quot;).each(function (el) {
-            ipe = Hobo._makeInPlaceEditor(el)
+            var ipe = Hobo._makeInPlaceEditor(el)
             ipe.getText = function() {
                 return this.element.innerHTML.gsub(/&lt;br\s*\/?&gt;/, &quot;\n&quot;).unescapeHTML()
             }
         })
 
         select(&quot;.in-place-textarea-bhv&quot;).each(function (el) {
-            ipe = Hobo._makeInPlaceEditor(el, {rows: 2})
+            var ipe = Hobo._makeInPlaceEditor(el, {rows: 2})
             ipe.getText = function() {
                 return this.element.innerHTML.gsub(/&lt;br\s*\/?&gt;/, &quot;\n&quot;).unescapeHTML()
             }
@@ -258,12 +259,12 @@ var Hobo = {
 
         select(&quot;select.number-editor-bhv&quot;).each(function(el) {
             el.onchange = function() {
-                Hobo.ajaxSetFieldForElement(el, el.value)
+                Hobo.ajaxSetFieldForElement(el, $F(el))
             }
         })
                                                 
         select(&quot;.autocomplete-bhv&quot;).each(function (el) {
-            options = {paramName: &quot;query&quot;, minChars: 3, method: 'get' }
+            var options = {paramName: &quot;query&quot;, minChars: 3, method: 'get' }
             if (el.hasClassName(&quot;autosubmit&quot;)) {
                 options.afterUpdateElement = function(el, item) { el.form.onsubmit(); }
             }
@@ -324,34 +325,50 @@ var Hobo = {
         return res
     },
 
+
     fadeObjectElement: function(el) {
-        new Effect.Fade(Hobo.objectElementFor(el),
-                        { duration: 0.5,
-                          afterFinish: function (ef) { ef.element.remove() } });
+        var fadeEl = Hobo.objectElementFor(el)
+        new Effect.Fade(fadeEl, { duration: 0.5, afterFinish: function (ef) { 
+            ef.element.remove() 
+        } });
+        Hobo.showEmptyMessageAfterLastRemove(fadeEl)
     },
 
+
     removeButton: function(el, url, updates, options) {
         if (options.fade == null) { options.fade = true; }
         if (options.confirm == null) { options.fade = &quot;Are you sure?&quot;; }
 
         if (options.confirm == false || confirm(options.confirm)) {
-            objEl = Hobo.objectElementFor(el)
+            var objEl = Hobo.objectElementFor(el)
             Hobo.showSpinner('Removing');
             function complete() {
                 if (options.fade) { Hobo.fadeObjectElement(el) }
                 Hobo.hideSpinner()
             }
             if (updates &amp;&amp; updates.length &gt; 0) {
-                new Hobo.ajaxRequest(url, &quot;Removing&quot;, updates, { method:'delete',
-                                                                 onComplete: complete});
+                new Hobo.ajaxRequest(url, updates, { method:'delete', message: &quot;Removing...&quot;, onComplete: complete});
             } else {
-                new Ajax.Request(url, {asynchronous:true, evalScripts:true, method:'delete',
-                                       onComplete: complete});
+                var ajaxOptions = {asynchronous:true, evalScripts:true, method:'delete', onComplete: complete}
+                if (typeof(formAuthToken) != &quot;undefined&quot;) {
+                    ajaxOptions.parameters = formAuthToken.name + &quot;=&quot; + formAuthToken.value
+                }
+                new Ajax.Request(url, ajaxOptions);
             }
         }
     },
 
 
+    showEmptyMessageAfterLastRemove: function(el) {
+        var empty
+        var container = el.parentNode
+        if (container.getElementsByTagName(el.nodeName).length == 1 &amp;&amp;
+            (empty = container.next('.empty-collection-message'))) {
+            new Effect.Appear(empty, {delay:0.3})
+        }
+    },
+
+
     parseFieldId: function(el) {
         id = el.getAttribute(&quot;hobo-model-id&quot;)
         if (!id) return
@@ -359,6 +376,7 @@ var Hobo = {
         if (m) return { name: m[1], id: m[2], field: m[3] }
     },
 
+
     appendRow: function(el, rowSrc) {
         // IE friendly method to add a &lt;tr&gt; (from html source) to a table
         // el should be an element that contains *only* a table
@@ -367,6 +385,7 @@ var Hobo = {
         Hobo.applyEvents(el)
     },
 
+
     objectElementFor: function(el) {
         var m
         while(el.getAttribute) {
@@ -385,7 +404,7 @@ var Hobo = {
         if(t = $('ajax-progress-text')) Element.update(t, message);
         if(e = $('ajax-progress')) {
             if (nextTo) {
-                var pos = nextTo.cumulativeOffset()
+                var pos = $(nextTo).cumulativeOffset()
                 e.style.top = pos.top + &quot;px&quot;
                 e.style.left = (pos.left + nextTo.offsetWidth) + &quot;px&quot;
             }
@@ -431,8 +450,14 @@ var Hobo = {
         return pluralisations[s] || s + &quot;s&quot;
     },
 
-    addUrlParams: function(params) {
+    addUrlParams: function(params, options) {
         params = $H(window.location.search.toQueryParams()).merge(params)
+
+        if (options.remove) {
+            var remove = (options.remove instanceof Array) ? options.remove : [options.remove]
+            remove.each(function(k) { params.unset(k) })
+        }
+
         return window.location.href.sub(/(\?.*|$)/, &quot;?&quot; + params.toQueryString())
     }
 
@@ -485,7 +510,7 @@ Element.Methods.$$ = function(e, css) {
 
 // --- has_many_through_input --- //
 
-HasManyThroughInput = Behavior.create({
+SelectManyInput = Behavior.create({
 
     initialize : function() {
         // onchange doesn't bubble in IE6 so...
@@ -493,15 +518,16 @@ HasManyThroughInput = Behavior.create({
     },
 
     addOne : function() {
-        var select = this.element.down('select')
+        var select = this.element.down('select') 
         var selected = select.options[select.selectedIndex]
-        if (selected.style.display != &quot;none&quot; &amp; selected.value != &quot;&quot;) {
-            var newItem = strToDom(this.element.down('.item-proto').innerHTML)
+        if (selected.style.display != &quot;none&quot; &amp; selected.text != &quot;&quot;) {
+            var newItem = $(DOM.Builder.fromHTML(this.element.down('.item-proto').innerHTML.strip()))
             this.element.down('.items').appendChild(newItem);
             newItem.down('span').innerHTML = selected.innerHTML
-            newItem.down('input[type=hidden]').value = selected.innerHTML
+            this.itemAdded(newItem, selected)
             selected.style.display = 'none'
             select.value = &quot;&quot;
+            Event.addBehavior.reload()
         }
     },
 
@@ -518,14 +544,37 @@ HasManyThroughInput = Behavior.create({
         var label = el.down('span').innerHTML
         var option = $A(this.element.getElementsByTagName('option')).find(function(o) { return o.innerHTML == label })
         option.style.display = 'block'
+    },
+
+    itemAdded: function(item, option) {
+        this.hiddenField(item).value = option.innerHTML
+    },
+
+    hiddenField: function(item) {
+        return item.down('input[type=hidden]') 
+        //return item.getElementsByClassName(&quot;hidden-field&quot;)[0]
     }
 
+
 })
 
 Event.addBehavior({
-    'div.has-many-through.input' : HasManyThroughInput(),
-		'.dependent-collection-count:click' : function(e) {
-			new Effect.ScrollTo('dependent-collection', {duration: 1.0, offset: -10, transition: Effect.Transitions.sinoidal});
-			Event.stop(e);
-		}
+    'div.select-many.input' : SelectManyInput(),
+
+    '.association-count:click' : function(e) {
+	new Effect.ScrollTo('primary-collection', {duration: 1.0, offset: -20, transition: Effect.Transitions.sinoidal});
+	Event.stop(e);
+    },
+    'form.filter-menu select:change': function(event) {
+        var paramName = this.up('form').down('input[type=hidden]').value.gsub(&quot;-&quot;, &quot;_&quot;)
+        var params = {}
+        var remove = [ 'page' ]
+	if ($F(this) == '') { 
+            remove.push(paramName)
+        } else {
+            params[paramName] = $F(this)
+	}
+	location.href = Hobo.addUrlParams(params, {remove: remove})
+    }
+
 });</diff>
      <filename>public/javascripts/hobo-rapid.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,8 @@
 LowPro = {};
 LowPro.Version = '0.5';
-LowPro.CompatibleWithPrototype = '1.6.0';
+LowPro.CompatibleWithPrototype = '1.6';
 
-if (Prototype.Version != LowPro.CompatibleWithPrototype &amp;&amp; console &amp;&amp; console.warn)
+if (Prototype.Version.indexOf(LowPro.CompatibleWithPrototype) != 0 &amp;&amp; window.console &amp;&amp; window.console.warn)
   console.warn(&quot;This version of Low Pro is tested with Prototype &quot; + LowPro.CompatibleWithPrototype + 
                   &quot; it may not work as expected with this version (&quot; + Prototype.Version + &quot;)&quot;);
 
@@ -15,19 +15,19 @@ DOM = {};
 // DOMBuilder for prototype
 DOM.Builder = {
 	tagFunc : function(tag) {
-	  return function() {
-	    var attrs, children; 
-	    if (arguments.length&gt;0) { 
-	      if (arguments[0].nodeName || 
-	        typeof arguments[0] == &quot;string&quot;) 
-	        children = arguments; 
-	      else { 
-	        attrs = arguments[0]; 
-	        children = Array.prototype.slice.call(arguments, 1); 
-	      };
-	    }
-	    return DOM.Builder.create(tag, attrs, children);
-	  };
+    return function() {
+     var attrs, children;
+     if (arguments.length&gt;0) {
+       if (arguments[0].constructor == Object) {
+         attrs = arguments[0];
+         children = Array.prototype.slice.call(arguments, 1);
+       } else {
+         children = arguments;
+       };
+       children = $A(children).flatten()
+     }
+     return DOM.Builder.create(tag, attrs, children);
+    };
   },
 	create : function(tag, attrs, children) {
 		attrs = attrs || {}; children = children || []; tag = tag.toLowerCase();
@@ -71,7 +71,8 @@ DOM.Builder.fromHTML = function(html) {
 // Event.onReady(callbackFunction);
 Object.extend(Event, {
   onReady : function(f) {
-    document.observe('dom:loaded', f);
+    if (document.body) f();
+    else document.observe('dom:loaded', f);
   }
 });
 
@@ -96,7 +97,7 @@ Event.addBehavior = function(rules) {
     Ajax.Responders.register({
       onComplete : function() { 
         if (Event.addBehavior.reassignAfterAjax) 
-          setTimeout(function() { ab.unload(); ab.load(ab.rules) }, 10);
+          setTimeout(function() { ab.reload() }, 10);
       }
     });
     ab.responderApplied = true;
@@ -145,6 +146,12 @@ Object.extend(Event.addBehavior, {
     this.cache = [];
   },
   
+  reload: function() {
+    var ab = Event.addBehavior;
+    ab.unload(); 
+    ab.load(ab.rules);
+  },
+  
   _wrapObserver: function(observer) {
     return function(event) {
       if (observer.call(this, event) === false) event.stop(); 
@@ -277,7 +284,7 @@ Remote.Form = Behavior.create(Remote.Base, {
   onclick : function(e) {
     var sourceElement = e.element();
     
-    if (sourceElement.nodeName.toLowerCase() == 'input' &amp;&amp; 
+    if (['input', 'button'].include(sourceElement.nodeName.toLowerCase()) &amp;&amp; 
         sourceElement.type == 'submit')
       this._submitButton = sourceElement;
   },</diff>
      <filename>public/javascripts/lowpro.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,909 @@
+== Hobo 0.7.5 ===
+
+   Restructuring the git repo -- all the different gems/plugins live
+   in the same repo now (github.com/tablatom/hobo)
+
+   Change to themes - the stylesheet and tablib are now called
+   (e.g. for the 'clean' theme) clean.css and clean.dryml
+
+   New rake task generate_tag_reference. Creates simple HTML reference
+   docs, including any inline documentation comments (found
+   immediately above the &lt;def&gt;)
+   
+    ModelController
+
+      Made it possible to have a custom #permission_denined and
+      #not_found in ApplicationController
+
+      Improved logic for figuring out redirect after destroy
+
+      Fix to bug where we would turn pagination off when we shouldn't
+    
+        We now have a list of mime-types that we *don't* paginate for
+        (can be extended)
+
+      hobo_index now silently skips pagination when the passed finder doesn't support it
+
+      Fix to auto_actions :except =&gt; :collections
+
+      Fix to rendering permission denied errors
+
+
+    Fixes for IE6
+
+      The rapid-pages now include the fabulous IE7.js when the client
+      is IE6.
+
+        http://dean.edwards.name/IE7/
+
+      The clean theme now works much better in IE6.
+
+      hobo-rapid.js -- fix displaying the empty-message after a removeButton
+
+
+    Hobo models
+
+      No longer defines Model[...] (in Hobo use Model.named(foo) instead)
+
+      Fix to MyHoboModel.defined_scopes sometimes returning nil
+
+      Allowing belongs_to and has_one associations to be set by name
+      during mass-assignment
+     
+        e.g. person.attributes = { :department =&gt; 'Sales' }
+
+      Automatic scopes -- give up silently if there's a problem with the DB
+
+      Fix to :managed =&gt; true option on has_many. No longer requires
+      the through association to be declared first.
+
+    Auto-completers are now working again. The tag to use is
+    &lt;name-one&gt;. Should be a how-to for this availabe soon.
+
+    In-place-edits are working again 
+
+    New controller method call_dryml_tag (added by DRYML template
+    handler). Calls a tag and returns the result as a string.
+
+    Fix to &lt;after-submit stay-here&gt; following a part update
+
+    Bundles -- fixes to class renaming
+
+    &lt;remote-method-button&gt; is working again
+
+    Refactored the DRYML parser to make it easier to parse DRYML in other contexts
+
+    Rapid:
+    
+      Improvements to wording on index-page
+
+      Display the users name, not login in the account-nav
+
+      Small tweaks to generic tags
+
+      Fix: The &lt;view&gt; for various types was reverting to a plain String view
+
+
+    Switching to dependency on hobofields and will_paginate as gems rather than plugins
+
+    Fix: DRYML parser works again with REXML versions back to 3.1.4
+
+    Fix: Hobo No longer depends on redcloth unless you're using markdown
+
+    Routing
+
+      Recover from ActiveRecord::StatementInvalid (routes may not be
+      declared). Without this you sometimes can't even run rake if
+      something is invalid
+
+      Don't do routing (and hence load models) during
+      script/generate. This *finally* fixes the dreaded 'User is
+      reserved by Rails' error
+
+
+== Hobo 0.7.4 ===
+
+Switching to a new style for the changelog. Now that we're using git
+the commit logs are a lot better, so this file is just going to
+contain edited highlights of the commit log.
+
+    Rapid: making &lt;view&gt; make more use of the to_html API from
+    HoboFields
+
+    Reworked REXML extensions. Now compatible with REXML 3.7.1.1
+    through 3.7.1.3
+
+    ModelController -- fix to chosing whether or not to paginate
+
+    Various IE Javascript fixes
+
+    Rapid, &lt;select-many&gt;: Additional params, and IE fix
+
+    Moving the part-contexts javascripts from the very end of the
+    output, to the end of &lt;body&gt;
+    
+      The script tag must now be output by the &lt;page&gt;, instead of
+      being appended automatically by DRYML. This is done in
+      &lt;simple-layout/&gt;
+
+    Rapid -- removing &lt;view for='percentage'&gt;
+
+    DRYML -- improved error reporting
+
+    Hobo::Model: fix -- we were breaking inheritence of validations
+
+    Model generators -- adding some helpful comments
+
+    Rapid: Fix to permission check for adding to primary collection on
+    &lt;show-page&gt;
+
+    Rapid: removing empty message from &lt;index-page&gt;
+
+    Rapid: Fix to enum-string editor
+
+    Rapid: Fix to auto-label in &lt;count&gt;
+
+    User model generator -- change create permission to allow admin to
+    create another admin
+
+    New lowpro version
+
+    New automatic css class 'with-owner' on default &lt;card/&gt;
+
+    Hobo::ModelController -- fix to automatic inclusion of reorder
+    action for models that act as list
+
+    Improvements to default &lt;card&gt;
+
+    Rapid javascript: try to automatically bring back the
+    empty-message on removing the last item from a collection
+
+    Scopes: fix to apply_scopes
+
+    Fix to &lt;name-for-collection&gt;
+
+    Rapid tags: Support for the empty-message on a collection
+    re-appearing automatically if all the items are removed by the
+    user
+
+    DRYML: scoped variables -- assigning to a variable now modifies
+    it's value in a parent scope if it's present in one
+
+    &lt;delete-button&gt; fix -- was incorrectly guessing 'in-place' during
+    ajax update
+
+    New tag &lt;name-for-collection&gt;
+
+    Fix to &lt;view for='text'/&gt;
+
+    Improvements to &lt;table-plus&gt;
+    
+      Ability to sort by name of 'this' when 'this' is one of the
+      fields
+    
+      Keep table headings in when the table is empty
+    
+      Don't include page nav if the collection doesn't support it
+
+    Improvements to rapid pages
+    
+      Allowing has_many associations in forms
+    
+      Allowing the primary collection on a show page to be provided by
+      an instance variable (so that filtering/searching/pagination are
+      supported)
+    
+      Tidy of &lt;show-page&gt;
+    
+      &lt;index-page&gt; -- improved wording
+
+      Fixes to &lt;index-page&gt;
+
+    &lt;account-nav&gt; make 'logged in as bob' a link to current_user
+    
+    Factored out default &lt;collection&gt; into &lt;base-collection&gt;, and
+    fixes to &lt;collection-preview&gt;
+    
+    &lt;base-card&gt; -- add 'edit' link
+
+    &lt;base-card&gt; change 'title' param to 'heading' and make it easier
+    to provide your own
+    
+    &lt;creation-details&gt; use a div rather than a span
+    
+    Fix to &lt;select-many&gt;
+
+    Fix to &lt;select-menu&gt;
+    
+    Fix to &lt;input for='datetime'&gt;
+    
+    Rapid forms: switching to &lt;select-one&gt; and &lt;select-many&gt; naming
+    
+    &lt;form&gt; -- don't include auth token on GET forms
+    
+    Rapid: &lt;belongs-to-menu-editor&gt; -- don't show 'View' link if the
+    thing is not linkable
+    
+    Rapid: adding sort attribute to &lt;belongs-to-menu-editor&gt;
+
+    Rapid: default 'no-filter' option for filter-menu
+    
+    Rapid: Fix to extraneous whitespace in &lt;you&gt;
+    
+    Rapid: better guess of label on &lt;count&gt;
+    
+    Rapid: Use association name rather than class name in css classes
+    on &lt;a&gt; tags (when available)
+
+    Rapid: Fix to use of &lt;else&gt; with &lt;a&gt;
+    
+    Removed 'all' method from ScopedProxy - delegates to the model
+    class instead
+    
+    Fix to origin_attribute on scoped associations
+
+    Fix to with_abc automatic scopes
+
+    New automatic scopes 'include' and 'search', and improvements to
+    order_by scope
+    
+    Scopes: fix to parameterised scopes on associations
+
+    Scopes: new apply_scopes method for models and has_many
+    associations
+    
+        Pass a hash of scope-name =&gt; scope-arguments (single valur or
+        array), the result will be scoped accordingly, but only if the
+        first argument to the scope is not blank. It's designed to be
+        used from controllers: if a param (e.g. a search or filter) is
+        not included in the request, the scope is not applied
+
+    Hobo::ModelController -- fix to permission denied response for
+    ajax actions
+
+    Hobo::ModelController -- Fix to flash message after update
+    
+    Hobo::ModelController -- fix to redirection after submitting a
+    form
+
+    Hobo::ModelController -- cleaning up sorting and filtering
+    
+    Hobo::ModelController -- make reorder one of the automatic
+    :write_only actions if the model has the position_column method
+
+    &lt;with-fields&gt; better error reporting
+
+    Hobo::ModelController -- adding automatic reorder action
+    
+      Works with acts_as_list and scriptaculous drag and drop
+      re-ordering
+    
+    Hobo::Model adding :managed =&gt; true option to has_many
+    
+      Only valid with the :through option. Records of the joining
+      model will be created and destroyed as required when this record
+      is saved, in order to honour the contents of the association at
+      save time.
+    
+    Hobo::Model -- adding .user_update
+
+    Hobo::Model -- fix to user_find
+    
+    Hobo::HoboHelper -- #map_this (used by repeat) will set this_key
+    when iterating over a hash
+    
+    DRYML: made this_field_reflection do a better job of figuring out
+    the current reflection
+    
+    DRYML: adding this_key -- gives you the current key when
+    &lt;repeat&gt;ing on a hash
+    
+    DRYML: Error message for invalid attributes on parameter tags,
+    e.g. &lt;foo: if-'...'&gt;
+    
+    Hobo module - fix to permission checks on unexpected objects
+
+    Hobo module -- always return false for can_create? on has_many
+    associations that are not 'simple'
+    
+      (i.e. they have conditions)
+    
+    DRYML: Fix for replacing an overridden tag parameter
+    
+    Rapid pages: new submit label on show page (add to collection)
+    
+    Rapid pages: Add param to &lt;show-page&gt;
+    
+    Rapid pages: by default, append app-name to every title, override
+    with full-title attribute (&lt;base-page&gt;)
+    
+    Rapid pages: &lt;show-page&gt; -- update primary collection count when
+    an item is deleted
+    
+    Rapid pages: fix on &lt;show-page&gt;
+    
+    Rapid pages: make edit link on show-page check if the edit action
+    is linkable
+    
+    Rapid forms: adding inputs (just textareas) for textile and
+    markdown
+    
+    front_controller generator -- use &lt;collection-preview&gt; in
+    index.dryml
+    
+    Rapid generics: adding support for &lt;collection with='&amp;MyModel'/&gt;
+
+    Rapid tags: switching to &lt;main-nav&gt; tag, called from simple-layout
+    
+    hobo_model_resource generator -- removing creating of (rails
+    style) resource route
+
+    Improvements to rapid pages
+    
+      Allowing has_many associations in forms
+    
+      Allowing the primary collection on a show page to be provided by
+      an instance variable (so that filtering/searching/pagination are
+      supported)
+    
+    Rapid: Fix to use of &lt;else&gt; with &lt;a&gt;
+
+    Removed 'all' method from ScopedProxy - delegates to the model
+    class instead
+
+    Fix to origin_attribute on scoped associations
+
+    Rapid tags: disabled &lt;belongs-to-view&gt;, new &lt;view
+    for='ActiveRecord::Base'&gt; is used now
+
+    Hobo::Model -- fix to reverse_refelction
+
+    Rapid tags: Adding &lt;head:&gt; and &lt;foot:&gt; params to &lt;ul&gt;
+
+    Rapid tags: Adding params to &lt;select-many&gt;
+
+    Rapid tags: adding support for &lt;ul empty&gt; (force the &lt;ul&gt; to
+    appear even if empty)
+
+    Fix: automatic scopes not_ and &quot;association name&quot; were not being
+    created
+
+    Rapid forms: Renaming &lt;name-array-input&gt; to &lt;select-many&gt; (and
+    improvements)
+
+    Hobo::Model -- adding assigment by arrays of names to has_many
+    associations
+
+    Hobo::Model -- adding Model.manage_join_records
+
+    Routing: fix -- don't try to load assemble.rb if
+    ApplicationController not defined
+
+    Rapid pages: Fix to body class on &lt;new-page&gt;
+
+    Rapid pages: fix to show page
+
+    Rapid pages -- fixing pluralisation of &lt;index-page&gt; title
+
+    Adding support for 'this' psuedo-field to &lt;with-fields&gt; and
+    &lt;with-fields-names&gt;
+    
+      In particular, this makes it possible to include the object
+      itself in a table plus column
+  
+    Adding a general &lt;view&gt; for ActiveRecord objects (just &lt;a/&gt;)
+
+    Allowing lookup of polymorphic tags to include ActiveRecord::Base
+    or Object as a catch-all
+
+    Rapid generics: make collection preview hide the show-all link if
+    there are none
+
+    &lt;page-nav&gt; now uses will_paginate's helper to provide much better
+    page navigation than before.
+
+
+=== Release 0.7.3 ===
+
+hobo command -- options are now:
+
+  --user-model &lt;model-name-or-false&gt;
+  --svn                               # Use 'svn co' to checkout Hobo
+  --create-dbs                        # Run rake db:create:all
+  --hobo-src &lt;path to hobo src&gt;
+  -d | --database &lt;database&gt;          # e.g. mysql, sqlite
+
+
+Core extentions
+
+  Extracted from Hobo into new project: HoboSupport
+
+  Some notable changes:
+
+    #every has gone: users.every(:name) is now users.*.name
+
+
+hobo generator
+ 
+  application.dryml now has a generated &lt;app-name&gt; tag
+
+
+hobo_front_controller generator
+
+  Generated &quot;search&quot; route is now called &quot;site_search&quot;
+
+  Removed unused --no-user option
+
+
+hobo_migration_generator
+
+  Removed from Hobo - now part of the HoboFields spinn-off project
+
+
+New generator hobo_model_resource creates a model + controller pair
+
+
+Rapid Javascripts
+
+  Default ajax message changed to &quot;Saving...&quot;
+
+  Hobo.ajaxRequest now takes the message as an option (was the second argument)
+
+  Fix to showing the ajax spinner in the right place in IE
+
+  Hobo.addUrlParams now has an option to remove specified parameters
+
+  Fixes to HasManyThroughInput behaviour
+
+  New behaviour supporting filtering (e.g. on index pages) using &lt;select&gt; tags
+
+
+Rich data types
+
+  These are all part of the HoboFields project now
+
+  Booleans are now represented by the type Hobo::Boolean which is still part of Hobo
+
+
+Active record extensions
+
+  Monkey-patch to make AR complain less about missing classes for
+  polymorphic associations
+
+
+The block-based &quot;composable query mechanism&quot; is gone. Named scopes are much better.
+
+
+Hobo's original implementation of what later became &quot;Sexy Migrations&quot; is gone.
+
+
+Hobo module utility methods
+
+  Hobo.field_types and Hobo.symbolic_type_name are gone - now part of
+  HoboFields
+
+  Fixes to can_edit?
+
+
+Site-search:
+
+  Now automatically skips searching of non-linkable models.
+
+  Can now be passed an array of models (including scoped finders) to search
+
+  Now renders &lt;search-card&gt; instead of &lt;card&gt;
+
+
+New feature: Hobo::DevController
+
+  A controller that adds developer support features (not in production
+  mode). For now it just adds a method that can be used to change the current_user:
+
+    /dev/set_current_user?name=Fred+Bloggs
+
+    Very useful
+
+
+Hobo Routing
+
+  Hobo::ModelRouter.linkable? can now be used with all the routes
+  created by Hobo.
+
+
+Hobo helpers
+
+  object_url now always returns nil if the URL requested is not a
+  route known to Hobo routing
+
+
+  object_url never includes _method= in the query string
+
+  dom_id is now moved to DRYML's TemplateEnvironment
+
+  can_view? now utilises some simple cacheing
+
+  signup_url now defaults to the 
+
+  linkable? no longer accepts an array as parameter
+
+
+Rapid helpers
+
+  ajax_updater(url_or_form, message, update, options) is now
+  ajax_updater(url_or_form, update, options) (message has become an
+  option)
+
+
+DRYML
+
+  DRYML integration changes -- this is a start to the work of
+  extracting DRYML from Hobo.
+
+    The fallback from a template file to a tag, e.g. from index.dryml
+    to &lt;index-page/&gt; is now handled by DRYML, not by Hobo's model
+    controller. You can customise the chosed tag by calling
+
+      dryml_fallback_tag(&quot;my-tag&quot;) from your controller action
+
+    The default DRYML context is now the value returned by
+    #dryml_context (was previously the value of @this). #dryml_context
+    returns @this by default but can be overridden.
+
+  &lt;include src=&quot;mytags&quot; bundle=&quot;foo&quot;&gt;
+
+    includes a taglib from the plugin that the 'foo' bundle came from,
+    and puts into effect any class renames from that bundle (for
+    polymorphic tags).
+
+  Fix - tags with capitalised names now work
+
+  &lt;my-param:&gt;&lt;/my-param:&gt; can now be used to clear the contents of a
+  parameter. It's not the same as &lt;my-param:/&gt; (which does nothing)
+
+  New psuedo parameters for insterting content in and around
+  parameters. e.g. for a param 'title':
+
+    &lt;before-title:&gt; and &lt;after-title:&gt; for insterting content
+    immediately before and after the parameter.
+
+    &lt;append-content:&gt; and &lt;prepend-content:&gt; for inserting content at
+    the begining and end of the default content.
+
+    Note these are all just syntactic sugar for things you can do
+    already with &lt;param-content/&gt; and &lt;title: replace&gt; / 
+    &lt;title: restore/&gt;
+
+  When changing the context with the ':' shorthand, now use dashes,
+  not underscores (the idea being that underscores are soley for use
+  in Ruby code
+
+    e.g. &lt;with:my-field&gt;
+
+  Control attributes &lt;my-tag if&gt; is equivalent to &lt;my-tag if=&quot;&amp;this&quot;/&gt;
+  (remember these test for blank? / not blank?, not Ruby trueness)
+    
+  this_type now never returns AssociationReflections,
+  this_field_reflection does
+
+  this_type now returns Hobo::Boolean for boolean types.
+
+  this_field_dom_id is now just dom_id -- pass an object and an
+  attriubte, or nothing to default to this and this_field
+
+  Can now do merge-params=&quot;param1, param2&quot; to merge just the named
+  params
+
+  Fix: &lt;foo:foo&gt; (i.e. field name same as tag name)
+
+  Fix: using alias-of with a reserved word was broken
+
+  
+Hobo models
+
+  New method Model.named to find things by name,
+  e.g. Category.named(&quot;Cars&quot;), also aliased as Category[&quot;Cars&quot;]
+
+  Various new methods to support permission. This moves much of the
+  logic into the model (from Hobo::ModelController). These methods are
+  all passed, as the first argument, the user performing the action. A
+  Hobo::Model::PermissionDeniedError is raised if the permission check
+  fails.
+
+    Class methods:
+
+    user_find    -- find with view-permission check
+    user_new     -- new with create-permission check
+    user_create  -- create with create-permission check
+
+    user_can_create? -- test to see if a user is allowed to create the model.
+
+    Instace methods:
+
+    user_can_create?
+    user_save_changes
+    user_view
+    user_destroy
+
+  Models now get a new_foo method for each has_one :foo
+
+  The old id_name system is gone. Will be coming back in various
+  guises (e.g. see .named above)
+
+  def_scope implementation factored out into separate modules
+
+  New metod #get_creator returns the value of the creator attribute
+  (if there is one)
+
+  Various features exrtacted and now part of HoboFields
+
+
+Scopes
+
+  Note: Hobo will probably switch to has_finder which provides
+  (nearly) all the functionality of Hobo's scopes and has a superior
+  implementation. We'll extend has_finder to add the bits that it is
+  missing.
+
+  The implementation of scopes has been factored out into separate models.
+
+  Large new set of automatically defined scopes. These are defined
+  automatically when you call them for the first time, much like the
+  magic finders in ActiveRecord (e.g. find_by_name_and_address):
+
+    For every has_many relationship (e.g. tags)
+
+      with_tag(t)             -- find records that have tag c
+      with_tags(a, b, c)      -- find records that have all these tags
+      without_tag(t)
+      without_tags(a, b, c)
+
+    For every belongs_to and has_one (e.g. manager)
+
+      manager_is(m)
+      manager_is_not(m)
+
+    For every regular field (e.g. name)
+
+      name_is(x)
+      name_is_not(x)
+
+    For textual fields
+
+      name_contains(x)
+      name_does_not_contain(x)
+      name_starts(x)
+      name_does_not_start(x)
+      name_ends(x)
+      name_does_not_end(x)
+
+    For boolean fields (e.g. published)
+
+      published
+      not_published
+      
+    For the various date/time columns (these must end _at, e.g. pulished_at)
+
+      published_before(x)
+      published_after(x)
+      published_between(x, y)
+
+    And finally
+
+      order_by(field_name) -- add an ORDER BY clause
+      limit(n)             -- add a LIMIT clause
+      recent(n)            -- orders by created_at and limits to n records
+      
+
+
+Hobo model controller
+
+  Permission and not-found errors are now handled centrally using the
+  new rescue_from declaration in Rails. No need to worry about these
+  in individual actions.
+
+  All the hobo actions (hobo_show, hobo_new etc) are greatly
+  simplified. Much of the logic has been moved elsewhere, so it's now
+  a lot easier to avoid using those actions at all and still get the
+  usual features such as permission checks, fallback on page tags etc.
+
+    hobo_index now takes a &quot;finder&quot; (model class or scoped model
+    class) as the first argument. If you want to retrieve the
+    collection yourself, there's no reason to call hobo_index at all -
+    just write a regular Rails style action.
+
+    The hobo_actions don't set any extra instance variables any more
+    beyond @this. e.g. hobo_show_collection doesn't set @owner,
+    because you can now use this.origin
+
+  As part of the previous change, #not_allowed? is gone.
+
+  Some work on autocomplete - might be working now :-)
+
+  auto_actions -- can now say
+
+    auto_actions :write_only # just gives you create, update and destroy
+    auto_actions :read_only  # gives you all except the above three
+
+  Support for scopes on declarative index actions:
+
+    index_action :scope =&gt; :my_scope 
+
+  New method filter_by for easily adding filtering to index actions, e.g.
+
+    # assuming category and price_range are scopes defined on the current model
+    def index
+      finder = filter_by(:category =&gt; params[:category], price_range =&gt; [params[:min_price], params[:max_price])
+      hobo_index finder
+    end
+
+  The old data_filters stuff is gone. This was based on the
+  &quot;composable query&quot; thing which is gone too (see above).
+
+  Dependency on classic_pagination is gone. Now uses will_paginate
+
+
+
+User model
+
+  Hobo::UserController.user_models is now Hobo::User.user_models
+
+  New method Hobo::User.default_user_model returns the user model used
+  in various places if no model is specified.
+
+  Now requires the current password to be provided when changing the
+  password (at the model level and in the related rapid-pages)
+
+  User.login_attr renamed to User.login_attribute
+
+
+User controller
+
+  Now includes the rapid_user_pages taglib (these have been separated
+  from the main rapid-pages taglib)
+
+  New helper: logout_current_user
+
+
+Bundles
+
+  Support for multiple bundles per plugin -- bundles can now be
+  selective about which models and controllers they include.
+
+  Adding support for:   belongs_to :foo, :polymorphic =&gt; :optional
+  
+  Magic option names are now available inside the model and controller *instances*
+
+  Bundle options with defaults now work with nested option hashes
+
+  belongs_to in a bundle model can now be given an :alias option - an
+  alias of the (parameterised) belongs_to is created so that the model
+  has a known API
+
+
+Core DRYML tags
+
+  &lt;wrap&gt; can be given a parameter=&quot;...&quot; attribute to wrap the content
+  in a template using the given parameter name.
+
+  &lt;repeat&gt; can now be used with &lt;else&gt; (i.e. the collection was empty)
+
+
+Rapid tags: general
+
+  New standardised API for getting metadata from collections
+
+    #association_name returns th, er, name of the association. If
+    called on a named scope it returns the original association name,
+    not the scope name
+
+    #member_class returns the (expected) class of items in the array
+
+    #origin returns the object from which the collection was obtained
+
+    #origin_attribute returns the name of the attribute from which the
+    collection was obtained
+
+    So, unless something has changed:
+     
+      collection.origin.send(collection.origin_attribute) == collection
+
+    This API is made available on all association proxies, regular
+    arrays and will_paginate collections.
+
+  New taglib - rapid_generics - provides various tags used by the
+  default pages. These tags are designed to work generically with your
+  models, but can also be customised of course.
+
+  &lt;table&gt; -- the &quot;Edit&quot; link now goes to action=&quot;edit&quot; (was linking to
+  the show page)
+
+  &lt;count&gt; is now better at guessing the label to use, also now
+  supports &lt;count lowercase/&gt; to downcase the label.
+
+  &lt;clearer&gt; is gone - hooray for overflow: hidden
+
+  Added do to &lt;you&gt; as in: &lt;you do/&gt;
+
+  New tag &lt;filter-menu/&gt; creates a form with a &lt;select&gt;. Used for
+  adding menu-based filters to index pages.
+
+  New tag &lt;comma-list&gt;
+
+
+Rapid tags: editing
+
+  &lt;editor&gt; now merges params and attributes (e.g. you can add your own css classes)
+
+  Most ajax controls now just give &quot;Saving...&quot; as the default ajax
+  message.
+
+
+Rapid tags: forms
+
+  &lt;form&gt; (without an action attribute) now renders nothing if the
+  calculated action is not linkable
+
+  If you provide action=&quot;...&quot; to &lt;form&gt; (i.e. a manual form) the
+  automatic css classes are not added.
+
+  &lt;delete-button&gt; now renders nothing if the destory action is not linkable
+
+  &lt;delete-button&gt; now automatically switche to non-in-place delete if
+  the thing being deleted is the top-level context if the page.
+
+  New tag &lt;select-menu&gt;
+
+  &lt;remote-method-button&gt; now switches to in-place mode if you give any
+  ajax attribute (e.g. success=&quot;...&quot;)
+
+  &lt;after-submit&gt; -- can now do
+
+    &lt;after-submit go-back&gt; (requires session[:previous_uri])
+    
+    and
+
+    &lt;after-submit stay-here&gt;
+
+  
+Rapid tags: navigation
+
+  Now assumes will_paginate style pagination instead of classic_pagination
+
+  &lt;account-nav&gt; now proivides a link to the account page by default
+
+  &lt;account-nav&gt; now provides a complete set of parameters for customisation
+
+
+Rapid tags: pages
+
+  login, signup and account-disabled pages have been moved out to
+  rapid-user-pages
+
+  simple-layout -- the main-nav parameter is now on a wrapper around
+  &lt;magic-nav&gt; rather than actually on &lt;magic-nav&gt;, so this now works
+  as you'd expect:
+
+    &lt;main-nav:&gt; --- your nav here --- &lt;/main-nav:&gt;
+
+  aside-layout -- the aside now appears in the output even if it's
+  empty
+
+  &lt;show-page&gt; -- various improvements and new parameters
+
+  &lt;permission-denied-page&gt; now sets a body class, and the message can
+  be changed with an attribute.
+
+  &lt;app-name&gt; is gone -- this is now automatically generated in your
+  application.dryml (you may need to manually add this to existing
+  apps)
+
+
+Rapid tags: plus
+
+  New tag &lt;change-password-form&gt;
+
+
+
 === Release 0.7.2 ===
 
 Migration generator</diff>
      <filename>vendor/plugins/hobo/CHANGES.txt</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,8 @@ require 'rake'
 require 'rake/rdoctask'
 require 'rake/testtask'
 
+load &quot;tasks/generate_tag_reference.rb&quot;
+
 desc 'Default: run specs.'
 task :default =&gt; :spec
 </diff>
      <filename>vendor/plugins/hobo/Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -7,19 +7,21 @@ class HoboGenerator &lt; Rails::Generator::Base
       route = &quot;  Hobo.add_routes(map)\n&quot;
 
       route_src = File.read(routes_path)
-      return if route_src.include?(route)
-
-      head = &quot;ActionController::Routing::Routes.draw do |map|&quot;
-      route_src.sub!(head, head + &quot;\n\n&quot; + route)
-      File.open(routes_path, 'w') {|f| f.write(route_src) }
+      
+      unless route_src.include?(route)
+        head = &quot;ActionController::Routing::Routes.draw do |map|&quot;
+        route_src.sub!(head, head + &quot;\n\n&quot; + route)
+        File.open(routes_path, 'w') {|f| f.write(route_src) }
+      end
     end
 
     record do |m|
       m.directory File.join(&quot;app/views/taglibs&quot;)
       m.directory File.join(&quot;app/views/taglibs/themes&quot;)
       m.directory File.join(&quot;public/hobothemes&quot;)
-      m.file &quot;application.dryml&quot;, File.join(&quot;app/views/taglibs/application.dryml&quot;)
+      m.template &quot;application.dryml&quot;, File.join(&quot;app/views/taglibs/application.dryml&quot;)
       m.file &quot;guest.rb&quot;, File.join(&quot;app/models/guest.rb&quot;)
+      m.file &quot;dryml-support.js&quot;, File.join(&quot;public/javascripts/dryml-support.js&quot;)
     end
   end
 </diff>
      <filename>vendor/plugins/hobo/generators/hobo/hobo_generator.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,2 +1,5 @@
 &lt;!-- Define your application-wide tags here --&gt;
 
+&lt;def tag=&quot;app-name&quot;&gt;&lt;%= File.basename(Dir.chdir(RAILS_ROOT) { Dir.getwd }).strip.titleize %&gt;&lt;/def&gt;
+
+</diff>
      <filename>vendor/plugins/hobo/generators/hobo/templates/application.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 class HoboFrontControllerGenerator &lt; Rails::Generator::NamedBase
 
-  default_options :no_user =&gt; false, :delete_index =&gt; false, :add_routes =&gt; false
+  default_options :delete_index =&gt; false, :add_routes =&gt; false
 
   def full_class_path
     class_path.blank? ? file_name : File.join(class_path, file_name)
@@ -34,8 +34,7 @@ class HoboFrontControllerGenerator &lt; Rails::Generator::NamedBase
                  File.join('app/helpers', class_path, &quot;#{file_name}_helper.rb&quot;))
 
 
-      pages = options[:no_user] ? %w{index search} : %w{index search}
-      for page in pages
+      for page in %w{index search}
         m.template(&quot;#{page}.dryml&quot;, File.join('app/views', class_path, file_name, &quot;#{page}.dryml&quot;))
       end
     end
@@ -49,7 +48,7 @@ class HoboFrontControllerGenerator &lt; Rails::Generator::NamedBase
     routes_path = File.join(RAILS_ROOT, &quot;config/routes.rb&quot;)
     name = full_class_path
 
-    route = (&quot;  map.search  'search', :controller =&gt; '#{name}', :action =&gt; 'search'\n&quot; +
+    route = (&quot;  map.site_search  'search', :controller =&gt; '#{name}', :action =&gt; 'search'\n&quot; +
              &quot;  map.homepage '', :controller =&gt; '#{name}', :action =&gt; 'index'&quot;)
 
     route_src = File.read(routes_path)
@@ -68,7 +67,7 @@ class HoboFrontControllerGenerator &lt; Rails::Generator::NamedBase
 
   protected
     def banner
-      &quot;Usage: #{$0} #{spec.name} &lt;controller-name&gt; [--add-routes] [--no-user] [--delete-index]&quot;
+      &quot;Usage: #{$0} #{spec.name} &lt;controller-name&gt; [--add-routes] [--delete-index]&quot;
     end
 
     def add_options!(opt)</diff>
      <filename>vendor/plugins/hobo/generators/hobo_front_controller/hobo_front_controller_generator.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,15 +15,8 @@
   &lt;content-body:&gt;
     &lt;ul with=&quot;&amp;front_models&quot;&gt;
       &lt;li:&gt;
-        &lt;header&gt;&lt;h2&gt;&lt;a/&gt;&lt;/h2&gt;&lt;/header&gt;
-        &lt;section&gt;
-          &lt;p if=&quot;&amp;this.count == 0&quot;&gt;There are no &lt;type-name plural/&gt;&lt;/p&gt;
-          &lt;else&gt;
-            &lt;card repeat=&quot;&amp;select_viewable(this.recent.all)&quot;/&gt;
-            &lt;p&gt;&lt;a&gt;More&lt;/a&gt; (&lt;count/&gt;)&lt;/p&gt;
-          &lt;/else&gt;
-          &lt;p if=&quot;&amp;can_create? &amp;&amp; linkable?(:new)&quot;&gt;Create a &lt;a to=&quot;&amp;this&quot; action=&quot;new&quot;/&gt;.&lt;/p&gt;
-        &lt;/section&gt;
+        &lt;collection-preview/&gt;
+        &lt;p if=&quot;&amp;can_create? &amp;&amp; linkable?(:new)&quot;&gt;Create a &lt;a to=&quot;&amp;this&quot; action=&quot;new&quot;/&gt;.&lt;/p&gt;
       &lt;/li:&gt;
     &lt;/ul&gt;  
   &lt;/content-body&gt;</diff>
      <filename>vendor/plugins/hobo/generators/hobo_front_controller/templates/index.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 class &lt;%= class_name %&gt; &lt; ActiveRecord::Base
 
-  hobo_model
+  hobo_model # Don't put anything above this
 
   fields do
 &lt;% for attribute in attributes -%&gt;</diff>
      <filename>vendor/plugins/hobo/generators/hobo_model/templates/model.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,6 +12,7 @@ class HoboRapidGenerator &lt; Hobo::Generator
     record do |m|
       m.file &quot;hobo-rapid.js&quot;, &quot;public/javascripts/hobo-rapid.js&quot;
       m.file &quot;lowpro.js&quot;, &quot;public/javascripts/lowpro.js&quot;
+      m.file &quot;IE7.js&quot;, &quot;public/javascripts/IE7.js&quot;
       m.file &quot;reset.css&quot;, &quot;public/stylesheets/reset.css&quot;
       m.file &quot;hobo-rapid.css&quot;, &quot;public/stylesheets/hobo-rapid.css&quot;
       create_all(m, &quot;themes/clean/public&quot;, &quot;public/hobothemes/clean&quot;)</diff>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/hobo_rapid_generator.rb</filename>
    </modified>
    <modified>
      <diff>@@ -37,7 +37,7 @@ var Hobo = {
 
         var opts = Object.merge(options || {}, { params: params})
         Hobo.ajaxRequest(Hobo.putUrl(el),
-                         el.getAttribute(&quot;hobo-ajax-message&quot;) || &quot;Changing...&quot;,
+                         el.getAttribute(&quot;hobo-ajax-message&quot;) || &quot;Saving...&quot;,
                          updates,
                          opts)
     },
@@ -49,8 +49,7 @@ var Hobo = {
             updates.each(function(id_or_el) {
                 var el = $(id_or_el)
                 if (el) { // ignore update of parts that do not exist
-                    var partDomId
-                    partDomId = el.id
+                    var partDomId = el.id
                     if (!hoboParts[partDomId]) { throw &quot;Update of dom-id that is not a part: &quot; + partDomId }
                     params.push(&quot;render[&quot;+i+&quot;][part_context]=&quot; + encodeURIComponent(hoboParts[partDomId]))
                     params.push(&quot;render[&quot;+i+&quot;][id]=&quot; + partDomId)
@@ -73,11 +72,12 @@ var Hobo = {
         return params.join('&amp;')
     },
 
-    ajaxRequest: function(url_or_form, message, updates, options) {
+    ajaxRequest: function(url_or_form, updates, options) {
         options = Object.merge({ asynchronous:true,
                                  evalScripts:true,
                                  resetForm: false,
-                                 refocusForm: false
+                                 refocusForm: false,
+                                 message: &quot;Saving...&quot;
                                }, options)
         if (typeof url_or_form == &quot;string&quot;) {
             var url = url_or_form
@@ -104,7 +104,7 @@ var Hobo = {
             params.push(Form.serialize(form))
         }
 
-        Hobo.showSpinner(message, options.spinnerNextTo)
+        Hobo.showSpinner(options.message, options.spinnerNextTo)
         var complete = function() {
             if (form &amp;&amp; options.resetForm) form.reset();
             Hobo.hideSpinner();
@@ -112,6 +112,7 @@ var Hobo = {
             if (options.onComplete)
                 options.onComplete.apply(this, arguments)
             if (form &amp;&amp; options.refocusForm) Form.focusFirstElement(form)
+            Event.addBehavior.reload()
         }
         if (options.method &amp;&amp; options.method.toLowerCase() == &quot;put&quot;) {
             delete options.method
@@ -184,30 +185,30 @@ var Hobo = {
         var updateParams = Hobo.ajaxUpdateParams(updates, [{id: id,
                                                             result: 'new_field_value',
                                                             func: &quot;Hobo.onFieldEditComplete&quot;}])
-        opts = {okButton: false,
-                cancelLink: false,
-                submitOnBlur: true,
-                evalScripts: true,
-                htmlResponse: false,
-                ajaxOptions: { method: &quot;put&quot; },
-                onEnterHover: null,
-                onLeaveHover: null,
-                callback: function(form, val) {
-                    old = val
-                    return (Hobo.fieldSetParam(el, val) + &quot;&amp;&quot; + updateParams)
-                },
-                onFailure: function(resp) { 
-                    alert(resp.responseText); el.innerHTML = old
-                },
-                onEnterEditMode: function() {
-                    var blank_message = el.getAttribute(&quot;hobo-blank-message&quot;)
-                    if (el.innerHTML.gsub(&quot;&amp;nbsp;&quot;, &quot; &quot;) == blank_message) {
-                        el.innerHTML = &quot;&quot; 
-                    } else {
-                        Hobo.ipeOldValues[el.id] = el.innerHTML
+        var opts = {okButton: false,
+                    cancelLink: false,
+                    submitOnBlur: true,
+                    evalScripts: true,
+                    htmlResponse: false,
+                    ajaxOptions: { method: &quot;put&quot; },
+                    onEnterHover: null,
+                    onLeaveHover: null,
+                    callback: function(form, val) {
+                        old = val
+                        return (Hobo.fieldSetParam(el, val) + &quot;&amp;&quot; + updateParams)
+                    },
+                    onFailure: function(resp) { 
+                        alert(resp.responseText); el.innerHTML = old
+                    },
+                    onEnterEditMode: function() {
+                        var blank_message = el.getAttribute(&quot;hobo-blank-message&quot;)
+                        if (el.innerHTML.gsub(&quot;&amp;nbsp;&quot;, &quot; &quot;) == blank_message) {
+                            el.innerHTML = &quot;&quot; 
+                        } else {
+                            Hobo.ipeOldValues[el.id] = el.innerHTML
+                        }
                     }
-                }
-               }
+                   }
         Object.extend(opts, options)
         return new Ajax.InPlaceEditor(el, Hobo.putUrl(el), opts)
     },
@@ -219,14 +220,14 @@ var Hobo = {
         }
 
         select(&quot;.in-place-textfield-bhv&quot;).each(function (el) {
-            ipe = Hobo._makeInPlaceEditor(el)
+            var ipe = Hobo._makeInPlaceEditor(el)
             ipe.getText = function() {
                 return this.element.innerHTML.gsub(/&lt;br\s*\/?&gt;/, &quot;\n&quot;).unescapeHTML()
             }
         })
 
         select(&quot;.in-place-textarea-bhv&quot;).each(function (el) {
-            ipe = Hobo._makeInPlaceEditor(el, {rows: 2})
+            var ipe = Hobo._makeInPlaceEditor(el, {rows: 2})
             ipe.getText = function() {
                 return this.element.innerHTML.gsub(/&lt;br\s*\/?&gt;/, &quot;\n&quot;).unescapeHTML()
             }
@@ -258,19 +259,10 @@ var Hobo = {
 
         select(&quot;select.number-editor-bhv&quot;).each(function(el) {
             el.onchange = function() {
-                Hobo.ajaxSetFieldForElement(el, el.value)
+                Hobo.ajaxSetFieldForElement(el, $F(el))
             }
         })
                                                 
-        select(&quot;.autocomplete-bhv&quot;).each(function (el) {
-            options = {paramName: &quot;query&quot;, minChars: 3, method: 'get' }
-            if (el.hasClassName(&quot;autosubmit&quot;)) {
-                options.afterUpdateElement = function(el, item) { el.form.onsubmit(); }
-            }
-            new Ajax.Autocompleter(el, el.id + &quot;-completions&quot;, el.getAttribute(&quot;autocomplete-url&quot;),
-                                   options);
-        });
-
         select(&quot;.search-bhv&quot;).each(function(el) {
             new Form.Element.Observer(el, 1.0, function() { Hobo.doSearch(el) })
         });
@@ -314,6 +306,14 @@ var Hobo = {
         return urlBase + &quot;/&quot; + Hobo.pluralise(spec.name) + &quot;/&quot; + spec.id + &quot;?_method=PUT&quot;
     },
 
+    
+    urlForId: function(id) {
+        var spec = Hobo.parseId(id)
+        var url = urlBase + &quot;/&quot; + Hobo.pluralise(spec.name)
+        if (spec.id) { url += &quot;/&quot; + spec.id }
+        return url
+    },
+
         
     fieldSetParam: function(el, val) {
         spec = Hobo.parseFieldId(el)
@@ -324,41 +324,62 @@ var Hobo = {
         return res
     },
 
+
     fadeObjectElement: function(el) {
-        new Effect.Fade(Hobo.objectElementFor(el),
-                        { duration: 0.5,
-                          afterFinish: function (ef) { ef.element.remove() } });
+        var fadeEl = Hobo.objectElementFor(el)
+        new Effect.Fade(fadeEl, { duration: 0.5, afterFinish: function (ef) { 
+            ef.element.remove() 
+        } });
+        Hobo.showEmptyMessageAfterLastRemove(fadeEl)
     },
 
+
     removeButton: function(el, url, updates, options) {
         if (options.fade == null) { options.fade = true; }
         if (options.confirm == null) { options.fade = &quot;Are you sure?&quot;; }
 
         if (options.confirm == false || confirm(options.confirm)) {
-            objEl = Hobo.objectElementFor(el)
+            var objEl = Hobo.objectElementFor(el)
             Hobo.showSpinner('Removing');
             function complete() {
                 if (options.fade) { Hobo.fadeObjectElement(el) }
                 Hobo.hideSpinner()
             }
             if (updates &amp;&amp; updates.length &gt; 0) {
-                new Hobo.ajaxRequest(url, &quot;Removing&quot;, updates, { method:'delete',
-                                                                 onComplete: complete});
+                new Hobo.ajaxRequest(url, updates, { method:'delete', message: &quot;Removing...&quot;, onComplete: complete});
             } else {
-                new Ajax.Request(url, {asynchronous:true, evalScripts:true, method:'delete',
-                                       onComplete: complete});
+                var ajaxOptions = {asynchronous:true, evalScripts:true, method:'delete', onComplete: complete}
+                if (typeof(formAuthToken) != &quot;undefined&quot;) {
+                    ajaxOptions.parameters = formAuthToken.name + &quot;=&quot; + formAuthToken.value
+                }
+                new Ajax.Request(url, ajaxOptions);
             }
         }
     },
 
 
+    showEmptyMessageAfterLastRemove: function(el) {
+        var empty
+        var container = $(el.parentNode)
+        if (container.getElementsByTagName(el.nodeName).length == 1 &amp;&amp;
+            (empty = container.next('.empty-collection-message'))) {
+            new Effect.Appear(empty, {delay:0.3})
+        }
+    },
+
+
     parseFieldId: function(el) {
         id = el.getAttribute(&quot;hobo-model-id&quot;)
-        if (!id) return
-        m = id.match(/^([a-z_]+)_([0-9]+)_([a-z_]+)$/)
+        return id &amp;&amp; parseId(id)
+    },
+
+
+    parseId: function(id) {
+        m = id.match(/^([a-z_]+)_([0-9]+)(?:_([a-z_]+))?$/)
         if (m) return { name: m[1], id: m[2], field: m[3] }
     },
 
+
     appendRow: function(el, rowSrc) {
         // IE friendly method to add a &lt;tr&gt; (from html source) to a table
         // el should be an element that contains *only* a table
@@ -367,6 +388,7 @@ var Hobo = {
         Hobo.applyEvents(el)
     },
 
+
     objectElementFor: function(el) {
         var m
         while(el.getAttribute) {
@@ -385,7 +407,7 @@ var Hobo = {
         if(t = $('ajax-progress-text')) Element.update(t, message);
         if(e = $('ajax-progress')) {
             if (nextTo) {
-                var pos = nextTo.cumulativeOffset()
+                var pos = $(nextTo).cumulativeOffset()
                 e.style.top = pos.top + &quot;px&quot;
                 e.style.left = (pos.left + nextTo.offsetWidth) + &quot;px&quot;
             }
@@ -431,8 +453,14 @@ var Hobo = {
         return pluralisations[s] || s + &quot;s&quot;
     },
 
-    addUrlParams: function(params) {
+    addUrlParams: function(params, options) {
         params = $H(window.location.search.toQueryParams()).merge(params)
+
+        if (options.remove) {
+            var remove = (options.remove instanceof Array) ? options.remove : [options.remove]
+            remove.each(function(k) { params.unset(k) })
+        }
+
         return window.location.href.sub(/(\?.*|$)/, &quot;?&quot; + params.toQueryString())
     }
 
@@ -485,7 +513,7 @@ Element.Methods.$$ = function(e, css) {
 
 // --- has_many_through_input --- //
 
-HasManyThroughInput = Behavior.create({
+SelectManyInput = Behavior.create({
 
     initialize : function() {
         // onchange doesn't bubble in IE6 so...
@@ -493,15 +521,16 @@ HasManyThroughInput = Behavior.create({
     },
 
     addOne : function() {
-        var select = this.element.down('select')
+        var select = this.element.down('select') 
         var selected = select.options[select.selectedIndex]
-        if (selected.style.display != &quot;none&quot; &amp; selected.value != &quot;&quot;) {
-            var newItem = strToDom(this.element.down('.item-proto').innerHTML)
+        if (selected.style.display != &quot;none&quot; &amp; selected.text != &quot;&quot;) {
+            var newItem = $(DOM.Builder.fromHTML(this.element.down('.item-proto').innerHTML.strip()))
             this.element.down('.items').appendChild(newItem);
             newItem.down('span').innerHTML = selected.innerHTML
-            newItem.down('input[type=hidden]').value = selected.innerHTML
+            this.itemAdded(newItem, selected)
             selected.style.display = 'none'
             select.value = &quot;&quot;
+            Event.addBehavior.reload()
         }
     },
 
@@ -518,14 +547,50 @@ HasManyThroughInput = Behavior.create({
         var label = el.down('span').innerHTML
         var option = $A(this.element.getElementsByTagName('option')).find(function(o) { return o.innerHTML == label })
         option.style.display = 'block'
+    },
+
+    itemAdded: function(item, option) {
+        this.hiddenField(item).value = option.innerHTML
+    },
+
+    hiddenField: function(item) {
+        return item.down('input[type=hidden]') 
+        //return item.getElementsByClassName(&quot;hidden-field&quot;)[0]
     }
 
+
 })
 
 Event.addBehavior({
-    'div.has-many-through.input' : HasManyThroughInput(),
-		'.dependent-collection-count:click' : function(e) {
-			new Effect.ScrollTo('dependent-collection', {duration: 1.0, offset: -10, transition: Effect.Transitions.sinoidal});
-			Event.stop(e);
-		}
+    'div.select-many.input' : SelectManyInput(),
+
+    '.association-count:click' : function(e) {
+	new Effect.ScrollTo('primary-collection', {duration: 1.0, offset: -20, transition: Effect.Transitions.sinoidal});
+	Event.stop(e);
+    },
+
+    'form.filter-menu select:change': function(event) {
+        var paramName = this.up('form').down('input[type=hidden]').value.gsub(&quot;-&quot;, &quot;_&quot;)
+        var params = {}
+        var remove = [ 'page' ]
+	if ($F(this) == '') { 
+            remove.push(paramName)
+        } else {
+            params[paramName] = $F(this)
+	}
+	location.href = Hobo.addUrlParams(params, {remove: remove})
+    },
+
+    '.autocompleter' : function(event) {
+        var target    = this.className.match(/complete-on:([\S]+)/)[1].split(':')
+        var model     = target[0]
+        var completer = target[1]
+
+        var spec = Hobo.parseId(model)
+        var url = urlBase + &quot;/&quot; + Hobo.pluralise(spec.name) +  &quot;/complete_&quot; + completer
+        var parameters = spec.id ? &quot;id=&quot; + spec.id : &quot;&quot;
+        new Ajax.Autocompleter(this, this.next('.completions-popup'), url, {paramName:'query', method:'get', parameters: parameters});
+    }
+
+
 });</diff>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/templates/hobo-rapid.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,8 @@
 LowPro = {};
 LowPro.Version = '0.5';
-LowPro.CompatibleWithPrototype = '1.6.0';
+LowPro.CompatibleWithPrototype = '1.6';
 
-if (Prototype.Version != LowPro.CompatibleWithPrototype &amp;&amp; console &amp;&amp; console.warn)
+if (Prototype.Version.indexOf(LowPro.CompatibleWithPrototype) != 0 &amp;&amp; window.console &amp;&amp; window.console.warn)
   console.warn(&quot;This version of Low Pro is tested with Prototype &quot; + LowPro.CompatibleWithPrototype + 
                   &quot; it may not work as expected with this version (&quot; + Prototype.Version + &quot;)&quot;);
 
@@ -15,19 +15,19 @@ DOM = {};
 // DOMBuilder for prototype
 DOM.Builder = {
 	tagFunc : function(tag) {
-	  return function() {
-	    var attrs, children; 
-	    if (arguments.length&gt;0) { 
-	      if (arguments[0].nodeName || 
-	        typeof arguments[0] == &quot;string&quot;) 
-	        children = arguments; 
-	      else { 
-	        attrs = arguments[0]; 
-	        children = Array.prototype.slice.call(arguments, 1); 
-	      };
-	    }
-	    return DOM.Builder.create(tag, attrs, children);
-	  };
+    return function() {
+     var attrs, children;
+     if (arguments.length&gt;0) {
+       if (arguments[0].constructor == Object) {
+         attrs = arguments[0];
+         children = Array.prototype.slice.call(arguments, 1);
+       } else {
+         children = arguments;
+       };
+       children = $A(children).flatten()
+     }
+     return DOM.Builder.create(tag, attrs, children);
+    };
   },
 	create : function(tag, attrs, children) {
 		attrs = attrs || {}; children = children || []; tag = tag.toLowerCase();
@@ -71,7 +71,8 @@ DOM.Builder.fromHTML = function(html) {
 // Event.onReady(callbackFunction);
 Object.extend(Event, {
   onReady : function(f) {
-    document.observe('dom:loaded', f);
+    if (document.body) f();
+    else document.observe('dom:loaded', f);
   }
 });
 
@@ -96,7 +97,7 @@ Event.addBehavior = function(rules) {
     Ajax.Responders.register({
       onComplete : function() { 
         if (Event.addBehavior.reassignAfterAjax) 
-          setTimeout(function() { ab.unload(); ab.load(ab.rules) }, 10);
+          setTimeout(function() { ab.reload() }, 10);
       }
     });
     ab.responderApplied = true;
@@ -145,6 +146,12 @@ Object.extend(Event.addBehavior, {
     this.cache = [];
   },
   
+  reload: function() {
+    var ab = Event.addBehavior;
+    ab.unload(); 
+    ab.load(ab.rules);
+  },
+  
   _wrapObserver: function(observer) {
     return function(event) {
       if (observer.call(this, event) === false) event.stop(); 
@@ -277,7 +284,7 @@ Remote.Form = Behavior.create(Remote.Base, {
   onclick : function(e) {
     var sourceElement = e.element();
     
-    if (sourceElement.nodeName.toLowerCase() == 'input' &amp;&amp; 
+    if (['input', 'button'].include(sourceElement.nodeName.toLowerCase()) &amp;&amp; 
         sourceElement.type == 'submit')
       this._submitButton = sourceElement;
   },</diff>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/templates/lowpro.js</filename>
    </modified>
    <modified>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/templates/themes/clean/public/images/pencil.png</filename>
    </modified>
    <modified>
      <diff>@@ -17,11 +17,6 @@ table.new-record textarea, table.new-record input {
 	display: inline;	
 }
 
-.edit-page .content-header {overflow: hidden; height: 100%;}
-.edit-page .content-header h1 {float: left;}
-.edit-page .content-header .delete-button {float: right;}
-form .actions {margin: 30px 0;width: 100%; text-align: center;}
-
 /**** Admin ****/
 
 .admin-banner {
@@ -39,25 +34,6 @@ form .actions {margin: 30px 0;width: 100%; text-align: center;}
 	float: right;
 }
 
-/* rails error message */
-.error-messages {
-	font-family: &quot;Lucida Grande&quot;, arial, sans-serif;
-	background: #9d0018;
-	border: 1px solid #7a0013;
-	padding: 15px 30px;
-	color: white;
-	margin-bottom: 20px;
-}
-.error-messages h2 {
-	text-transform: none;
-	letter-spacing: normal;
-	color: white;
-	margin-bottom: 10px;
-}
-.error-messages li {
-	margin-left: 20px;
-}
-
 /********* everything below here came from hobo_rapid.css, needs looking at ********/
 
 /**** Default styling for Rapid ***/
@@ -66,30 +42,6 @@ form .actions {margin: 30px 0;width: 100%; text-align: center;}
 	float: right; margin: 20px;
 	position: fixed; display: none; z-index: 10;
 }
-/*
-#ajax-progress {
-    color: grey;
-    float: right;
-    margin: 20px;
-    position: fixed;
-    background: white;
-    font-family: Tahoma &quot;sans serif&quot;;
-    display: none;
-	z-index: 10;
-}
-
-#ajax-progress div {
-    border: 1px dashed grey;
-    margin: 10px;
-    padding: 3px;
-    padding-top: -15px;
-}
-
-#ajax-progress img {
-    padding-left: 6px;
-    vertical-align: middle;
-}
-*/
 
 /* Scriptaculous Autocompleter ---*/
 
@@ -126,7 +78,7 @@ div.completions-popup ul li {
     text-align: left; width: 1px; white-space: nowrap; vertical-align: top;
     padding-top: 10px; padding-bottom: 10px;
 }
-.field-list textarea, .field-list input.string, .field-list input.password { width: 100%; margin: 0; }
+.field-list textarea, .field-list input.string, .field-list input.password { width: 99%; margin: 0; }
 
 /*input[type=text].wide { width: 100%; }*/
 textarea { height: 170px; }</diff>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 class &lt;%= class_name %&gt; &lt; ActiveRecord::Base
 
-  hobo_user_model
+  hobo_user_model # Don't put anything above this
 
   fields do
     username :string, :login =&gt; true, :name =&gt; true
@@ -17,7 +17,7 @@ class &lt;%= class_name %&gt; &lt; ActiveRecord::Base
   # def super_user?; true; end
 
   def creatable_by?(creator)
-    true
+    creator.administrator? || !administrator
   end
 
   def updatable_by?(updater, new)</diff>
      <filename>vendor/plugins/hobo/generators/hobo_user_model/templates/model.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,16 +1,23 @@
+# gem dependencies
+require 'hobosupport'
+require 'hobofields'
+begin
+  require 'will_paginate'
+rescue MissingSourceFile
+  # OK, Hobo won't do pagination then
+end
+
 # Monkey patches, ooh ooh
-require 'extensions'
-require 'rexml'
 require 'active_record/has_many_association'
 require 'active_record/has_many_through_association'
-require 'active_record/table_definition'
+require 'active_record/association_proxy'
+require 'active_record/association_reflection'
 require 'action_view_extensions/base'
 
 require 'hobo'
 require 'hobo/dryml'
 
 require 'hobo/model'
-require 'hobo/field_declaration_dsl'
 
 require 'hobo/dryml/template'
 require 'hobo/dryml/taglib'
@@ -19,15 +26,6 @@ require 'hobo/dryml/template_handler'
 
 require 'extensions/test_case' if RAILS_ENV == &quot;test&quot;
 
-# Rich data types
-require &quot;hobo/html_string&quot;
-require &quot;hobo/markdown_string&quot;
-require &quot;hobo/textile_string&quot;
-require &quot;hobo/password_string&quot;
-require &quot;hobo/text&quot;
-require &quot;hobo/email_address&quot;
-require &quot;hobo/enum_string&quot;
-require &quot;hobo/percentage&quot;
 
 
 ActionView::Base.register_template_handler(&quot;dryml&quot;, Hobo::Dryml::TemplateHandler)
@@ -54,6 +52,7 @@ end
 class ActiveRecord::Base
   def self.hobo_model
     include Hobo::Model
+    fields # force hobofields to load
   end
   def self.hobo_user_model
     include Hobo::Model
@@ -64,3 +63,39 @@ end
 # Default settings
 
 Hobo.developer_features = RAILS_ENV.in?([&quot;development&quot;, &quot;test&quot;]) if Hobo.developer_features?.nil?
+
+
+module ::Hobo
+  # Empty class to represent the boolean type.
+  class Boolean; end
+end
+
+
+if defined? HoboFields
+  HoboFields.never_wrap(Hobo::Undefined)
+end
+
+
+# Add support for type metadata to arrays
+class ::Array
+  
+  attr_accessor :member_class, :origin, :origin_attribute
+  
+  def to_url_path
+    base_path = origin_object.try.to_url_path
+    &quot;#{base_path}/#{origin_attribute}&quot; unless base_path.blank?
+  end
+  
+  def typed_id
+    origin_id = origin.try.typed_id
+    &quot;#{origin_id}_#{origin_attribute}&quot; if origin_id
+  end
+  
+end
+
+
+class NilClass
+  def typed_id
+    &quot;nil&quot;
+  end
+end</diff>
      <filename>vendor/plugins/hobo/init.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,12 +2,13 @@ module ActionView
 
   class Base
 
-    alias_method :render_file_without_hobo, :render_file
-    def render_file(template_path, *args)
+    def render_file_with_dryml(template_path, *args)
       @hobo_template_path = template_path
-      render_file_without_hobo(template_path, *args)
+      render_file_without_dryml(template_path, *args)
     end
 
+    alias_method_chain :render_file, :dryml
+
   end
 
 end</diff>
      <filename>vendor/plugins/hobo/lib/action_view_extensions/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -18,7 +18,7 @@ module ActiveRecord::Associations
       record = @reflection.klass.new(attributes)
       if hobo_has_many?
         set_belongs_to_association_for(record)
-        set_reverse_association(record)
+        set_reverse_association(record) unless proxy_reflection.options[:as]
       end
       record
     end
@@ -29,16 +29,15 @@ module ActiveRecord::Associations
     end
     
     
-    def find_with_block(*args, &amp;b)
-      if b
-        options = args.extract_options!
-        args &lt;&lt; options.merge(:conditions =&gt; member_class.conditions(&amp;b))
-        find_without_block(*args)
-      else
-        find_without_block(*args)
-      end
+    def origin
+      proxy_owner
     end
-    alias_method_chain :find, :block
+
+    
+    def origin_attribute
+      proxy_reflection.association_name
+    end
+    
     
     private
     </diff>
      <filename>vendor/plugins/hobo/lib/active_record/has_many_association.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,6 +5,14 @@ module ActiveRecord::Associations
     def member_class
       proxy_reflection.klass
     end
+    
+    def origin
+      proxy_owner
+    end
+
+    def origin_attribute
+      proxy_reflection.association_name
+    end
 
   end
 </diff>
      <filename>vendor/plugins/hobo/lib/active_record/has_many_through_association.rb</filename>
    </modified>
    <modified>
      <diff>@@ -102,7 +102,7 @@ class Test::Unit::TestCase
     def replace_objects_in_params!(hash)
       hash.each do |k,v|
         if v.is_a? ActiveRecord::Base
-          hash[k] = &quot;@&quot; + Hobo.dom_id(v)
+          hash[k] = &quot;@&quot; + v.typed_id
         elsif v.is_a? Hash
           replace_objects_in_params!(v)
         end</diff>
      <filename>vendor/plugins/hobo/lib/extensions/test_case.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,24 +1,18 @@
 class HoboError &lt; RuntimeError; end
 
 module Hobo
+  
+  VERSION = &quot;0.7.5&quot;
 
   class RawJs &lt; String; end
 
   @models = []
-  @field_types = HashWithIndifferentAccess.new
   
   class &lt;&lt; self
 
-    attr_accessor :current_theme, :field_types
+    attr_accessor :current_theme
     attr_writer :developer_features
     
-    def symbolic_type_name(type)
-      field_types.index(type)
-    end
-    
-    def type_id(type)
-      symbolic_type_name(type) || type.name.underscore.gsub(&quot;/&quot;, &quot;__&quot;)
-    end
 
     def developer_features?
       @developer_features
@@ -41,7 +35,7 @@ module Hobo
 
     
     def models=(models)
-      @models = models.every(:name)
+      @models = models.*.name
     end
 
     
@@ -52,7 +46,7 @@ module Hobo
         end
         @models_loaded = true
       end
-      @models.every(:constantize)
+      @models.*.constantize
     end
 
     
@@ -87,64 +81,38 @@ module Hobo
     end
 
     def dom_id(obj, attr=nil)
-      if obj.nil?
-        raise ArgumentError, &quot;Tried to get dom id of nil.#{attr}&quot; if attr
-        return 'nil'
-      end
-
-      if obj.is_a?(Array) and obj.respond_to?(:proxy_owner)
-        attr = obj.proxy_reflection.name
-        obj = obj.proxy_owner
-      elsif obj.is_a?(Class)
-        return type_id(obj)
-      elsif !obj.respond_to?(:typed_id)
-        return (if attr
-                  dom_id(get_field(obj, attr))
-                elsif obj.respond_to?(:id)
-                  &quot;#{obj.class.name.underscore}_#{obj.id}&quot;
-                else
-                  raise ArgumentError, &quot;Can't create dom id for #{obj.inspect}&quot;
-                end)
-      end
       attr ? &quot;#{obj.typed_id}_#{attr}&quot; : obj.typed_id
     end
 
-    def find_by_search(query)
-      sql = Hobo.models.map do |model|
-        if model.superclass == ActiveRecord::Base &amp;&amp; # filter out STI subclasses
-            ModelRouter.linkable?(nil, model, :show) # filter out non-linkables
-          cols = model.search_columns
-          if cols.blank?
-            nil
-          else
-            where = cols.map {|c| &quot;(#{c} like ?)&quot;}.join(' or ')
-            type = model.column_names.include?(&quot;type&quot;) ? &quot;type&quot; : &quot;'#{model.name}'&quot;
-            ActiveRecord::Base.send(:sanitize_sql,
-                                    [&quot;select #{type} as type, id &quot; +
-                                     &quot;from #{model.table_name} &quot; +
-                                     &quot;where #{where}&quot;] +
-                                    [&quot;%#{query}%&quot;] * cols.length)
+    def find_by_search(query, search_targets=nil)
+      search_targets ||= 
+        begin
+          # FIXME: This should interrogate the model-router directly, there's no need to enumerate models
+          # By default, search all models, but filter out...
+          Hobo.models.select do |m| 
+          ModelRouter.linkable?(m, :show) &amp;&amp; # ...non-linkables
+            m.search_columns.any?             # and models with no search-columns
           end
         end
-      end.compact.join(&quot; union &quot;)
-
-      rows = ActiveRecord::Base.connection.select_all(sql)
-      records = Hash.new {|h,k| h[k] = []}
-      for row in rows
-        records[row['type']] &lt;&lt; row['id']
-      end
-      results = []
-      for type, ids in records
-        results.concat(type.constantize.find(:all, :conditions =&gt; &quot;id in (#{ids * ','})&quot;))
-      end
       
-      results
+      query_words = ActiveRecord::Base.connection.quote_string(query).split
+                    
+      search_targets.build_hash do |search_target|
+        conditions = query_words.map do |word| 
+          &quot;(&quot; + search_target.search_columns.map { |column| %(#{column} like &quot;%#{word}%&quot;) }.join(&quot; or &quot;) + &quot;)&quot;
+        end.join(&quot; and &quot;)
+        
+        results = search_target.find(:all, :conditions =&gt; conditions)
+        [search_target.name, results] unless results.empty?
+      end
     end
 
     def add_routes(m)
       Hobo::ModelRouter.add_routes(m)
     end
 
+    
+    # FIXME: This method won't be needed
     def all_models
       Hobo.models.map { |m| m.name.underscore }
     end
@@ -159,6 +127,16 @@ module Hobo
     end
     
     
+    def can_create_in_association?(array_or_reflection)
+      refl = 
+        (array_or_reflection.is_a?(ActiveRecord::Reflection::AssociationReflection) and array_or_reflection) or
+        array_or_reflection.try.proxy_reflection or
+        (origin = array_or_reflection.try.origin and origin.send(array_or_reflection.origin_attribute).try.proxy_reflection)
+
+      refl &amp;&amp; refl.macro == :has_many &amp;&amp; (!refl.through_reflection) &amp;&amp; (!refl.options[:conditions])      
+    end
+    
+    
     def get_field(object, field)
       return nil if object.nil?
       if field.to_s =~ /^\d+$/
@@ -195,9 +173,13 @@ module Hobo
       if object.is_a?(Class) and object &lt; ActiveRecord::Base
         object = object.new
         object.set_creator(person)
-      elsif Hobo.simple_has_many_association?(object)
-        object = object.new
-        object.set_creator(person)
+      elsif (refl = object.try.proxy_reflection) &amp;&amp; refl.macro == :has_many
+        if Hobo.simple_has_many_association?(object)
+          object = object.new
+          object.set_creator(person)
+        else
+          return false
+        end
       end
       check_permission(:create, person, object)
     end
@@ -213,10 +195,12 @@ module Hobo
       return false if !can_view?(person, object, field)
       
       if field.nil?
-        if respond_to?(:editable_by?)
+        if object.has_hobo_method?(:editable_by?)
           object.editable_by?(person) 
-        else
+        elsif object.has_hobo_method?(:updatable_by?)
           object.updatable_by?(person, nil)
+        else
+          false
         end
         
       else
@@ -232,13 +216,13 @@ module Hobo
           object.send(&quot;#{field}_editable_by?&quot;, person)
         elsif object.has_hobo_method?(:editable_by?)
           check_permission(:edit, person, object)
+        elsif refl._?.macro == :has_many
+          # The below technique to figure out edit permission based on
+          # update permission doesn't work for has_many associations
+          false
         else
           # Fake an edit test by setting the field in question to
           # Hobo::Undefined and then testing for update permission
-          
-          # This technique is not suitable for has_many associations
-          return false if refl._?.macro == :has_many
-          
           current = object.send(field)
           new = object.duplicate
 
@@ -257,7 +241,7 @@ module Hobo
                              end)
           rescue Hobo::UndefinedAccessError
             raise HoboError, (&quot;#{object.class.name}##{field} does not support undefined assignements, &quot; + 
-                              &quot;define editable_by?(user, field)&quot;)
+                              &quot;define #{field}_editable_by?(user)&quot;)
           end
           
           begin
@@ -333,7 +317,7 @@ module Hobo
                                 else
                                     File.join(File.dirname(__FILE__), &quot;hobo/static_tags&quot;)
                                 end
-                         File.readlines(path).every(:chop)
+                         File.readlines(path).*.chop
                        end
     end
     
@@ -355,7 +339,7 @@ module Hobo
                    when :edit;   :editable_by?
                    when :view;   :viewable_by?
                    end
-      p = if object.has_hobo_method?(obj_method)
+      p = if (obj_method.respond_to?(:has_hobo_method) ? object.has_hobo_method?(obj_method) : object.respond_to?(obj_method))
             begin
               object.send(obj_method, person, *args)
             rescue Hobo::UndefinedAccessError</diff>
      <filename>vendor/plugins/hobo/lib/hobo.rb</filename>
    </modified>
    <modified>
      <diff>@@ -38,7 +38,7 @@ module Hobo
     #   skip_before_filter :login_required
     #
     def login_required(user_model=nil)
-      auth_model = user_model || UserController.user_models.first
+      auth_model = user_model || User.default_user_model
       if current_user.guest? 
         username, passwd = get_auth_data
         self.current_user = auth_model.authenticate(username, passwd) || nil if username &amp;&amp; passwd &amp;&amp; auth_model</diff>
      <filename>vendor/plugins/hobo/lib/hobo/authentication_support.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,3 @@
-require 'extensions'
-
 module ::Hobo
   
   class Bundle
@@ -8,48 +6,93 @@ module ::Hobo
     
     class &lt;&lt; self
       
-      attr_accessor :bundles, :plugin
+      # Hobo::Bundle.bundles is a hash of all instantiated bundles by name
+      attr_accessor :bundles
+      
+      # Used by subclasses, e.g MyBundle.plugin is the name of the
+      # plugin the bundle came from
+      attr_reader :plugin
+      
+      attr_reader :model_declarations, :controller_declarations
+      
+      attr_accessor :dirname
       
       def inherited(base)
         filename = caller[0].match(/^(.*):\d+/)[1]
-        dirname = filename.match(%r(^.*/plugins/[^/]+))[0]
-        base.plugin = File.basename(dirname)
+        base.dirname = filename.match(%r(^.*/plugins/[^/]+))[0]
+      end
+
+      
+      def load_models_and_controllers
+        return if models_and_controllers_loaded?
         
-        base.meta_eval do 
-          attr_accessor :models, :controllers
-        end
+        @plugin = File.basename(dirname)
         
-        base.models      = []
-        base.controllers = []
+        @model_declarations      = []
+        @controller_declarations = []
         
-        eval_ruby_files(base, &quot;#{dirname}/models&quot;)
-        eval_ruby_files(base, &quot;#{dirname}/controllers&quot;) 
+        class_eval do
+          eval_ruby_files(&quot;#{dirname}/models&quot;, @models)
+          eval_ruby_files(&quot;#{dirname}/controllers&quot;, @controllers)
+        end
       end
       
+      def [](bundle_name)
+        bundles[bundle_name]
+      end
+      
+
+      private
       
       def bundle_model(name, &amp;block)
-        models &lt;&lt; [name, block]
+        @model_declarations &lt;&lt; [name, block]
       end
 
       
       def bundle_model_controller(model_name, &amp;block)
-        controllers &lt;&lt; [model_name, block]
+        @controller_declarations &lt;&lt; [model_name, block]
+      end
+
+      
+      def models_and_controllers_loaded?
+        @model_declarations
       end
       
       
-      private
       
-      def eval_ruby_files(base, dir)
-        Dir[&quot;#{dir}/*.rb&quot;].each do |f|
-          base.instance_eval(File.read(f), f, 1)
-        end
+      def eval_ruby_files(dir, filenames)
+        files = if filenames == [:none]
+                  []
+                elsif filenames.blank? || filenames == [:all]
+                  Dir[&quot;#{dir}/*.rb&quot;]
+                else
+                  filenames.map { |f| &quot;#{dir}/#{f}.rb&quot; }
+                end
+        
+        files.each { |f| instance_eval(File.read(f), f, 1) }
+      end
+      
+      
+      # Declarations
+      
+      def models(*models)
+        @models = models
+      end
+      
+      def controllers(*controllers)
+        @controllers = controllers.map {|c| case c.to_s
+                                              when /controller$/, &quot;all&quot;, &quot;none&quot; then c
+                                              else &quot;#{c.to_s.pluralize}_controller&quot; 
+                                            end }
       end
       
     end
     
     def initialize(*args)
+      self.class.load_models_and_controllers
+      
       options = defaults.with_indifferent_access
-      options.update(args.extract_options!)
+      options.recursive_update(args.extract_options!)
       
       self.name = args.first || self.class.name.match(/[^:]+$/)[0].underscore
       Bundle.bundles[name] = self
@@ -78,19 +121,53 @@ module ::Hobo
     
     
     def create_models
-      self.class.models.each do |name, block|
-        klass = make_class(new_name_for(name), ActiveRecord::Base) do
-          hobo_model
+      self.class.model_declarations.each do |name, block|
+        klass = make_class(new_name_for(name), ActiveRecord::Base)
+        
+        klass.meta_def :belongs_to_with_optional_polymorphism do |*args|
+          opts = args.extract_options!
+          
+          if opts[:polymorphic] == :optional
+            if bundle.options[&quot;polymorphic_#{name}&quot;]
+              opts[:polymorphic] = true
+              opts.delete(:class_name)
+            else
+              opts.delete(:polymorphic)
+            end
+          end
+          belongs_to_without_optional_polymorphism(name, opts)
         end
-        klass.class_eval(&amp;block)
+        klass.meta_eval { alias_method_chain :belongs_to, :optional_polymorphism }
+        
+        klass.class_eval { hobo_model }
+        
+        # FIXME this extension breaks passing a block to belongs_to
+        klass.meta_def :belongs_to_with_alias do |*args|
+          opts = args.extract_options!
+          name = args.first.to_sym
+          
+          alias_name = opts.delete(:alias)
+          
+          belongs_to_without_alias(name, opts)
+
+          if alias_name &amp;&amp; name != alias_name
+            klass.send(:alias_method, alias_name, name)
+            # make the aliased name available in the classes metadata
+            klass.reflections[alias_name] = klass.reflections[name]
+          end
+          
+        end
+        klass.meta_eval { alias_method_chain :belongs_to, :alias }
+
+        klass.class_eval(&amp;block)        
       end
     end
     
     
     def create_controllers
       bundle = self
-      self.class.controllers.each do |model_name, block|
-        klass = make_class(&quot;#{new_name_for(model_name).to_s.pluralize}Controller&quot;, ApplicationController) do 
+      self.class.controller_declarations.each do |model_name, block|
+        klass = make_class(&quot;#{new_name_for(model_name).to_s.pluralize}Controller&quot;, ::ApplicationController) do 
           hobo_model_controller
         end
         klass.class_eval(&amp;block)
@@ -106,6 +183,14 @@ module ::Hobo
         def self.feature(name, &amp;block)
           _feature(name, block)
         end
+        
+        def method_missing(name, *args)
+          if name.to_s =~ /^_.*_$/
+            self.class.bundle.magic_option(name)
+          else
+            super
+          end
+        end
       end
       
       klass.meta_def(:bundle) do 
@@ -134,18 +219,34 @@ module ::Hobo
       end
       
       klass.class_eval(&amp;b) if b
+      
       klass
     end
     
     
     def new_name_for(name)
-      while renames.has_key?(name)
-        name = renames[name] 
-        name2 = name.to_s.gsub(/_.*?_/) { |s| new_name_for(s[1..-2]) }
-        
-        # Make sure symbols stay symbols
-        name = name.is_a?(Symbol) ? name2.to_sym : name2
+      name = name.to_s
+      underscore = name =~ /^[a-z]/
+      name = name.camelize if underscore
+      
+      plural = !renames.has_key?(name) &amp;&amp; (p = name.singularize) &amp;&amp; renames.has_key?(p)
+      name = p if plural
+      
+      # Keep a track of names we've seen to avoid cycles
+      seen = [ name ]
+      
+      name = name.gsub(/_.*?_/) { |s| new_name_for(s[1..-2]) }
+      while (newname = renames[name])
+        name = newname
+        name = name.gsub(/_.*?_/) { |s| new_name_for(s[1..-2]) }
+
+        break if name.in?(seen)
+        seen &lt;&lt; name        
       end
+      
+      name = name.underscore if underscore
+      name = name.pluralize  if plural
+      name = name.to_sym if underscore || plural
       name
     end
     
@@ -155,8 +256,6 @@ module ::Hobo
       options.each do |k, v| 
         if k.to_s =~ /^[A-Z]/
           renames[k] = v.to_s
-          renames[k.to_s.underscore] = v.to_s.underscore.to_sym
-          renames[k.to_s.underscore.pluralize] = v.to_s.underscore.pluralize.to_sym
         else
           simple_options[k] = v
         end
@@ -169,7 +268,7 @@ module ::Hobo
       new_name_for(name).to_s.constantize.class_eval(&amp;block)
     end
     
-    
+
     def method_missing(name, *args)
       if name.to_s =~ /^_.*_$/
         magic_option(name)
@@ -213,7 +312,15 @@ module ::Hobo
       external_options = self.options[option_name]
       external_options = {} if external_options.nil? || external_options == true
       name = &quot;#{self.name}_#{option_name}&quot;
-      class_name.to_s.constantize.new(name, external_options.merge(local_options).merge(renames))
+      
+      sub_bundle_options = external_options.merge(local_options).merge(renames)
+      sub_bundle = class_name.to_s.constantize.new(name, sub_bundle_options)
+      
+      conflicting_renames = (renames.keys &amp; sub_bundle.renames.keys).select { |k| renames[k] != sub_bundle.renames[k] }
+      unless conflicting_renames.empty?
+        raise ArgumentError, &quot;Conflicting renames in included bundle '#{name}' of '#{self.name}': #{conflicting_renames * ', '}&quot;
+      end
+      renames.update(sub_bundle.renames)
       self.options[&quot;#{option_name}_bundle&quot;] = name
     end
 </diff>
      <filename>vendor/plugins/hobo/lib/hobo/bundle.rb</filename>
    </modified>
    <modified>
      <diff>@@ -62,7 +62,7 @@ module Hobo
     
     def id
       objects = self.class.models.map {|m| instance_variable_get(&quot;@#{m.underscore}&quot;)}
-      objects.every(:id).join(&quot;_&quot;)
+      objects.*.id.join(&quot;_&quot;)
     end
     
     alias_method :to_param, :id</diff>
      <filename>vendor/plugins/hobo/lib/hobo/composite_model.rb</filename>
    </modified>
    <modified>
      <diff>@@ -87,12 +87,10 @@ module Hobo
         page &lt;&lt; renderer.part_contexts_storage if renderer
       end
     end
-
-
-    def render_tag(tag, options={}, render_options={})
-      add_variables_to_assigns
-      text = Hobo::Dryml.render_tag(@template, tag, options)
-      text &amp;&amp; render({:text =&gt; text, :layout =&gt; false }.merge(render_options))
+    
+    
+    def dryml_context
+      @this
     end
 
 
@@ -111,11 +109,12 @@ module Hobo
 
 
     def site_search(query)
-      results = Hobo.find_by_search(query).select {|r| Hobo.can_view?(current_user, r, nil)}
+      results = Hobo.find_by_search(query).select{|r| Hobo.can_view?(current_user, r, nil)}
       if results.empty?
         render :text =&gt; &quot;&lt;p&gt;Your search returned no matches.&lt;/p&gt;&quot;
       else
-        render_tags(results, :card, :for_type =&gt; true)
+        # TODO: call one tag that renders all the search results with headings for each model
+        render_tags(results.map {|r|r.last}.flatten, :search_card, :for_type =&gt; true)
       end
     end
 </diff>
      <filename>vendor/plugins/hobo/lib/hobo/controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -27,7 +27,11 @@ module Hobo
     APPLICATION_TAGLIB = { :src =&gt; &quot;taglibs/application&quot; }
     CORE_TAGLIB        = { :src =&gt; &quot;core&quot;, :plugin =&gt; &quot;hobo&quot; }
     
-    DEFAULT_IMPORTS = [Hobo::HoboHelper, ApplicationHelper]
+    DEFAULT_IMPORTS = (if defined?(ApplicationHelper) 
+                         [Hobo::HoboHelper, ApplicationHelper]
+                       else
+                         [Hobo::HoboHelper]
+                       end)
 
     @renderer_classes = {}
     @tag_page_renderer_classes = {}</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,13 +2,17 @@ module Hobo::Dryml
   
   class DRYMLBuilder
 
-    def initialize(template_path)
-      @template_path = template_path
+    def initialize(template)
+      @template = template
       @build_instructions = Array.new
       @part_names = []
     end
     
-    attr_reader :template_path
+    attr_reader :template
+    
+    def template_path
+      template.template_path
+    end
 
 
     def set_environment(environment)
@@ -34,7 +38,7 @@ module Hobo::Dryml
     
     def add_part(name, src, line_num)
       raise DrymlException.new(&quot;duplicate part: #{name}&quot;, template_path, line_num) if name.in?(@part_names)
-      add_build_instruction(:part, :src =&gt; src, :line_num =&gt; line_num)
+      add_build_instruction(:def, :src =&gt; src, :line_num =&gt; line_num)
       @part_names &lt;&lt; name
     end
 
@@ -50,7 +54,7 @@ module Hobo::Dryml
       (&quot;def render_page(__page_this__, __local_assigns__); &quot; +
             &quot;#{locals} new_object_context(__page_this__) do &quot; +
             src +
-           &quot;; _erbout; end + part_contexts_storage_tag; end&quot;)
+           &quot;; _erbout; end; end&quot;)
     end
     
     
@@ -73,9 +77,6 @@ module Hobo::Dryml
           src = erb_process(instruction[:src])
           @environment.class_eval(src, template_path, instruction[:line_num])
           
-        when :part
-          @environment.class_eval(erb_process(instruction[:src]), template_path, instruction[:line_num])
-          
         when :render_page
           method_src = render_page_source(erb_process(instruction[:src]), local_names)
           @environment.compiled_local_names = local_names
@@ -107,7 +108,12 @@ module Hobo::Dryml
         import_module(options[:module].constantize, options[:as])
       else
         template_dir = File.dirname(template_path)
-        taglib = Taglib.get(options.merge(:template_dir =&gt; template_dir))
+        options = options.merge(:template_dir =&gt; template_dir)
+        
+        # Pass on the current bundle, if there is one, to the sub-taglib
+        options[:bundle] = template.bundle.name unless template.bundle.nil? || options[:bundle] || options[:plugin]
+        
+        taglib = Taglib.get(options)
         taglib.import_into(@environment, options[:as])
       end
     end
@@ -122,7 +128,7 @@ module Hobo::Dryml
     def set_theme(name)
       if Hobo.current_theme.nil? or Hobo.current_theme == name
         Hobo.current_theme = name
-        import_taglib(:src =&gt; &quot;taglibs/themes/#{name}/application&quot;)
+        import_taglib(:src =&gt; &quot;taglibs/themes/#{name}/#{name}&quot;)
       end
     end
   end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml/dryml_builder.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,14 +7,12 @@ module Hobo::Dryml
     end
     
     def [](key)
-      @scopes.reverse_each do |s|
-        return s[key] if s.has_key?(key)
-      end 
-      nil
+     s = scope_with_key(key) and s[key]
     end
     
     def []=(key, val)
-      @scopes.last[key] = val
+      s = scope_with_key(key) || @scopes.last
+      s[key] = val
     end
     
     def new_scope
@@ -24,6 +22,13 @@ module Hobo::Dryml
       res
     end
     
+    def scope_with_key(key)
+      @scopes.reverse_each do |s|
+        return s if s.has_key?(key)
+      end 
+      nil
+    end
+    
     def method_missing(name, *args)
       if name.to_s =~ /=$/
         self[name.to_s[0..-2].to_sym] = args.first</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml/scoped_variables.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,10 +14,9 @@ module Hobo
             taglib.reload
           else
             src_file = taglib_filename(options)
-            renames = (bundle = options[:bundle] and
-                       Bundle.bundles[bundle]._?.renames)
+            bundle = options[:bundle] &amp;&amp; Bundle.bundles[options[:bundle]]
      
-            taglib = Taglib.new(src_file, renames)
+            taglib = Taglib.new(src_file, bundle)
             @cache[options] = taglib
           end
           taglib
@@ -38,7 +37,7 @@ module Hobo
                  elsif options[:src] =~ /\//
                    &quot;app/views&quot;
                  else
-                   options[:template_dir]
+                   options[:template_dir].gsub(/^\//, &quot;&quot;) # remove leading / if there is one
                  end
           
           filename = &quot;#{RAILS_ROOT}/#{base}/#{options[:src]}.dryml&quot;
@@ -48,9 +47,9 @@ module Hobo
      
       end
      
-      def initialize(src_file, renames)
+      def initialize(src_file, bundle)
         @src_file = src_file
-        @renames = renames
+        @bundle = bundle
         load
       end
      
@@ -84,7 +83,7 @@ module Hobo
           end
           
         end
-        template = Template.new(File.read(@src_file), @module, @src_file, @renames)
+        template = Template.new(File.read(@src_file), @module, @src_file, @bundle)
         template.compile([], [])
         @last_load_time = File.mtime(@src_file)
       end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml/taglib.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,11 +12,15 @@ module Hobo::Dryml
     
     CODE_ATTRIBUTE_CHAR = &quot;&amp;&quot;
     
+    NO_METADATA_TAGS = %w(doctype if else unless repeat do with name type-name)
+    
     SPECIAL_ATTRIBUTES = %w(param merge merge-params merge-attrs 
                             for-type 
                             if unless repeat 
                             part part-locals
                             restore)
+    
+    VALID_PARAMETER_TAG_ATTRIBUTES = %w(param replace)
 
     @build_cache = {}
     
@@ -28,19 +32,19 @@ module Hobo::Dryml
       end
     end
 
-    def initialize(src, environment, template_path, renames={})
+    def initialize(src, environment, template_path, bundle=nil)
       @src = src
       @environment = environment # a class or a module
       @template_path = template_path.sub(/^#{Regexp.escape(RAILS_ROOT)}/, &quot;&quot;)
-      @class_renames = renames
+      @bundle = bundle
 
-      @builder = Template.build_cache[@template_path] || DRYMLBuilder.new(@template_path)
+      @builder = Template.build_cache[@template_path] || DRYMLBuilder.new(self)
       @builder.set_environment(environment)
 
       @last_element = nil
     end
 
-    attr_reader :tags, :template_path, :class_renames
+    attr_reader :tags, :template_path, :bundle
     
     def compile(local_names=[], auto_taglibs=[])
       now = Time.now
@@ -74,6 +78,7 @@ module Hobo::Dryml
 
     def create_render_page_method
       erb_src = process_src
+      
       @builder.add_build_instruction(:render_page, :src =&gt; erb_src, :line_num =&gt; 1)
     end
 
@@ -84,30 +89,14 @@ module Hobo::Dryml
 
     
     def process_src
-      # Replace &lt;%...%&gt; scriptlets with xml-safe references into a hash of scriptlets
-      @scriptlets = {}
-      src = @src.gsub(/&lt;%(.*?)%&gt;/m) do
-        _, scriptlet = *Regexp.last_match
-        id = @scriptlets.size + 1
-        @scriptlets[id] = scriptlet
-        newlines = &quot;\n&quot; * scriptlet.count(&quot;\n&quot;)
-        &quot;[![HOBO-ERB#{id}#{newlines}]!]&quot;
-      end
-
-      @xmlsrc = &quot;&lt;dryml_page&gt;&quot; + src + &quot;&lt;/dryml_page&gt;&quot;
-      begin
-        @doc = REXML::Document.new(RexSource.new(@xmlsrc), :dryml_mode =&gt; true)
-      rescue REXML::ParseException =&gt; e
-        raise DrymlSyntaxError, &quot;File: #{@template_path}\n#{e}&quot;
-      end
-      @doc.default_attribute_value = &quot;&amp;true&quot;
-      
-      restore_erb_scriptlets(children_to_erb(@doc.root))
+      @doc = Hobo::Dryml::Parser::Document.new(@src, @template_path)
+      result = children_to_erb(@doc.root)
+      restore_erb_scriptlets(result)
     end
 
 
     def restore_erb_scriptlets(src)
-      src.gsub(/\[!\[HOBO-ERB(\d+)\s*\]!\]/m) {|s| &quot;&lt;%#{@scriptlets[$1.to_i]}%&gt;&quot; }
+      @doc.restore_erb_scriptlets(src)
     end
 
     
@@ -127,7 +116,7 @@ module Hobo::Dryml
         REXML::Comment::START + node.to_s + REXML::Comment::STOP
 
       when REXML::Text
-        node.to_s
+        strip_suppressed_whiteaspace(node.to_s)
 
       when REXML::Element
         element_to_erb(node)
@@ -135,6 +124,11 @@ module Hobo::Dryml
     end
 
 
+    def strip_suppressed_whiteaspace(s)
+      s # s.gsub(/ -(\s*\n\s*)/, '&lt;% \1 %&gt;')
+    end
+    
+    
     def element_to_erb(el)
       dryml_exception(&quot;old-style parameter tag (&lt;#{el.name}&gt;)&quot;, el) if el.name.starts_with?(&quot;:&quot;)
 
@@ -194,7 +188,7 @@ module Hobo::Dryml
 
     def set_element(el)
       assigns = el.attributes.map do |name, value|
-        dryml_exception(el, &quot;invalid name in set&quot;) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
+        dryml_exception(&quot;invalid name in &lt;set&gt;&quot;, el) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
         &quot;#{ruby_name name} = #{attribute_to_ruby(value)}; &quot;
       end.join
       code = apply_control_attributes(&quot;begin; #{assigns}; end&quot;, el)
@@ -204,7 +198,7 @@ module Hobo::Dryml
     
     def set_scoped_element(el)
       assigns = el.attributes.map do |name, value|
-        dryml_exception(el, &quot;invalid name in set-scoped&quot;) unless name =~ DRYML_NAME_RX
+        dryml_exception(&quot;invalid name in &lt;set-scoped&gt;&quot;, el) unless name =~ DRYML_NAME_RX
         &quot;scope[:#{ruby_name name}] = #{attribute_to_ruby(value)}; &quot;
       end.join
       &quot;&lt;% scope.new_scope { #{assigns}#{tag_newlines(el)} %&gt;#{children_to_erb(el)}&lt;% } %&gt;&quot;
@@ -214,7 +208,7 @@ module Hobo::Dryml
     def declared_attributes(def_element)
       attrspec = def_element.attributes[&quot;attrs&quot;]
       attr_names = attrspec ? attrspec.split(/\s*,\s*/).map{ |n| n.underscore.to_sym } : []
-      invalids = attr_names &amp; ([:with, :field, :this] + SPECIAL_ATTRIBUTES.every(:to_sym))
+      invalids = attr_names &amp; ([:with, :field, :this] + SPECIAL_ATTRIBUTES.*.to_sym)
       dryml_exception(&quot;invalid attrs in def: #{invalids * ', '}&quot;, def_element) unless invalids.empty?
       attr_names
     end
@@ -243,11 +237,12 @@ module Hobo::Dryml
       unsafe_name = el.attributes[&quot;tag&quot;]
       name = Hobo::Dryml.unreserve(unsafe_name)
       if (for_type = el.attributes['for'])
-        type_name = case for_type
-                    when /^[a-z]/
+        type_name = if defined?(HoboFields) &amp;&amp; for_type =~ /^[a-z]/
                       # It's a symbolic type name - look up the Ruby type name
-                      Hobo.field_types[for_type].name
-                    when /^_.*_$/
+                      klass = HoboFields.to_class(for_type)
+                      dryml_exception(&quot;No such type in polymorphic tag definition: '#{for_type}'&quot;, el) unless klass
+                      klass.name
+                    elsif for_type =~ /^_.*_$/
                       rename_class(for_type)
                     else
                       for_type
@@ -257,13 +252,8 @@ module Hobo::Dryml
         unsafe_name += suffix
       end
       
-      # While processing this def, @def_name contains
-      # the names of all nested defs join with '_'. It's used to
-      # disambiguate local variables as a workaround for the broken
-      # scope semantics of Ruby 1.8.
-      old_def_name = @def_name
-      @def_name = @def_name ? &quot;#{@def_name}_#{unsafe_name}&quot; : unsafe_name
-
+      @def_element = el
+      
       alias_of = el.attributes['alias-of']
       extend_with = el.attributes['extend-with']
 
@@ -272,7 +262,8 @@ module Hobo::Dryml
       
       
       @builder.add_build_instruction(:alias_method,
-                                     :new =&gt; ruby_name(name).to_sym, :old =&gt; ruby_name(alias_of).to_sym) if alias_of
+                                     :new =&gt; ruby_name(name).to_sym, 
+                                     :old =&gt; ruby_name(Hobo::Dryml.unreserve(alias_of)).to_sym) if alias_of
       
       res = if alias_of
               &quot;&lt;% #{tag_newlines(el)} %&gt;&quot;
@@ -293,7 +284,7 @@ module Hobo::Dryml
               # keep line numbers matching up
               &quot;&lt;% #{&quot;\n&quot; * src.count(&quot;\n&quot;)} %&gt;&quot;
             end
-      @def_name = old_def_name
+      @def_element = nil
       res
     end
     
@@ -332,13 +323,52 @@ module Hobo::Dryml
       &quot;#{start} &quot; +
         # reproduce any line breaks in the start-tag so that line numbers are preserved
         tag_newlines(el) + &quot;%&gt;&quot; +
-        children_to_erb(el) +
+        wrap_tag_method_body_with_metadata(children_to_erb(el)) +
         &quot;&lt;% _erbout; end&quot;
     end
     
+    
+    def wrap_source_with_metadata(content, kind, name, *args)
+      if (!include_source_metadata) || name.in?(NO_METADATA_TAGS)
+        content
+      else
+        metadata = [kind, name] + args + [@template_path]
+        &quot;&lt;!--[DRYML|#{metadata * '|'}[--&gt;&quot; + content + &quot;&lt;!--]DRYML]--&gt;&quot;
+      end
+    end
+    
+    
+    def wrap_tag_method_body_with_metadata(content)
+      name   = @def_element.attributes['tag']
+      extend = @def_element.attributes['extend-with']
+      for_   = @def_element.attributes['for']
+      name = extend ? &quot;#{name}-with-#{extend}&quot; : name
+      name += &quot; for #{for_}&quot; if for_
+      wrap_source_with_metadata(content, &quot;def&quot;, name, element_line_num(@def_element))
+    end
+    
+    
+    def wrap_tag_call_with_metadata(el, content)
+      name = el.expanded_name
+      param = el.attributes['param']
+        
+      if param == &quot;&amp;true&quot;
+        name += &quot; param&quot;
+      elsif param
+        name += &quot; param='#{param}'&quot; 
+      end
+        
+      wrap_source_with_metadata(content, &quot;call&quot;, name, element_line_num(el))
+    end
+    
        
-    def param_content_element(el)
-      name = el.attributes['for'] || @containing_tag_name
+    def param_content_element(name_or_el)
+      name = if name_or_el.is_a?(String)
+               name_or_el
+             else
+               el = name_or_el
+               el.attributes['for'] || @containing_tag_name
+             end
       local_name = param_content_local_name(name)
       &quot;&lt;%= #{local_name} &amp;&amp; #{local_name}.call %&gt;&quot;
     end
@@ -346,10 +376,19 @@ module Hobo::Dryml
 
     def part_element(el, content)
       require_attribute(el, &quot;part&quot;, DRYML_NAME_RX)
+      
+      if contains_param?(el)
+        delegated_part_element(el, content)
+      else
+        simple_part_element(el, content)
+      end
+    end
+
+    
+    def simple_part_element(el, content)
       part_name  = el.attributes['part']
       dom_id = el.attributes['id'] || part_name
       part_name = ruby_name(part_name)
-      
       part_locals = el.attributes[&quot;part-locals&quot;]
       
       part_src = &quot;&lt;% def #{part_name}_part(#{part_locals._?.gsub('@', '')}) #{tag_newlines(el)}; new_context do %&gt;&quot; +
@@ -363,6 +402,22 @@ module Hobo::Dryml
     end
     
     
+    def delegated_part_element(el, content)
+      # TODO 
+    end
+    
+    
+    def contains_param?(el)
+      # TODO
+      false
+    end
+    
+    
+    def part_delegate_tag_name(el)
+      &quot;#{@def_name}_#{el.attributes['part']}__part_delegate&quot;
+    end
+    
+    
     def get_param_name(el)
       param_name = el.attributes[&quot;param&quot;]
       
@@ -391,8 +446,8 @@ module Hobo::Dryml
         'this_type'
       elsif t =~ /^[A-Z]/
         t
-      elsif t =~ /^[a-z]/
-        &quot;Hobo.field_types[:#{t}]&quot;
+      elsif t =~ /^[a-z]/ &amp;&amp; defined? HoboFields.to_class
+        &quot;Hobo.to_class(:#{t})&quot;
       elsif is_code_attribute?(t)
         t[1..-1]
       else
@@ -431,14 +486,21 @@ module Hobo::Dryml
                elsif (call_type = polymorphic_call_type(el))
                  &quot;send(find_polymorphic_tag(:#{ruby_name name}, #{call_type}), #{attributes}, #{parameters})&quot;
                elsif attributes == &quot;{}&quot; &amp;&amp; parameters == &quot;{}&quot;
-                 &quot;#{ruby_name name}.to_s&quot;
+                 if name =~ /^[A-Z]/
+                   # it's a tag with a cap name - not a local
+                   &quot;#{ruby_name name}()&quot;
+                 else
+                   # could be a tag or a local variable
+                   &quot;#{ruby_name name}.to_s&quot;
+                 end
                else
                  &quot;#{ruby_name name}(#{attributes}, #{parameters})&quot;
                end
              end
 
       call = apply_control_attributes(call, el)
-      maybe_make_part_call(el, &quot;&lt;% _output(#{call}) %&gt;&quot;)
+      call = maybe_make_part_call(el, &quot;&lt;% _output(#{call}) %&gt;&quot;)
+      wrap_tag_call_with_metadata(el, call)
     end
     
     
@@ -449,29 +511,20 @@ module Hobo::Dryml
     end
     
 
-    def parameter_tags_hash(el)
+    def parameter_tags_hash(el, containing_tag_name=nil)
       call_type = nil
       
+      metadata_name = containing_tag_name || el.expanded_name
+      
       param_items = el.map do |node|
         case node
         when REXML::Text
           text = node.to_s
-          if text.blank?
-            # include whitespace in hash literal to keep line numbers
-            # matching
-            text
-          else
-            case call_type
-            when nil
-              call_type = :default_param_only
-              text
-            when :default_param_only
-              text
-            when :named_params
-              dryml_exception(&quot;mixed content and parameter tags&quot;, el)
-            end
+          unless text.blank?
+            dryml_exception(&quot;mixed content and parameter tags&quot;, el) if call_type == :named_params
+            call_type = :default_param_only
           end
-          node.to_s
+          text
         when REXML::Element
           e = node
           is_parameter_tag = e.parameter_tag?
@@ -487,19 +540,14 @@ module Hobo::Dryml
           end
           
           if is_parameter_tag
-            param_name = get_param_name(e)
-            if param_name
-              &quot;:#{ruby_name e.name} =&gt; merge_tag_parameter(#{param_proc(e)}, all_parameters[:#{param_name}]), &quot;
-            else
-              &quot;:#{ruby_name e.name} =&gt; #{param_proc(e)}, &quot;
-            end
+            parameter_tag_hash_item(e, metadata_name) + &quot;, &quot;
           end
         end
       end.join
       
-      if call_type == :default_param_only
+      if call_type == :default_param_only || (el.children.empty? &amp;&amp; el.has_end_tag?)
         with_containing_tag_name(el) do
-          param_items = &quot; :default =&gt; #{default_param_proc(el)}, &quot;
+          param_items = &quot; :default =&gt; #{default_param_proc(el, containing_tag_name)}, &quot;
         end
       end
       
@@ -510,17 +558,73 @@ module Hobo::Dryml
                        elsif is_code_attribute?(merge_params)
                          merge_params[1..-1]
                        else
-                         dryml_exception(&quot;invalid merge_params&quot;, el)
+                         merge_param_names = merge_params.split(/\s*,\s*/).*.gsub(&quot;-&quot;, &quot;_&quot;)
+                         &quot;all_parameters &amp; #{merge_param_names.inspect}&quot;
                        end
         &quot;{#{param_items}}.merge((#{extra_params}) || {})&quot;
       else
         &quot;{#{param_items}}&quot;
       end
     end
+    
+
+    def parameter_tag_hash_item(el, metadata_name)
+      name = el.name.dup
+      if name.sub!(/^before-/, &quot;&quot;)
+        before_parameter_tag_hash_item(name, el, metadata_name)
+      elsif name.sub!(/^after-/, &quot;&quot;)
+        after_parameter_tag_hash_item(name, el, metadata_name)
+      elsif name.sub!(/^prepend-/, &quot;&quot;)
+        prepend_parameter_tag_hash_item(name, el, metadata_name)
+      elsif name.sub!(/^append-/, &quot;&quot;)
+        append_parameter_tag_hash_item(name, el, metadata_name)
+      else
+        hash_key = ruby_name name
+        hash_key += &quot;_replacement&quot; if el.attribute(&quot;replace&quot;)
+        if (param_name = get_param_name(el))
+          &quot;:#{hash_key} =&gt; merge_tag_parameter(#{param_proc(el, metadata_name)}, all_parameters[:#{param_name}])&quot;
+        else
+          &quot;:#{hash_key} =&gt; #{param_proc(el, metadata_name)}&quot;
+        end
+      end
+    end
+    
+    
+    def before_parameter_tag_hash_item(name, el, metadata_name)
+      param_name = get_param_name(el)
+      dryml_exception(&quot;param declaration not allowed on 'before' parameters&quot;, el) if param_name
+      content = children_to_erb(el) + &quot;&lt;% _output(#{param_restore_local_name(name)}.call({}, {})) %&gt;&quot;
+      &quot;:#{ruby_name name}_replacement =&gt; #{replace_parameter_proc(el, metadata_name, content)}&quot;
+    end
 
     
-    def default_param_proc(el)
-      &quot;proc { |#{param_content_local_name(el.dryml_name)}| new_context { %&gt;#{children_to_erb(el)}&lt;% } #{tag_newlines(el)}}&quot;
+    def after_parameter_tag_hash_item(name, el, metadata_name)
+      param_name = get_param_name(el)
+      dryml_exception(&quot;param declaration not allowed on 'after' parameters&quot;, el) if param_name
+      content = &quot;&lt;% _output(#{param_restore_local_name(name)}.call({}, {})) %&gt;&quot; + children_to_erb(el) 
+      &quot;:#{ruby_name name}_replacement =&gt; #{replace_parameter_proc(el, metadata_name, content)}&quot;
+    end
+    
+    
+    def append_parameter_tag_hash_item(name, el, metadata_name)
+      &quot;:#{ruby_name name} =&gt; proc { [{}, { :default =&gt; proc { |#{param_content_local_name(name)}| new_context { %&gt;&quot; + 
+        param_content_element(name) + children_to_erb(el) +
+        &quot;&lt;% } } } ] }&quot;
+    end
+    
+    
+    def prepend_parameter_tag_hash_item(name, el, metadata_name)
+      &quot;:#{ruby_name name} =&gt; proc { [{}, { :default =&gt; proc { |#{param_content_local_name(name)}| new_context { %&gt;&quot; + 
+        children_to_erb(el) + param_content_element(name) +
+        &quot;&lt;% } } } ] }&quot;
+    end
+
+    
+    def default_param_proc(el, containing_param_name=nil)
+      content = children_to_erb(el)
+      content = wrap_source_with_metadata(content, &quot;param&quot;, containing_param_name,
+                                          element_line_num(el)) if containing_param_name
+      &quot;proc { |#{param_content_local_name(el.dryml_name)}| new_context { %&gt;#{content}&lt;% } #{tag_newlines(el)}}&quot;
     end
     
     
@@ -529,26 +633,45 @@ module Hobo::Dryml
     end
     
     
-    def param_proc(el)
-      param_name = el.dryml_name
+    def wrap_replace_parameter(el, name)
+      wrap_source_with_metadata(children_to_erb(el), &quot;replace&quot;, name, element_line_num(el))
+    end
+    
+    
+    def param_proc(el, metadata_name_prefix)
+      metadata_name = &quot;#{metadata_name_prefix}&gt;&lt;#{el.name}&quot;
+      
       nl = tag_newlines(el)
             
       if (repl = el.attribute(&quot;replace&quot;))
         dryml_exception(&quot;replace attribute must not have a value&quot;, el) if repl.has_rhs?
         dryml_exception(&quot;replace parameters must not have attributes&quot;, el) if el.attributes.length &gt; 1
         
-        &quot;proc { |#{param_restore_local_name(param_name)}| new_context { %&gt;#{children_to_erb(el)}&lt;% } #{nl}}&quot;
+        replace_parameter_proc(el, metadata_name)
       else
-        attributes = el.attributes.map do 
-          |name, value| &quot;:#{ruby_name name} =&gt; #{attribute_to_ruby(value, el)}&quot; unless name.in?(SPECIAL_ATTRIBUTES)
+        attributes = el.attributes.map do |name, value| 
+          if name.in?(VALID_PARAMETER_TAG_ATTRIBUTES)
+            # just ignore
+          elsif name.in?(SPECIAL_ATTRIBUTES)
+            dryml_exception(&quot;attribute '#{name}' is not allowed on parameter tags (&lt;#{el.name}:&gt;)&quot;, el)
+          else
+            &quot;:#{ruby_name name} =&gt; #{attribute_to_ruby(value, el)}&quot;
+          end
         end.compact
         
-        nested_parameters_hash = parameter_tags_hash(el)
+        nested_parameters_hash = parameter_tags_hash(el, metadata_name)
         &quot;proc { [{#{attributes * ', '}}, #{nested_parameters_hash}] #{nl}}&quot;
       end
     end
     
     
+    def replace_parameter_proc(el, metadata_name, content=nil)
+      content ||= wrap_replace_parameter(el, metadata_name)
+      param_name = el.dryml_name.sub(/^(before|after|append|prepend)-/, &quot;&quot;)
+      &quot;proc { |#{param_restore_local_name(param_name)}| new_context { %&gt;#{content}&lt;% } #{tag_newlines(el)}}&quot;
+    end
+    
+    
     def param_content_local_name(name)
       &quot;_#{ruby_name name}__default_content&quot;
     end
@@ -565,6 +688,11 @@ module Hobo::Dryml
     end
     
     
+    def field_shorthand_element?(el)
+      el.expanded_name =~ /:./
+    end
+    
+    
     def tag_attributes(el)
       attributes = el.attributes
       items = attributes.map do |n,v|
@@ -576,7 +704,7 @@ module Hobo::Dryml
       end.compact
       
       # if there's a ':' el.name is just the part after the ':'
-      items &lt;&lt; &quot;:field =&gt; \&quot;#{el.name}\&quot;&quot; if el.name != el.dryml_name
+      items &lt;&lt; &quot;:field =&gt; \&quot;#{ruby_name el.name}\&quot;&quot; if field_shorthand_element?(el)
       
       items = items.join(&quot;, &quot;)
       
@@ -637,7 +765,7 @@ module Hobo::Dryml
     
     
     def static_element_to_erb(el)
-      if %w(part merge-attrs if unless repeat).any? {|x| el.attributes[x]}
+      if promote_static_tag_to_method_call?(el)
         static_tag_to_method_call(el)
       else
         start_tag_src = el.start_tag_source.gsub(REXML::CData::START, &quot;&quot;).gsub(REXML::CData::STOP, &quot;&quot;)
@@ -656,18 +784,23 @@ module Hobo::Dryml
     end
     
     
+    def promote_static_tag_to_method_call?(el)
+      %w(part merge-attrs if unless repeat).any? {|x| el.attributes[x]}
+    end
+    
+    
     def apply_control_attributes(expression, el)
-      if_, unless_, repeat = controls = %w(if unless repeat).map {|x| el.attributes[x]}
-      controls.compact!
+      controls = %w(if unless repeat).map_hash { |x| el.attributes[x] }.compact
       
       dryml_exception(&quot;You can't have multiple control attributes on the same element&quot;, el) if
         controls.length &gt; 1
       
-      val = controls.first
+      attr = controls.keys.first
+      val = controls.values.first
       if val.nil?
         expression
       else
-        control = if repeat &amp;&amp; val == &quot;&amp;true&quot;
+        control = if !el.attribute(attr).has_rhs?
                     &quot;this&quot;
                   elsif is_code_attribute?(val)
                     &quot;#{val[1..-1]}&quot;
@@ -676,13 +809,14 @@ module Hobo::Dryml
                   end
         
         x = gensym
-        if if_
+        case attr
+        when &quot;if&quot;
           &quot;(if !(#{control}).blank?; (#{x} = #{expression}; Hobo::Dryml.last_if = true; #{x}) &quot; +
             &quot;else (Hobo::Dryml.last_if = false; ''); end)&quot;
-        elsif unless_
+        when &quot;unless&quot; 
           &quot;(if (#{control}).blank?; (#{x} = #{expression}; Hobo::Dryml.last_if = true; #{x}) &quot; +
             &quot;else (Hobo::Dryml.last_if = false; ''); end)&quot;
-        elsif repeat
+        when &quot;repeat&quot;
           &quot;repeat_attribute(#{control}) { #{expression} }&quot;
         end
       end
@@ -690,7 +824,7 @@ module Hobo::Dryml
     
 
     def attribute_to_ruby(*args)
-      options = extract_options_from_args!(args)
+      options = args.extract_options!
       attr, el = args
       
       dryml_exception('erb scriptlet not allowed in this attribute (use #{ ... } instead)', el) if
@@ -746,8 +880,7 @@ module Hobo::Dryml
     end
 
     def element_line_num(el)
-      offset = el.source_offset
-      line_no = @xmlsrc[0..offset].count(&quot;\n&quot;) + 1
+      @doc.element_line_num(el)
     end
 
     def tag_newlines(el)
@@ -770,9 +903,14 @@ module Hobo::Dryml
     end
     
     def rename_class(name)
-      name = name[1..-2]
-      name = class_renames[name] while class_renames.has_key?(name)
-      name
+      @bundle &amp;&amp; name.starts_with?(&quot;_&quot;) ? @bundle.send(name) : name
+    end
+    
+    def include_source_metadata
+      # disabled for now -- we're still getting broken rendering with this feature on
+      return false
+      @include_source_metadata = RAILS_ENV == &quot;development&quot; &amp;&amp; !ENV['DRYML_EDITOR'].blank? if @include_source_metadata.nil?
+      @include_source_metadata
     end
 
   end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml/template.rb</filename>
    </modified>
    <modified>
      <diff>@@ -49,14 +49,51 @@ module Hobo::Dryml
       end
     end
 
-    attr_accessor 
 
     for attr in [:erb_binding, :part_contexts, :view_name,
-                 :this, :this_parent, :this_field, :this_type,
+                 :this, :this_parent, :this_field, :this_key,
                  :form_field_path, :form_this, :form_field_names]
       class_eval &quot;def #{attr}; @_#{attr}; end&quot;
     end
     
+    def this_key=(key)
+      @_this_key = key
+    end
+    
+    
+    # The type of this, or when this is nil, the type that would be expected in the current field
+    def this_type
+      @_this_type ||= if this == false || this == true
+                        Hobo::Boolean
+                      elsif this
+                        this.class
+                      elsif this_parent &amp;&amp; this_field &amp;&amp; (parent_class = this_parent.class).respond_to?(:attr_type)
+                        type = parent_class.attr_type(this_field)
+                        if type.is_a?(ActiveRecord::Reflection::AssociationReflection)
+                          reflection = type
+                          if reflection.macro == :has_many
+                            Array
+                          elsif reflection.options[:polymorphic]
+                            # All we know is that it will be some active-record type
+                            ActiveRecord::Base
+                          else
+                            reflection.klass
+                          end
+                        else
+                          type
+                        end
+                      else
+                        # Nothing to go on at all 
+                        Object
+                      end
+    end
+    
+    
+    def this_field_reflection
+      this.try.proxy_reflection ||
+        (this_parent &amp;&amp; this_field &amp;&amp; this_parent.class.respond_to?(:reflections) &amp;&amp; this_parent.class.reflections[this_field.to_sym])
+    end
+    
     
     def attrs_for(name)
       self.class.tag_attrs[name.to_sym]
@@ -95,51 +132,50 @@ module Hobo::Dryml
     end
 
     
-    def this_field_dom_id
-      if this_parent &amp;&amp; this_field
-        Hobo.dom_id(this_parent, this_field)
-      else
-        Hobo.dom_id(this)
+    def dom_id(object=nil, attribute=nil)      
+      if object.nil?
+        # nothing passed -- use context
+        if this_parent &amp;&amp; this_field
+          object, attribute = this_parent, this_field
+        else
+          object = this
+        end
       end
-    end
-
-    
-    def context_id
-      if this_parent and this_parent.is_a?(ActiveRecord::Base) and this_field
-        this_field_dom_id
-      elsif this.respond_to?(:typed_id)
-        this.typed_id
-      elsif this.is_a?(Array) and !this.respond_to?(:proxy_reflection)
-        &quot;nil&quot;
+      
+      id = object.try.typed_id
+      if id
+        attribute ? &quot;#{id}_#{attribute}&quot; : id
       else
-        Hobo.dom_id(this)
+        &quot;nil&quot;
       end
     end
-
     
-    def call_part(dom_id, part_name, part_this=nil, *locals)
+    
+    def call_part(part_node_id, part_name, part_this=nil, *locals)
       res = ''
       if part_this
         new_object_context(part_this) do
-          @_part_contexts[dom_id] = PartContext.new(part_name, context_id, locals)
+          @_part_contexts[part_node_id] = PartContext.new(part_name, dom_id, locals)
           res = send(&quot;#{part_name}_part&quot;, *locals)
         end
       else
         new_context do
-          @_part_contexts[dom_id] = PartContext.new(part_name, context_id, locals)
+          @_part_contexts[part_node_id] = PartContext.new(part_name, dom_id, locals)
           res = send(&quot;#{part_name}_part&quot;, *locals)
         end
       end
       res
     end
+
     
     def call_polymorphic_tag(name, *args)
+      name = name.to_s.gsub('-', '_')
       type = args.first.is_a?(Class) ? args.shift : nil
       attributes, parameters = args
       
       tag = find_polymorphic_tag(name, type)
       if tag != name
-        send(tag, attributes, parameters || {})
+        send(tag, attributes || {}, parameters || {})
       else
         nil
       end
@@ -147,26 +183,17 @@ module Hobo::Dryml
 
     
     def find_polymorphic_tag(name, call_type=nil)
-      call_type ||= case this_type
-                    when ActiveRecord::Reflection::AssociationReflection
-                      # Don't blow up with non-existent polymorphic types
-                      return name if this_type.options[:polymorphic] &amp;&amp; !Object.const_defined?(this_type.class_name)
-                      this_type.klass
-                    when Array
-                      this.member_class
-                    else
-                      this_type
-                    end
-      
-      call_type = TrueClass if call_type == FalseClass
+      call_type ||= (this.is_a?(Array) &amp;&amp; this.respond_to?(:member_class) &amp;&amp; this.member_class) || this_type
 
       while true
-        if call_type == ActiveRecord::Base || call_type == Object
-          return name
-        elsif respond_to?(poly_name = &quot;#{name}__for_#{call_type.name.to_s.underscore.gsub('/', '__')}&quot;)
+        if respond_to?(poly_name = &quot;#{name}__for_#{call_type.name.to_s.underscore.gsub('/', '__')}&quot;)
           return poly_name
         else
-          call_type = call_type.superclass
+          if call_type == ActiveRecord::Base || call_type == Object
+            return name
+          else
+            call_type = call_type.superclass
+          end
         end
       end
     end
@@ -192,8 +219,9 @@ module Hobo::Dryml
     def new_context
       ctx = [ @_erb_output,
               @_this, @_this_parent, @_this_field, @_this_type,
-              @_form_field_path]
+              @_form_field_path ]
       @_erb_output = &quot;&quot;
+      @_this_type = nil
       res = yield
       @_erb_output, @_this, @_this_parent, @_this_field, @_this_type, @_form_field_path = ctx
       res.to_s
@@ -202,14 +230,7 @@ module Hobo::Dryml
 
     def new_object_context(new_this)
       new_context do
-        @_this_parent,@_this_field,@_this_type = if new_this.respond_to?(:proxy_reflection)
-                                                   refl = new_this.proxy_reflection
-                                                   [new_this.proxy_owner, refl.name, refl]
-                                                 else
-                                                   # In dryml, TrueClass is the 'boolean' class
-                                                   t = new_this.class == FalseClass ? TrueClass : new_this.class
-                                                   [nil, nil, t]
-                                                 end
+        @_this_parent, @_this_field = [new_this.origin, new_this.origin_attribute] if new_this.respond_to?(:origin) 
         @_this = new_this
         yield
       end
@@ -226,17 +247,7 @@ module Hobo::Dryml
                  [field_path]
                end
         parent, field, obj = Hobo.get_field_path(tag_this || this, path)
-
-        type = if parent.class.respond_to?(:field_type) &amp;&amp; field_type = parent.class.field_type(field)
-                 field_type
-               elsif obj == false
-                 # In dryml, TrueClass is the 'boolean' class
-                 TrueClass
-               else
-                 obj.class
-               end
-        
-        @_this, @_this_parent, @_this_field, @_this_type = obj, parent, field, type
+        @_this, @_this_parent, @_this_field = obj, parent, field
         @_form_field_path += path if @_form_field_path
         yield
       end
@@ -310,13 +321,14 @@ module Hobo::Dryml
     
     def call_tag_parameter(the_tag, attributes, parameters, caller_parameters, param_name)
       overriding_proc = caller_parameters[param_name]
+      replacing_proc  = caller_parameters[:&quot;#{param_name}_replacement&quot;]
       
       if param_name == :default &amp;&amp; overriding_proc
         # :default content is handled specially
         
         call_tag_parameter_with_default_content(the_tag, attributes, parameters[:default], overriding_proc)
-        
-      elsif overriding_proc &amp;&amp; overriding_proc.arity == 1
+
+      elsif replacing_proc
         # The caller is replacing this parameter. Don't call the tag
         # at all, just the overriding proc, but pass the restorable
         # tag as a parameter to the overriding proc
@@ -325,9 +337,17 @@ module Hobo::Dryml
           # Call the replaced tag with the attributes and parameters
           # as given in the original tag definition, and with the
           # specialisation given on the 'restore' call
+          
+          if overriding_proc
+            overriding_attributes, overriding_parameters = overriding_proc.call
+            restore_attrs  = overriding_attributes.merge(restore_attrs)
+            restore_params = overriding_parameters.merge(restore_params)
+          end
+          
           override_and_call_tag(the_tag, attributes, parameters, restore_attrs, restore_params)
         end
-        overriding_proc.call(tag_restore)
+        replacing_proc.call(tag_restore)
+        
         
       else
         overriding_attributes, overriding_parameters = overriding_proc._?.call
@@ -418,7 +438,7 @@ module Hobo::Dryml
     end
     
 
-    def part_contexts_storage_tag
+    def part_contexts_javascripts
       storage = part_contexts_storage
       storage.blank? ? &quot;&quot; : &quot;&lt;script&gt;\n#{storage}&lt;/script&gt;\n&quot;
     end
@@ -432,7 +452,14 @@ module Hobo::Dryml
     def render_tag(tag_name, attributes)
       method_name = tag_name.gsub('-', '_')
       if respond_to?(method_name)
-        (send(method_name, attributes) + part_contexts_storage_tag).strip
+        res = (send(method_name, attributes) + part_contexts_javascripts).strip
+
+        # TODO: Temporary hack to get the dryml metadata comments in the right place
+        if false &amp;&amp; RAILS_ENV == &quot;development&quot;
+          res.gsub(/^(.*?)(&lt;!DOCTYPE.*?&gt;).*?(&lt;html.*?&gt;)/m, &quot;\\2\\3\\1&quot;) 
+        else
+          res
+        end
       else
         false
       end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml/template_environment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,12 +8,70 @@ module Hobo::Dryml
 
     def render(src, local_assigns)
       renderer = Hobo::Dryml.page_renderer(@view, local_assigns.keys)
-      s = renderer.render_page(@view.assigns[&quot;this&quot;] || local_assigns[:this], local_assigns)
-      # Important to strip whitespace, or the browser hangs around for ages (FF2)
+      this = @view.instance_variable_set(&quot;@this&quot;, @view.controller.send(:dryml_context) || local_assigns[:this])
+      s = renderer.render_page(this, local_assigns) 
 
-      s.strip
+      # Important to strip whitespace, or the browser hangs around for ages (FF2)
+      s = s.strip
+      
+      # TODO: Temporary hack to get the dryml metadata comments in the right place
+      if RAILS_ENV == &quot;development&quot;
+        s.gsub(/^(.*?)(&lt;!DOCTYPE.*?&gt;).*?(&lt;html.*?&gt;)/m, &quot;\\2\\3\\1&quot;) 
+      else
+        s
+      end
     end
 
   end
 
 end
+
+module ActionController
+  
+
+  class Base
+    
+    def dryml_context
+      @this
+    end
+    
+    def dryml_fallback_tag(tag_name)
+      @dryml_fallback_tag = tag_name
+    end
+    
+    
+    def call_dryml_tag(tag, options={})
+      add_variables_to_assigns
+      
+      # TODO: Figure out what this bit is all about :-)
+      if options[:with]
+        @this = options[:with] unless options[:field]
+      else
+        options[:with] = dryml_context
+      end
+      
+      Hobo::Dryml.render_tag(@template, tag, options)
+    end
+    
+    
+    # TODO: This is namespace polution, should be called render_dryml_tag
+    def render_tag(tag, options={}, render_options={})
+      text = call_dryml_tag(tag, options)
+      text &amp;&amp; render({:text =&gt; text, :layout =&gt; false }.merge(render_options))
+    end
+
+    def render_for_file_with_dryml(template_path, *args)
+      if template_path !~ /^\// &amp;&amp;                             # not an absolute path (e.g. an exception ERB template)
+          !template_exists?(template_path) &amp;&amp;                  # no template available in app/views
+          tag_name = @dryml_fallback_tag || &quot;#{File.basename(template_path).dasherize}-page&quot;
+
+          # The template was missing, try to use a DRYML &lt;page&gt; tag instead
+          render_tag(tag_name) or raise ActionController::MissingTemplate, &quot;Missing template #{template_path}.html.erb in view path #{RAILS_ROOT}/app/views&quot;
+      else
+        render_for_file_without_dryml(template_path, *args)
+      end
+    end
+    alias_method_chain :render_for_file, :dryml
+    
+  end
+end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/dryml/template_handler.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,6 +8,13 @@ module Hobo
     end
      
     protected
+    
+    
+    def uid
+      @hobo_uid ||= 0
+      @hobo_uid += 1
+    end
+    
      
     def current_user
       # simple one-hit-per-request cache
@@ -40,66 +47,51 @@ module Hobo
     def subsite
       params[:controller].match(/([^\/]+)\//)._?[1]
     end
+
+    
+    IMPLICIT_ACTIONS = [:index, :show, :create, :update, :destroy]
      
-     
-    def object_url(*args)
+    def object_url(obj, *args)
       params = args.extract_options!
-      obj, action = args
-      action &amp;&amp;= action.to_s
-      
-      controller_name = controller_for(obj)
-      
-      subsite = params[:subsite] || self.subsite
-      
-      # TODO - what if you want if_available as a query param?
-      if_available = params.delete(:if_available)
-      return nil if if_available &amp;&amp; 
-        ((action.nil? &amp;&amp; obj.respond_to?(:typed_id) &amp;&amp; !linkable?(obj.class, :show,  :subsite =&gt; subsite)) ||
-         (action.nil? &amp;&amp; obj.is_a?(Class) &amp;&amp;           !linkable?(obj,       :index, :subsite =&gt; subsite)))
-      
-      base = subsite.blank? ? base_url : &quot;/#{subsite}#{base_url}&quot;
+      action = args.first._?.to_sym
+      options, params = params.partition_hash([:subsite, :method, :format])
+      options[:subsite] ||= self.subsite
       
-      parts = if obj.is_a? Class
-                [base, controller_name]
-                
-              elsif obj.is_a? Hobo::CompositeModel
-                [base, controller_name, obj.to_param]
-                
-              elsif obj.is_a? ActiveRecord::Base
-                if obj.new_record?
-                  [base, controller_name]
-                else
-                  raise HoboError.new(&quot;invalid object url: new for existing object&quot;) if action == &quot;new&quot;
-     
-                  klass = obj.class
-                  id = if klass.id_name?
-                         obj.id_name(true)
-                       else
-                         obj.to_param
-                       end
-                  
-                  [base, controller_name, id]
-                end
-                
-              elsif obj.is_a? Array    # warning - this breaks if we use `case/when Array`
-                owner = obj.proxy_owner
-                new_model = obj.proxy_reflection.klass
-                [object_url(owner), obj.proxy_reflection.name]
-                
-              else
-                raise HoboError.new(&quot;cannot create url for #{obj.inspect} (#{obj.class})&quot;)
-              end
-      url = parts.join(&quot;/&quot;)
+      if obj.respond_to?(:origin)
+        # Asking for URL of a collection, e.g. category/1/adverts or category/1/adverts/new
+        if action == :new
+          action_path = &quot;#{obj.origin_attribute}/new&quot;
+          action = &quot;new_#{obj.origin_attribute.to_s.singularize}&quot;
+        elsif action.nil?
+          action = obj.origin_attribute
+        end
+        obj = obj.origin
+        
+      else
+        action ||= case options[:method].to_s
+                   when 'put';    :update
+                   when 'post';   :create
+                   when 'delete'; :destroy
+                   else; obj.is_a?(Class) ? :index : :show
+                   end
 
-      case action
-      when nil       # do nothing
-      when &quot;destroy&quot; then params[&quot;_method&quot;] = &quot;DELETE&quot;
-      when &quot;update&quot;  then params[&quot;_method&quot;] = &quot;PUT&quot;
-      else url += &quot;/#{action}&quot; 
+        if action == :create &amp;&amp; obj.try.new_record?
+          # Asking for url to post new record to
+          obj = obj.class
+        end
       end
+    
+      klass = obj.is_a?(Class) ? obj : obj.class
+      if Hobo::ModelRouter.linkable?(klass, action, options)
+
+        path = obj.to_url_path or HoboError.new(&quot;cannot create url for #{obj.inspect} (#{obj.class})&quot;)
+        url = &quot;#{base_url}#{'/' + subsite unless subsite.blank?}/#{path}&quot;
 
-      params = make_params(params - [:subsite])
-      params.blank? ? url : &quot;#{url}?#{params}&quot;
+        url += &quot;/#{action_path || action}&quot; unless action.in?(IMPLICIT_ACTIONS)
+
+        params = make_params(params)
+        params.blank? ? url : &quot;#{url}?#{params}&quot;
+      end
     end
      
      
@@ -127,31 +119,16 @@ module Hobo
       hash.map {|k,v| _as_params(k, v)}.join(&quot;&amp;&quot;)
     end
      
-     
-    def dom_id(*args)
-      if args.length == 0
-        Hobo.dom_id(this)
-      else
-        Hobo.dom_id(*args)
-      end
-    rescue ArgumentError
-      &quot;&quot;
-    end
-    
     
     def type_id(type=nil)
-      type ||= this.is_a?(Class) ? this : this_type
-      type == NilClass ? &quot;&quot; : Hobo.type_id(type || this.class)
+      type ||= (this.is_a?(Class) &amp;&amp; this) || this_type || this.class
+      HoboFields.to_name(type) || type.name.underscore.gsub(&quot;/&quot;, &quot;__&quot;)
     end
-    
+
     
     def type_and_field(*args)
-      if args.empty?
-        this_parent &amp;&amp; this_field &amp;&amp; &quot;#{Hobo.type_id(this_parent.class)}_#{this_field}&quot;
-      else
-        type, field = args
-        &quot;#{Hobo.type_id(type)}_#{field}&quot;
-      end
+      type, field = args.empty? ? [this_parent.class, this_field] : args
+      &quot;#{type.typed_id}_#{field}&quot; if type.respond_to?(:typed_id)
     end
      
      
@@ -160,6 +137,8 @@ module Hobo
       empty = true
       if this.respond_to?(:each_index)
         this.each_index {|i| empty = false; new_field_context(i) { res &lt;&lt; yield } }
+      elsif this.is_a?(Hash)
+        this.map {|key, value| empty = false; self.this_key = key; new_object_context(value) { res &lt;&lt; yield } }
       else
         this.map {|e| empty = false; new_object_context(e) { res &lt;&lt; yield } }
       end
@@ -203,8 +182,8 @@ module Hobo
       else
         object, field = args.length == 2 ? args : [this, args.first]
         
-        if !field and object.respond_to?(:proxy_reflection)
-          Hobo.can_edit?(current_user, object.proxy_owner, object.proxy_reflection.name)
+        if !field &amp;&amp; object.respond_to?(:origin)
+          Hobo.can_edit?(current_user, object.origin, object.origin_attribute)
         else
           Hobo.can_edit?(current_user, object, field)
         end
@@ -225,12 +204,14 @@ module Hobo
           object = this
         end
       end
-      
-      if !field and object.respond_to?(:proxy_reflection)
-        Hobo.can_view?(current_user, object.proxy_owner, object.proxy_reflection.name)
-      else
-        Hobo.can_view?(current_user, object, field)
-      end
+
+      @can_view_cache ||= {}
+      @can_view_cache[ [object, field] ] ||= 
+        if !field &amp;&amp; object.respond_to?(:origin)
+          Hobo.can_view?(current_user, object.origin, object.origin_attribute)
+        else
+          Hobo.can_view?(current_user, object, field)
+        end
     end
      
      
@@ -272,8 +253,8 @@ module Hobo
      
     def param_name_for_this(foreign_key=false)
       return &quot;&quot; unless form_this
-      name = if foreign_key and this_type.respond_to?(:macro) and this_type.macro == :belongs_to
-               param_name_for(form_this, form_field_path[0..-2] + [this_type.primary_key_name])
+      name = if foreign_key &amp;&amp; (refl = this_field_reflection) &amp;&amp; refl.macro == :belongs_to
+               param_name_for(form_this, form_field_path[0..-2] + [refl.primary_key_name])
              else
                param_name_for(form_this, form_field_path)
              end
@@ -282,21 +263,10 @@ module Hobo
     end
      
      
-    def selector_type
-      if this.is_a? ActiveRecord::Base
-        this.class
-      elsif this.respond_to? :member_class
-        this.member_class
-      elsif this == @this
-        @model
-      end
-    end
-     
-     
     def transpose_with_field(field, collection=nil)
       collection ||= this
       matrix = collection.map {|obj| obj.send(field) }
-      max_length = matrix.every(:length).max
+      max_length = matrix.*.length.max
       matrix = matrix.map do |a|
         a + [nil] * (max_length - a.length)
       end
@@ -306,9 +276,7 @@ module Hobo
      
     def new_for_current_user(model_or_assoc=nil)
       model_or_assoc ||= this
-      record = model_or_assoc.new
-      record.set_creator(current_user)
-      record
+      model_or_assoc.user_new(current_user)
     end
     
     
@@ -338,8 +306,12 @@ module Hobo
     
 
     # Sign-up url for a given user record or user class
-    def signup_url(user_or_class)
-      c = user_or_class.is_a?(Class) ? user_or_class : user_or_class.class
+    def signup_url(user_or_class=nil)
+      c = case user_or_class
+          when Class; user_or_class
+          when nil;   Hobo::User.default_user_model
+          else user_or_class
+          end
       send(&quot;#{c.name.underscore}_signup_url&quot;) rescue nil
     end
     
@@ -366,23 +338,28 @@ module Hobo
       target = args.empty? || args.first.is_a?(Symbol) ? this : args.shift
       action = args.first
 
-      if target.is_a?(Class)
+      if (origin = target.try.origin)
+        klass = origin.class
+        action = if action == :new
+                   &quot;new_#{target.origin_attribute.to_s.singularize}&quot; 
+                 elsif action.nil?
+                   target.origin_attribute
+                 end
+      elsif target.is_a?(Class)
         klass = target
         action ||= :index
-      elsif target.respond_to?(:member_class)
-        klass = target.member_class
-        action ||= :show
       else
         klass = target.class
         action ||= :show
       end      
       
-      Hobo::ModelRouter.linkable?(subsite, klass, action.to_sym)
+      Hobo::ModelRouter.linkable?(klass, action, options.reverse_merge(:subsite =&gt; subsite))
     end
-   
+    
     
     # Convenience helper for the default app
     
+    # FIXME: this should interrogate the routes to find index methods, not the models
     def front_models
       Hobo.models.select {|m| linkable?(m) &amp;&amp; !(m &lt; Hobo::User)}
     end
@@ -398,7 +375,7 @@ module Hobo
     def log_debug(*args)
       logger.debug(&quot;\n### DRYML Debug ###&quot;)
       logger.debug(args.map {|a| PP.pp(a, &quot;&quot;)}.join(&quot;-------\n&quot;))
-      logger.debug(&quot;DRYML THIS = #{Hobo.dom_id(this) rescue this.inspect}&quot;)
+      logger.debug(&quot;DRYML THIS = #{this.typed_id rescue this.inspect}&quot;)
       logger.debug(&quot;###################\n&quot;)
       args.first unless args.empty?
     end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/hobo_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,160 +2,176 @@ module Hobo
 
   module Model
     
+    class PermissionDeniedError &lt; RuntimeError; end
+    class NoNameError &lt; RuntimeError; end
+    
     NAME_FIELD_GUESS      = %w(name title)
     PRIMARY_CONTENT_GUESS = %w(description body content profile)
     SEARCH_COLUMNS_GUESS  = %w(name title body content profile)
     
-    PLAIN_TYPES = { :boolean       =&gt; TrueClass,
-                    :date          =&gt; Date,
-                    :datetime      =&gt; Time,
-                    :integer       =&gt; Fixnum,
-                    :big_integer   =&gt; BigDecimal,
-                    :float         =&gt; Float,
-                    :string        =&gt; String
-                  }
-    
-    Hobo.field_types.update(PLAIN_TYPES)
     
     def self.included(base)
-      Hobo.register_model(base)
       base.extend(ClassMethods)
+      
+      included_in_class_callbacks(base)
+
+      Hobo.register_model(base)
+      
+      patch_will_paginate
+
       base.class_eval do
+        inheriting_cattr_reader :default_order
         alias_method_chain :attributes=, :hobo_type_conversion
-        default_scopes
       end
+      
       class &lt;&lt; base
-        alias_method_chain :has_many, :defined_scopes
-        alias_method_chain :belongs_to, :foreign_key_declaration
-        alias_method_chain :belongs_to, :hobo_metadata
-        alias_method_chain :acts_as_list, :fields if defined?(ActiveRecord::Acts::List)
+        alias_method_chain :has_many,   :defined_scopes
+        alias_method_chain :has_many,   :join_record_management
+        alias_method_chain :belongs_to, :creator_metadata
+        
+        alias_method_chain :has_one, :new_method
+        
         def inherited(klass)
+          super
           fields do
             Hobo.register_model(klass)
             field(klass.inheritance_column, :string)
           end
         end
       end
+      
+    end
+    
+    def self.patch_will_paginate
+      if defined?(WillPaginate) &amp;&amp; !WillPaginate::Collection.respond_to?(:member_class)
+        
+        WillPaginate::Collection.class_eval do
+          attr_accessor :member_class, :origin, :origin_attribute
+        end
+        
+        WillPaginate::Finder::ClassMethods.class_eval do
+          def paginate_with_hobo_metadata(*args, &amp;block)
+            returning paginate_without_hobo_metadata(*args, &amp;block) do |collection|
+              collection.member_class     = self
+              collection.origin           = try.proxy_owner
+              collection.origin_attribute = try.proxy_reflection._?.name
+            end
+          end
+          alias_method_chain :paginate, :hobo_metadata
+          
+        end
+        
+      end
     end
 
+    
     module ClassMethods
       
       # include methods also shared by CompositeModel
-      include ModelSupport::ClassMethods
+      #include ModelSupport::ClassMethods
       
       attr_accessor :creator_attribute
       attr_writer :name_attribute, :primary_content_attribute
       
-      def default_scopes
-        def_scope :recent do |*args|
-          count = args.first || 3
-          { :limit =&gt; count, :order =&gt; &quot;#{table_name}.created_at DESC&quot; }
-        end
+      def named(*args)
+        raise NoNameError, &quot;Model #{name} has no name attribute&quot; unless name_attribute
+        send(&quot;find_by_#{name_attribute}&quot;, *args)
       end
       
-      def name_attribute
-        @name_attribute ||= begin
-                              cols = columns.every :name
-                              NAME_FIELD_GUESS.detect {|f| f.in? columns.every(:name) }
-                            end
-      end
       
-
-      def primary_content_attribute
-        @description_attribute ||= begin
-                                     cols = columns.every :name
-                                     PRIMARY_CONTENT_GUESS.detect {|f| f.in? columns.every(:name) }
-                                   end
+      def field_added(name, type, args, options)
+        self.name_attribute            = name.to_sym if options.delete(:name)
+        self.primary_content_attribute = name.to_sym if options.delete(:primary_content)
+        self.creator_attribute         = name.to_sym if options.delete(:creator)
+        validate = options.delete(:validate) {true}
+        
+        #FIXME - this should be in Hobo::User
+        send(:login_attribute=, name.to_sym, validate) if options.delete(:login) &amp;&amp; respond_to?(:login_attribute=)
       end
       
-      def dependent_collections
-        reflections.values.select do |refl| 
-          refl.macro == :has_many &amp;&amp; refl.options[:dependent]
-        end.every(:name)
+      
+      def user_find(user, *args)
+        record = find(*args)
+        raise PermissionDeniedError unless Hobo.can_view?(user, record)
+        record
       end
       
       
-      def dependent_on
-        reflections.values.select do |refl| 
-          refl.macro == :belongs_to &amp;&amp; (rev = reverse_reflection(refl.name) and rev.options[:dependent])
-        end.every(:name)
+      def user_new(user, attributes={})
+        record = new(attributes)
+        record.user_changes(user) and record
       end
       
-      private
       
-      def return_type(type)
-        @next_method_type = type
+      def user_new!(user, attributes={})
+        user_new(user, attributes) or raise PermissionDeniedError
       end
       
-      def method_added(name)
-        if @next_method_type
-          set_field_type(name =&gt; @next_method_type)
-          @next_method_type = nil
-        end
+      
+      def user_create(user, attributes={})
+        record = new(attributes)
+        record.user_save_changes(user)
+        record
       end
       
       
-      def fields(&amp;b)
-        dsl = FieldDeclarationsDsl.new(self)
-        if b.arity == 1
-          yield dsl
-        else
-          dsl.instance_eval(&amp;b)
-        end
+      def user_can_create?(user, attributes={})
+        record = new(attributes)
+        record.user_changes(user)
       end
       
       
-      def belongs_to_with_foreign_key_declaration(name, *args, &amp;block)
-        options = args.extract_options!
-        res = belongs_to_without_foreign_key_declaration(name, *args + [options], &amp;block)
-        refl = reflections[name]
-        fkey = refl.primary_key_name
-        column_options = {}
-        column_options[:null] = options[:null] if options.has_key?(:null)
-        field_specs[fkey] ||= FieldSpec.new(self, fkey, :integer, column_options)
-        if refl.options[:polymorphic]
-          type_col = &quot;#{name}_type&quot;
-          field_specs[type_col] ||= FieldSpec.new(self, type_col, :string, column_options)
-        end
-        res
+      def user_update(user, id, attributes={})
+        find(id).user_save_changes(user, attributes)
       end
       
       
-      def belongs_to_with_hobo_metadata(name, *args, &amp;block)
-        options = args.extract_options!
-        self.creator_attribute = name.to_sym if options.delete(:creator)
-        belongs_to_without_hobo_metadata(name, *args + [options], &amp;block)
+      def name_attribute
+        @name_attribute ||= begin
+                              cols = columns.*.name
+                              NAME_FIELD_GUESS.detect {|f| f.in? columns.*.name }
+                            end
       end
+      
 
-
-      def acts_as_list_with_fields(options = {})
-        fields { |f| f.send(options._?[:column] || &quot;position&quot;, :integer) }
-        acts_as_list_without_fields(options)
+      def primary_content_attribute
+        @primary_content_attribute ||= begin
+                                         cols = columns.*.name
+                                         PRIMARY_CONTENT_GUESS.detect {|f| f.in? columns.*.name }
+                                       end
       end
-
-
-      def field_specs
-        @field_specs ||= HashWithIndifferentAccess.new
+      
+      def dependent_collections
+        reflections.values.select do |refl| 
+          refl.macro == :has_many &amp;&amp; refl.options[:dependent]
+        end.*.name
       end
-      public :field_specs
       
-      def set_field_type(types)
-        types.each_pair do |field, type|
-          type_class = Hobo.field_types[type] || type
-          field_types[field] = type_class
-          
-          if type_class &amp;&amp; &quot;validate&quot;.in?(type_class.instance_methods)
-            self.validate do |record|
-              v = record.send(field)._?.validate
-              record.errors.add(field, v) if v.is_a?(String)
-            end
-          end
-        end
+      
+      def dependent_on
+        reflections.values.select do |refl| 
+          refl.macro == :belongs_to &amp;&amp; (rev = reverse_reflection(refl.name) and rev.options[:dependent])
+        end.*.name
       end
       
       
-      def field_types
-        @hobo_field_types ||= superclass.respond_to?(:field_types) ? superclass.field_types : {}
+      def default_dependent_on
+        dependent_on.first
+      end
+      
+      
+      private
+      
+            
+      def belongs_to_with_creator_metadata(name, options={}, &amp;block)
+        self.creator_attribute = name.to_sym if options.delete(:creator)
+        belongs_to_without_creator_metadata(name, options, &amp;block)
+      end
+      
+            
+      def has_one_with_new_method(name, options={}, &amp;block)
+        has_one_without_new_method(name, options)
+        class_eval &quot;def new_#{name}(attributes={}); build_#{name}(attributes, false); end&quot;
       end
       
       
@@ -163,181 +179,65 @@ module Hobo
         @default_order = order
       end
       
-      inheriting_attr_accessor :default_order, :id_name_options
-
 
       def never_show(*fields)
         @hobo_never_show ||= []
-        @hobo_never_show.concat(fields.every(:to_sym))
-      end
-
-      def never_show?(field)
-        (@hobo_never_show &amp;&amp; field.to_sym.in?(@hobo_never_show)) || (superclass &lt; Hobo::Model &amp;&amp; superclass.never_show?(field))
+        @hobo_never_show.concat(fields.*.to_sym)
       end
-      public :never_show?
 
+      
       def set_search_columns(*columns)
         class_eval %{
           def self.search_columns
-            %w{#{columns.every(:to_s) * ' '}}
+            %w{#{columns.*.to_s * ' '}}
           end
         }
       end
       
-
-      def id_name(*args)
-        @id_name_options = [] + args
-        
-        underscore = args.delete(:underscore)
-        insenstive = args.delete(:case_insensitive)
-        id_name_field = args.first || :name
-        @id_name_column = id_name_field.to_s
-
-        if underscore
-          class_eval %{
-            def id_name(underscore=false)
-              underscore ? #{id_name_field}.gsub(' ', '_') : #{id_name_field}
-            end
-          }
-        else
-          class_eval %{
-            def id_name(underscore=false)
-              #{id_name_field}
-            end
-          }
-        end
-        
-        key = &quot;id_name#{if underscore; &quot;.gsub('_', ' ')&quot;; end}&quot;
-        finder = if insenstive
-          &quot;find(:first, options.merge(:conditions =&gt; ['lower(#{id_name_field}) = ?', #{key}.downcase]))&quot;
-        else
-          &quot;find_by_#{id_name_field}(#{key}, options)&quot;
-        end
-
-        class_eval %{
-          def self.find_by_id_name(id_name, options={})
-            #{finder}
-          end
-        }
-        
-        model = self
-        validate do
-          erros.add id_name_field, &quot;is taken&quot; if model.find_by_id_name(name)
-        end
-        validates_format_of id_name_field, :with =&gt; /^[^_]+$/, :message =&gt; &quot;cannot contain underscores&quot; if
-          underscore
-      end
-
-      public
       
-      def id_name?
-        respond_to?(:find_by_id_name)
-      end
-
-      attr_reader :id_name_column
+      public
 
       
-      def field_type(name)
-        name = name.to_sym
-        field_types[name] or
-          reflections[name] or begin
-                                 col = column(name)
-                                 return nil if col.nil?
-                                 case col.type
-                                 when :boolean
-                                   TrueClass
-                                 when :text
-                                   Hobo::Text
-                                 else
-                                   col.klass
-                                 end
-                               end
-      end
-      
-      
-      def column(name)
-        columns.find {|c| c.name == name.to_s} rescue nil
-      end
-      
-      
-      def conditions(*args, &amp;b)
-        if args.empty?
-          ModelQueries.new(self).instance_eval(&amp;b)._?.to_sql
-        else
-          ModelQueries.new(self).instance_exec(*args, &amp;b)._?.to_sql
-        end
+      def never_show?(field)
+        (@hobo_never_show &amp;&amp; field.to_sym.in?(@hobo_never_show)) || (superclass &lt; Hobo::Model &amp;&amp; superclass.never_show?(field))
       end
       
 
       def find(*args, &amp;b)
         options = args.extract_options!
-        if args.first.in?([:all, :first]) &amp;&amp; options[:order] == :default
+        if options[:order] == :default
           options = if default_order.blank?
-                      options - [:order]
+                      options.except :order
                     else
                       options.merge(:order =&gt; &quot;#{table_name}.#{default_order}&quot;)
                     end
         end
-          
-        res = if b &amp;&amp; !(block_conditions = conditions(&amp;b)).blank?
-                c = if !options[:conditions].blank?
-                      &quot;(#{sanitize_sql options[:conditions]}) AND (#{sanitize_sql block_conditions})&quot;
-                    else
-                      block_conditions
-                    end
-                super(args.first, options.merge(:conditions =&gt; c))
-              else
-                super(*args + [options])
-              end
-        if args.first == :all
-          def res.member_class
-            @member_class
-          end
-          res.instance_variable_set(&quot;@member_class&quot;, self)
-        end
-        res
+        result = super(*args + [options])
+        result.member_class = self if result.is_a?(Array)
+        result
       end
-      
+
       
       def all(options={})
         find(:all, options.reverse_merge(:order =&gt; :default))
       end
       
       
-      def count(*args, &amp;b)
-        if b
-          sql = ModelQueries.new(self).instance_eval(&amp;b).to_sql
-          options = extract_options_from_args!(args)
-          super(*args + [options.merge(:conditions =&gt; sql)])
-        else
-          super(*args)
-        end
-      end
-
-
-      def subclass_associations(association, *subclass_associations)
-        refl = reflections[association]
-        for assoc in subclass_associations
-          class_name = assoc.to_s.classify
-          options = { :class_name =&gt; class_name, :conditions =&gt; &quot;type = '#{class_name}'&quot; }
-          options[:source] = refl.source_reflection.name if refl.source_reflection
-          has_many(assoc, refl.options.merge(options))
-        end
-      end
-
       def creator_type
         reflections[creator_attribute]._?.klass
       end
 
+      
       def search_columns
-        cols = columns.every(:name)
+        cols = columns.*.name
         SEARCH_COLUMNS_GUESS.select{|c| c.in?(cols) }
       end
       
-      # This should really be a method on AssociationReflection
+      
+      # FIXME: This should really be a method on AssociationReflection
       def reverse_reflection(association_name)
         refl = reflections[association_name]
-        return nil if refl.options[:conditions]
+        return nil if refl.options[:conditions] || refl.options[:polymorphic]
         
         reverse_macro = if refl.macro == :has_many
                           :belongs_to
@@ -353,153 +253,124 @@ module Hobo
       end
       
       
-      class ScopedProxy
-        
-        def initialize(klass, scope)
-          @klass = klass
-          
-          # If there's no :find, or :create specified, assume it's a find scope
-          @scope = if scope.has_key?(:find) || scope.has_key?(:create)
-                     scope
-                   else
-                     { :find =&gt; scope }
-                   end
-        end
-        
-        
-        def method_missing(name, *args, &amp;block)
-          if name.to_sym.in?(@klass.defined_scopes.keys)
-            proxy = @klass.send(name, *args)
-            proxy.instance_variable_set(&quot;@parent_scope&quot;, self)
-            proxy
-          else
-            _apply_scope { @klass.send(name, *args, &amp;block) }
-          end
-        end
-        
-        def all
-          self.find(:all)
-        end
-        
-        def first
-          self.find(:first)
-        end
-        
-        def member_class
-          @klass
-        end
-        
-        private
-        def _apply_scope
-          if @parent_scope
-            @parent_scope.send(:_apply_scope) do
-              @scope ? @klass.send(:with_scope, @scope) { yield } : yield
-            end
-          else
-            @scope ? @klass.send(:with_scope, @scope) { yield } : yield
-          end
+      def has_inheritance_column?
+        columns_hash.include?(inheritance_column)
+      end
+      
+
+      def method_missing(name, *args, &amp;block)
+        name = name.to_s
+        if name =~ /\./
+          # FIXME: Do we need this now?
+          call_method_chain(name, args, &amp;block)
+        elsif create_automatic_scope(name)
+          send(name, *args, &amp;block)
+        else
+          super(name.to_sym, *args, &amp;block)
         end
-        
       end
-      (Object.instance_methods + 
-       Object.private_instance_methods +
-       Object.protected_instance_methods).each do |m|
-        ScopedProxy.send(:undef_method, m) unless
-          m.in?(%w{initialize method_missing send instance_variable_set instance_variable_get puts}) || m.starts_with?('_')
+
+      
+      def call_method_chain(chain, args, &amp;block)
+        parts = chain.split(&quot;.&quot;)
+        s = parts[0..-2].inject(self) { |m, scope| m.send(scope) }
+        s.send(parts.last, *args)
       end
       
-      attr_accessor :defined_scopes
-
       
-      def def_scope(name, scope=nil, &amp;block)
-        @defined_scopes ||= {}
-        @defined_scopes[name.to_sym] = block || scope
-        
-        meta_def(name) do |*args|
-          ScopedProxy.new(self, block ? block.call(*args) : scope)
-        end
+      def to_url_path
+        &quot;#{name.underscore.pluralize}&quot;
       end
       
       
-      module DefinedScopeProxyExtender
-        
-        attr_accessor :reflections
+      def typed_id
+        HoboFields.to_name(self) || name.underscore.gsub(&quot;/&quot;, &quot;__&quot;)
+      end
+      
+      
+      def manage_join_records(association)
         
-        def method_missing(name, *args, &amp;block)
-          scope = (proxy_reflection.klass.respond_to?(:defined_scopes) and
-                   scopes = proxy_reflection.klass.defined_scopes and 
-                   scopes[name.to_sym])
-          
-          scope = scope.call(*args) if scope.is_a?(Proc)
-          
-          # If there's no :find, or :create specified, assume it's a find scope
-          find_scope = if scope &amp;&amp; (scope.has_key?(:find) || scope.has_key?(:create))
-                         scope[:find]
-                       else
-                         scope
-                       end
-          
-          if find_scope
-            scope_name = &quot;@#{name.to_s.gsub('?','')}_scope&quot;
-
-            # Calling instance_variable_get directly causes self to
-            # get loaded, hence this trick
-            assoc = Kernel.instance_method(:instance_variable_get).bind(self).call(scope_name)
-            
-            unless assoc
-              options = proxy_reflection.options
-              has_many_conditions = options[:conditions]
-              has_many_conditions = nil if has_many_conditions.blank?
-              source = proxy_reflection.source_reflection
-              scope_conditions = find_scope[:conditions]
-              scope_conditions = nil if scope_conditions.blank?
-              conditions = if has_many_conditions &amp;&amp; scope_conditions
-                             &quot;(#{sanitize_sql scope_conditions}) AND (#{sanitize_sql has_many_conditions})&quot;
-                           else
-                             scope_conditions || has_many_conditions
-                           end
-              
-              options = options.merge(find_scope).update(:class_name =&gt; proxy_reflection.klass.name,
-                                                         :foreign_key =&gt; proxy_reflection.primary_key_name)
-              options[:conditions] = conditions unless conditions.blank?
-              options[:source] = source.name if source
-
-              r = ActiveRecord::Reflection::AssociationReflection.new(:has_many,
-                                                                      name,
-                                                                      options,
-                                                                      proxy_owner.class)
-              
-              @reflections ||= {}
-              @reflections[name] = r
+        method = &quot;manage_join_records_for_#{association}&quot;
+        after_save method
+        class_eval %{
+          def #{method}
+            assigned = #{association}.dup
+            current = #{association}.reload
               
-              assoc = if source
-                        ActiveRecord::Associations::HasManyThroughAssociation
-                      else
-                        ActiveRecord::Associations::HasManyAssociation
-                      end.new(self.proxy_owner, r)
-
-              # Calling directly causes self to get loaded
-              Kernel.instance_method(:instance_variable_set).bind(self).call(scope_name, assoc)
-            end
-            assoc
-          else
-            super
+            through = #{association}.proxy_reflection.through_reflection
+            source  = #{association}.proxy_reflection.source_reflection
+
+            to_delete = current - assigned
+            to_add    = assigned - current
+            through.klass.delete_all([&quot;\#{through.primary_key_name} = ? and \#{source.primary_key_name} in (?)&quot;, 
+                                             self.id, to_delete.*.id]) if to_delete.any?
+            to_add.each { |record| #{association} &lt;&lt; record }
           end
-        end
-        
+        }
       end
       
-      
-      def has_many_with_defined_scopes(name, *args, &amp;block)
-        options = args.extract_options!
-        if options.has_key?(:extend) || block
-          # Normal has_many
-          has_many_without_defined_scopes(name, *args + [options], &amp;block)
-        else
-          options[:extend] = DefinedScopeProxyExtender
-          has_many_without_defined_scopes(name, *args + [options], &amp;block)
+      def has_many_with_join_record_management(name, options={}, &amp;b)
+        manage = options.delete(:managed)
+        returning (has_many_without_join_record_management(name, options, &amp;b)) do 
+          manage_join_records(name) if manage
         end
       end
+
+    end # --- of ClassMethods --- #
+    
+    
+    include Scopes
+    
+    
+    def to_url_path
+      &quot;#{self.class.to_url_path}/#{to_param}&quot; unless new_record?
+    end
+    
+    
+    def user_changes(user, changes={})
+      if new_record?
+        self.attributes = changes
+        set_creator(user) 
+        Hobo.can_create?(user, self)
+      else
+        original = duplicate
+        # 'duplicate' can cause these to be set, but they can conflict
+        # with the changes so we clear them
+        clear_aggregation_cache
+        clear_association_cache
+        
+        self.attributes = changes
+        
+        Hobo.can_update?(user, original, self)
+      end        
+    end
+    
+    
+    def user_changes!(user, changes={})
+      user_changes(user, changes) or raise PermissionDeniedError
+    end
+    
+    
+    def user_can_create?(user, attributes={})
+      raise ArgumentError, &quot;Called #user_can_create? on existing record&quot; unless new_record?
+      user_changes(user, attributes)
+    end
+      
+
+    def user_save_changes(user, changes={})
+      user_changes!(user, changes)
+      save
+    end
+    
+
+    def user_view(user, field=nil)
+      raise PermissionDeniedError, self.inspect unless Hobo.can_view?(user, self, field)
+    end
+    
+    
+    def user_destroy(user)
+      raise PermissionDeniedError unless Hobo.can_delete?(user, self)
+      destroy
     end
     
     
@@ -509,13 +380,18 @@ module Hobo
     
     
     def attributes_with_hobo_type_conversion=(attributes, guard_protected_attributes=true)
-      converted = attributes.map_hash { |k, v| convert_type_for_mass_assignment(self.class.field_type(k), v) }
+      converted = attributes.map_hash { |k, v| convert_type_for_mass_assignment(self.class.attr_type(k), v) }
       send(:attributes_without_hobo_type_conversion=, converted, guard_protected_attributes)
     end
       
 
     
     def set_creator(user)
+      set_creator!(user) unless get_creator
+    end
+    
+    
+    def set_creator!(user)
       attr = self.class.creator_attribute
       return unless attr
       
@@ -528,22 +404,30 @@ module Hobo
         self.send(&quot;#{attr}=&quot;, user.to_s) unless user.guest?
       end
     end
+    
+    
+    # We deliberately give this method an unconventional name to avoid
+    # polluting the application namespace too badly
+    def get_creator
+      self.class.creator_attribute &amp;&amp; send(self.class.creator_attribute)
+    end
 
 
     def duplicate
-      res = self.class.new
-      res.instance_variable_set(&quot;@attributes&quot;, @attributes.dup)
-      res.instance_variable_set(&quot;@new_record&quot;, nil) unless new_record?
+      copy = self.class.new
+      copy.copy_instance_variables_from(self, [&quot;@attributes_cache&quot;])
+      copy.instance_variable_set(&quot;@attributes&quot;, @attributes.dup)
+      copy.instance_variable_set(&quot;@new_record&quot;, nil) unless new_record?
       
       # Shallow copy of belongs_to associations
       for refl in self.class.reflections.values
         if refl.macro == :belongs_to and (target = self.send(refl.name))
-          bta = ActiveRecord::Associations::BelongsToAssociation.new(res, refl)
+          bta = ActiveRecord::Associations::BelongsToAssociation.new(copy, refl)
           bta.replace(target)
-          res.instance_variable_set(&quot;@#{refl.name}&quot;, bta)
+          copy.instance_variable_set(&quot;@#{refl.name}&quot;, bta)
         end
       end
-      res
+      copy
     end
 
 
@@ -554,29 +438,25 @@ module Hobo
       fields.all?{|f| self.send(f) == other.send(f)}
     end
     
+    
     def only_changed_fields?(other, *changed_fields)
       return true if other.nil?
       
-      changed_fields = changed_fields.flatten.every(:to_s)
-      all_cols = self.class.columns.every(:name) - []
+      changed_fields = changed_fields.flatten.*.to_s
+      all_cols = self.class.columns.*.name - []
       all_cols.all?{|c| c.in?(changed_fields) || self.send(c) == other.send(c) }
     end
     
+    
     def compose_with(object, use=nil)
       CompositeModel.new_for([self, object])
     end
     
-    def created_date
-      created_at.to_date
-    end
-    
-    def modified_date
-      modified_at.to_date
-    end
 
     def typed_id
-      id ? &quot;#{self.class.name.underscore}_#{self.id}&quot; : nil
+      &quot;#{self.class.name.underscore}_#{self.id}&quot; if id 
     end
+
     
     def to_s
       if self.class.name_attribute
@@ -586,21 +466,22 @@ module Hobo
       end
     end
     
+    
     private
     
+    
     def parse_datetime(s)
       defined?(Chronic) ? Chronic.parse(s) : Time.parse(s)
     end
 
+    
     def convert_type_for_mass_assignment(field_type, value)
-      if field_type.is_a?(ActiveRecord::Reflection::AssociationReflection) and field_type.macro.in?([:belongs_to, :has_one])
-        if value.is_a?(String) &amp;&amp; value.starts_with?('@')
-          Hobo.object_from_dom_id(value[1..-1])
-        else
-          value
-        end
+      if field_type.is_a?(ActiveRecord::Reflection::AssociationReflection)
+        convert_associated_records_for_mass_assignment(field_type, value)
+        
       elsif !field_type.is_a?(Class)
         value
+        
       elsif field_type &lt;= Date
         if value.is_a? Hash
           Date.new(*(%w{year month day}.map{|s| value[s].to_i}))
@@ -610,6 +491,7 @@ module Hobo
         else
           value
         end
+        
       elsif field_type &lt;= Time
         if value.is_a? Hash
           Time.local(*(%w{year month day hour minute}.map{|s| value[s].to_i}))
@@ -618,62 +500,52 @@ module Hobo
         else
           value
         end
-      elsif field_type &lt;= TrueClass
+        
+      elsif field_type &lt;= Hobo::Boolean
         (value.is_a?(String) &amp;&amp; value.strip.downcase.in?(['0', 'false']) || value.blank?) ? false : true
+        
       else
         # primitive field
         value
       end
     end
     
-  end
-end
-
-
-# Hack AR to get Hobo type wrappers in
-
-module ActiveRecord::AttributeMethods::ClassMethods
-
-  # Define an attribute reader method.  Cope with nil column.
-  def define_read_method(symbol, attr_name, column)
-    cast_code = column.type_cast_code('v') if column
-    access_code = cast_code ? &quot;(v=@attributes['#{attr_name}']) &amp;&amp; #{cast_code}&quot; : &quot;@attributes['#{attr_name}']&quot;
-
-    unless attr_name.to_s == self.primary_key.to_s
-      access_code = access_code.insert(0, &quot;missing_attribute('#{attr_name}', caller) &quot; +
-                                       &quot;unless @attributes.has_key?('#{attr_name}'); &quot;)
-    end
-
-    # This is the Hobo hook - add a type wrapper around the field
-    # value if we have a special type defined
-    src = if connected? &amp;&amp; respond_to?(:field_type) &amp;&amp; (type_wrapper = field_type(symbol)) &amp;&amp;
-              type_wrapper.is_a?(Class) &amp;&amp; type_wrapper.not_in?(Hobo::Model::PLAIN_TYPES.values)
-            &quot;val = begin; #{access_code}; end; &quot; +
-              &quot;if val.nil? || (val.respond_to?(:hobo_undefined?) &amp;&amp; val.hobo_undefined?); val; &quot; + 
-              &quot;else; self.class.field_type(:#{attr_name}).new(val); end&quot;
+    def convert_associated_records_for_mass_assignment(reflection, value)
+      if reflection.macro.in?([:belongs_to, :has_one])
+        if value.is_a?(String)
+          if value.starts_with?('@')
+            # TODO: This @foo_1 feature is rarely (never?) used - get rid of it
+            Hobo.object_from_dom_id(value[1..-1])
           else
-            access_code
+            reflection.klass.named(value)
           end
-    
-    evaluate_attribute_method(attr_name, 
-                              &quot;def #{symbol}; @attributes_cache['#{attr_name}'] ||= begin; #{src}; end; end&quot;)
+        else
+          value
+        end
+      elsif reflection.macro == :has_many
+        if reflection.klass.try.name_attribute
+          value.map do |x| 
+            if x.is_a?(String) 
+              reflection.klass.named(x) unless x.blank?
+            else
+              x
+            end
+          end.compact
+        else
+          value
+        end
+        
+      else
+        # unknown kind of accociation - no conversion
+        value
+      end
+    end
+        
   end
   
-  def define_write_method(attr_name)
-    src = if connected? &amp;&amp; respond_to?(:field_type) &amp;&amp; (type_wrapper = field_type(attr_name)) &amp;&amp;
-              type_wrapper.is_a?(Class) &amp;&amp; type_wrapper.not_in?(Hobo::Model::PLAIN_TYPES.values)
-            &quot;if val.nil? || (val.respond_to?(:hobo_undefined?) &amp;&amp; val.hobo_undefined?); val; &quot; + 
-              &quot;else; self.class.field_type(:#{attr_name}).new(val); end&quot;
-          else
-            &quot;val&quot;
-          end
-    evaluate_attribute_method(attr_name, &quot;def #{attr_name}=(val); &quot; + 
-                              &quot;write_attribute('#{attr_name}', #{src});end&quot;, &quot;#{attr_name}=&quot;)
-    
-  end
-
 end
 
+
 class ActiveRecord::Base
   alias_method :has_hobo_method?, :respond_to_without_attributes?
 end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/model.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,66 +6,50 @@ module Hobo
 
     VIEWLIB_DIR = &quot;taglibs&quot;
     
-    PAGINATE_FORMATS = [ Mime::HTML, Mime::ALL ]
+    DONT_PAGINATE_FORMATS = [ Mime::CSV, Mime::YAML, Mime::JSON, Mime::XML, Mime::ATOM, Mime::RSS ]
+    
+    READ_ONLY_ACTIONS  = [:index, :show]
+    WRITE_ONLY_ACTIONS = [:create, :update, :destroy]
+    FORM_ACTIONS       = [:new, :edit]
     
     class &lt;&lt; self
 
       def included(base)
         base.class_eval do 
           @auto_actions ||= {}
+
+          inheriting_cattr_reader :web_methods =&gt; [], :show_actions =&gt; [], :index_actions =&gt; []
           
           extend ClassMethods
           
+          
           helper_method :model, :current_user
           before_filter :set_no_cache_headers
+          
+          rescue_from ActiveRecord::RecordNotFound, :with =&gt; :not_found
+              
+          rescue_from Hobo::Model::PermissionDeniedError, :with =&gt; :permission_denied
+          
+          alias_method_chain :render, :hobo_model
+
         end
-        base.template_path_cache = {}        
 
         Hobo::Controller.included_in_class(base)
       end
       
     end
+    
 
     module ClassMethods
 
       attr_writer :model
       
-      attr_accessor :template_path_cache
-      
-      def add_collection_actions(name)
-        defined_methods = instance_methods
-        
-        show_collection_method = &quot;show_#{name}&quot;.to_sym
-        if show_collection_method.not_in?(defined_methods) &amp;&amp; include_action?(show_collection_method, true)
-          define_method show_collection_method do
-            hobo_show_collection(name)
-          end
-        end
-          
-        if Hobo.simple_has_many_association?(model.reflections[name])
-          new_method = &quot;new_#{name.to_s.singularize}&quot;
-          if new_method.not_in?(defined_methods) &amp;&amp; include_action?(new_method, true)
-            define_method new_method do
-              hobo_new_in_collection(name)
-            end
-          end
-        end
-      end
-            
-      def web_methods
-        @web_methods ||= superclass.respond_to?(:web_methods) ? superclass.web_methods : []
-      end
-      
-      def show_actions
-        @show_actions ||= superclass.respond_to?(:show_actions) ? superclass.show_actions : []
-      end
-      
-      def index_actions
-        @index_actions ||= superclass.respond_to?(:index_actions) ? superclass.index_actions : []
-      end
-      
       def collections
-        # By default, all has_many associations are published
+        # FIXME The behaviour here is weird if the superclass does
+        # define collections *and* this class adds some more. The
+        # added ones won't be published
+        
+        # by default By default, all has_many associations are published
         @collections ||= if superclass.respond_to?(:collections)
                            superclass.collections
                          else
@@ -73,72 +57,110 @@ module Hobo
                          end
       end
 
+      
       def model
         @model ||= name.sub(/Controller$/, &quot;&quot;).singularize.constantize
       end
-
-
-      def autocomplete_for(attr, options={}, &amp;b)
-        options = options.reverse_merge(:limit =&gt; 15)
-        options[:data_filters_block] = b
-        @completers ||= HashWithIndifferentAccess.new
-        @completers[attr.to_sym] = options
+      
+      
+      def autocomplete(name, options={}, &amp;block)
+        options = options.dup
+        field = options.delete(:field) || name
+        if block
+          index_action &quot;complete_#{name}&quot;, &amp;block
+        else
+          index_action &quot;complete_#{name}&quot; do
+            hobo_completetions name, model, options
+          end
+        end
       end
 
 
-      def autocompleter(name)
-        (@completers &amp;&amp; @completers[name]) ||
-          (superclass.respond_to?(:autocompleter) &amp;&amp; superclass.autocompleter(name))
-      end
-      
-      
       def web_method(web_name, options={}, &amp;block)
         web_methods &lt;&lt; web_name.to_sym
-        method = options[:method] || web_name
+        method = options.delete(:method) || web_name
         got_block = block_given?
         define_method web_name do
           # Make sure we have a copy of the options - it is being mutated somewhere
           opts = {}.merge(options)
           @this = find_instance(opts) unless opts[:no_find]
-          set_status(Hobo.can_call?(current_user, @this, method) ? :valid : :not_allowed)
-          # TODO - block should get to handle permission denied?
-          if not_allowed?
-            permission_denied
-          elsif got_block
+          raise Hobo::Model::PermissionDeniedError unless Hobo.can_call?(current_user, @this, method)
+          if got_block
             instance_eval(&amp;block)
           else
             @this.send(method)
           end
           
-          hobo_ajax_response unless performed?
+          hobo_ajax_response || render(:nothing =&gt; true) unless performed?
         end
       end
       
       
       def auto_actions(*args)
-        # auto_actions is either an array - the actions to provide, or a hash: { :except =&gt; [...] }
+        options = args.extract_options!
+        
         @auto_actions = case args.first
-                          when :all  then args.extract_options!
-                          when :none then []
+                          when :all        then available_auto_actions
+                          when :write_only then available_auto_write_actions + args.rest
+                          when :read_only  then available_auto_read_actions  + args.rest
                           else args
                         end
 
+        except = Array(options[:except])
+        except_actions = except.map do |arg|
+          if arg == :collections
+            available_auto_collection_actions
+          else
+            arg
+          end
+        end
+        
+        @auto_actions -= except_actions.flatten
+        
+        def_auto_actions
+      end
+      
+      
+      def def_auto_actions
         self.class_eval do
           def index;   hobo_index   end if include_action?(:index) 
           def show;    hobo_show    end if include_action?(:show) 
           def new;     hobo_new     end if include_action?(:new) 
           def create;  hobo_create  end if include_action?(:create) 
-          def edit;    hobo_edit    end if include_action?(:edit) 
+          def edit;    hobo_show    end if include_action?(:edit) 
           def update;  hobo_update  end if include_action?(:update) 
           def destroy; hobo_destroy end if include_action?(:destroy) 
           
           def completions; hobo_completions end if include_action?(:completions)
           
-          collections.each { |c| add_collection_actions(c.to_sym) } 
+          def reorder; hobo_reorder end if include_action?(:reorder) 
         end
+
+        collections.each { |c| def_collection_actions(c.to_sym) }
       end
       
       
+      def def_collection_actions(name)
+        defined_methods = instance_methods
+        
+        show_collection_method = name
+        if show_collection_method.not_in?(defined_methods) &amp;&amp; include_action?(show_collection_method)
+          define_method show_collection_method do
+            hobo_show_collection(name)
+          end
+        end
+          
+        if Hobo.simple_has_many_association?(model.reflections[name])
+          new_method = &quot;new_#{name.to_s.singularize}&quot;
+          if new_method.not_in?(defined_methods) &amp;&amp; include_action?(new_method)
+            define_method new_method do
+              hobo_new_in_collection(name)
+            end
+          end
+        end
+      end
+            
+      
       def show_action(*names, &amp;block)
         options = names.extract_options!
         show_actions.concat(names)
@@ -158,504 +180,349 @@ module Hobo
           if block
             define_method(name, &amp;block)
           else
-            define_method(name) { hobo_index options }
+            if scope = options.delete(:scope)
+              define_method(name) { hobo_index scope, options }
+            else
+              define_method(name) { hobo_index options }
+            end
           end
         end
       end
       
       def publish_collection(*names)
         collections.concat(names)
-        names.each {|n| add_collection_actions(n)}
+        names.each {|n| def_collection_actions(n)}
       end
       
       
-      def find_instance(id, options={})
-        if model.id_name? and id !~ /^\d+$/
-          model.find_by_id_name(id, options)
-        else
-          model.find(id, options)
-        end
+      def include_action?(name)
+        name.to_sym.in?(@auto_actions)
       end
-            
-      def include_action?(name, collection_action=false)
-        name = name.to_sym
-        if @auto_actions.is_a?(Array)
-          # White list
-          name.in?(@auto_actions) || (collection_action &amp;&amp; :collections.in?(@auto_actions))
+      
+      
+      def available_auto_actions
+        (available_auto_read_actions +
+         available_auto_write_actions + 
+         FORM_ACTIONS + 
+         available_auto_collection_actions).uniq
+      end
+      
+      
+      def available_auto_read_actions
+        READ_ONLY_ACTIONS + collections
+      end
+      
+      
+      def available_auto_write_actions
+        if &quot;position_column&quot;.in?(model.instance_methods)
+          WRITE_ONLY_ACTIONS + [:reorder]
         else
-          # Black list
-          except = Array(@auto_actions[:except])
-          return !(name.in?(except) || (collection_action &amp;&amp; :collections.in?(except)))
+          WRITE_ONLY_ACTIONS
         end
       end
+      
+      
+      def available_auto_collection_actions
+        collections + collections.map {|c| &quot;new_#{c.to_s.singularize}&quot;.to_sym}
+      end
 
     end
     
 
     protected
     
-    def data_filter(name, &amp;b)
-      @data_filters ||= HashWithIndifferentAccess.new
-      @data_filters[name] = b
-    end
     
-    def search(*args)
-      if args.first.is_a?(Class)
-        model, search_string, *columns = args
-      else
-        model = self.model
-        search_string, *columns = args
-      end
-      return nil if search_string.blank?
-      
-      model.conditions do
-        words = search_string.split
-        terms = words.map do |word|
-          cols = columns.map do |c|
-            if c.is_a?(Symbol)
-              send(&quot;#{c}_contains&quot;, word)
-            elsif c.is_a?(Hash)
-              c.map do |k, v| 
-                related = send(k)
-                v = [v] unless v.is_a?(Array)
-                v.map { |related_col| related.send(&quot;#{related_col}_contains&quot;, word) }
-              end
-            end
-          end.flatten
-          any?(*cols)
+    def parse_sort_param(*sort_fields)
+      _, desc, field = *params[:sort]._?.match(/^(-)?([a-z_]+(?:\.[a-z_]+)?)$/)
+
+      if field
+        if field.in?(sort_fields.*.to_s)
+          @sort_field = field
+          @sort_direction = desc ? &quot;desc&quot; : &quot;asc&quot;
+        
+          [@sort_field, @sort_direction]
         end
-        all?(*terms)
       end
     end
     
-    attr_accessor :data_filters
     
-
     # --- Action implementation helpers --- #
     
-    def find_instance(*args)
-      options = args.extract_options!
-      res = self.class.find_instance(args.first || params[:id], options)
-      instance_variable_set(&quot;@#{model.name.underscore}&quot;, res)
-      res
-    end
-    
-    
-    def set_named_this!
-      instance_variable_set(&quot;@#{@this.class.name.underscore}&quot;, @this)      
-    end
 
-    
-    def response_block(&amp;b)
-      if b
-        if b.arity == 1
-          respond_to {|wants| yield(wants) }
-        else
-          yield
-        end
-        performed?
-      end
+    def find_instance(options={})
+      model.user_find(current_user, params[:id], options)
     end
-
     
-    def paginated_find(*args, &amp;b)
-      options = args.extract_options!
-      filter_conditions = data_filter_conditions
-      conditions_proc = if b &amp;&amp; filter_conditions
-                          proc { block(b) &amp; block(filter_conditions) }
-                        else
-                          b || filter_conditions
-                        end
-      
-      @association = options.delete(:association) ||
-        if args.length == 1
-          scopes = args.first
-          @association = scopes.to_s.split(&quot;.&quot;).inject(model) { |m, name| m.send(name) }
-        elsif args.length == 2
-          owner, collection_name = args
-          @association = collection_name.to_s.split(&quot;.&quot;).inject(owner) { |m, name| m.send(name) }
-        end
-      @reflection = @association.proxy_reflection if @association._?.respond_to?(:proxy_reflection)
-      
-      model_or_assoc, @member_class = if @association
-                                        [@association, @association.member_class]
-                                      else
-                                        [model, model]
-                                      end
-
-      page_size = options.delete(:page_size) || 20
-      page = options.delete(:page) || params[:page]
-      
-      paginate = options.fetch(:paginate, request.format.in?(PAGINATE_FORMATS))
-      options.delete(:paginate)
-      if paginate
-        total_number = options.delete(:total_number) ||
-          begin
-            # If there is a conditions block, it may depend on the includes
-            count_options = conditions_proc ? { :include =&gt; options[:include] } : {}
-            model_or_assoc.count(count_options, &amp;conditions_proc)
-          end
-        
-        @pages = ::ActionController::Pagination::Paginator.new(self, total_number, page_size, page)
-
-        options = options.reverse_merge(:limit  =&gt; @pages.items_per_page,
-                                        :offset =&gt; @pages.current.offset)
-      end
-      
-      unless options.has_key?(:order)
-        _, desc, field = *params[:sort]._?.match(/^(-)?([a-z_]+(?:\.[a-z_]+)?)$/)
-        if field
-          @sort_field = field
-          @sort_direction = desc ? &quot;desc&quot; : &quot;asc&quot;
-          
-          table, column = if field =~ /^(.*)\.(.*)$/
-                            [$1.camelize.constantize.table_name, $2]
-                          else
-                            [@member_class.table_name, field]
-                         end
-          options[:order] = &quot;#{table}.#{column} #{@sort_direction}&quot;
-        elsif !@association
-          options[:order] = :default
-        end
-      end
-      
-      model_or_assoc.find(:all, options, &amp;conditions_proc) 
-    end
-
     
-    def find_instance_or_not_found(this=nil)
-      begin
-        this || find_instance
-      rescue ActiveRecord::RecordNotFound
-        not_found
-        false
-      end
-    end
+    def invalid?; !valid?; end
     
-    def save_and_set_status!(record, original=nil)
-      can = if record.new_record?
-              Hobo.can_create?(current_user, record)
-            else
-              Hobo.can_update?(current_user, original, record)
-            end
-      status = if can
-                 record.save ? :valid : :invalid
-               else
-                 :not_allowed
-               end
-      set_status(status)
-    end
-
-    def set_status(status)
-      @status = status
-    end
-
-    def invalid?; @status == :invalid; end
     
-    def valid?; @status == :valid; end
+    def valid?; this.errors.empty?; end
 
-    def not_allowed?; @status == :not_allowed; end
-    
     
     def re_render_form(default_action)
       if params[:page_path]
         controller, view = Controller.controller_and_view_for(params[:page_path])
         view = default_action if view == Dryml::EMPTY_PAGE
-        hobo_render(view, model_for(controller))
+        render :action =&gt; view, :controller =&gt; controller
       else
-        hobo_render(default_action)
+        render :action =&gt; default_action
       end
     end
     
     
-    def model_for(controller_name)
-      &quot;#{controller_name.camelize}Controller&quot;.constantize.model
-    end
-    
-    
-    def destination_after_create(record)
+    def destination_after_submit(record=nil, destroyed=false)
+      record ||= this
+      
+      after_submit = params[:after_submit]
+      
       # The after_submit post parameter takes priority
-      params[:after_submit] || 
+      (after_submit == &quot;stay-here&quot; ? :back : after_submit) || 
+        
         
-        # Then try the records show page
-        object_url(@this,       :if_available =&gt; true) || 
+        # Then try the record's show page
+        (!destroyed &amp;&amp; object_url(@this)) || 
         
         # Then the show page of the 'owning' object if there is one
-        (@this.dependent_on.first &amp;&amp; object_url(@this.dependent_on.first, :if_available =&gt; true)) ||
+        (!destroyed &amp;&amp; (@this.class.default_dependent_on &amp;&amp; object_url(@this.send(@this.class.default_dependent_on)))) ||
         
         # Last try - the index page for this model
-        object_url(@this.class, :if_available =&gt; true) ||
+        object_url(@this.class) ||
         
         # Give up
         home_page
     end
     
     
+    def response_block(&amp;b)
+      if b
+        if b.arity == 1
+          respond_to {|wants| yield(wants) }
+        else
+          yield
+        end
+        performed?
+      end
+    end
+    
+    
+    def request_requires_pagination?
+      request.format.not_in?(DONT_PAGINATE_FORMATS)
+    end
+    
+    
+    def find_or_paginate(finder, options)
+      options = options.reverse_merge(:paginate =&gt; request_requires_pagination?)
+      do_pagination = options.delete(:paginate) &amp;&amp; finder.respond_to?(:paginate)
+      
+      if do_pagination
+        finder.paginate(options.reverse_merge(:page =&gt; params[:page] || 1))
+      else
+        finder.all(options)
+      end
+    end
+    
+    
     # --- Action implementations --- #
 
     def hobo_index(*args, &amp;b)
       options = args.extract_options!
-      options = LazyHash.new(options)
-      @model = model
-      collection = args.first
-      @this = if collection.blank?
-                paginated_find(options)
-              elsif collection.is_a?(String, Symbol)
-                paginated_find(collection, options) # a scope name
-              else
-                collection
-              end
-      instance_variable_set(&quot;@#{@model.name.pluralize.underscore}&quot;, @this)
-      response_block(&amp;b) or hobo_render
+      finder = args.first || model
+      self.this = find_or_paginate(finder, options)
+      response_block(&amp;b)
     end
     
 
     def hobo_show(*args, &amp;b)
       options = args.extract_options!
-      options = LazyHash.new(options)
-      
-      @this = find_instance_or_not_found(args.first) and
-        begin
-          set_status(:not_allowed) unless Hobo.can_view?(current_user, @this)
-          set_named_this!
-          response_block(&amp;b) or
-            if not_allowed?
-              permission_denied
-            else
-              hobo_render
-            end
-        end
+      self.this = find_instance(options)
+      response_block(&amp;b)
     end
-
-
-    def hobo_new(*args, &amp;b)
-      options = args.extract_options!
-      options = LazyHash.new(options)
-      @this = args.first || model.new
-      @this.set_creator(current_user) if options.fetch(:set_creator, true)
-      
-      set_status(:not_allowed) unless Hobo.can_create?(current_user, @this)
-      set_named_this!
-      response_block(&amp;b) or 
-        if not_allowed?
-          permission_denied
-        else
-          hobo_render
-        end
+    
+    
+    def hobo_new(new_record=nil, &amp;b)
+      self.this = new_record || model.new
+      this.user_changes!(current_user) # set_creator and permission check
+      response_block(&amp;b)
     end
     
-
+    
     def hobo_create(*args, &amp;b)
       options = args.extract_options!
-      options = LazyHash.new(options)
-      
-      @this = args.first || 
-        begin
-          create_model = if 'type'.in?(model.column_names) &amp;&amp;
-                             (type_attr = params['type']) &amp;&amp;
-                             type_attr.in?(model.send(:subclasses).every(:name))
-                           type_attr.constantize
-                         else
-                           model
-                         end
-          create_model.new(params[model.name.underscore])
-        end
-      @this.set_creator(current_user) if options.fetch(:set_creator, true)
-      save_and_set_status!(@this)
-      set_named_this!
-      
-      flash[:notice] = &quot;The #{model.name.titleize.downcase} was created successfully&quot; if !request.xhr? &amp;&amp; valid? 
+      self.this = args.first || new_for_create
+      this.user_save_changes(current_user, options[:attributes] || attribute_parameters)
+      create_response(&amp;b)
+    end
+    
+    
+    def attribute_parameters
+      params[this.class.name.underscore]
+    end
+    
+
+    def new_for_create
+      if model.has_inheritance_column? &amp;&amp; (type_attr = params['type']) &amp;&amp; type_attr.in?(model.send(:subclasses).*.name)
+        type_attr.constantize
+      else
+        model
+      end.new
+    end
+    
+    
+    def create_response(&amp;b)
+      flash[:notice] = &quot;The #{@this.class.name.titleize.downcase} was created successfully&quot; if !request.xhr? &amp;&amp; valid? 
       
       response_block(&amp;b) or
         if valid?
           respond_to do |wants|
-            wants.html { redirect_to(destination_after_create(@this)) }
-            wants.js   { hobo_ajax_response || render(:text =&gt; &quot;&quot;) }
+            wants.html { redirect_to destination_after_submit }
+            wants.js   { hobo_ajax_response || render(:nothing =&gt; true) }
           end
-        elsif invalid?
+        else
           respond_to do |wants|
             wants.html { re_render_form(:new) }
             wants.js   { render(:status =&gt; 500,
-                                :text =&gt; (&quot;There was a problem creating that #{create_model.name}.\n&quot; +
-                                          @this.errors.full_messages.join(&quot;\n&quot;))) }
+                                :text =&gt; (&quot;Couldn't create the #{this.class.name.titleize.downcase}.\n&quot; +
+                                          this.errors.full_messages.join(&quot;\n&quot;))) }
           end
-        elsif not_allowed?
-          permission_denied
         end
     end
     
 
-    def hobo_edit(*args, &amp;b)
-      hobo_show(*args, &amp;b)
-    end
-    
-    
     def hobo_update(*args, &amp;b)
       options = args.extract_options!
-      options = LazyHash.new(options)
-      
-      @this = find_instance_or_not_found(args.first) or return
-      original = @this.duplicate
-      # 'duplicate' can cause these to be set, but they can conflict
-      # with the changes so we clear them
-      @this.send(:clear_aggregation_cache)
-      @this.send(:clear_association_cache)
       
-      changes = params[@this.class.name.underscore]
-      @this.attributes = changes 
-      save_and_set_status!(@this, original)
+      self.this = args.first || find_instance
+      changes = options[:attributes] || attribute_parameters
+      this.user_save_changes(current_user, changes)
 
       # Ensure current_user isn't out of date
       @current_user = @this if @this == current_user
       
+      in_place_edit_field = changes.keys.first if changes.size == 1 &amp;&amp; params[:render]
+      update_response(in_place_edit_field, &amp;b)
+    end
+    
+    
+    def update_response(in_place_edit_field=nil, &amp;b)
       flash[:notice] = &quot;Changes to the #{@this.class.name.titleize.downcase} were saved&quot; if !request.xhr? &amp;&amp; valid?
       
-      set_named_this!
       response_block(&amp;b) or 
         if valid?
           respond_to do |wants|
             wants.html do
-              redirect_to(params[:after_submit] || object_url(@this))
+              redirect_to destination_after_submit
             end
             wants.js do
-              if changes.size == 1 &amp;&amp; params[:render]
+              if in_place_edit_field
                 # Decreasingly hacky support for the scriptaculous in-place-editor
-                new_val = Hobo::Dryml.render_tag(@template, &quot;view&quot;,
-                                                 :with =&gt; @this, :field =&gt; changes.keys.first,
-                                                 :no_wrapper =&gt; true)
-                hobo_ajax_response(@this, :new_field_value =&gt; new_val)
+                new_val = call_dryml_tag(&quot;view&quot;, :field =&gt; in_place_edit_field, :no_wrapper =&gt; true)
+                hobo_ajax_response(this, :new_field_value =&gt; new_val)
               else
-                hobo_ajax_response(@this)
+                hobo_ajax_response(this)
               end
                
               # Maybe no ajax requests were made
               render :nothing =&gt; true unless performed?
             end
           end
-        elsif invalid?
+        else
           respond_to do |wants|
             wants.html { re_render_form(:edit) }
             wants.js { render(:status =&gt; 500,
                               :text =&gt; (&quot;There was a problem with that change.\n&quot; + 
                                         @this.errors.full_messages.join(&quot;\n&quot;))) }
           end
-        elsif not_allowed?
-          permission_denied
         end
     end
     
     
     def hobo_destroy(*args, &amp;b)
       options = args.extract_options!
-      options = LazyHash.new(options)
-      @this = find_instance_or_not_found(args.first) or return
-      
-      set_named_this!
-
-      set_status(Hobo.can_delete?(current_user, @this) ? :valid : :not_allowed)
-      unless not_allowed?
-        @this.destroy 
-        flash[:notice] = &quot;The #{model.name.titleize.downcase} was deleted&quot; unless request.xhr?
-      end
-
+      self.this = args.first || find_instance
+      this.user_destroy(current_user)
+      flash[:notice] = &quot;The #{model.name.titleize.downcase} was deleted&quot; unless request.xhr?
+      destroy_response(&amp;b)
+    end
+    
+    
+    def destroy_response(&amp;b)
       response_block(&amp;b) or
-        if not_allowed?
-          permission_denied
-        else
-          respond_to do |wants|
-            wants.html { redirect_to(:action =&gt; &quot;index&quot;) }
-            wants.js   { hobo_ajax_response || render(:text =&gt; &quot;&quot;) }
-          end
+        respond_to do |wants|
+          wants.html { redirect_to destination_after_submit(this, true) }
+          wants.js   { hobo_ajax_response || render(:nothing =&gt; true) }
         end
     end
-
+ 
     
-    def hobo_show_collection(collection, options={}, &amp;b)
-      options = LazyHash.new(options)
-      
-      @owner = find_instance_or_not_found(options[:owner]) or return
-      
-      if collection.is_a?(Array)
-        @this = collection
-        @reflection = collection.proxy_reflection if collection.respond_to?(:proxy_reflection)
-      else
-        toplevel_collection = collection.to_s.split(&quot;.&quot;).first
-        set_status(:not_allowed) unless Hobo.can_view?(current_user, @owner, toplevel_collection)
-        @this = paginated_find(@owner, collection, options) unless not_allowed?
+    def hobo_show_collection(association, *args, &amp;b)
+      options = args.extract_options!
+      association = find_instance.send(association) if association.is_a?(String, Symbol)
+      if association.respond_to?(:origin)
+        association.origin.user_view(current_user, association.origin_attribute) # permission check
       end
-      
-      response_block(&amp;b) or 
-        if not_allowed?
-          permission_denied
-        else
-          hobo_render(params[:action]) or (@reflection and hobo_render(:show_collection, @reflection.klass))
-        end
+      self.this = find_or_paginate(association, options)
+      dryml_fallback_tag(&quot;show_collection_page&quot;)
+      response_block(&amp;b) 
     end
     
     
-    def hobo_new_in_collection(collection, *args, &amp;b)
+    # TODO: This action needs some more tidying up    
+    def hobo_new_in_collection(association, *args, &amp;b)
       options = args.extract_options!
-      this = args.first
-      options = LazyHash.new(options)
-      
-      @owner = find_instance_or_not_found(options[:owner]) or return
-      @association = collection.is_a?(Array) ? collection : @owner.send(collection)
-      @this = this || @association.new
-      set_named_this!
-      @this.set_creator(current_user) if options.fetch(:set_creator, true)
-
-      set_status(:not_allowed) unless Hobo.can_create?(current_user, @this)
-      
-      response_block(&amp;b) or
-        if not_allowed?
-          permission_denied
-        else
-          hobo_render(&quot;new_#{collection.to_s.singularize}&quot;) or hobo_render(&quot;new_in_collection&quot;, @this.class)
-        end
+      @association = association.is_a?(String, Symbol) ? find_instance.send(association) : association
+      self.this = args.first || @association.new
+      this.user_changes(current_user) # set_creator and permission check
+      dryml_fallback_tag(&quot;new_in_collection_page&quot;)
+      response_block(&amp;b)
     end
     
 
-    def hobo_completions
-      opts = self.class.autocompleter(params[:for])
-      if opts
-        # Eval any defined filters
-        instance_eval(&amp;opts[:data_filters_block]) if opts[:data_filters_block]
-        conditions = data_filter_conditions
-        q = params[:query]
-        items = model.find(:all) { all?(send(&quot;#{attr}_contains&quot;, q), conditions &amp;&amp; block(conditions)) }
-
-        render :text =&gt; &quot;&lt;ul&gt;\n&quot; + items.map {|i| &quot;&lt;li&gt;#{i.send(attr)}&lt;/li&gt;\n&quot;}.join + &quot;&lt;/ul&gt;&quot;
-      else
-        render :text =&gt; &quot;No completer for #{attr}&quot;, :status =&gt; 404
+    def hobo_completions(attribute, finder, options={})
+      options = options.reverse_merge(:limit =&gt; 10, :param =&gt; :query)
+      finder = finder.limit(options[:limit]) unless finder.scope(:find, :limit)
+      finder = finder.send(&quot;#{attribute}_contains&quot;, params[options[:param]])
+      items = finder.find(:all)
+      render :text =&gt; &quot;&lt;ul&gt;\n&quot; + items.map {|i| &quot;&lt;li&gt;#{i.send(attribute)}&lt;/li&gt;\n&quot;}.join + &quot;&lt;/ul&gt;&quot;
+    end
+    
+    
+    def hobo_reorder
+      params[&quot;#{model.name.underscore}_ordering&quot;].each_with_index do |id, position|
+        model.user_update(current_user, id, :position =&gt; position+1)
       end
+      hobo_ajax_response || render(:nothing =&gt; true)
     end
     
-    #def hobo_create_in_collection(collection, options={}, &amp;b)
-    #  hobo_create do
-    #    hobo_new_in_collection(collection, :this =&gt; @this, &amp;b)
-    #  end
-    #end
     
     
     # --- Response helpers --- #
-
     
-    def permission_denied(options={})
-      if respond_to? :permission_denied_response
-        permission_denied_response
-      elsif render_tag(&quot;permission-denied-page&quot;, { :with =&gt; @this }, :status =&gt; 403)
-        # cool
+    def permission_denied(error)
+      self.this = nil # Otherwise this gets sent user_view
+      if :permission_denied.in?(superclass.instance_methods)
+        super
       else
-        message = options[:message] || &quot;Permission Denied&quot;
-        render :text =&gt; message, :status =&gt; 403
+        respond_to do |wants|
+          wants.html do
+            if render_tag(&quot;permission-denied-page&quot;, { }, :status =&gt; 403)
+              # job done
+            else
+              render :text =&gt; &quot;Permission Denied&quot;, :status =&gt; 403
+            end
+          end
+          wants.js do 
+            render :text =&gt; &quot;Permission Denied&quot;, :status =&gt; 403
+          end
+        end
       end
     end
     
     
-    def not_found
-      if respond_to? :not_found_response
-        not_found_response
-      elsif render_tag(&quot;not-found-page&quot;, { :with =&gt; @this }, :status =&gt; 404)
+    def not_found(error)
+      if :not_found_response.in?(superclass.instance_methods)
+        super
+      elsif render_tag(&quot;not-found-page&quot;, {}, :status =&gt; 404)
         # cool
       else
         render(:text =&gt; &quot;The page you requested cannot be found.&quot;, :status =&gt; 404)
@@ -663,46 +530,34 @@ module Hobo
     end
     
     
-    def hobo_template_exists?(dir, name)
-      self.class.template_path_cache.clear if RAILS_ENV == &quot;development&quot;
-      self.class.template_path_cache.fetch([dir, name], 
-                                           begin
-                                             full_dir = &quot;#{RAILS_ROOT}/app/views/#{dir}&quot;
-                                             !Dir[&quot;#{full_dir}/#{name}.*&quot;].empty?
-                                           end)
+    def this
+      @this ||= (instance_variable_get(&quot;@#{model.name.underscore}&quot;) || 
+                 instance_variable_get(&quot;@#{model.name.underscore.pluralize}&quot;))
     end
-        
 
-    def find_model_template(klass, name)
-      while klass and klass != ActiveRecord::Base
-        dir = klass.name.underscore.pluralize
-        dir = File.join(subsite, dir) if subsite
-        if hobo_template_exists?(dir, name)
-          return &quot;#{dir}/#{name}&quot;
-        end
-        klass = klass.superclass
-      end
-      nil
+    
+    def this=(object)
+      ivar = if object.is_a?(Array)
+               (object.try.member_class || model).name.underscore.pluralize
+             else
+               model.name.underscore
+             end
+      @this = instance_variable_set(&quot;@#{ivar}&quot;, object)
     end
-
     
-    def hobo_render(page_kind = nil, page_model=nil)
-      page_kind ||= params[:action].to_sym
-      page_model ||= model
-
-      if hobo_template_exists?(controller_path, page_kind)
-        render :action =&gt; page_kind
-        true
-      elsif (template = find_model_template(page_model, page_kind))
-        render :template =&gt; template
-        true
-      else
-        # This returns false if no such tag exists
-        render_tag(&quot;#{page_kind.to_s.dasherize}-page&quot;, :with =&gt; @this)
-      end
+    
+    def dryml_context
+      this
     end
 
     
+    def render_with_hobo_model(*args, &amp;block)
+      options = args.extract_options!
+      self.this = options[:object] if options[:object]
+      this.user_view(current_user) if this &amp;&amp; this.respond_to?(:user_view)
+      render_without_hobo_model(*args + [options], &amp;block)
+    end
+    
     # --- filters --- #
     
     def set_no_cache_headers
@@ -715,21 +570,12 @@ module Hobo
 
     # --- end filters --- #
     
+    public
 
     def model
       self.class.model
     end
     
-
-    def data_filter_conditions
-      active_filters = data_filters &amp;&amp; (params.keys &amp; data_filters.keys)
-      filters = data_filters
-      params = self.params
-      proc do
-        all?(*active_filters.map {|f| instance_exec(params[f], &amp;filters[f])})
-      end unless active_filters.blank?
-    end
-    
   end
 
 end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/model_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,9 +2,21 @@ class ActionController::Routing::RouteSet
   # Monkey patch this method so routes are reloaded on *every*
   # request. Without this Rails checks the mtime of config/routes.rb
   # which doesn't take into account Hobo's auto routing
-  def reload
-    load!
+  #def reload
+  #  # TODO: This can get slow - quicker to stat routes.rb and the
+  #  # controllers and only do a load if there's been a change
+  #  load!
+  #end
+  
+  # temporay hack -- reload assemble.rb whenever routes need reloading
+  def reload_with_hobo_assemble
+    if defined? ::ApplicationController
+      load &quot;#{RAILS_ROOT}/app/assemble.rb&quot; if File.exists? &quot;#{RAILS_ROOT}/app/assemble.rb&quot;
+      reload_without_hobo_assemble
+    end
   end
+  alias_method_chain :reload, :hobo_assemble
+  
 end
 
 module Hobo
@@ -16,19 +28,30 @@ module Hobo
     class &lt;&lt; self
       
       def reset_linkables
-        @linkable = Hash.new {|h, k| h[k] = Hash.new {|h, k| h[k] = {} } }
+        @linkable =Set.new
       end
       
-      def linkable(subsite, klass, action)
-        @linkable[subsite][klass.name][action] = true
+      def linkable_key(klass, action, options)
+        opts = options.map { |k, v| &quot;#{k}=#{v}&quot; unless v.blank? }.compact.join(',')
+        &quot;#{klass.name}/#{action}/#{opts}&quot;
       end
       
-      def linkable?(subsite, klass, action)
-        @linkable[subsite][klass.name][action]
+      def linkable!(klass, action, options={})
+        options[:method] ||= :get
+        @linkable &lt;&lt; linkable_key(klass, action, options)
+      end
+      
+      def linkable?(klass, action, options={})
+        options[:method] ||= :get
+        @linkable.member? linkable_key(klass, action, options)
       end
       
       def add_routes(map)
+        # Don't create routes if it's a generator that's running
+        return if caller[-1] =~ /script[\/\\]generate:\d+$/
+        
         reset_linkables
+        
         begin 
           ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.active?
         rescue
@@ -45,11 +68,17 @@ module Hobo
         # Any directory inside app/controllers defines a subsite
         subsites = Dir[&quot;#{APP_ROOT}/controllers/*&quot;].map { |f| File.basename(f) if File.directory?(f) }.compact
         subsites.each { |subsite| add_routes_for(map, subsite) }
+        
+        add_developer_routes(map) if Hobo.developer_features?
+      rescue ActiveRecord::StatementInvalid
+        # Database problem? Just continue without routes
       end
       
       
       def add_routes_for(map, subsite)
         module_name = subsite._?.camelize
+        
+        # FIXME: This should go directly to the controllers, not load the models first.
         Hobo.models.each do |model|
           controller_name = &quot;#{model.name.pluralize}Controller&quot;
           is_defined = if subsite 
@@ -59,12 +88,18 @@ module Hobo
                        end
           controller_filename = File.join(*[&quot;#{APP_ROOT}/controllers&quot;, subsite, &quot;#{controller_name.underscore}.rb&quot;].compact) 
           if is_defined || File.exists?(controller_filename)
-            owner_module = subsite ? module_name.constantize : Object
-            controller = owner_module.const_get(controller_name)
+            controller = (subsite ? &quot;#{module_name}::#{controller_name}&quot; : controller_name).constantize
             ModelRouter.new(map, model, controller, subsite)
           end
         end
       end
+      
+      
+      def add_developer_routes(map)
+        map.dryml_support &quot;dryml/:action&quot;, :controller =&gt; &quot;hobo/dryml/dryml_support&quot;
+        map.dev_support   &quot;dev/:action&quot;,   :controller =&gt; &quot;hobo/dev&quot;
+      end
+      
     end
     
 
@@ -103,7 +138,10 @@ module Hobo
         collection_routes
         web_method_routes
         show_action_routes
-        user_routes if controller &lt; Hobo::UserController
+        reorder_route
+
+        lifecycle_routes if defined? model::Lifecycle
+        user_routes      if controller &lt; Hobo::UserController
       end
     end
     
@@ -111,6 +149,9 @@ module Hobo
     def resource_routes
       # We re-implement resource routing - routes are not created for
       # actions that the controller does not provide
+      
+      # FIX ME -- what about routes with formats (e.g. .xml)?
+      
       linkable_route(plural, plural, :index, :conditions =&gt; { :method =&gt; :get })
                                                                                                                           
       linkable_route(&quot;new_#{singular}&quot;,  &quot;#{plural}/new&quot;,      :new,  :conditions =&gt; { :method =&gt; :get })  
@@ -126,49 +167,52 @@ module Hobo
     
     def collection_routes
       controller.collections.each do |collection|
-        new_method = Hobo.simple_has_many_association?(model.reflections[collection])
-        named_route(&quot;#{singular}_#{collection}&quot;,
-                    &quot;#{plural}/:id/#{collection}&quot;,
-                    :action =&gt; &quot;show_#{collection}&quot;,
-                    :conditions =&gt; { :method =&gt; :get })
+        linkable_route(&quot;#{singular}_#{collection}&quot;,
+                       &quot;#{plural}/:id/#{collection}&quot;,
+                       collection.to_s,
+                       :conditions =&gt; { :method =&gt; :get })
 
-        named_route(&quot;new_#{singular}_#{collection.to_s.singularize}&quot;,
-                    &quot;#{plural}/:id/#{collection}/new&quot;,
-                    :action =&gt; &quot;new_#{collection.to_s.singularize}&quot;,
-                    :conditions =&gt; { :method =&gt; :get }) if new_method
+        if Hobo.simple_has_many_association?(model.reflections[collection])
+          linkable_route(&quot;new_#{singular}_#{collection.to_s.singularize}&quot;,
+                         &quot;#{plural}/:id/#{collection}/new&quot;,
+                         &quot;new_#{collection.to_s.singularize}&quot;,
+                         :conditions =&gt; { :method =&gt; :get }) 
+        end
       end
     end
     
     
     def web_method_routes
       controller.web_methods.each do |method|
-        named_route(&quot;#{plural.singularize}_#{method}&quot;, &quot;#{plural}/:id/#{method}&quot;,
-                    :action =&gt; method.to_s, :conditions =&gt; { :method =&gt; :post })
+        linkable_route(&quot;#{plural.singularize}_#{method}&quot;, &quot;#{plural}/:id/#{method}&quot;, method.to_s, :conditions =&gt; { :method =&gt; :post })
       end
     end
     
     
     def index_action_routes
       controller.index_actions.each do |view|
-        named_route(&quot;#{view}_#{plural}&quot;, &quot;#{plural}/#{view}&quot;,
-                    :action =&gt; view.to_s, :conditions =&gt; { :method =&gt; :get })
+        linkable_route(&quot;#{view}_#{plural}&quot;, &quot;#{plural}/#{view}&quot;, view.to_s, :conditions =&gt; { :method =&gt; :get })
       end
     end
 
     
     def show_action_routes
       controller.show_actions.each do |view|
-        named_route(&quot;#{plural.singularize}_#{view}&quot;, &quot;#{plural}/:id/#{view}&quot;,
-                    :action =&gt; view.to_s, :conditions =&gt; { :method =&gt; :get })
+        linkable_route(&quot;#{plural.singularize}_#{view}&quot;, &quot;#{plural}/:id/#{view}&quot;, view.to_s, :conditions =&gt; { :method =&gt; :get })
       end
     end
     
+    
+    def reorder_route
+      linkable_route(&quot;reorder_#{plural}&quot;, &quot;#{plural}/reorder&quot;, 'reorder', :conditions =&gt; { :method =&gt; :post })
+    end
+    
         
     def user_routes
       prefix = plural == &quot;users&quot; ? &quot;&quot; : &quot;#{singular}_&quot;
-      named_route(&quot;#{singular}_login&quot;,  &quot;#{prefix}login&quot;,  :action =&gt; 'login')
-      named_route(&quot;#{singular}_logout&quot;, &quot;#{prefix}logout&quot;, :action =&gt; 'logout')
-      named_route(&quot;#{singular}_signup&quot;, &quot;#{prefix}signup&quot;, :action =&gt; 'signup')
+      linkable_route(&quot;#{singular}_login&quot;,  &quot;#{prefix}login&quot;,  'login')
+      linkable_route(&quot;#{singular}_logout&quot;, &quot;#{prefix}logout&quot;, 'logout')
+      linkable_route(&quot;#{singular}_signup&quot;, &quot;#{prefix}signup&quot;, 'signup')
     end
     
     
@@ -187,8 +231,12 @@ module Hobo
     end
     
     
-    def linkable_route(name, route, action, options)
-      named_route(name, route, options.merge(:action =&gt; action.to_s)) and self.class.linkable(subsite, model, action)
+    def linkable_route(name, route, action, options={})
+      named_route(name, route, options.merge(:action =&gt; action.to_s)) and 
+        begin
+          linkable_options = { :method =&gt; options[:conditions]._?[:method], :subsite =&gt; subsite }
+          self.class.linkable!(model, action, linkable_options)
+        end
     end
     
    </diff>
      <filename>vendor/plugins/hobo/lib/hobo/model_router.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,20 @@
 module Hobo::RapidHelper
 
-  def options_for_hobo_ajax(options)
-    js_options = build_callbacks(options)
+  def rapid_build_callbacks(options)
+    callbacks = {}
+    options.each do |callback, code|
+      if AJAX_CALLBACKS.include?(callback.to_sym)
+        name = 'on' + callback.to_s.capitalize
+        callbacks[name] = &quot;function(request){#{code}}&quot;
+      end
+    end
+    callbacks
+  end
+
 
+  def options_for_hobo_ajax(options)
+    js_options = rapid_build_callbacks(options)
+    
     js_options['asynchronous']  = false if options[:type] == :synchronous
     js_options['method']        = method_option_to_s(options[:method]) if options[:method]
     js_options['evalScripts']   = false if options[:script] == false
@@ -11,7 +23,8 @@ module Hobo::RapidHelper
     js_options['resultUpdate']  = js_result_updates(options[:result_update]) if options[:result_update]
     js_options['resetForm']     = options[:reset_form] if options.has_key?(:reset_form)
     js_options['refocusForm']   = options[:refocus_form] if options.has_key?(:refocus_form)
-    js_options['spinnerNextTo'] = options[:spinner_next_to] if options.has_key?(:spinner_next_to)
+    js_options['spinnerNextTo'] = js_str(options[:spinner_next_to]) if options.has_key?(:spinner_next_to)
+    js_options['message']       = js_str(options[:message]) if options[:message]
     
     js_options.empty? ? nil : options_for_javascript(js_options)
   end
@@ -39,7 +52,7 @@ module Hobo::RapidHelper
   end
 
 
-  def ajax_updater(url_or_form, message, update, options={})
+  def ajax_updater(url_or_form, update, options={})
     options ||= {}
     options.symbolize_keys!
     
@@ -49,7 +62,7 @@ module Hobo::RapidHelper
                js_str(url_or_form)
              end
     js_options = options_for_hobo_ajax(options)
-    args = [target, js_str(message || &quot;...&quot;), js_updates(update), js_options].compact
+    args = [target, js_updates(update), js_options].compact
     
     confirm = options.delete(:confirm)
     
@@ -83,7 +96,7 @@ module Hobo::RapidHelper
     blank_message = attributes.delete(:blank_message) || &quot;(click to edit)&quot;
 
     attributes = add_classes(attributes, behaviour_class)
-    attributes.update(:hobo_model_id =&gt; this_field_dom_id,
+    attributes.update(:hobo_model_id =&gt; dom_id,
                       :hobo_blank_message =&gt; blank_message,
                       :if_blank =&gt; blank_message,
                       :no_wrapper =&gt; false)
@@ -96,12 +109,39 @@ module Hobo::RapidHelper
     
   
 
-  AJAX_ATTRS = [:before, :success, :failure, :complete, :type, :method,
-                :script, :form, :params, :confirm,
-                :reset_form, :refocus_form, :result_update, :spinner_next_to]
+  AJAX_CALLBACKS = [ :before, :success, :failure, :complete ]
+  
+  AJAX_ATTRS = AJAX_CALLBACKS + [ :type, :method,
+                                  :script, :form, :params, :confirm, :message,
+                                  :reset_form, :refocus_form, :result_update, :spinner_next_to ]
 
 
   def editor_class
   end
 
+
+  def through_collection_names(object=this)
+    object.class.reflections.values.select do |refl| 
+      refl.macro == :has_many &amp;&amp; refl.options[:through]
+    end.map {|x| x.options[:through]}
+  end
+
+
+  def primary_collection_name(object=this)
+    dependent_collection_names = object.class.reflections.values.select do |refl| 
+      refl.macro == :has_many &amp;&amp; refl.options[:dependent]
+    end.*.name
+    
+    (dependent_collection_names - through_collection_names(object)).first
+  end
+
+
+  def non_through_collections(object=this)
+    names = object.class.reflections.values.select do |refl| 
+      refl.macro == :has_many
+    end.*.name
+    
+    names - through_collection_names
+  end
+  
 end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/rapid_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,3 +40,5 @@ module Hobo
   end
 
 end
+
+</diff>
      <filename>vendor/plugins/hobo/lib/hobo/undefined.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,12 @@ module Hobo
 
   module User
     
+    @user_models = []
+    
+    def self.default_user_model
+      @user_models.first._?.constantize
+    end
+    
     AUTHENTICATION_FIELDS = [:salt, :crypted_password, :remember_token, :remember_token_expires_at]
 
     # Extend the base class with AuthenticatedUser functionality
@@ -12,9 +18,12 @@ module Hobo
     # - plaintext password validation
     # - login token for rembering a login during multiple browser sessions
     def self.included(base)
+      @user_models &lt;&lt; base.name
+      
       base.extend(ClassMethods)
 
       base.class_eval do
+        
         fields do
           crypted_password          :string, :limit =&gt; 40
           salt                      :string, :limit =&gt; 40
@@ -22,21 +31,20 @@ module Hobo
           remember_token_expires_at :datetime
         end
         
-        # Virtual attribute for the unencrypted password
-        attr_accessor :password
+        validates_confirmation_of :password, :if =&gt; :password_required?
 
-        validates_presence_of     :password,                   :if =&gt; :password_required?
-        validates_presence_of     :password_confirmation,      :if =&gt; :password_required?
-        validates_confirmation_of :password,                   :if =&gt; :password_required?
-      
+        # Virtual attributes for setting and changing the password
+        attr_accessor :current_password, :password, :password_confirmation, :type =&gt; :password
+
+
+        validate :validate_current_password_when_changing_password
+        
         before_save :encrypt_password
         
         never_show *AUTHENTICATION_FIELDS
         
         attr_protected *AUTHENTICATION_FIELDS
         
-        set_field_type :password =&gt; :password, :password_confirmation =&gt; :password
-        
         password_validations
       end
     end
@@ -50,23 +58,22 @@ module Hobo
       end
       
       def login_attribute=(attr, validate=true)
-        @login_attr = attr = attr.to_sym
+        @login_attribute = attr = attr.to_sym
         unless attr == :login
           alias_attribute(:login, attr)
-          set_field_type :login =&gt; field_type(attr)
+          declare_attr_type(:login, attr_type(attr)) if table_exists? # this breaks if the table doesn't exist
         end
         
         if validate
-          validates_presence_of   attr
           validates_length_of     attr, :within =&gt; 3..100
           validates_uniqueness_of attr, :case_sensitive =&gt; false
         end
       end
-      attr_reader :login_attr
+      attr_reader :login_attribute
 
       # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
       def authenticate(login, password)
-        u = find(:first, :conditions =&gt; [&quot;#{@login_attr} = ?&quot;, login]) # need to get the salt
+        u = find(:first, :conditions =&gt; [&quot;#{@login_attribute} = ?&quot;, login]) # need to get the salt
         
         if u &amp;&amp; u.authenticated?(password)
           if u.respond_to?(:last_login_at) || u.respond_to?(:login_count)
@@ -125,6 +132,10 @@ module Hobo
       false
     end
     
+    def changing_password?
+      crypted_password? &amp;&amp; (password || password_confirmation)
+    end
+
     protected
     # Before filter that encrypts the password before having it stored in the database.
     def encrypt_password
@@ -133,9 +144,15 @@ module Hobo
       self.crypted_password = encrypt(password)
     end
 
+    
     # Is a password required for login? (or do we have an empty password?)
     def password_required?
-      (crypted_password.blank? &amp;&amp; password != nil) || !password.blank?
+      (crypted_password.blank? &amp;&amp; password != nil) || !password.blank? || changing_password?
+    end
+
+    
+    def validate_current_password_when_changing_password
+      changing_password? &amp;&amp; !authenticated?(current_password) and errors.add :current_password, &quot;is not correct&quot; 
     end
     
   end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,15 +2,18 @@ module Hobo
 
   module UserController
 
-    @user_models = []
-
     class &lt;&lt; self
-      attr_reader :user_models
-      
       def included(base)
-        base.filter_parameter_logging &quot;password&quot;
-        base.skip_before_filter :login_required, :only =&gt; [:login]
-        user_models &lt;&lt; base.model
+        base.class_eval do 
+          filter_parameter_logging &quot;password&quot;
+          skip_before_filter :login_required, :only =&gt; [:login, :signup]
+          
+          include_taglib &quot;rapid_user_pages&quot;, :plugin =&gt; &quot;hobo&quot;
+          
+          show_action :account
+          
+          alias_method_chain :hobo_update, :account_flash
+        end
       end
     end
     
@@ -20,9 +23,10 @@ module Hobo
     
     def logout; hobo_logout; end
     
+    private
+    
     def hobo_login(options={})
-      options = LazyHash.new(options)
-      login_attr = model.login_attr.to_s.titleize.downcase
+      login_attr = model.login_attribute.to_s.titleize.downcase
       options.reverse_merge!(:success_notice =&gt; &quot;You have logged in.&quot;,
                              :failure_notice =&gt; &quot;You did not provide a valid #{login_attr} and password.&quot;)
       
@@ -36,10 +40,12 @@ module Hobo
           
           # If supplied, a block can be used to test if this user is
           # allowed to log in (e.g. the account may be disabled)
-          if block_given? &amp;&amp; !yield
+          account_available = block_given? ? yield : true
+
+          if !account_available
             # block returned false - cancel this login
             self.current_user = old_user
-            hobo_render(:account_disabled)
+            render :action =&gt; :account_disabled unless performed?
           else
             if params[:remember_me] == &quot;1&quot;
               current_user.remember_me
@@ -50,30 +56,21 @@ module Hobo
           end
         end
       end
-
-      hobo_render unless performed?
     end
 
     
     def hobo_signup(&amp;b)
       if request.post?
-        @user = model.new(params[model.name.underscore])
-        @this = @user
-        save_and_set_status!(@user)
-        self.current_user = @user if valid?
+        self.this = model.user_create(current_user, params[model.name.underscore])
+        self.current_user = this if valid?
         response_block(&amp;b) or
           if valid?
             flash[:notice] ||= &quot;Thanks for signing up!&quot;
             redirect_back_or_default(home_page)
-          elsif invalid?
-            hobo_render
-          elsif not_allowed?
-            permission_denied
           end
       else
-        @this = @user = model.new
+        self.this = model.new
         yield if block_given?
-        hobo_render unless performed?
       end
     end
 
@@ -81,15 +78,32 @@ module Hobo
     def hobo_logout(options={})
       options = options.reverse_merge(:notice =&gt; &quot;You have logged out.&quot;,
                                       :redirect_to =&gt; base_url)
-        
-      current_user.forget_me if logged_in?
-      cookies.delete :auth_token
-      reset_session
+
+      logout_current_user
       yield if block_given?
       flash[:notice] ||= options[:notice]
       redirect_back_or_default(options[:redirect_to]) unless performed?
     end
     
+    
+    def hobo_update_with_account_flash(*args)
+      hobo_update_without_account_flash(*args) do
+        flash[:notice] = &quot;Changes to your account were saved&quot; if valid? &amp;&amp; @this == current_user
+        yield if block_given?
+      end
+    end
+    
+    private
+    
+    def logout_current_user
+      if logged_in?
+        current_user.forget_me
+        cookies.delete :auth_token
+        reset_session
+        self.current_user = nil
+      end
+    end
+    
   end
   
 end</diff>
      <filename>vendor/plugins/hobo/lib/hobo/user_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,9 +3,9 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;wrap&quot; attrs=&quot;tag, when&quot;&gt;
-  &lt;% body = parameters.default %&gt;
-  &lt;%= when_ ? call_tag(tag, attributes, &amp;proc { body }) : body %&gt;
+&lt;def tag=&quot;wrap&quot; attrs=&quot;tag, when, parameter&quot;&gt;
+  &lt;% parameter ||= :default %&gt;
+  &lt;%= when_ ? send(tag, attributes, { parameter.to_sym =&gt; parameters[:default] }) : parameter.default %&gt;
 &lt;/def&gt;
 
     
@@ -15,8 +15,8 @@
 %&gt;&lt;/def&gt;
 
 
-&lt;def tag=&quot;repeat&quot; attrs=&quot;even-odd, join&quot;&gt;&lt;%= 
-  if !this.blank? 
+&lt;def tag=&quot;repeat&quot; attrs=&quot;even-odd, join&quot;&gt;
+  &lt;if&gt;&lt;%= 
     if even_odd
       map_this do
         klass = [attributes[:class], cycle(&quot;even&quot;, &quot;odd&quot;)].compact.join(' ')
@@ -31,15 +31,13 @@
           res
         end.join(join)
       end
-    end
-  else
-    &quot;&quot;
-  end
-%&gt;&lt;/def&gt;
+    end %&gt;
+  &lt;/if&gt;
+&lt;/def&gt;
 
 
 &lt;def tag=&quot;do&quot;&gt;&lt;%= parameters.default %&gt;&lt;/def&gt;
-&lt;def tag=&quot;with&quot;&gt;&lt;%= parameters.default %&gt;&lt;/def&gt;
+&lt;def tag=&quot;with&quot; alias-of=&quot;do&quot;/&gt;
 
 
 &lt;def tag=&quot;if&quot; attrs=&quot;test&quot;&gt;&lt;%= </diff>
      <filename>vendor/plugins/hobo/taglibs/core.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -7,6 +7,8 @@
 &lt;include src=&quot;rapid_forms&quot;/&gt;
 &lt;include src=&quot;rapid_navigation&quot;/&gt;
 &lt;include src=&quot;rapid_plus&quot;/&gt;
+&lt;include src=&quot;rapid_generics&quot;/&gt;
+
 
 &lt;def tag=&quot;field-list&quot; attrs=&quot;tag&quot;&gt;
   &lt;% tag ||= scope.in_form ? &quot;input&quot; : &quot;view&quot; %&gt;
@@ -29,18 +31,20 @@
 
 &lt;def tag=&quot;nil-view&quot;&gt;&lt;%= scope.nil_view || &quot;(Not Available)&quot; %&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;ul&quot;&gt;
+&lt;def tag=&quot;ul&quot; attrs=&quot;empty&quot;&gt;
   &lt;% if all_parameters.li? # don't use dryml if, because it will mess up &lt;ul/&gt;&lt;else&gt; %&gt;
-    &lt;unless test=&quot;&amp;this.empty?&quot;&gt;
+    &lt;if test=&quot;&amp;empty || all_parameters[:head] || all_parameters[:foot] || !this.empty?&quot;&gt;
       &lt;% element &quot;ul&quot;, attributes do %&gt;
+      &lt;do param=&quot;head&quot;/&gt;
       &lt;repeat&gt;
         &lt;li param if=&quot;&amp;can_view?&quot; class=&quot;#{scope.even_odd} #{this_type.name.underscore.dasherize}&quot;
             merge-attrs=&quot;&amp;{'hobo-model-id' =&gt; dom_id(this)} if this.respond_to?(:typed_id)&quot;&gt;
           &lt;do param=&quot;default&quot;&gt;&lt;a/&gt;&lt;/do&gt;
         &lt;/li&gt;
       &lt;/repeat&gt;
+      &lt;do param=&quot;foot&quot;/&gt;
       &lt;% end %&gt;
-    &lt;/unless&gt;
+    &lt;/if&gt;
   &lt;% else %&gt;
     &lt;%= element(&quot;ul&quot;, attributes, all_parameters.default) %&gt;
   &lt;% end %&gt;      
@@ -73,7 +77,7 @@
                 &lt;td param=&quot;#{this_field.to_s.sub('?', '').gsub('.', '-')}-view&quot;&gt;&lt;call-tag tag=&quot;&amp;field_tag&quot;/&gt;&lt;/td&gt;
               &lt;/with-fields&gt;
               &lt;td class=&quot;controls&quot; param=&quot;controls&quot; if=&quot;&amp;all_parameters[:controls]&quot;&gt;
-                &lt;a param=&quot;edit-link&quot;&gt;Edit&lt;/a&gt;
+                &lt;a param=&quot;edit-link&quot; action=&quot;edit&quot;&gt;Edit&lt;/a&gt;
                 &lt;delete-button param/&gt;
               &lt;/td&gt;
             &lt;/if&gt;
@@ -97,39 +101,9 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;theme-image&quot; attrs=&quot;src&quot;&gt;
-  &lt;img src=&quot;#{theme_asset('images/' + src)}&quot; merge-attrs/&gt;
-&lt;/def&gt;
-
-
-&lt;def tag=&quot;card&quot;&gt;
-  &lt;if test=&quot;&amp;can_view?&quot;&gt;
-    &lt;%= poly = call_polymorphic_tag('card', attributes, parameters) %&gt;
-    &lt;div class=&quot;card #{linkable? ? 'linkable' : 'content'} #{type_name :dasherize =&gt; true}&quot; unless=&quot;&amp;poly&quot;&gt;
-      &lt;a if=&quot;&amp;linkable?&quot;/&gt;
-      &lt;div class=&quot;content&quot; param=&quot;content&quot;&gt;
-        &lt;primary-content if=&quot;&amp;!linkable?&quot;/&gt;
-      &lt;/div&gt;
-      &lt;creation-details/&gt;
-      &lt;span class=&quot;dependents&quot;&gt;&lt;count-dependents /&gt;&lt;/span&gt;
-    &lt;/div&gt;
-  &lt;/if&gt;
-&lt;/def&gt;
-  
-
-&lt;def tag=&quot;collection&quot;&gt;
-  &lt;%= poly = call_polymorphic_tag('collection', attributes, parameters) %&gt;
-  &lt;ul class=&quot;collection&quot; merge-attrs unless=&quot;&amp;poly&quot;&gt;
-    &lt;li:&gt;&lt;card param/&gt;&lt;/li:&gt;
-  &lt;/ul&gt;
-  &lt;p class=&quot;empty-collection-message&quot; if=&quot;&amp;this.empty?&quot; param=&quot;empty-message&quot;&gt;
-    There are no &lt;type-name plural/&gt;
-  &lt;/p&gt;
-&lt;/def&gt;
-
-
 &lt;def tag=&quot;hobo-rapid-javascripts&quot; attrs=&quot;tiny-mce&quot;&gt;&lt;%=
    res = '&lt;script type=&quot;text/javascript&quot;&gt;var hoboParts = {};'
+   # FIXME: This should interrogate the model-router - not the models
    unless Hobo.all_models.empty?
      # Tell JS code how to pluralize names, unless they follow the simple rule
      names = Hobo.all_models.map do |m|
@@ -147,7 +121,7 @@
    if tiny_mce
      res += javascript_include_tag(&quot;tiny_mce/tiny_mce_src&quot;) + %{
                &lt;script type=&quot;text/javascript&quot;&gt;
-                 tinyMCE.init({ mode: &quot;textareas&quot;, editor_selector: &quot;tiny_mce&quot;,
+                 tinyMCE.init({ mode: &quot;textareas&quot;, editor_selector: &quot;tiny-mce&quot;,
                        plugins: 'save',
                        theme_advanced_buttons1 : &quot;bold, italic, separator, &quot; +
                                                  &quot;bullist, outdent, indent, separator, &quot; +
@@ -183,18 +157,23 @@
 %&gt;&lt;/def&gt;
 
 &lt;def tag=&quot;type-name&quot; attrs=&quot;type, plural, lowercase, dasherize&quot;&gt;&lt;%=
-  type ||= if this.is_a?(Class)
-             this
-           elsif this.respond_to? :proxy_reflection
-             this.proxy_reflection.klass
-           else
-             this.class
-           end
+  type ||= (this if this.is_a?(Class)) || this.try.member_class || this.class
+
   name = dasherize ? type.name.underscore.dasherize : type.name.titleize
   name = name.pluralize if plural
   name = name.downcase if lowercase
   name
 %&gt;&lt;/def&gt;
+     
+
+&lt;def tag=&quot;name-for-collection&quot; attrs=&quot;singular, lowercase&quot;&gt;&lt;%=
+  name = (attr = this.try.origin_attribute and attr.to_s) || type_name(:plural =&gt; true)
+  name = name.titleize
+  name = name.singularize if singular
+  name = name.downcase if lowercase
+  name
+%&gt;&lt;/def&gt;
+
 
 
 &lt;def tag=&quot;a&quot; attrs=&quot;action, to, params, query-params, href, format, subsite&quot;&gt;&lt;%=
@@ -216,17 +195,18 @@
       # Link to a new object form
       new_record = target.new
       new_record.set_creator(current_user)
-      if can_create?(new_record)
-        
+      href = object_url(target, &quot;new&quot;, params._?.merge(:subsite =&gt; subsite))
+      
+      if href &amp;&amp; can_create?(new_record)
         new_class_name = if target.respond_to?(:proxy_reflection)
                            target.proxy_reflection.klass.name
                          else
                            target.name
                          end
 
-        href = object_url(target, &quot;new&quot;, params._?.merge(:subsite =&gt; subsite))
         add_classes!(attributes, &quot;new-#{new_class_name.underscore}-link&quot;)
         content = &quot;New #{new_class_name.titleize}&quot; if content.blank?
+        Hobo::Dryml.last_if = true
         element(:a, attributes.update(:href =&gt; href), content)
       else
         Hobo::Dryml.last_if = false
@@ -235,22 +215,15 @@
     else
       # Link to an existing object
 
-      if target.is_a?(Array) &amp;&amp; !target.respond_to?(:proxy_reflection) &amp;&amp; target.respond_to?(:member_class)
-        # Not much to go on here - last guess is that this is an index page
-        target = target.member_class
-      end
-      
       content = name if content.blank?
       
-      # Do we want automatic disabling of links to thinks that are not
-      # linkable?
-      only_if_linkable = format.blank?
-      href = object_url(target, action, (params || {}).merge(:subsite =&gt; subsite, :if_available =&gt; only_if_linkable))
+      href = object_url(target, action, (params || {}).merge(:subsite =&gt; subsite))
       if href.nil?
         # This target is registered with ModelRouter as not linkable
         content
       else
-        add_classes!(attributes, &quot;#{target.class.name.underscore}-link&quot;)
+        css_class = target.try.origin_attribute || target.class.name.underscore
+        add_classes!(attributes, &quot;#{css_class}-link&quot;)
       
         href.sub!(/\?|$/, &quot;.#{format}\\0&quot;) unless format.blank?
       
@@ -268,21 +241,23 @@
    
   res = if this.nil? &amp;&amp; if_blank.nil?
           this_type.is_a?(Class) &amp;&amp; this_type &lt;= String ? &quot;&quot; : nil_view
-        elsif this_type.respond_to?(:macro)
-          if this_type.macro == :belongs_to
-            belongs_to_view(attributes)
-          elsif this_type.macro == :has_many
-            has_many_view(attributes)
-          end
+        elsif (refl = this_field_reflection) &amp;&amp; refl.macro == :has_many
+          has_many_view(attributes)
         else
-          attrs = add_classes(attributes, &quot;view&quot;, type_id, type_and_field)
-          attrs['hobo-model-id'] = this_field_dom_id if this_parent &amp;&amp; this_parent.respond_to?(:typed_id)
          
           view_tag = find_polymorphic_tag(&quot;view&quot;)
           
           if view_tag == &quot;view&quot; # i.e. it didn't find a type specific tag
-            raise HoboError, &quot;Cannot view: #{this.inspect} (field is #{this_field}, type is #{this.class})&quot;
+            if this.respond_to?(:to_html)
+              this.to_html
+            else
+              raise HoboError, &quot;Cannot view: #{this.inspect} (field is #{this_field}, type is #{this.class})&quot;
+            end
           else
+            attrs = add_classes(attributes, &quot;view&quot;, type_and_field)
+            id = dom_id
+            attrs['hobo-model-id'] = id unless id == &quot;nil&quot;
+
             view_attrs = attrs_for(view_tag)
             the_view = send(view_tag, attrs &amp; view_attrs)
             
@@ -321,36 +296,27 @@
 
 &lt;def tag=&quot;view&quot; for=&quot;Numeric&quot; attrs=&quot;format&quot;&gt;&lt;%= format ? format % this : this.to_s %&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;view&quot; for=&quot;Hobo::Text&quot;&gt;&lt;%= h(this).gsub(&quot;\n&quot;, &quot;&lt;br/&gt;&quot;) %&gt;&lt;/def&gt;
+&lt;def tag=&quot;view&quot; for=&quot;string&quot;&gt;&lt;%= this.try.to_html || h(this).gsub(&quot;\n&quot;, &quot;&lt;br/&gt;&quot;) %&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;view&quot; for=&quot;html&quot;&gt;&lt;%= this %&gt;&lt;/def&gt;
+&lt;def tag=&quot;view&quot; for=&quot;boolean&quot;&gt;&lt;%= this ? 'Yes' : 'No' %&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;view&quot; for=&quot;Hobo::MarkdownString&quot;&gt;&lt;%= this.to_html %&gt;&lt;/def&gt;
+&lt;def tag=&quot;view&quot; for=&quot;ActiveRecord::Base&quot;&gt;&lt;a/&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;view&quot; for=&quot;Hobo::TextileString&quot;&gt;&lt;%= this.to_html %&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;view&quot; for=&quot;Hobo::PasswordString&quot;&gt;[password withheld]&lt;/def&gt;
-
-&lt;def tag=&quot;view&quot; for=&quot;String&quot;&gt;&lt;%= h(this).gsub(&quot;\n&quot;, &quot;&lt;br/&gt;&quot;) %&gt;&lt;/def&gt;
-
-&lt;def tag=&quot;view&quot; for=&quot;TrueClass&quot;&gt;&lt;%= this ? 'Yes' : 'No' %&gt;&lt;/def&gt;
-
-&lt;def tag=&quot;count&quot; attrs=&quot;label, prefix, if-any&quot;&gt;&lt;%=
+&lt;def tag=&quot;count&quot; attrs=&quot;label, prefix, if-any, lowercase&quot;&gt;&lt;%=
   raise Exception.new(&quot;asked for count of a string&quot;) if this.is_a?(String)
    
-  if this.is_a?(Class) and this &lt; ActiveRecord::Base
-    c = this.count
-    label ||= this.name.titleize
-  else
-    label ||= this.respond_to?(:proxy_reflection) &amp;&amp; this.proxy_reflection.name.to_s.singularize.titleize
-    c = if this.is_a?(Fixnum)
-          this
-        elsif this.respond_to?(:count)
-          this.count
-        else
-          this.length
-        end
-  end
+  c = this.try.to_int || this.try.total_entries || this.try.count || this.try.length
+
+  label ||= if this.is_a?(Class)
+              this.name
+            elsif (attr = this.try.origin_attribute)
+              attr.to_s.singularize
+            else
+              this.member_class.name
+            end.titleize
+
+  label = label.downcase if lowercase
    
   Hobo::Dryml.last_if = c &gt; 0 if if_any
   if if_any &amp;&amp; c == 0
@@ -360,7 +326,7 @@
    
     if prefix.in? %w(are is)
       p = c == 1 ? &quot;is&quot; : &quot;are&quot;
-      p + ' ' + main
+      p + ' ' + main.to_s
     else
       main
     end
@@ -369,15 +335,12 @@
 
 
 &lt;def tag=&quot;theme-stylesheet&quot; attrs=&quot;name&quot;&gt;
-  &lt;% name ||= 'application' -%&gt;
-  &lt;link href=&quot;&lt;%= base_url %&gt;/hobothemes/&lt;%= Hobo.current_theme %&gt;/stylesheets/&lt;%= name %&gt;.css&quot;
+  &lt;% name ||= Hobo.current_theme -%&gt;
+  &lt;link href=&quot;#{base_url}/hobothemes/#{Hobo.current_theme}/stylesheets/#{name}.css&quot;
         media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&gt;
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;clearer&quot;&gt;&lt;div class='clearer'&gt;&amp;nbsp;&lt;/div&gt;&lt;/def&gt;
-
-
 &lt;!-- The Tags defined below here are a bit rough and will be improved
 in the future - use at your own risk. --&gt;
 
@@ -386,12 +349,12 @@ in the future - use at your own risk. --&gt;
 
     &lt;do param=&quot;default&quot;/&gt;
 
-    &lt;if test=&quot;&amp;delete_buttons != false and can_delete?(this)&quot;&gt;
+    &lt;if test=&quot;&amp;delete_buttons != false &amp;&amp; can_delete?(this)&quot;&gt;
       &lt;td&gt;&lt;delete-button/&gt;&lt;/td&gt;
     &lt;/if&gt;
   &lt;/table-for&gt;
   &lt;else&gt;
-    &lt;p&gt;There are no &lt;%= this_type.klass.name.titleize.pluralize.downcase %&gt;&lt;/p&gt;
+    &lt;p&gt;There are no &lt;%= this.member_class.name.titleize.pluralize.downcase %&gt;&lt;/p&gt;
   &lt;/else&gt;
   &lt;div&gt;
     &lt;create-button update=&quot;&amp;id || part_id&quot;/&gt;
@@ -423,16 +386,9 @@ in the future - use at your own risk. --&gt;
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;you&quot; attrs=&quot;have, are&quot;&gt;
-  &lt;if test=&quot;&amp;this == current_user&quot;&gt;you &lt;%= if have then 'have' elsif are then 'are' end %&gt;&lt;/if&gt;
-  &lt;else&gt;&lt;do param=&quot;default&quot;&gt;&lt;name/&gt; &lt;%= if have then 'has' elsif are then 'is' end %&gt;&lt;/do&gt;&lt;/else&gt;
-&lt;/def&gt;
-
+&lt;def tag=&quot;you&quot; attrs=&quot;have, are, do, titleize&quot;&gt;&lt;if test=&quot;&amp;this == current_user&quot;&gt;&lt;%= &quot;#{titleize ? 'Y' : 'y'}ou#{' have' if have}#{' are' if are}#{' do' if do_}&quot; %&gt;&lt;/if&gt;&lt;else&gt;&lt;do param=&quot;default&quot;&gt;&lt;name/&gt;&lt;%= &quot;#{' has' if have}#{' is' if are}#{' does' if do_}&quot; %&gt;&lt;/do&gt;&lt;/else&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;You&quot; attrs=&quot;have, are&quot;&gt;
-  &lt;if test=&quot;&amp;this == current_user&quot;&gt;You &lt;%= if have then 'have' elsif are then 'are' end %&gt;&lt;/if&gt;
-  &lt;else&gt;&lt;do param=&quot;default&quot;&gt;&lt;name/&gt; &lt;%= if have then 'has' elsif are then 'is' end %&gt;&lt;/do&gt;&lt;/else&gt;
-&lt;/def&gt;
+&lt;def tag=&quot;You&quot;&gt;&lt;you merge titleize/&gt;&lt;/def&gt;
 
 &lt;def tag=&quot;your&quot;&gt;
   &lt;if test=&quot;&amp;this == current_user&quot;&gt;your&lt;/if&gt;
@@ -446,17 +402,6 @@ in the future - use at your own risk. --&gt;
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;creation-details&quot;&gt;
-  &lt;span class=&quot;creation-details&quot;&gt;
-    &lt;view with=&quot;&amp;this.send(this.class.creator_attribute)&quot; class=&quot;creator&quot; if=&quot;&amp;this.class.creator_attribute&quot; /&gt;
-    &lt;view:created_at class=&quot;created-at&quot; if=&quot;&amp;this.respond_to?(:created_at)&quot;/&gt;
-  &lt;/span&gt;
-&lt;/def&gt;
-
-&lt;def tag=&quot;primary-content&quot;&gt;
-  &lt;view class=&quot;primary-content&quot; field=&quot;&amp;this.class.primary_content_attribute&quot; if=&quot;&amp;this.class.primary_content_attribute&quot;/&gt;
-&lt;/def&gt;
-
 &lt;def tag=&quot;live-search&quot;&gt;
   &lt;div class=&quot;search&quot;&gt;
     &lt;label for=&quot;search-field&quot;&gt;Search&lt;/label&gt;&lt;input type=&quot;search&quot; id=&quot;search-field&quot; name=&quot;search-field&quot; class=&quot;search-bhv search&quot;/&gt;
@@ -468,11 +413,6 @@ in the future - use at your own risk. --&gt;
   &lt;/section&gt;
 &lt;/def&gt;
 
-&lt;def tag=&quot;count-dependents&quot;&gt;
-  &lt;% assoc = this.class.dependent_collections.first if this.class.dependent_collections.length == 1 %&gt;
-  &lt;count field=&quot;&amp;assoc&quot; if=&quot;&amp;assoc&quot;/&gt;
-&lt;/def&gt;
-
 
 &lt;def tag=&quot;a-or-an&quot; attrs=&quot;word&quot;&gt;&lt;%=
   (word =~ /^[aeiouh]/i ? &quot;an &quot; : &quot;a &quot;) + word
@@ -482,3 +422,15 @@ in the future - use at your own risk. --&gt;
 &lt;def tag=&quot;A-or-An&quot; attrs=&quot;word&quot;&gt;&lt;%=
   (word =~ /^[aeiouh]/i ? &quot;An &quot; : &quot;A &quot;) + word
 %&gt;&lt;/def&gt;
+
+
+&lt;def tag=&quot;filter-menu&quot; attrs=&quot;param-name, options, no-filter&quot;&gt;
+  &lt;% no_filter ||= &quot;All&quot; %&gt;
+  &lt;form action=&quot;&amp;request.request_uri&quot; method=&quot;get&quot; class=&quot;filter-menu&quot;&gt;
+    &lt;hidden-field name=&quot;filter-parameter&quot; value=&quot;&amp;param_name&quot;/&gt;
+    &lt;select-menu name=&quot;&amp;param_name&quot; options=&quot;&amp;options&quot; selected=&quot;&amp;params[param_name.gsub('-', '_')]&quot; first-option=&quot;&amp;no_filter&quot; merge-params/&gt;
+  &lt;/form&gt;
+&lt;/def&gt;
+
+
+&lt;def tag=&quot;comma-list&quot; attrs=&quot;separator&quot;&gt;&lt;%= this.join(separator || &quot;, &quot;) %&gt;&lt;/def&gt;</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -1,14 +1,14 @@
 &lt;def tag=&quot;editor&quot; &gt;&lt;%= 
   if !can_edit?
     view(attributes)
-  elsif this_type.respond_to?(:macro)
-    if this_type.macro == :belongs_to
+  elsif (refl = this_field_reflection)
+    if refl.macro == :belongs_to
       belongs_to_editor(attributes)
     else
       has_many_editor(attributes)
     end
   else
-    attrs = add_classes(attributes, &quot;#{type_and_field}&quot;, &quot;#{type_id}&quot;, &quot;editor&quot;)
+    attrs = add_classes(attributes, type_id, type_and_field, &quot;editor&quot;)
     call_polymorphic_tag(&quot;editor&quot;, attrs) or
       raise HoboError.new(&quot;&lt;editor&gt; not implemented for #{this.class.name}\##{this_field} &quot; +
                           &quot;(#{this.inspect}:#{this_type})&quot;)
@@ -43,8 +43,8 @@
 
 &lt;def tag=&quot;editor&quot; for=&quot;big_integer&quot;&gt;&lt;%= in_place_editor &quot;in_place_textfield_bhv&quot;, attributes %&gt;&lt;/def&gt;
 
-&lt;def tag=&quot;editor&quot; for=&quot;Hobo::EnumString&quot;&gt;
- &lt;string-select-editor values=&quot;&amp;this_type.values&quot;/&gt;
+&lt;def tag=&quot;editor&quot; for=&quot;HoboFields::EnumString&quot;&gt;
+ &lt;string-select-editor values=&quot;&amp;this_type.values&quot; merge/&gt;
 &lt;/def&gt;
 
 
@@ -56,36 +56,37 @@
      else
        completer_model = completer_model.constantize if completer_model.is_a? String
      end
-     id ||= this_field_dom_id + &quot;_completer&quot;
+     id ||= dom_id + &quot;_completer&quot;
      url = object_url(completer_model, &quot;completions&quot;,
                       { :for =&gt; completer_attr }.update(attributes.select_hash {|k,v| k.to_s.starts_with? &quot;where_&quot;}))
   %&gt;
   &lt;input type=&quot;text&quot; name=&quot;#{name}&quot; id=&quot;#{id}&quot; class=&quot;autocomplete-bhv&quot;
          autocomplete-url=&quot;#{url}&quot; value=&quot;#{value}&quot;
          merge-attrs/&gt;
-  &lt;div id=&quot;&lt;%= id %&gt;-completions&quot; class=&quot;completions-popup&quot; style=&quot;display:none&quot;&gt;&lt;/div&gt;
+    &lt;div id=&quot;#{id}-completions&quot; class=&quot;completions-popup&quot; style=&quot;display:none&quot;&gt;&lt;/div&gt;
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;belongs-to-menu-editor&quot;&gt;&lt;%
+&lt;def tag=&quot;belongs-to-menu-editor&quot; attrs=&quot;sort&quot;&gt;&lt;%
   raise HoboError.new(&quot;Not allowed to edit&quot;) unless can_edit?
-  link_id = &quot;#{this_field_dom_id}_editor&quot; %&gt;
-  &lt;span id=&quot;#{link_id}&quot; part=&quot;rapid-belongs-to-edit&quot;&gt;
-    &lt;% select_options = this_type.klass.find(:all).select {|x| can_view?(x)}.map {|x|
+  link_id = &quot;#{dom_id}_editor&quot; %&gt;
+  &lt;span id=&quot;#{link_id}&quot; part=&quot;rapid-belongs-to-edit&quot; part-locals=&quot;sort&quot;&gt;
+    &lt;% select_options = this_field_reflection.klass.find(:all).select {|x| can_view?(x)}.map {|x|
             [ name(:with =&gt; x, :no_wrapper =&gt; true), x.id ]
           }
+       select_options = select_options.sort if sort
        select_options.insert(0, [&quot;(No #{this_type.name.to_s.titleize})&quot;, &quot;&quot;]) if this.nil?
-       link_id = &quot;#{this_field_dom_id}_editor&quot;
+       link_id = &quot;#{dom_id}_editor&quot;
        f = ajax_updater(object_url(this_parent),
-                        &quot;Change #{this_field.to_s.titleize}&quot;, [link_id],
+                        [link_id],
                         :method =&gt; &quot;put&quot;,
                         :params =&gt; { this_parent.class.name.underscore =&gt; {
-                            this_type.primary_key_name =&gt; Hobo.raw_js('this.value')
+                            this_field_reflection.primary_key_name =&gt; Hobo.raw_js('this.value')
                           } })
     %&gt;
     &lt;select onchange=&quot;#{f}&quot;&gt;
-      &lt;%= options_for_select(select_options.sort, this ? this.id : &quot;&quot;) %&gt;
-    &lt;/select&gt;&amp;nbsp;&lt;a if=&quot;&amp;this&quot;&gt;View&lt;/a&gt;
+      &lt;%= options_for_select(select_options, this ? this.id : &quot;&quot;) %&gt;
+    &lt;/select&gt;&amp;nbsp;&lt;a if=&quot;&amp;this &amp;&amp; linkable?&quot;&gt;View&lt;/a&gt;
   &lt;/span&gt;
 &lt;/def&gt;
 
@@ -94,9 +95,9 @@
   &lt;if-can-edit&gt;&lt;%
   return object_link unless can_edit?
 
-  id ||= this_field_dom_id + &quot;_completer&quot;
+  id ||= dom_id + &quot;_completer&quot;
   f = ajax_updater(object_url(this_parent),
-                   &quot;Change #{this_field.to_s.titleize}&quot;, update,
+                   update,
                    :method =&gt; &quot;put&quot;,
                    :params =&gt; { this_parent.class.name.underscore =&gt; {
                        this_field =&gt; Hobo.raw_js(&quot;$('#{id}').value&quot;)
@@ -125,7 +126,7 @@
 
   values = comma_split(values)
   f = ajax_updater(object_url(this_parent),
-                   &quot;Change #{this_field.to_s.titleize}&quot;, update,
+                   update,
                    :method =&gt; &quot;put&quot;,
                    :params =&gt; { this_parent.class.name.underscore =&gt; {
                        this_field =&gt; Hobo.raw_js('this.value')
@@ -141,9 +142,9 @@
 &lt;def tag=&quot;boolean-checkbox-editor&quot; attrs=&quot;update, message&quot;&gt;&lt;%
   raise HoboError.new(&quot;Not allowed to edit&quot;) unless can_edit?
   f = ajax_updater(object_url(this_parent),
-                   message || &quot;Change #{this_field.to_s.titleize}&quot;,
                    update,
                    :method =&gt; &quot;put&quot;,
+                   :message =&gt; message,
                    :spinner_next_to =&gt; Hobo.raw_js(&quot;this&quot;),
                    :params =&gt; { this_parent.class.name.underscore =&gt; {
                        this_field =&gt; Hobo.raw_js('this.checked')
@@ -159,11 +160,11 @@
   &lt;% base_class = this.class
      base_class = base_class.superclass while base_class.superclass != ActiveRecord::Base
    f = ajax_updater(&quot;#{base_url}/#{controller_for base_class}/#{this.id}&quot;,
-                      &quot;Change #{this_field.to_s.titleize}&quot;, update,
-                      :method =&gt; &quot;put&quot;,
-                      :params =&gt; { base_class.name.underscore =&gt; {
-                          &quot;type&quot; =&gt; Hobo.raw_js('this.value')
-                       } })
+                    update,
+                    :method =&gt; &quot;put&quot;,
+                    :params =&gt; { base_class.name.underscore =&gt; {
+                        &quot;type&quot; =&gt; Hobo.raw_js('this.value')
+                    } })
   %&gt;
 
   &lt;select onchange=&quot;#{f}&quot;&gt;
@@ -175,7 +176,7 @@
       
 &lt;def tag=&quot;integer-select-editor&quot; attrs=&quot;min, max, update, nil-option, message&quot;&gt;
  &lt;select class=&quot;number-editor-bhv&quot; hobo-update=&quot;#{update}&quot;
-          hobo-model-id=&quot;#{this_field_dom_id}&quot;
+          hobo-model-id=&quot;#{dom_id}&quot;
           merge-attrs=&quot;&amp;message ? attributes.merge(:hobo_message =&gt; message) : attributes&quot;&gt;
    &lt;if test=&quot;&amp;this.nil?&quot;&gt;&lt;option value=&quot;&quot;&gt;&lt;%= nil_option || &quot;Choose a value&quot; %&gt;&lt;/option&gt;&lt;/if&gt;
    &lt;%= options_for_select((min.to_i..max.to_i).to_a, this) %&gt;
@@ -197,17 +198,17 @@
   if obj == nil
     new = klass.new(fields)
     permission = if can_create?(new)
-      message ||= &quot;Setting #{new.class.name.titleize}&quot;
       class_name = new.class.name.underscore
-      checkbox_attrs[:onclick] = ajax_updater(object_url(new.class), message, update,
-                          ({:params =&gt; { class_name =&gt; fields }} unless fields.empty?))
+      ajax_options = { :message =&gt; message }
+      ajax_options[:params] = { class_name =&gt; fields } unless fields.empty?
+      checkbox_attrs[:onclick] = ajax_updater(object_url(new.class), update, ajax_options)
     end
   else
     permission = if can_delete?(obj)
       checkbox_attrs[:checked] = 'checked'
       message ||= &quot;Unsetting #{obj.class.name.titleize}&quot;
       class_name = obj.class.name.underscore
-      checkbox_attrs[:onclick] = ajax_updater(object_url(obj, &quot;destroy&quot;), message, update, {:method =&gt; 'delete'})                          
+      checkbox_attrs[:onclick] = ajax_updater(object_url(obj, &quot;destroy&quot;), update, {:message =&gt; message, :method =&gt; 'delete'})                          
     end
   end
   element(:input, add_classes(attributes.merge(checkbox_attrs),</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid_editing.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -20,58 +20,73 @@
 %&gt;&lt;/def&gt;
 
 
-&lt;def tag=&quot;form&quot; attrs=&quot;message, update, hidden-fields, action, method, web-method&quot;&gt;&lt;%= 
+&lt;def tag=&quot;form&quot; attrs=&quot;update, hidden-fields, action, method, web-method&quot;&gt;&lt;%= 
   ajax_attrs, html_attrs = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
    
-  html_attrs[:action] = action || object_url(this, web_method)
+  new_record = this.try.new_record?
 
-  new_record = this.respond_to?(:new_record?) &amp;&amp; this.new_record?
-   
   method = if method.nil?
              (action || web_method || new_record) ? &quot;post&quot; : &quot;put&quot;
            else
              method.downcase
            end
-  if method == &quot;put&quot;
-    http_method_hidden = hidden_field_tag(&quot;_method&quot;, &quot;PUT&quot;) 
-    html_attrs[:method] = &quot;post&quot;
-  else
-    html_attrs[:method] = method
-  end
-   
-  if update || !ajax_attrs.empty?
-    message ||= &quot;Creating #{this.class.name.titleize}&quot; if new_record
-    # add an onsubmit to convert to an ajax form if `update` is given
-    function = ajax_updater(:post_form, message, update, ajax_attrs)
-    html_attrs[:onsubmit] = [html_attrs[:onsubmit], &quot;#{function}; return false;&quot;].compact.join(&quot;; &quot;)
-  end
 
-  body, field_names = scope.new_scope do
-    scope[:in_form] = true
-    with_form_context { parameters.default }
-  end
-
-  hiddens = hidden_fields(:fields =&gt; hidden_fields, :skip =&gt; field_names) if new_record
-  
-  auth_token = if request_forgery_protection_token.nil?
-                 ''
-               else
-                 element(:input, :type =&gt; &quot;hidden&quot;, 
-                         :name =&gt; request_forgery_protection_token.to_s,
-                         :value =&gt; form_authenticity_token)
-               end
-
-  page_path_hidden = hidden_field_tag(&quot;page_path&quot;, view_name) 
-   
-  body = [http_method_hidden, page_path_hidden, auth_token, hiddens, body].join
+  html_attrs[:action] = action || object_url(this, web_method, :method =&gt; method)
    
-  if web_method
-    add_classes!(html_attrs, &quot;#{type_id}_#{web_method}_form&quot;)
+  if html_attrs[:action].nil? || (new_record &amp;&amp; !this.user_can_create?(current_user))
+    Hobo::Dryml.last_if = false
+    &quot;&quot;
   else
-    add_classes!(html_attrs, &quot;#{'new_' if new_record}#{type_id}&quot;)
+    if method == &quot;put&quot;
+      # browsers don't support put -- use post and add the Rails _method hack
+      http_method_hidden = hidden_field_tag(&quot;_method&quot;, &quot;PUT&quot;) 
+      html_attrs[:method] = &quot;post&quot; 
+    else
+      html_attrs[:method] = method
+    end
+     
+    if update || !ajax_attrs.empty?
+      # add an onsubmit to convert to an ajax form if `update` is given
+      function = ajax_updater(:post_form, update, ajax_attrs)
+      html_attrs[:onsubmit] = [html_attrs[:onsubmit], &quot;#{function}; return false;&quot;].compact.join(&quot;; &quot;)
+    end
+     
+    body, field_names = scope.new_scope do
+      scope[:in_form] = true
+      with_form_context { parameters.default }
+    end
+     
+    hiddens = hidden_fields(:fields =&gt; hidden_fields, :skip =&gt; field_names) if new_record
+     
+    auth_token = if method.nil? || method == 'get' || request_forgery_protection_token.nil?
+                   ''
+                 else
+                   element(:input, :type =&gt; &quot;hidden&quot;, 
+                           :name =&gt; request_forgery_protection_token.to_s,
+                           :value =&gt; form_authenticity_token)
+                 end
+     
+    page_path = if request.post? || request.put? &amp;&amp; params[:page_path]
+                  params[:page_path]
+                else
+                  view_name.sub(Hobo::Dryml::EMPTY_PAGE, params[:action])
+                end
+    page_path_hidden = hidden_field_tag(&quot;page_path&quot;, page_path) unless method == &quot;get&quot;
+     
+    body = [http_method_hidden, page_path_hidden, auth_token, hiddens, body].join
+     
+    
+    if action.nil? # don't add automatic css classes if the action was specified
+      if web_method
+        add_classes!(html_attrs, &quot;#{type_id}_#{web_method}_form&quot;)
+      else
+        add_classes!(html_attrs, &quot;#{'new_' if new_record}#{type_id}&quot;)
+      end
+    end
+     
+    Hobo::Dryml.last_if = true
+    element(&quot;form&quot;, html_attrs, body)
   end
-   
-  element(&quot;form&quot;, html_attrs, body)
 %&gt;&lt;/def&gt;
 
 
@@ -89,19 +104,18 @@
   elsif !can_edit?
     view
   else
-    attrs = add_classes(attributes, type_and_field)
-    the_input = if this_type.respond_to?(:macro)
-                  if this_type.macro == :belongs_to
-                    belongs_to_input(attrs)
-                  elsif this_type.macro == :has_many
-                    if this_type.options[:through]
-                      has_many_through_input(attrs)
+    attrs = add_classes(attributes, type_id, type_and_field)
+    the_input = if (refl = this_field_reflection)
+                  if refl.macro == :belongs_to
+                    select_one(attrs)
+                  elsif refl.macro == :has_many
+                    if refl.options[:through]
+                      select_many(attrs)
                     else
                       has_many_input(attrs)
                     end
                   end
                 else
-                  add_classes!(attrs, type_id)
                   attrs[:name] ||= param_name_for_this
                   the_input = call_polymorphic_tag('input', attrs) or
                     raise HoboError, (&quot;No input tag for #{this_field}:#{this_type} (this=#{this.inspect})&quot;)
@@ -132,12 +146,12 @@
 &lt;/def&gt;
 
 &lt;def tag=&quot;input&quot; for=&quot;date&quot; attrs=&quot;order&quot;&gt;
-  &lt;% order = order.nil? ? [:year, :month, :day] : comma_split(order).every(:to_sym) -%&gt;
+  &lt;% order = order.nil? ? [:year, :month, :day] : comma_split(order).*.to_sym -%&gt;
   &lt;%= select_date(this || Time.now, attributes.merge(:prefix =&gt; param_name_for_this, :order =&gt; order)) %&gt;
 &lt;/def&gt;
 
 &lt;def tag=&quot;input&quot; for=&quot;datetime&quot; attrs=&quot;order&quot;&gt;
-  &lt;% order = order.nil? ? [:year, :month, :day, :hour, :minute] : comma_split(order).every(:to_sym) -%&gt;
+  &lt;% order = order.nil? ? [:year, :month, :day ] : comma_split(order).*.to_sym -%&gt;
   &lt;%= select_datetime(this || Time.now, attributes.merge(:prefix =&gt; param_name_for_this, :order =&gt; order)) %&gt;
 &lt;/def&gt;
 
@@ -157,7 +171,7 @@
   &lt;%= text_field_tag(name, this, attributes) %&gt;
 &lt;/def&gt;
 
-&lt;def tag=&quot;input&quot; for=&quot;Hobo::EnumString&quot; attrs=&quot;labels,titleize&quot;&gt;
+&lt;def tag=&quot;input&quot; for=&quot;HoboFields::EnumString&quot; attrs=&quot;labels,titleize&quot;&gt;
   &lt;% labels ||= {} %&gt;
   &lt;% titleize = true if titleize.nil? %&gt;
   &lt;select name=&quot;#{param_name_for_this}&quot; merge-attrs&gt;
@@ -165,26 +179,18 @@
   &lt;/select&gt;
 &lt;/def&gt;
 
-&lt;def tag=&quot;input&quot; for=&quot;percentage&quot; attrs=&quot;name&quot;&gt;
-  &lt;%= text_field_tag(name, this, attributes.merge(:size =&gt; '3', :maxlength =&gt; '3')) %&gt;%
-&lt;/def&gt;
-
-
-&lt;def tag=&quot;belongs-to-input&quot;&gt;
-  &lt;%= belongs_to_menu_input(attributes) %&gt;
-&lt;/def&gt;
 
-&lt;!--- Buttons ---&gt;
+&lt;!-- Buttons --&gt;
 
-&lt;def tag=&quot;remote-method-button&quot; attrs=&quot;method, update, result-update, params, label, message&quot;&gt;&lt;%= 
+&lt;def tag=&quot;remote-method-button&quot; attrs=&quot;method, update, label&quot;&gt;&lt;%= 
   ajax_attributes, html_attributes = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
 
-  url = object_url(this, method)
+  url = object_url(this, method, :method =&gt; :post)
   add_classes!(html_attributes, &quot;button remote-method-button #{method}-button&quot;)
-  if update || result_update
-    message ||= method.titleize
-    func = ajax_updater(url, message, update,
-                        ajax_attributes.merge(:params =&gt; params, :result_update =&gt; result_update))
+  if update || result_update || !ajax_attrs.empty?
+    label ||= method.titleize
+    ajax_attributes[:message] ||= label
+    func = ajax_updater(url, update, ajax_attributes)
     html_attributes.update(:onclick =&gt; &quot;var e = this; &quot; + func, :type =&gt;'button', :value =&gt; label)
     element(:input, html_attributes)
   else
@@ -193,10 +199,10 @@
 %&gt;&lt;/def&gt;
   
 
-&lt;def tag=&quot;update-button&quot; attrs=&quot;label, message, update, fields, params&quot;&gt;&lt;%=
+&lt;def tag=&quot;update-button&quot; attrs=&quot;label, update, fields, params&quot;&gt;&lt;%=
   raise HoboError.new(&quot;no update specified&quot;) unless update
-  message ||= label
-  func = ajax_updater(object_url(this), message, update,
+  ajax_attributes[:message] ||= label
+  func = ajax_updater(object_url(this), update,
                       :params =&gt; { this.class.name.underscore =&gt; fields }.merge(params || {}),
                       :method =&gt; :put)
   element :input, add_classes(attributes.merge(:type =&gt;'button', :onclick =&gt; func, :value =&gt; label),
@@ -204,8 +210,10 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;delete-button&quot; attrs=&quot;label, message, update, in-place, image, confirm, fade, subsite&quot;&gt;&lt;%=
-  if (Hobo::Dryml.last_if = can_delete?)
+&lt;def tag=&quot;delete-button&quot; attrs=&quot;label, update, in-place, image, confirm, fade, subsite&quot;&gt;&lt;%=
+  in_place = false if in_place.nil? &amp;&amp; this == @this &amp;&amp; !request.xhr?
+  url = object_url(this, :method =&gt; :delete, :subsite =&gt; subsite)
+  if (Hobo::Dryml.last_if = url &amp;&amp; can_delete?)
     attributes = attributes.merge(if image
                                     { :type =&gt; &quot;image&quot;, :src =&gt; &quot;#{base_url}/images/#{image}&quot; }
                                   else
@@ -217,15 +225,18 @@
     add_classes!(attributes,
                  image ? &quot;image-button&quot; : &quot;button&quot;,
                  &quot;delete-button delete-#{this.class.name.underscore.dasherize}-button&quot;)
-    url = object_url(this, &quot;destroy&quot;, :subsite =&gt; subsite)
-    if in_place == false
-      attributes[:confirm] = confirm if confirm
-      button_to(label, url, attributes)
-    else
-      fade = true if fade.nil?
-      attributes[:value] = label
-      attributes[:onclick] = &quot;Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: #{confirm.inspect}})&quot;
-      element(:input, attributes)
+    if url
+      if in_place == false
+        attributes[:confirm] = confirm if confirm
+        attributes[:method] = :delete
+        button_to(label, url, attributes)
+      else
+        fade = true if fade.nil?
+        scope.collection_contains_delete_button = true if fade
+        attributes[:value] = label
+        attributes[:onclick] = &quot;Hobo.removeButton(this, '#{url}', #{js_updates(update)}, {fade:#{fade}, confirm: #{confirm.inspect}})&quot;
+        element(:input, attributes)
+      end
     end
   else
     &quot;&quot;
@@ -233,14 +244,14 @@
 %&gt;&lt;/def&gt;
 
 
-&lt;def tag=&quot;create-button&quot; attrs=&quot;model, update, label, message, fields&quot;&gt;&lt;%=
+&lt;def tag=&quot;create-button&quot; attrs=&quot;model, update, label, fields, message&quot;&gt;&lt;%=
   raise HoboError.new(&quot;no update specified&quot;) unless update
 
   fields ||= {}
   class_or_assoc = if model
                      model.is_a?(String) ? model.constantize : model
                    elsif Hobo.simple_has_many_association?(this)
-                     fields[this.proxy_reflection.primary_key_name] = this.proxy_owner.id
+                     fields[this_field_reflection.primary_key_name] = this.proxy_owner.id
                      this
                    else
                      raise HoboError.new(&quot;invalid context for &lt;create-button&gt;&quot;)
@@ -249,25 +260,27 @@
   new.set_creator(current_user)
   if can_create?(new)
     label ||= &quot;New #{new.class.name.titleize}&quot;
-    message ||= label
+    ajax_attributes = { :message =&gt; message }
+    ajax_attributes[:params] = { class_name =&gt; fields } unless fields.empty?
     class_name = new.class.name.underscore
-    func = ajax_updater(object_url(new.class), message, update,
-                        ({:params =&gt; { class_name =&gt; fields }} unless fields.empty?))
+    func = ajax_updater(object_url(new.class), message, update, ajax_attributes)
     element :input, add_classes(attributes.merge(:type =&gt;'button', :onclick =&gt; func, :value =&gt; label),
-                            &quot;button create-button create-#{class_name}-button&quot;)
+                                &quot;button create-button create-#{class_name}-button&quot;)
   end
 %&gt;&lt;/def&gt;
 
 
-&lt;def tag=&quot;belongs-to-menu-input&quot; attrs=&quot;include-none, blank-message, options&quot;&gt;&lt;%
+&lt;def tag=&quot;select-one&quot; attrs=&quot;include-none, blank-message, options, sort&quot;&gt;&lt;%
   raise HoboError.new(&quot;Not allowed to edit&quot;) unless can_edit?
    
   blank_message ||= &quot;(No #{this_type.name.to_s.titleize})&quot;
-  options ||= this_type.klass.find(:all, :conditions =&gt; this_type.options[:conditions]).select {|x| can_view?(x)}
-  #Todo: switch to autocompleter for id_name when too many records, and id_name supported
+  conditions = ActiveRecord::Associations::BelongsToAssociation.new(this, this_field_reflection).conditions
+  options ||= this_field_reflection.klass.all(:conditions =&gt; conditions).select {|x| can_view?(x)}
+    #Todo: switch to autocompleter for id_name when too many records, and id_name supported
   select_options = options.map { |x|
             [ name(:with =&gt; x, :no_wrapper =&gt; true), x.id ]
-          }.sort
+          }
+  select_options = select_options.sort if sort
   select_options.insert(0, [blank_message, &quot;&quot;]) if include_none || (this.nil? &amp;&amp; include_none != false)
   attributes = add_classes(attributes, &quot;input&quot;, &quot;belongs_to&quot;, type_and_field)
   %&gt;
@@ -277,18 +290,11 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;belongs-to-autocompleting-input&quot;&gt;
-&lt;% refl = this_type
-   completer_model ||= refl.klass
-   completer_attr ||= refl.klass.id_name_column
-   id ||= this_field_dom_id + &quot;_completer&quot;
-   where_attributes = attributes.select_hash {|k,v| k.to_s.starts_with? &quot;where_&quot;}
-   url = object_url(completer_model, :completions, { :for =&gt; completer_attr }.update(where_attributes))
-  %&gt;
-
-  &lt;input type=&quot;text&quot; id=&quot;#{id}&quot; class=&quot;autocomplete-bhv&quot; autocomplete-url=&quot;#{url}&quot;
-         name=&quot;#{param_name_for_this}&quot; merge-attrs/&gt;
-  &lt;div id=&quot;#{id}-completions&quot; class=&quot;completions-popup&quot; style=&quot;display:none&quot;&gt;&lt;/div&gt;
+&lt;def tag=&quot;name-one&quot; attrs=&quot;complete-target, completer&quot;&gt;
+  &lt;input type=&quot;text&quot; name=&quot;#{param_name_for_this}&quot; 
+         class=&quot;autocompleter #{type_and_field} complete-on:#{dom_id complete_target}:#{completer}&quot;
+         merge-attrs/&gt;
+  &lt;div class=&quot;completions-popup&quot; style=&quot;display:none&quot;&gt;&lt;/div&gt;
 &lt;/def&gt;
 
 
@@ -316,35 +322,52 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;name-array-input&quot; attrs=&quot;targets, remove-label&quot;&gt;
+&lt;def tag=&quot;select-many&quot; attrs=&quot;options, targets, remove-label, prompt&quot;&gt;
   &lt;%
-  all ||= this.member_class.find(:all)
+  prompt ||= &quot;Add a #{this_field.titleize.singularize}&quot;
+  options ||= this.member_class.all
   values = this
   %&gt;
-  &lt;div class=&quot;input has-many-through&quot; merge-attrs&gt;
+  &lt;div class=&quot;input select-many&quot; merge-attrs&gt;
     &lt;div style=&quot;display:none&quot; class=&quot;item-proto&quot;&gt;
-      &lt;div class=&quot;item&quot;&gt;
+      &lt;div class=&quot;item&quot; param=&quot;proto-item&quot;&gt;
         &lt;span&gt;&lt;/span&gt;
-        &lt;input type=&quot;hidden&quot; name=&quot;#{param_name_for_this}[]&quot; /&gt;
-        &lt;input type=&quot;button&quot; class=&quot;remove-item&quot; value=&quot;#{remove_label || 'Remove'}&quot;/&gt;
+        &lt;input type=&quot;hidden&quot; name=&quot;#{param_name_for_this}[]&quot; param=&quot;proto-hidden&quot;/&gt;
+        &lt;input type=&quot;button&quot; class=&quot;remove-item&quot; value=&quot;#{remove_label || 'Remove'}&quot; param=&quot;proto-remove-button&quot;/&gt;
       &lt;/div&gt;
     &lt;/div&gt;
     &lt;div class=&quot;items&quot;&gt;
-      &lt;div class=&quot;item&quot; repeat&gt;
-        &lt;span&gt;&lt;%= this %&gt;&lt;/span&gt;
-        &lt;input type=&quot;hidden&quot; name=&quot;#{param_name_for_this}[]&quot; value=&quot;#{this}&quot;/&gt;
-        &lt;input type=&quot;button&quot; class=&quot;remove-item&quot; value=&quot;#{remove_label || 'Remove'}&quot;/&gt;
-      &lt;/div&gt;
+      &lt;set param-name=&quot;&amp;param_name_for_this&quot;/&gt;
+      &lt;repeat&gt;
+        &lt;div class=&quot;item&quot; param=&quot;item&quot;&gt;
+          &lt;span&gt;&lt;%= this %&gt;&lt;/span&gt;
+          &lt;input type=&quot;hidden&quot; name=&quot;#{param_name}[]&quot; value=&quot;#{this}&quot; param=&quot;hidden&quot;/&gt;
+          &lt;input type=&quot;button&quot; class=&quot;remove-item&quot; value=&quot;#{remove_label || 'Remove'}&quot; param=&quot;remove-button&quot;/&gt;
+        &lt;/div&gt;
+      &lt;/repeat&gt;
     &lt;/div&gt;
     &lt;select&gt;
-      &lt;option value=&quot;&quot;&gt;Add a &lt;%= this_field.titleize.singularize %&gt;&lt;/option&gt;
-      &lt;option repeat=&quot;&amp;all.sort_by {|x| name(:no_wrapper =&gt; true, :with =&gt; x).downcase}&quot; 
+      &lt;option value=&quot;&quot;&gt;&lt;prompt/&gt;&lt;/option&gt;
+      &lt;option repeat=&quot;&amp;options.sort_by {|x| name(:no_wrapper =&gt; true, :with =&gt; x).downcase}&quot; 
               merge-attrs=&quot;&amp;{:style =&gt; 'display:none'} if this.in?(values)&quot;&gt;&lt;name/&gt;&lt;/option&gt;
     &lt;/select&gt;
   &lt;/div&gt;
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;after-submit&quot; attrs=&quot;uri&quot;&gt;
-  &lt;input type=&quot;hidden&quot; value=&quot;&amp;params[:after_submit] || uri&quot; name=&quot;after_submit&quot;/&gt;
+&lt;def tag=&quot;after-submit&quot; attrs=&quot;uri, stay-here, go-back&quot;&gt;
+  &lt;% uri = &quot;stay-here&quot; if stay_here %&gt;
+  &lt;% uri = session[:previous_uri] if go_back %&gt;
+  &lt;input type=&quot;hidden&quot; value=&quot;&amp;params[:after_submit] || uri&quot; name=&quot;after_submit&quot; if=&quot;&amp;uri&quot;/&gt;
+&lt;/def&gt;
+
+
+&lt;def tag=&quot;hidden-field&quot; attrs=&quot;name, value&quot;&gt;&lt;input type=&quot;hidden&quot; name=&quot;&amp;name&quot; value=&quot;&amp;value&quot; merge-attrs/&gt;&lt;/def&gt;
+
+
+&lt;def tag=&quot;select-menu&quot; attrs=&quot;options, selected, first-option, first-value&quot;&gt;
+  &lt;select merge-attrs param=&quot;default&quot;&gt;
+    &lt;option value=&quot;#{first_value}&quot; unless=&quot;&amp;first_option.nil?&quot;&gt;&lt;first-option/&gt;&lt;/option&gt;
+    &lt;do param=&quot;options&quot;&gt;&lt;% options_for_select(options.*.to_s, selected) %&gt;&lt;/do&gt;
+  &lt;/select&gt;
 &lt;/def&gt;</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid_forms.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -3,9 +3,10 @@
   --&gt;
 
 &lt;!--- General Navigation --&gt;
-  
+
+&lt;!-- General purpose navigation bar. Renders a `&lt;ul class=&quot;navigation&quot;&gt;` --&gt;
 &lt;def tag=&quot;navigation&quot; attrs=&quot;current&quot;&gt;
-  &lt;ul merge-attrs&gt;
+  &lt;ul class=&quot;navigation&quot; merge-attrs&gt;
     &lt;set-scoped current-navigation=&quot;&amp;current&quot;&gt;
       &lt;do param=&quot;default&quot;/&gt;
     &lt;/set-scoped&gt;
@@ -21,18 +22,22 @@
 &lt;/def&gt;
 
 
+&lt;def tag=&quot;main-nav&quot;&gt;&lt;magic-nav class=&quot;main-nav&quot;/&gt;&lt;/def&gt;
+
+
 &lt;!--- Account Navigation (log in / out / signup) --&gt;
 
 &lt;def tag=&quot;account-nav&quot;&gt;
-  &lt;ul class=&quot;account-nav&quot;&gt;
+  &lt;ul with=&quot;&amp;current_user&quot; class=&quot;account-nav&quot; param&gt;
     &lt;if test=&quot;&amp;logged_in?&quot;&gt;
-      &lt;li class='nav-item'&gt;Logged in as &lt;view:login with=&quot;&amp;current_user&quot;/&gt;&lt;/li&gt;
-      &lt;li class='nav-item'&gt;&lt;a href=&quot;&amp;logout_url&quot;&gt;Log out&lt;/a&gt;&lt;/li&gt;
+      &lt;li class='nav-item' param=&quot;logged-in-as&quot;&gt;&lt;a to=&quot;&amp;current_user&quot;&gt;Logged in as &lt;name/&gt;&lt;/a&gt;&lt;/li&gt;
+      &lt;li class='nav-item' param=&quot;account&quot;&gt;&lt;a action=&quot;account&quot;&gt;Account&lt;/a&gt;&lt;/li&gt;
+      &lt;li class='nav-item' param=&quot;log-out&quot;&gt;&lt;a href=&quot;&amp;logout_url&quot;&gt;Log out&lt;/a&gt;&lt;/li&gt;
     &lt;/if&gt;
     &lt;else&gt;
-      &lt;set user=&quot;&amp;Hobo::UserController.user_models.first&quot;/&gt;
-      &lt;li class='nav-item'&gt;&lt;a href=&quot;&amp;login_url(user)&quot;&gt;Log in&lt;/a&gt;&lt;/li&gt;
-      &lt;li if=&quot;&amp;signup_url(user)&quot; class='nav-item'&gt;&lt;a href=&quot;&amp;signup_url(user)&quot;&gt;Sign up&lt;/a&gt;&lt;/li&gt;
+      &lt;set user=&quot;&amp;Hobo::User.default_user_model&quot;/&gt;
+      &lt;li class='nav-item' param=&quot;log-in&quot;&gt;&lt;a href=&quot;&amp;login_url(user)&quot;&gt;Log in&lt;/a&gt;&lt;/li&gt;
+      &lt;li if=&quot;&amp;signup_url(user)&quot; class=&quot;nav-item&quot; param=&quot;sign-up&quot;&gt;&lt;a href=&quot;&amp;signup_url(user)&quot;&gt;Sign up&lt;/a&gt;&lt;/li&gt;
     &lt;/else&gt;
   &lt;/ul&gt;
 &lt;/def&gt;
@@ -41,54 +46,49 @@
 &lt;!--- Pagination Navigation --&gt;
 
 &lt;def tag=&quot;page-nav&quot; attrs=&quot;params&quot;&gt;
-  &lt;if test=&quot;&amp;@pages &amp;&amp; @pages.length &gt; 1&quot;&gt;
-    &lt;page-n-of-count/&gt; - 
-    &lt;first-page-link params=&quot;&amp;params&quot;&gt;|&amp;lt;&lt;/first-page-link&gt;
-    &lt;previous-page-link params=&quot;&amp;params&quot;&gt;Previous&lt;/previous-page-link&gt;
-    &lt;next-page-link params=&quot;&amp;params&quot;&gt;Next&lt;/next-page-link&gt;
-    &lt;last-page-link params=&quot;&amp;params&quot;&gt;&amp;gt|&lt;/last-page-link&gt;
-  &lt;/if&gt;
+  &lt;%= will_paginate this, attributes.symbolize_keys.reverse_merge(:inner_window =&gt; 2, :prev_label =&gt; &quot;&amp;laquo; Prev&quot;) %&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;page-n-of-count&quot;&gt;
-  Page &lt;%= @pages.current_page.number %&gt; of &lt;%= @pages.length %&gt;
+  Page &lt;%= this.current_page %&gt; of &lt;%= this.page_count %&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;previous-page-link&quot;&gt;
-  &lt;a if=&quot;&amp;@pages &amp;&amp; @pages.current.previous&quot;
-     href=&quot;&amp;url_for(params.merge(:page =&gt; @pages.current.previous))&quot;&gt;
+  &lt;a if=&quot;&amp;this.try.previous_page&quot;
+     href=&quot;&amp;url_for(params.merge(:page =&gt; this.previous_page))&quot;&gt;
     &lt;do param=&quot;default&quot;&gt;&amp;laquo; Previous page&lt;/do&gt;
   &lt;/a&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;next-page-link&quot;&gt;
-  &lt;a if=&quot;&amp;@pages &amp;&amp; @pages.current.next&quot;
-     href=&quot;&amp;url_for(params.merge(:page =&gt; @pages.current.next))&quot;&gt;
+  &lt;a if=&quot;&amp;this.try.next_page&quot;
+     href=&quot;&amp;url_for(params.merge(:page =&gt; this.next_page))&quot;&gt;
     &lt;do param=&quot;default&quot;&gt;Next page &amp;raqou;&lt;/do&gt;
   &lt;/a&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;first-page-link&quot;&gt;
-  &lt;a if=&quot;&amp;@pages &amp;&amp; @pages.first_page &amp;&amp; @pages.current != @pages.first_page&quot;
-     href=&quot;&amp;url_for(params.merge(:page =&gt; @pages.first_page))&quot;&gt;
+  &lt;a if=&quot;&amp;this.try.current_page &amp;&amp; this.current_page != 1&quot;
+     href=&quot;&amp;url_for(params.merge(:page =&gt; 1))&quot;&gt;
     &lt;do param=&quot;default&quot;&gt;&amp;laquo; First page&lt;/do&gt;
   &lt;/a&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;last-page-link&quot;&gt;
-  &lt;a if=&quot;&amp;@pages &amp;&amp; @pages.last_page &amp;&amp; @pages.current != @pages.last_page&quot;
-     href=&quot;&amp;url_for(params.merge(:page =&gt; @pages.last_page))&quot;&gt;
+  &lt;a if=&quot;&amp;this.try.current_page &amp;&amp; this.current_page != this.page_count&quot;
+     href=&quot;&amp;url_for(params.merge(:page =&gt; this.page_count))&quot;&gt;
     &lt;do param=&quot;default&quot;&gt;Last page &amp;raquo;&lt;/do&gt;
   &lt;/a&gt;
 &lt;/def&gt;
 
-&lt;!-- magic nav, just to get you started --&gt;
-&lt;!-- write your own navigation using the &lt;navigation&gt; tag --&gt;
+
+&lt;!-- magic nav, just to get you started. Don't try to customise this
+but rather write your own navigation using the `&lt;navigation&gt;` tag --&gt;
 &lt;def tag=&quot;magic-nav&quot;&gt;
   &lt;navigation merge-attrs&gt;
     &lt;nav-item href=&quot;#{base_url}/&quot;&gt;Home&lt;/nav-item&gt;</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid_navigation.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,9 @@
-&lt;def tag=&quot;base-page&quot; attrs=&quot;title, doctype&quot;&gt;
-  &lt;% title ||= default_page_title %&gt;
+&lt;def tag=&quot;base-page&quot; attrs=&quot;title, full-title, doctype&quot;&gt;
+  &lt;% full_title ||= &quot;#{title} : #{app_name}&quot; %&gt;
   &lt;doctype param version=&quot;&amp;doctype || 'HTML 4.01 STRICT'&quot;/&gt;
   &lt;html&gt;
     &lt;head param&gt;
-      &lt;title param&gt;&lt;%= title.gsub(/&lt;.*?&gt;/, '') %&gt;&lt;/title&gt;
+      &lt;title param&gt;&lt;%= full_title.gsub(/&lt;.*?&gt;/, '') %&gt;&lt;/title&gt;
       &lt;do param=&quot;stylesheets&quot;&gt;
         &lt;stylesheet name=&quot;reset&quot;/&gt;
         &lt;stylesheet name=&quot;hobo-rapid&quot;/&gt;
@@ -12,12 +12,13 @@
 
       &lt;do param=&quot;scripts&quot;&gt;
         &lt;javascript param name=&quot;prototype, effects, dragdrop, controls, lowpro, hobo-rapid, application&quot;/&gt;
+        &lt;do param=&quot;fix-ie6&quot;&gt;&lt;%= &quot;&lt;!--[if lt IE 7]&gt;&quot; %&gt;&lt;javascript name=&quot;IE7&quot;/&gt;&lt;%= &quot;&lt;![endif]--&gt;&quot; %&gt;&lt;/do&gt;
         &lt;hobo-rapid-javascripts param/&gt;
       &lt;/do&gt;
     &lt;/head&gt;
 
     &lt;body onload=&quot;Hobo.applyEvents();&quot; param/&gt;
-&lt;/html&gt;
+  &lt;/html&gt;
 &lt;/def&gt;
 
 
@@ -26,11 +27,11 @@
     &lt;body: param&gt;
       &lt;ajax-progress/&gt;
       &lt;header class=&quot;page-header&quot; param&gt;
-        &lt;heading param=&quot;app-name&quot;&gt;&lt;a href=&quot;/#{base_url}&quot;&gt;&lt;app-name/&gt;&lt;/a&gt;&lt;/heading&gt;
-        &lt;live-search param if=&quot;&amp;defined_route? :search&quot;/&gt;
+        &lt;heading param=&quot;app-name&quot;&gt;&lt;a href=&quot;#{base_url}/&quot;&gt;&lt;app-name/&gt;&lt;/a&gt;&lt;/heading&gt;
+        &lt;live-search param if=&quot;&amp;defined_route? :site_search&quot;/&gt;
         &lt;nav param&gt;
-          &lt;account-nav if=&quot;&amp;Hobo::UserController.user_models.first&quot; param/&gt;
-          &lt;magic-nav class=&quot;main-nav&quot; param=&quot;main-nav&quot;/&gt;
+          &lt;account-nav if=&quot;&amp;Hobo::User.default_user_model&quot; param/&gt;
+          &lt;main-nav param/&gt;
         &lt;/nav&gt;
       &lt;/header&gt;
       &lt;section class=&quot;page-content&quot; param=&quot;content&quot;&gt;
@@ -41,6 +42,7 @@
         &lt;footer class=&quot;content-footer&quot; param=&quot;content-footer&quot;/&gt;
       &lt;/section&gt;
       &lt;footer class=&quot;page-footer&quot; param/&gt;
+      &lt;part-contexts-javascripts/&gt;
     &lt;/body:&gt;
   &lt;/base-page&gt;
 &lt;/def&gt;
@@ -53,7 +55,7 @@
       &lt;section class=&quot;main-content&quot; param=&quot;main-content&quot;&gt;
         &lt;param-content for=&quot;content&quot;/&gt;
       &lt;/section&gt;
-      &lt;aside class=&quot;aside-content&quot; param/&gt;
+      &lt;aside class=&quot;aside-content&quot; empty param/&gt;
     &lt;/content:&gt;
   &lt;/simple-layout&gt;
 &lt;/def&gt;
@@ -63,40 +65,44 @@
 
 
 &lt;def tag=&quot;index-page&quot;&gt;
-  &lt;% model_name = @model.name.titleize %&gt;
-  &lt;page title=&quot;All #{model_name.pluralize}&quot; merge&gt;
-    &lt;body: class=&quot;index-page #{@model.name.underscore}&quot; param/&gt;
+  &lt;set model=&quot;&amp;this.try.member_class || self.model&quot;/&gt;
+  &lt;set model-name=&quot;&amp;model.name.titleize&quot;/&gt;
+  &lt;page title=&quot;All #{type_name :with =&gt; model, :plural =&gt; true}&quot; merge&gt;
+    &lt;body: class=&quot;index-page #{type_id model}&quot; param/&gt;
     &lt;content-header: param&gt;
-      &lt;heading param&gt;&lt;%= model_name.pluralize %&gt;&lt;/heading&gt;
-      &lt;p class=&quot;note&quot; param&gt;There &lt;do with=&quot;&amp;@model&quot;&gt;&lt;count part=&quot;item-count&quot; prefix=&quot;are&quot;/&gt;&lt;/do&gt;&lt;/p&gt;
-    &lt;/content-header&gt;
+      &lt;heading param&gt;&lt;type-name with=&quot;&amp;model&quot; plural/&gt;&lt;/heading&gt;
+      &lt;p class=&quot;note&quot; param=&quot;count&quot;&gt;
+        &lt;if&gt;There &lt;count prefix=&quot;are&quot;/&gt;&lt;/if&gt;
+        &lt;else&gt;There aren't any &lt;type-name lowercase plural/&gt; yet.&lt;/else&gt;
+      &lt;/p&gt;
+    &lt;/content-header:&gt;
 
     &lt;content-body: param&gt;
       &lt;nav param=&quot;top-pagination-nav&quot;&gt;&lt;page-nav/&gt;&lt;/nav&gt;
       
-      &lt;collection param/&gt;
+      &lt;collection param&gt;&lt;empty-message:&gt;&lt;/empty-message:&gt;&lt;/collection&gt;
       
       &lt;nav param=&quot;bottom-pagination-nav&quot;&gt;&lt;page-nav/&gt;&lt;/nav&gt;
     &lt;/content-body&gt;
     
     &lt;content-footer: param&gt;
-      &lt;a to=&quot;&amp;@model&quot; action=&quot;new&quot; param=&quot;new-link&quot; if=&quot;&amp;linkable?(@model, :new)&quot;/&gt;
+      &lt;a to=&quot;&amp;model&quot; action=&quot;new&quot; param=&quot;new-link&quot;/&gt;
       &lt;else&gt;
-        &lt;do with=&quot;&amp;new_for_current_user @model&quot;&gt;
-          &lt;section class=&quot;create-new&quot; if=&quot;&amp;can_create?&quot;&gt;
+        &lt;if with=&quot;&amp;model.user_new current_user&quot;&gt;
+          &lt;section class=&quot;create-new&quot;&gt;
             &lt;h2&gt;New &lt;type-name/&gt;&lt;/h2&gt;
-            &lt;form&gt;&lt;field-list/&gt;&lt;submit label=&quot;Create #{type_name}&quot;/&gt;&lt;/form&gt;
+            &lt;form&gt;&lt;field-list param=&quot;new-field-list&quot;/&gt;&lt;submit label=&quot;Create&quot;/&gt;&lt;/form&gt;
           &lt;/section&gt;
-        &lt;/do&gt;
+        &lt;/if&gt;
       &lt;/else&gt;
-    &lt;/content-footer&gt;
+    &lt;/content-footer:&gt;
   &lt;/page&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;new-page&quot;&gt;
   &lt;page title=&quot;New #{type_name}&quot; merge&gt;
-    &lt;body: class=&quot;new-page #{type_name.underscore}&quot; param/&gt;
+    &lt;body: class=&quot;new-page #{type_name :dasherize =&gt; true}&quot; param/&gt;
     &lt;content-header: param&gt;
       &lt;heading param&gt;New &lt;type-name title/&gt;&lt;/heading&gt;
     &lt;/content-header&gt;
@@ -105,7 +111,7 @@
       &lt;error-messages param/&gt;
 
       &lt;form param&gt;
-        &lt;field-list skip-associations=&quot;has_many&quot; param/&gt;
+        &lt;field-list param/&gt;
         &lt;div class=&quot;actions&quot; param=&quot;actions&quot;&gt;
           &lt;submit label=&quot;Create #{type_name}&quot; param/&gt;&lt;do param=&quot;back-link&quot;&gt; or &lt;a&gt;Cancel&lt;/a&gt;&lt;/do&gt;
         &lt;/div&gt;
@@ -115,89 +121,92 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;show-page&quot;&gt;
-  &lt;% has_many_assocs = this.class.reflections.values.map do |refl|
-       this.send(refl.name) if Hobo.simple_has_many_association?(refl)
-     end.compact
-     dependent_collection = this.class.dependent_collections.first if this.class.dependent_collections.length == 1 
-  %&gt;
+&lt;def tag=&quot;show-page&quot; attrs=&quot;primary-collection&quot;&gt;
+  &lt;set primary-collection-name=&quot;&amp;(primary_collection || self.primary_collection_name).to_s&quot; model=&quot;&amp;this.class&quot;/&gt;
+  &lt;set boolean-fields=&quot;&amp;model.columns.select {|c| c.type == :boolean }.*.name&quot;/&gt;
+  &lt;set skip-fields=&quot;&amp;boolean_fields + [model.name_attribute, model.primary_content_attribute, model.creator_attribute, model.dependent_on.first].compact&quot;/&gt;
+  &lt;set flags=&quot;&amp;boolean_fields.map {|f| f.titleize if this.send(f)}.compact.join(', ')&quot;/&gt;
+
   &lt;page merge title=&quot;#{name :no_wrapper =&gt; true}&quot;&gt;
-    &lt;body: class=&quot;show-page #{type_name.underscore}&quot; param/&gt;
+
+    &lt;body: class=&quot;show-page #{type_name :dasherize =&gt; true}&quot; param/&gt;
+
     &lt;content-header: param&gt;
       &lt;if with=&quot;&amp;this.dependent_on.reject{|x| x.is_a?(Hobo::User)}.first&quot;&gt;
-        &lt;div class=&quot;container&quot;&gt;&lt;a/&gt;&lt;/div&gt;
+        &lt;div class=&quot;container&quot;&gt;&lt;a&gt;&amp;laquo; &lt;name/&gt;&lt;/a&gt;&lt;/div&gt;
       &lt;/if&gt;
 
-      &lt;heading param&gt;&lt;%= this %&gt;&lt;/heading&gt;
+      &lt;heading param&gt;&lt;this/&gt;&lt;/heading&gt;
+
+      &lt;div class=&quot;flags&quot;&gt;&lt;flags/&gt;&lt;/div&gt;
+      
       &lt;creation-details param/&gt;
-      &lt;do param=&quot;dependent-collection-count&quot;&gt;&lt;a class=&quot;dependent-collection-count&quot; href=&quot;##{dependent_collection.to_s.underscore}&quot; part=&quot;dependent-collection-count&quot; part-locals=&quot;dependent_collection&quot; if=&quot;&amp;dependent_collection&quot;&gt;&lt;count-dependents/&gt;&lt;/a&gt;&lt;/do&gt;
-      &lt;a action=&quot;edit&quot; if=&quot;&amp;can_edit?&quot; class=&quot;edit&quot;&gt;Edit &lt;type-name/&gt;&lt;/a&gt;
+      &lt;do field=&quot;&amp;primary_collection_name&quot; if=&quot;&amp;primary_collection_name&quot; param=&quot;primary-collection-count&quot;&gt;
+        &lt;association-count class=&quot;primary-collection-count&quot; part=&quot;primary-collection-count&quot;/&gt;
+      &lt;/do&gt;
+                         
+      &lt;a action=&quot;edit&quot; class=&quot;edit&quot; if=&quot;&amp;linkable?(:edit) &amp;&amp; can_edit?&quot; param=&quot;edit-link&quot;&gt;Edit &lt;type-name/&gt;&lt;/a&gt;
     &lt;/content-header&gt;
     
     &lt;content-body: param&gt;
       &lt;primary-content param/&gt;
 
-      &lt;field-list skip=&quot;&amp;[this.class.name_attribute, this.class.primary_content_attribute, this.class.creator_attribute, this.class.dependent_on.first].compact &quot;
-                  skip-associations=&quot;has_many&quot; param/&gt;
+      &lt;field-list skip=&quot;&amp;skip_fields&quot; skip-associations=&quot;has_many&quot; param/&gt;
 
-      &lt;if test=&quot;&amp;dependent_collection&quot;&gt;
-        &lt;section class=&quot;dependent-collection&quot; field=&quot;&amp;dependent_collection&quot;&gt;
-          &lt;a name=&quot;#{dependent_collection.to_s.underscore}&quot;/&gt;
-          &lt;h2 param=&quot;dependent-collection-title&quot;&gt;&lt;%= dependent_collection.to_s.titleize %&gt;&lt;/h2&gt;
+      &lt;with-primary-collection name=&quot;&amp;primary_collection_name&quot; if=&quot;&amp;primary_collection_name&quot;&gt;
+        &lt;section class=&quot;primary-collection&quot;&gt;
+          &lt;h2 param=&quot;primary-collection-title&quot;&gt;&lt;primary-collection-name.titleize/&gt;&lt;/h2&gt;
 
-          &lt;do param=&quot;collection&quot;&gt;
-            &lt;collection part=&quot;dependent-collection&quot; part-locals=&quot;dependent_collection&quot;&gt;
-              &lt;empty-message:&gt;No &lt;%= dependent_collection.to_s %&gt; have been added yet.&lt;/empty-message&gt;
+          &lt;do param=&quot;primary-collection&quot;&gt;
+            &lt;collection part=&quot;primary-collection&quot;&gt;
+              &lt;card:&gt;&lt;delete-button: update=&quot;primary-collection-count&quot;&gt;&lt;/delete-button:&gt;&lt;/card:&gt;
             &lt;/collection&gt;
           &lt;/do&gt;
-        
-          &lt;do with=&quot;&amp;new_for_current_user&quot;&gt;
-            &lt;section class=&quot;create-new&quot; if=&quot;&amp;!linkable?(:new) &amp;&amp; can_create?&quot;&gt;
-              &lt;h2&gt;Add &lt;A-or-An word=&quot;&amp;dependent_collection.to_s.singularize.titleize&quot;/&gt;&lt;/h2&gt;
-              &lt;form update=&quot;dependent-collection, dependent-collection-count&quot; message=&quot;Adding #{dependent_collection.to_s.singularize.titleize}...&quot; reset-form&gt;
-                &lt;field-list skip=&quot;#{@this.class.reverse_reflection(@this.send(dependent_collection).proxy_reflection.name).name}&quot;
-                            skip-associations=&quot;has_many&quot; param=&quot;dependent-collection-field-list&quot;/&gt;
-                &lt;submit label=&quot;Create #{dependent_collection.to_s.singularize.titleize}&quot;/&gt;
-              &lt;/form&gt;
-            &lt;/section&gt;
+
+          &lt;do with=&quot;&amp;@this.send(primary_collection_name)&quot;&gt;
+            &lt;if test=&quot;&amp;can_create?&quot; param=&quot;primary-collection-add&quot;&gt;
+              &lt;nav class=&quot;new-link&quot;&gt;&lt;a action=&quot;new&quot; if=&quot;&amp;linkable?(:new) &amp;&amp; can_create?&quot;/&gt;&lt;/nav&gt;
+              &lt;else&gt;
+                &lt;section class=&quot;create-new&quot; with=&quot;&amp;new_for_current_user&quot;&gt;
+                  &lt;h2&gt;Add &lt;A-or-An word=&quot;&amp;primary_collection_name.singularize.titleize&quot;/&gt;&lt;/h2&gt;
+                  &lt;form update=&quot;primary-collection, primary-collection-count&quot;
+                        message=&quot;Adding #{primary_collection_name.singularize.titleize}...&quot; reset-form&gt;
+                    &lt;field-list skip=&quot;#{@this.class.reverse_reflection(@this.send(primary_collection_name).proxy_reflection.name).name}&quot;
+                                skip-associations=&quot;has_many&quot; param=&quot;primary-collection-field-list&quot;/&gt;
+                    &lt;submit label=&quot;Add&quot;/&gt;
+                  &lt;/form&gt;
+                &lt;/section&gt;
+              &lt;/else&gt;
+            &lt;/if&gt;
           &lt;/do&gt;
         &lt;/section&gt;
-      &lt;/if&gt;
-      &lt;else&gt;
-        &lt;section class=&quot;preview-collections&quot;&gt;
-          &lt;with-fields associations=&quot;has_many&quot;&gt;
-            &lt;section class=&quot;#{this_field.dasherize}&quot;&gt;
-              &lt;h2&gt;Recent &lt;this-field.titleize/&gt;&lt;/h2&gt;
-              &lt;collection with=&quot;&amp;this.recent&quot;/&gt;
-              &lt;a class=&quot;more&quot;&gt;More... (&lt;count/&gt;)&lt;/a&gt;
-            &lt;/section&gt;
-          &lt;/with-fields&gt;
-        &lt;/section&gt;
-      &lt;/else&gt;
-
-      &lt;nav class=&quot;new-links&quot; param=&quot;new-links&quot;&gt;
-        &lt;ul with=&quot;&amp;has_many_assocs&quot;&gt;
-          &lt;li: replace&gt;&lt;li if=&quot;&amp;can_create? &amp;&amp; linkable?(:new)&quot;&gt;&lt;a action=&quot;new&quot;/&gt;&lt;/li&gt;&lt;/li&gt;
-        &lt;/ul&gt;
-      &lt;/nav&gt;
+      &lt;/with-primary-collection&gt;
+    &lt;/content-body:&gt;
+
+    &lt;aside: param&gt;
+      &lt;section class=&quot;preview-collections&quot;&gt;
+        &lt;with-fields fields=&quot;&amp;non_through_collections - [(primary_collection_name.to_sym unless primary_collection_name.blank?)]&quot;&gt;
+          &lt;collection-preview class=&quot;#{this_field.dasherize}&quot;/&gt;
+        &lt;/with-fields&gt;
+      &lt;/section&gt;
+    &lt;/aside:&gt;
 
-    &lt;/content-body&gt;
   &lt;/page&gt;
 &lt;/def&gt;
 
 
 &lt;def tag=&quot;edit-page&quot;&gt;
   &lt;page merge&gt;    
-    &lt;body: class=&quot;edit-page #{this.class.name.underscore}&quot; param/&gt;
+    &lt;body: class=&quot;edit-page #{type_name :dasherize =&gt; true}&quot; param/&gt;
     &lt;content-header: param&gt;
-      &lt;heading&gt;&lt;if test=&quot;&amp;this.respond_to? :name&quot;&gt;&lt;name/&gt;&lt;/if&gt;&lt;else&gt;&lt;type-name/&gt;&lt;/else&gt;&lt;/heading&gt;
-      &lt;delete-button in-place=&quot;&amp;false&quot; label=&quot;Remove This #{this.class.name}&quot; param/&gt;
+      &lt;heading param&gt;&lt;if test=&quot;&amp;this.respond_to? :name&quot;&gt;&lt;name/&gt;&lt;/if&gt;&lt;else&gt;&lt;type-name/&gt;&lt;/else&gt;&lt;/heading&gt;
+      &lt;delete-button label=&quot;Remove This #{type_name}&quot; param/&gt;
     &lt;/content-header&gt;
 
     &lt;content-body: param&gt;
       &lt;error-messages param/&gt;
       &lt;form param&gt;
-        &lt;field-list skip-associations=&quot;has_many&quot; param/&gt;
+        &lt;field-list param/&gt;
         &lt;div class=&quot;actions&quot; param=&quot;actions&quot;&gt;
           &lt;submit label=&quot;Save Changes&quot; param/&gt;&lt;do param=&quot;back-link&quot;&gt; or &lt;a&gt;Cancel&lt;/a&gt;&lt;/do&gt;
         &lt;/div&gt;
@@ -209,22 +218,22 @@
 
 
 &lt;def tag=&quot;new-in-collection-page&quot;&gt;
-  &lt;set association-name=&quot;&amp;@association.proxy_reflection.name.to_s.singularize.titleize&quot;/&gt;
+  &lt;set association-name=&quot;&amp;@association.origin_attribute.to_s&quot;/&gt;
   &lt;page title=&quot;New #{type_name}&quot; merge&gt;
-    &lt;body: class=&quot;new-in-collection-page #{type_name(:with =&gt; @owner)} #{type_name}&quot; param/&gt;
+    &lt;body: class=&quot;new-in-collection-page #{association_name.underscore.dasherize} #{type_name :dasherize =&gt; true}&quot; param/&gt;
     &lt;content-header: param&gt;
-      &lt;heading param&gt;New &lt;association-name/&gt;&lt;/heading&gt;
-      &lt;sub-heading param&gt;For: &lt;a with=&quot;&amp;@owner&quot; /&gt;&lt;/sub-heading&gt;
+      &lt;heading param&gt;New &lt;association-name.singularize.titleize/&gt;&lt;/heading&gt;
+      &lt;sub-heading param&gt;For: &lt;a with=&quot;&amp;@association.origin&quot;/&gt;&lt;/sub-heading&gt;
     &lt;/content-header&gt;
 
     &lt;content-body: param&gt;
       &lt;error-messages/&gt;
 
       &lt;form param&gt;
-        &lt;field-list skip=&quot;#{@owner.class.reverse_reflection(@association.proxy_reflection.name).name}&quot;
-                    skip-associations=&quot;has_many&quot; param/&gt;
+        &lt;field-list skip=&quot;#{@association.origin.class.reverse_reflection(association_name.to_sym).name}&quot; param/&gt;
         &lt;div class=&quot;actions&quot; param=&quot;actions&quot;&gt;
-          &lt;submit label=&quot;Create #{association_name}&quot; param/&gt;&lt;do param=&quot;back-link&quot;&gt; or &lt;a with=&quot;&amp;@owner&quot;&gt;Cancel&lt;/a&gt;&lt;/do&gt;
+          &lt;submit label=&quot;Create #{association_name.singularize.titleize}&quot; param/&gt;
+          &lt;do param=&quot;back-link&quot;&gt; or &lt;a with=&quot;&amp;@association.origin&quot;&gt;Cancel&lt;/a&gt;&lt;/do&gt;
         &lt;/div&gt;
       &lt;/form&gt;
     &lt;/content-body&gt;
@@ -233,14 +242,14 @@
 
 
 &lt;def tag=&quot;show-collection-page&quot;&gt;
-  &lt;% title = &quot;#{@reflection.name.to_s.titleize} for #{name(:with =&gt; @owner)}&quot; %&gt;
+  &lt;% title = &quot;#{this.origin_attribute.to_s.titleize} for #{name(:with =&gt; this.origin)}&quot; %&gt;
   &lt;page title=&quot;&amp;title&quot; merge&gt;
-    &lt;body: class=&quot;show-collection-page #{type_name(:with =&gt; @owner)} #{type_name(:pluralize =&gt; true)}&quot;
+    &lt;body: class=&quot;show-collection-page #{type_name(:with =&gt; this.origin)} #{type_name(:pluralize =&gt; true) rescue debugger}&quot;
           param/&gt;
     &lt;content-header: param&gt;
-      &lt;nav&gt;Back to &lt;a with=&quot;&amp;@owner&quot;/&gt;&lt;/nav&gt;
+      &lt;nav&gt;Back to &lt;a:origin/&gt;&lt;/nav&gt;
       &lt;heading&gt;&lt;%= title %&gt;&lt;/heading&gt;
-      &lt;sub-heading&gt;&lt;count with=&quot;&amp;@pages.item_count&quot; label=&quot;&amp;@reflection.klass.name.titleize&quot;/&gt;&lt;/sub-heading&gt;
+      &lt;sub-heading&gt;&lt;count/&gt;&lt;/sub-heading&gt;
     &lt;/content-header&gt;
 
     &lt;content-body: param&gt;
@@ -250,114 +259,35 @@
       
       &lt;nav param=&quot;bottom-pagination-nav&quot;&gt;&lt;page-nav param/&gt;&lt;/nav&gt;
       
-      &lt;nav if=&quot;&amp;Hobo.simple_has_many_association?(@association)&quot; param=&quot;new-link&quot;&gt;
-        &lt;a to=&quot;&amp;@association&quot; action=&quot;new&quot;/&gt;
+      &lt;nav if=&quot;&amp;Hobo.simple_has_many_association?(this)&quot; param=&quot;new-link&quot;&gt;
+        &lt;a action=&quot;new&quot;/&gt;
       &lt;/nav&gt;
     &lt;/content-body&gt;
   &lt;/page&gt;
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;signup-page&quot;&gt;
-  &lt;page layout=&quot;simple&quot; title=&quot;Sign up to #{app_name}&quot; merge&gt;
-    &lt;body: class=&quot;signup-page&quot; param/&gt;
-
-    &lt;live-search: replace/&gt;
-    &lt;nav: replace/&gt;
-    
-    &lt;content-header: param&gt;
-      &lt;heading param&gt;Sign Up&lt;/heading&gt;
-    &lt;/content-header&gt;
-    
-    &lt;content-body: param&gt;
-      &lt;error-messages/&gt;
-      &lt;form action=&quot;&amp;request.request_uri&quot; param&gt;
-        &lt;field-list fields=&quot;login, password, password_confirmation&quot; param&gt;
-          &lt;password-confirmation-label:&gt;Confirm Password&lt;/password-confirmation-label&gt;
-        &lt;/field-list&gt;
-
-        &lt;div class=&quot;actions&quot; param=&quot;actions&quot;&gt;
-          &lt;submit label='Sign Up'/&gt;
-        &lt;/div&gt;
-      &lt;/form&gt;
-    &lt;/content-body&gt;
-
-  &lt;/page&gt;
-&lt;/def&gt;
-
-
-&lt;def tag=&quot;login-page&quot; attrs=&quot;remember-me&quot;&gt;
-  &lt;page layout=&quot;simple&quot; title=&quot;Log in to #{app_name}&quot; merge&gt;
-   
-    &lt;body: class=&quot;login-page&quot; param/&gt;
- 
-    &lt;live-search: replace/&gt;
-    &lt;nav: replace/&gt;
-    
+&lt;def tag=&quot;permission-denied-page&quot; attrs=&quot;message&quot;&gt;
+  &lt;% message ||= &quot;That operation is not allowed&quot; %&gt;
+  &lt;page merge&gt;
+    &lt;body: class=&quot;permission-denied&quot;/&gt;
     &lt;content-header: param&gt;
-      &lt;heading param&gt;Log In&lt;/heading&gt;
+      &lt;heading param&gt;&lt;message/&gt;&lt;/heading&gt;
     &lt;/content-header&gt;
-
-    &lt;content-body: param&gt;
-      &lt;form action=&quot;&amp;request.request_uri&quot; class=&quot;login&quot; param&gt;
-        &lt;labelled-item-list&gt;
-          &lt;labelled-item&gt;
-            &lt;item-label param=&quot;login-label&quot;&gt;&lt;%= model.login_attr.to_s.titleize %&gt;&lt;/item-label&gt;
-            &lt;item-value&gt;&lt;input type=&quot;text&quot; name=&quot;login&quot; id=&quot;login&quot; class=&quot;string&quot; param=&quot;login-input&quot; /&gt;&lt;/item-value&gt;
-          &lt;/labelled-item&gt;
-
-          &lt;labelled-item&gt;
-            &lt;item-label param=&quot;password-label&quot;&gt;Password&lt;/item-label&gt;
-            &lt;item-value&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot; id=&quot;password&quot; class=&quot;string&quot; param=&quot;password-input&quot;/&gt;&lt;/item-value&gt;
-          &lt;/labelled-item&gt;
-
-          &lt;labelled-item if=&quot;&amp;remember_me&quot;&gt;
-            &lt;item-label class=&quot;field-label&quot; param=&quot;remember-me-label&quot;&gt;Remember me:&lt;/item-label&gt;
-            &lt;item-value&gt;&lt;input type=&quot;checkbox&quot; name=&quot;remember_me&quot; id=&quot;remember-me&quot; param=&quot;remember-me-input&quot;/&gt;&lt;/item-value&gt;
-          &lt;/labelled-item&gt;
-        &lt;/labelled-item-list&gt;
-        &lt;set user=&quot;&amp;Hobo::UserController.user_models.first&quot;/&gt;
-        &lt;div class=&quot;actions&quot; param=&quot;actions&quot;&gt;
-          &lt;submit label='Log in' param/&gt;&lt;if test=&quot;&amp;signup_url(user)&quot; class='nav-item'&gt; or &lt;a href=&quot;&amp;signup_url(user)&quot;&gt;Sign up&lt;/a&gt;&lt;/if&gt;
-        &lt;/div&gt;
-      &lt;/form&gt;
-    &lt;/content-body&gt;
-  &lt;/page&gt;
-&lt;/def&gt;
-
-
-&lt;def tag=&quot;account-disabled-page&quot;&gt;
-
-  &lt;page layout=&quot;simple&quot; title=&quot;#{app_name} - account not available&quot; merge&gt;
-       
-    &lt;body: class=&quot;account-disabled-page&quot; param/&gt;
-
-    &lt;content-header: param&gt;&lt;heading param&gt;Account is not available&lt;/heading&gt;&lt;/content&gt;
-
-    &lt;content-body: param&gt;
-      &lt;p&gt;Your account is not available at this time.&lt;/p&gt;
-    &lt;/content-body&gt;
   &lt;/page&gt;
-
 &lt;/def&gt;
- 
 
 
-&lt;def tag=&quot;permission-denied-page&quot;&gt;
+&lt;def tag=&quot;not-found-page&quot; attrs=&quot;message&quot;&gt;
+  &lt;% message ||= &quot;The page you were looking for could not be found&quot; %&gt;
   &lt;page merge&gt;
+    &lt;body: class=&quot;not-found&quot;/&gt;
     &lt;content-header: param&gt;
-      &lt;heading param&gt;That operation is not allowed&lt;/heading&gt;
+      &lt;heading param&gt;&lt;message/&gt;&lt;/heading&gt;
     &lt;/content-header&gt;
   &lt;/page&gt;
 &lt;/def&gt;
 
-&lt;def tag=&quot;not-found-page&quot;&gt;
-  &lt;page merge&gt;
-    &lt;content-header: param&gt;
-      &lt;heading param&gt;The page you were looking for could not be found&lt;/heading&gt;
-    &lt;/content-header&gt;
-  &lt;/page&gt;
-&lt;/def&gt;
 
 &lt;def tag=&quot;doctype&quot; attrs=&quot;version&quot;&gt;&lt;%=
   case version.upcase
@@ -379,6 +309,7 @@
   end
 %&gt;&lt;/def&gt;
 
+
 &lt;def tag=&quot;stylesheet&quot; attrs=&quot;name, media&quot;&gt;
   &lt;repeat with=&quot;&amp;comma_split(name)&quot;&gt;
     &lt;link href=&quot;#{base_url}/stylesheets/#{this}.css&quot; media=&quot;#{ media || 'all' }&quot;
@@ -386,6 +317,7 @@
   &lt;/repeat&gt;
 &lt;/def&gt;
 
+
 &lt;def tag=&quot;javascript&quot; attrs=&quot;name&quot;&gt;
   &lt;if test=&quot;&amp;name.is_a?(Symbol)&quot;&gt;
     &lt;%= javascript_include_tag name %&gt;
@@ -397,11 +329,13 @@
   &lt;/else&gt;
 &lt;/def&gt;
 
+
 &lt;def tag=&quot;flash-message&quot; attrs=&quot;type&quot;&gt;
   &lt;% type = type ? type.to_sym : :notice %&gt;
   &lt;div class=&quot;flash #{type}&quot; if=&quot;&amp;flash[type]&quot; merge-attrs&gt;&lt;%= flash[type] %&gt;&lt;/div&gt;
 &lt;/def&gt;
 
+
 &lt;def tag=&quot;ajax-progress&quot;&gt;
   &lt;div id=&quot;ajax-progress&quot;&gt;
     &lt;div&gt;
@@ -411,7 +345,15 @@
 &lt;/def&gt;
 
 
-&lt;def tag=&quot;app-name&quot;&gt;&lt;%= @hobo_app_name ||= File.basename(Dir.chdir(RAILS_ROOT) { Dir.getwd }).strip.titleize %&gt;&lt;/def&gt;
-
 &lt;def tag=&quot;default-page-title&quot;&gt;&lt;%= t = this.to_s; ; &quot;#{t.blank? ? '' : t + ' - '}#{app_name}&quot; %&gt;&lt;/def&gt;
 
+
+&lt;def tag=&quot;with-primary-collection&quot; attrs=&quot;name&quot;&gt;&lt;%
+  ivar = &quot;@#{this.class.name.underscore}_#{name}&quot;
+    
+  if (collection = instance_variable_get(ivar))
+    %&gt;&lt;do with=&quot;&amp;collection&quot; param=&quot;default&quot;/&gt;&lt;%
+  else
+    %&gt;&lt;do field=&quot;&amp;name&quot; param=&quot;default&quot;/&gt;&lt;%
+  end
+%&gt;&lt;/def&gt;</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid_pages.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,19 @@
 &lt;def tag=&quot;table-plus&quot; attrs=&quot;sort-field, sort-direction, sort-columns&quot; &gt;
   &lt;% sort_field ||= @sort_field; sort_direction ||= @sort_direction; sort_columns ||= {} %&gt;
+  &lt;% sort_columns['this'] ||= this.member_class.name_attribute %&gt;
   &lt;div class=&quot;table-plus&quot; merge-attrs=&quot;&amp;attributes - attrs_for(:with_fields) - attrs_for(:table)&quot;&gt;
     &lt;div class=&quot;header&quot; param=&quot;header&quot;&gt;
       &lt;div class=&quot;search&quot;&gt;
         &lt;form param=&quot;search-form&quot; method=&quot;get&quot; action=&quot;&quot;&gt;
           &lt;hidden-fields for-query-string skip=&quot;page, search_form&quot;/&gt;
-          Search 
-          &lt;input class=&quot;search-field&quot; type=&quot;search&quot; name=&quot;search&quot; value=&quot;&amp;params[:search]&quot;/&gt;
+          &lt;span&gt;Search&lt;/span&gt;
+          &lt;input class=&quot;search&quot; type=&quot;search&quot; name=&quot;search&quot; value=&quot;&amp;params[:search]&quot;/&gt;
           &lt;submit label=&quot;Go&quot; class=&quot;search-button&quot; param=&quot;search-submit&quot;/&gt;
         &lt;/form&gt;
       &lt;/div&gt;
     &lt;/div&gt;
 
-    &lt;table merge-attrs=&quot;&amp;attributes &amp; (attrs_for(:table) + attrs_for(:with_fields))&quot; merge-params&gt;
+    &lt;table merge-attrs=&quot;&amp;attributes &amp; (attrs_for(:table) + attrs_for(:with_fields))&quot; empty merge-params&gt;
       &lt;field-heading-row:&gt;
         &lt;with-field-names merge-attrs=&quot;&amp;all_attributes &amp; attrs_for(:with_fields)&quot;&gt;
           &lt;% col = sort_columns[scope.field_path] || scope.field_path
@@ -32,12 +33,24 @@
         &lt;th if=&quot;&amp;all_parameters[:controls]&quot; class=&quot;controls&quot;/&gt;
       &lt;/field-heading-row&gt;
     &lt;/table&gt;    
-    &lt;else&gt;
-      &lt;do param=&quot;empty-message&quot;/&gt;
-    &lt;/else&gt;
+    &lt;do param=&quot;empty-message&quot; if=&quot;empty?&quot;/&gt;
    
-    &lt;nav class=&quot;page&quot;&gt;
+    &lt;nav class=&quot;page&quot; if=&quot;&amp;this.respond_to? :page_count&quot;&gt;
       &lt;page-nav param/&gt;
     &lt;/nav&gt;
   &lt;/div&gt;
 &lt;/def&gt;
+
+
+&lt;def tag=&quot;change-password-form&quot;&gt;
+  &lt;form class=&quot;change-password&quot; param&gt;
+    &lt;field-list fields=&quot;current_password, password, password_confirmation&quot; param&gt;
+      &lt;password-label:&gt;New Password&lt;/password-label:&gt;
+      &lt;password-confirmation-label:&gt;Confirm New Password&lt;/password-confirmation-label:&gt;
+    &lt;/field-list&gt;
+    
+    &lt;div class=&quot;actions&quot; param=&quot;actions&quot;&gt;
+      &lt;submit label=&quot;Change Password&quot; param/&gt;
+    &lt;/div&gt;
+  &lt;/form&gt;
+&lt;/def&gt;</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid_plus.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,13 @@
 &lt;def tag=&quot;with-fields&quot; attrs=&quot;fields, associations, skip, skip-associations, include-timestamps, force-all&quot;&gt;&lt;%
+  fields.nil? || associations.nil? or raise ArgumentError, &quot;with-fields -- specify either fields or associations but not both&quot;
+   
   field_names = if associations == &quot;has_many&quot;
                   this.class.reflections.values.select { |refl| refl.macro == :has_many }.map { |refl| refl.name.to_s }
                                                                                                                 
                 elsif fields.nil? || fields == &quot;*&quot; || fields.is_a?(Class)
                   klass = fields.is_a?(Class) ? fields : this.class
-                  columns = klass.content_columns.every(:name)
-                  columns -= %w{created_at updated_at created_on updated_on} unless
-                    include_timestamps
+                  columns = klass.content_columns.*.name
+                  columns -= %w{created_at updated_at created_on updated_on} unless include_timestamps
 
                   if skip_associations == &quot;has_many&quot;
                     assocs = this.class.reflections.values.reject {|r| r.macro == :has_many }.map &amp;its.name.to_s
@@ -22,13 +23,19 @@
                 end
   field_names -= comma_split(skip) if skip
   field_names = field_names.select {|f| can_view?(this, f)} unless force_all
-  field_names.each do |field| %&gt;&lt;with field=&quot;&amp;field&quot;&gt;&lt;do param=&quot;default&quot;/&gt;&lt;/with&gt;&lt;% end
+  field_names.each do |field| 
+    if field == &quot;this&quot;
+      %&gt;&lt;do param=&quot;default&quot;/&gt;&lt;%
+    else
+      %&gt;&lt;with field=&quot;&amp;field&quot;&gt;&lt;do param=&quot;default&quot;/&gt;&lt;/with&gt;&lt;%
+    end
+  end
 %&gt;&lt;/def&gt;
 
 &lt;def tag=&quot;with-field-names&quot; attrs=&quot;fields, skip, skip-associations, include-timestamps&quot;&gt;&lt;%=
   field_names = if fields.nil? || fields == &quot;*&quot; || fields.is_a?(Class)
-                  klass = fields.is_a?(Class) ? fields : this.proxy_reflectin.class
-                  columns = klass.content_columns.every(:name)
+                  klass = fields.is_a?(Class) ? fields : this.member_class
+                  columns = klass.content_columns.*.name
                   columns -= %w{created_at updated_at created_on updated_on} unless include_timestamps
 
                   if skip_associations == &quot;has_many&quot;
@@ -47,7 +54,7 @@
   field_names -= comma_split(skip) if skip
   scope.new_scope do
     field_names.map do |n|
-      scope.field_name = n.to_s.gsub(&quot;.&quot; , &quot;_&quot;)
+      scope.field_name = n == &quot;this&quot; ? this.member_class.name : n.to_s.gsub(&quot;.&quot; , &quot;_&quot;) 
       scope.field_path = n
       parameters.default
     end</diff>
      <filename>vendor/plugins/hobo/taglibs/rapid_support.dryml</filename>
    </modified>
    <modified>
      <diff>@@ -100,7 +100,7 @@ def fix_file(filename)
   # happy
   @src = &quot;&lt;root&gt;&quot; + src + &quot;&lt;/root&gt;&quot;
   begin
-    doc = REXML::Document.new(Hobo::Dryml::RexSource.new(@src), :dryml_mode =&gt; true)
+    doc = Hobo::Dryml::Parser::Document.new(Hobo::Dryml::Parser::Source.new(@src))
   rescue REXML::ParseException =&gt; e
     raise Exception, &quot;File: #{@template_path}\n#{e}&quot;
   end</diff>
      <filename>vendor/plugins/hobo/tasks/fix_dryml.rake</filename>
    </modified>
    <modified>
      <diff>@@ -0,0 +1,17 @@
+namespace :hobo do
+
+  desc &quot;Replace commonly used hobo assets with symlinks into the plugin so that they stay up to date&quot;
+  task :symlink_assets do
+    
+    Dir.chdir(RAILS_ROOT) do
+      Dir.chdir(&quot;public/javascripts&quot;) do 
+        puts &quot;hobo-rapid.js&quot;
+        `rm -f hobo-rapid.js`
+        `ln -s ../../vendor/plugins/hobo/generators/hobo_rapid/templates/hobo-rapid.js`
+      end
+    end
+    
+  end
+  
+end
+  </diff>
      <filename>vendor/plugins/hobo/tasks/hobo_tasks.rake</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>app/views/taglibs/themes/clean/application.dryml</filename>
    </removed>
    <removed>
      <filename>public/hobothemes/clean/stylesheets/hobo.css</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/CHANGELOG</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/README</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/Rakefile</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/init.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/install.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/lib/pagination.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/lib/pagination_helper.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/companies.yml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/company.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/developer.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/developers.yml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/developers_projects.yml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/project.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/projects.yml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/replies.yml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/reply.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/schema.sql</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/topic.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/fixtures/topics.yml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/helper.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/pagination_helper_test.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/classic_pagination/test/pagination_test.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/generators/hobo_migration/hobo_migration_generator.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/generators/hobo_migration/templates/migration.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/generators/hobo_rapid/templates/themes/clean/views/application.dryml</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/active_record/table_definition.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/extensions.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/email_address.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/enum_string.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/field_declaration_dsl.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/field_spec.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/html_string.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/lazy_hash.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/markdown_string.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/migrations.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/model_queries.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/password_string.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/percentage.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/predicate_dispatch.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/proc_binding.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/text.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/textile_string.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/hobo/where_fragment.rb</filename>
    </removed>
    <removed>
      <filename>vendor/plugins/hobo/lib/rexml.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>1d074b1ad8d343b703d42925c28999bc6192eef2</id>
    </parent>
  </parents>
  <author>
    <name>Blake Barnett</name>
    <email>bdb@bdb-debvm1.stanford.edu</email>
  </author>
  <url>http://github.com/shadoi/puppetshow/commit/aa14be64d4ede0cc1c531d1bebf1a2f20da71e3d</url>
  <id>aa14be64d4ede0cc1c531d1bebf1a2f20da71e3d</id>
  <committed-date>2008-05-01T18:20:00-07:00</committed-date>
  <authored-date>2008-05-01T18:20:00-07:00</authored-date>
  <message>Yeah.. so I didn't actually commit all the hobo 0.7.5 changes before, this should do it.</message>
  <tree>3b4e1516d4457ce663091d29ee9c5672be360e0f</tree>
  <committer>
    <name>Blake Barnett</name>
    <email>bdb@bdb-debvm1.stanford.edu</email>
  </committer>
</commit>
