<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -3,5 +3,6 @@
 ./log/*
 ./tmp/*
 ./doc/app/*
+./coverage
 ./public/entries/*
 ./themes/*</diff>
      <filename>.bzrignore</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,4 @@
 class AccountController &lt; ApplicationController
-  model   :user
 
   # Displays the login page and logs the user in if the request is a HTTP POST.
   def login</diff>
      <filename>app/controllers/account_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,7 +17,7 @@ class Admin::BaseController &lt; ApplicationController
   def flash_errors(object)
     if object.errors.size != 0
       object.errors.each do |attr, msg|
-        @flash[:notice] &lt;&lt; &quot;\n&lt;br /&gt;&quot; + msg
+        flash[:notice] &lt;&lt; &quot;\n&lt;br /&gt;&quot; + msg
       end
     end
   end</diff>
      <filename>app/controllers/admin/base_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,8 @@ class Admin::CommentsController &lt; Admin::BaseController
   # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
   verify :method =&gt; :get, :redirect_to =&gt; {:action =&gt; :index},
          :except =&gt; [:save, :mark, :delete],
-         :add_flash =&gt; {:notice =&gt; &quot;The page was requested with a POST request, while GET is prefered.&quot;}
+         :add_flash =&gt; {:notice =&gt; &quot;The page was requested with a POST &quot; +
+                                   &quot;request, while GET is prefered.&quot;}
 
   # Redirect to the manage page.
   def index
@@ -19,7 +20,8 @@ class Admin::CommentsController &lt; Admin::BaseController
         :conditions =&gt; &quot;approved = 0&quot;, :order =&gt; &quot;date DESC&quot;
     else
       params[:view] = &quot;approved&quot;
-      @spam_count = Comment.count_by_sql(&quot;SELECT COUNT(*) FROM comments WHERE approved = 0&quot;)
+      @spam_count = Comment.count_by_sql(&quot;SELECT COUNT(*) FROM comments &quot; +
+                                         &quot;WHERE approved = 0&quot;)
       @comments_pages, @comments = paginate :comments, :per_page =&gt; 10,
         :conditions =&gt; &quot;approved = 1&quot;, :order =&gt; &quot;date DESC&quot;
     end
@@ -27,33 +29,42 @@ class Admin::CommentsController &lt; Admin::BaseController
 
   # Edit a comment.
   def edit
-    if @params[:id]
-      @comment = Comment.find(@params[:id])
+    begin
+      if params[:id]
+        @comment = Comment.find(params[:id])
+      else
+        flash[:notice] = &quot;The comment could not be found.&quot;
+        redirect_to :action =&gt; &quot;manage&quot;
+      end
+    rescue ActiveRecord::RecordNotFound
+      flash[:notice] = &quot;The comment could not be found.&quot;
+      redirect_to :action =&gt; &quot;manage&quot;
     end
   end
 
   # Save an edited comment.
   def save
     begin
-      if @params[:comment][:id]
-        @comment = Comment.find(@params[:comment][:id])
-        author = html_escape @comment.author
-        entry = html_escape @comment.entry.title
-        if @comment.update_attributes(@params[:comment])
-          @flash[:notice] = &quot;The comment by &quot; + author + &quot; on '&quot; + entry + &quot;' was edited.&quot;
+      if params[:comment] and params[:comment][:id]
+        @comment = Comment.find(params[:comment][:id])
+        if @comment.update_attributes(params[:comment])
+          flash[:notice] = &quot;The comment by #{h(@comment.author)} on &quot; +
+                           &quot;'#{h(@comment.entry.title)}' was edited.&quot;
           view = if @comment.approved then &quot;approved&quot; else &quot;spam&quot; end
           redirect_to :action =&gt; &quot;manage&quot;, :view =&gt; view
         else
-          @flash[:notice] = &quot;The comment could not be edited.&quot;
+          flash[:notice] = &quot;The comment could not be edited.&quot;
           flash_errors @comment
           redirect_to :back
         end
       else
-        @flash[:notice] = &quot;The comment could not be found and as a result was not edited.&quot;
+        flash[:notice] = &quot;The comment could not be found and as a result was &quot; +
+                         &quot;not edited.&quot;
         redirect_to :back
       end
     rescue ActiveRecord::RecordNotFound
-      @flash[:notice] = &quot;The comment could not be found and as a result was not edited.&quot;
+      flash[:notice] = &quot;The comment could not be found and as a result was &quot; +
+                       &quot;not edited.&quot;
       redirect_to :back
     end
   end
@@ -61,67 +72,73 @@ class Admin::CommentsController &lt; Admin::BaseController
   # Mark a comment as ham/spam.
   def mark
     begin
-      if @params[:id]
-        @comment = Comment.find(@params[:id])
-        author = html_escape @comment.author
-        entry = html_escape @comment.entry.title
-        if @params[:mark] == &quot;ham&quot;
+      if params[:id]
+        @comment = Comment.find(params[:id])
+        author = h(@comment.author)
+        entry = h(@comment.entry.title)
+        if params[:mark] == &quot;ham&quot;
           @comment.approved = true
           @comment.save()
-          @flash[:notice] = &quot;The comment by &quot; + author + &quot; on '&quot; + entry + &quot;' was marked as approved.&quot;
+          flash[:notice] = &quot;The comment by #{author} on '#{entry}' was &quot; +
+                           &quot;marked as approved.&quot;
           redirect_to :action =&gt; &quot;manage&quot;, :view =&gt; &quot;approved&quot;
-        elsif @params[:mark] == &quot;spam&quot;
+        elsif params[:mark] == &quot;spam&quot;
           @comment.approved = false
           @comment.save()
-          @flash[:notice] = &quot;The comment by &quot; + author + &quot; on '&quot; + entry + &quot;' was marked as spam.&quot;
+          flash[:notice] = &quot;The comment by #{author} on '#{entry}' was &quot; +
+                           &quot;marked as spam.&quot;
           redirect_to :action =&gt; &quot;manage&quot;, :view =&gt; &quot;spam&quot;
         else
-          @flash[:notice] = &quot;An error occured while changing the \&quot;approved\&quot; state of a comment. Nothing was changed.&quot;
+          flash[:notice] = 'An error occured while changing the &quot;approved&quot; ' +
+                           'state of a comment. Nothing was changed.'
           redirect_to :back
         end
       else
-        @flash[:notice] = &quot;The comment could not be found and as a result was not updated.&quot;
+        flash[:notice] = &quot;The comment could not be found and as a result was &quot; +
+                         &quot;not updated.&quot;
         redirect_to :back
       end
     rescue ActiveRecord::RecordNotFound
-      @flash[:notice] = &quot;The comment could not be found and as a result was not updated.&quot;
+      flash[:notice] = &quot;The comment could not be found and as a result was &quot; +
+                       &quot;not updated.&quot;
       redirect_to :back
     end
   end
 
   # Delete a comment.
   def delete
-    begin
-      if @params[:id] == &quot;spam&quot;
-        if Comment.delete_all &quot;approved = 0&quot;
-          @flash[:notice] = &quot;All spam comments were deleted.&quot;
-          redirect_to :action =&gt; &quot;manage&quot;
-          return
-        else
-          @flash[:notice] = &quot;The comments could not be deleted.&quot;
-          redirect_to :back
-          return
-        end
+    if params[:id] == &quot;spam&quot;
+      if Comment.delete_all(&quot;approved = 0&quot;)
+        flash[:notice] = &quot;All spam comments were deleted.&quot;
+        redirect_to :action =&gt; &quot;manage&quot;
+        return
+      else
+        flash[:notice] = &quot;The spam comments could not be deleted.&quot;
+        redirect_to :back
+        return
       end
+    end
 
-      if @params[:id]
-        @comment = Comment.find(@params[:id])
-        author = html_escape @comment.author
-        entry = html_escape @comment.entry.title
+    begin
+      if params[:id]
+        @comment = Comment.find(params[:id])
         if @comment.destroy
-          @flash[:notice] = &quot;The comment by &quot; + author + &quot; on '&quot; + entry + &quot;' was deleted.&quot;
+          flash[:notice] = &quot;The comment by #{h(@comment.author)} on &quot; +
+                           &quot;'#{h(@comment.entry.title)}' was deleted.&quot;
           redirect_to :action =&gt; &quot;manage&quot;
         else
-          @flash[:notice] = &quot;The comment could not be deleted.&quot;
+          flash[:notice] = &quot;The comment could not be deleted.&quot;
           flash_errors @comment
           redirect_to :back
         end
       else
-        @flash[:notice] = &quot;The comment could not be found and as a result was not deleted.&quot;
+        flash[:notice] = &quot;The comment could not be found and as a result was &quot; +
+                         &quot;not deleted.&quot;
         redirect_to :back
       end
     rescue ActiveRecord::RecordNotFound
-      @flash[:notice] = &quot;The comment could not be found and as a result was not deleted.&quot;
+      flash[:notice] = &quot;The comment could not be found and as a result was &quot; +
+                       &quot;not deleted.&quot;
       redirect_to :back
     end
   end</diff>
      <filename>app/controllers/admin/comments_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,11 +20,10 @@ class Admin::DashboardController &lt; Admin::BaseController
     @total_entries_published = Entry.count(:conditions =&gt; &quot;state = 'P'&quot;)
     @total_entries_draft = Entry.count(:conditions =&gt; &quot;state = 'D'&quot;)
     #@total_entries_tobepublished = Entry.count(:conditions =&gt; &quot;state = 'W'&quot;)
-    @most_active_commenter = Comment.find(:first, {:select =&gt; &quot;author, website&quot;,
-      :order =&gt; &quot;COUNT(author) DESC&quot;, :conditions =&gt; &quot;approved = 1&quot;})
-    @most_used_tags = Tag.find_by_sql &quot;SELECT name, COUNT(*) FROM tags &quot; +
-      &quot;JOIN entries_tags ON tags.id = entries_tags.tag_id GROUP BY name &quot; +
-      &quot;ORDER BY COUNT(*) DESC LIMIT 5&quot;
+    @most_active_commenter = Comment.find_by_sql(&quot;SELECT author, website, &quot; +
+      &quot;COUNT(author) as count FROM comments WHERE approved = 1 &quot; +
+      &quot;GROUP BY author ORDER BY count DESC LIMIT 1&quot;)[0]
+    @popular_tags = Tag.popular_tags(5)
   end
 
 end</diff>
      <filename>app/controllers/admin/dashboard_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,8 @@ class Admin::EntriesController &lt; Admin::BaseController
   # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
   verify :method =&gt; :get, :redirect_to =&gt; {:action =&gt; :index},
          :except =&gt; [:save, :delete, :preview, :autosave],
-         :add_flash =&gt; {:notice =&gt; &quot;The page was requested with a POST request, while GET is prefered.&quot;}
+         :add_flash =&gt; {:notice =&gt; &quot;The page was requested with a POST &quot; +
+                                   &quot;request, while GET is prefered.&quot;}
 
   # Redirect to the manage page.
   def index
@@ -19,8 +20,8 @@ class Admin::EntriesController &lt; Admin::BaseController
     @entry = find_entry(false)
     # Else, try the autosave entry (if it doesn't exist it will be nil).
     @entry = session[:auto_save] if @entry == false
-    @authors = User.find_all.collect {|u| [u.nickname, u.id]}
-    @types = Type.find_all
+    @authors = User.find(:all).collect {|u| [u.nickname, u.id]}
+    @types = Type.find(:all)
     @plugins = [
       {&quot;id&quot; =&gt; 1, &quot;key&quot; =&gt; &quot;textcontrol&quot;, &quot;name&quot; =&gt; &quot;Text Control&quot;},
       {&quot;id&quot; =&gt; 2, &quot;key&quot; =&gt; &quot;commentcontrol&quot;, &quot;name&quot; =&gt; &quot;Comment Control&quot;},
@@ -41,41 +42,46 @@ class Admin::EntriesController &lt; Admin::BaseController
     # public/entries/entry_id.ext and entry_id.description.ext respectively.
     # Therefore, we however first need the ID, so we'll first create the entry
     # without description and/or content and add these later.
-    if @params[:entry][:description].kind_of? Tempfile or @params[:entry][:description].kind_of? StringIO
-      entry_description = @params[:entry][:description]
-      if entry_description.size == 0 and !@params[:entry][:id].empty? # We're editing and haven't uploaded a new file
-        @params[:entry][:description] = Entry.find(@params[:entry][:id]).description
+    if params[:entry][:description].is_a?(Tempfile) or
+       params[:entry][:description].is_a?(StringIO)
+      entry_description = params[:entry][:description]
+      if entry_description.size == 0 and !params[:entry][:id].empty?
+        # We're editing and haven't uploaded a new file
+        params[:entry][:description] = Entry.find(params[:entry][:id]).description
       else
-        @params[:entry][:description] = &quot;&quot;
+        params[:entry][:description] = &quot;&quot;
       end
     end
-    if @params[:entry][:contents].kind_of? Tempfile or @params[:entry][:contents].kind_of? StringIO
-      entry_contents = @params[:entry][:contents]
-      if entry_contents.size == 0 and !@params[:entry][:id].empty?    # We're editing and haven't uploaded a new file
-        @params[:entry][:contents] = Entry.find(@params[:entry][:id]).contents
+
+    if params[:entry][:contents].is_a?(Tempfile) or
+       params[:entry][:contents].is_a?(StringIO)
+      entry_contents = params[:entry][:contents]
+      if entry_contents.size == 0 and !params[:entry][:id].empty?
+        # We're editing and haven't uploaded a new file
+        params[:entry][:contents] = Entry.find(params[:entry][:id]).contents
       else
-        @params[:entry][:contents] = &quot;&quot;
+        params[:entry][:contents] = &quot;&quot;
       end
     end
 
     # Save the tags to a temporary location
-    tags = @params[:entry][:tags]
-    @params[:entry].delete(&quot;tags&quot;)
+    tags = params[:entry][:tags]
+    params[:entry].delete(&quot;tags&quot;)
 
     # If the entry was submitted using the &quot;Publish&quot; button, change the state
     # to published.
-    @params[:entry][:state] = &quot;P&quot; if @params[:submit] == &quot;entry_submit_publish&quot;
+    params[:entry][:state] = &quot;P&quot; if params[:submit] == &quot;entry_submit_publish&quot;
 
     # Create/Save the entry
     if !(@entry = find_entry(false))
-      @entry = Entry.new(@params[:entry])
+      @entry = Entry.new(params[:entry])
       if @entry.save
         success = true
       else
         success = false
       end
     else
-      if @entry.update_attributes(@params[:entry])
+      if @entry.update_attributes(params[:entry])
         success = true
       else
         success = false
@@ -83,7 +89,7 @@ class Admin::EntriesController &lt; Admin::BaseController
     end
 
     if success == false
-      @flash[:notice] = &quot;An error occurred while saving the post, please try &quot; +
+      flash[:notice] = &quot;An error occurred while saving the post, please try &quot; +
       &quot;again.&quot;
       flash_errors @entry
       redirect_to :action =&gt; &quot;write&quot;, :slug =&gt; @entry.slug
@@ -93,22 +99,24 @@ class Admin::EntriesController &lt; Admin::BaseController
     # Save description and/or contents if they're a file.
     if entry_description and entry_description.size != 0
       path = &quot;entries/&quot; + @entry.id.to_s + &quot;.description&quot;
-      if entry_description.content_type and !entry_description.content_type.split(&quot;/&quot;)[1].empty?
+      if entry_description.content_type and
+         !entry_description.content_type.split(&quot;/&quot;)[1].empty?
         path += &quot;.&quot; + entry_description.content_type.split(&quot;/&quot;)[1].strip
       end
-      File.open(RAILS_ROOT + &quot;/public/&quot; + path, &quot;wb&quot;) { |file|
+      File.open(RAILS_ROOT + &quot;/public/&quot; + path, &quot;wb&quot;) do |file|
         file.write(entry_description.read())
-      }
-      @entry.contents = path
+      end
+      @entry.description = path
     end
     if entry_contents and entry_contents.size != 0
       path = &quot;entries/&quot; + @entry.id.to_s
-      if entry_contents.content_type and !entry_contents.content_type.split(&quot;/&quot;)[1].empty?
+      if entry_contents.content_type and
+         !entry_contents.content_type.split(&quot;/&quot;)[1].empty?
         path += &quot;.&quot; + entry_contents.content_type.split(&quot;/&quot;)[1].strip
       end
-      File.open(RAILS_ROOT + &quot;/public/&quot; + path, &quot;wb&quot;) { |file|
+      File.open(RAILS_ROOT + &quot;/public/&quot; + path, &quot;wb&quot;) do |file|
         file.write(entry_contents.read())
-      }
+      end
       @entry.contents = path
     end
     if entry_description or entry_contents
@@ -123,7 +131,7 @@ class Admin::EntriesController &lt; Admin::BaseController
     end
 
     if success == false
-      @flash[:notice] = &quot;An error occurred while saving the uploaded &quot; +
+      flash[:notice] = &quot;An error occurred while saving the uploaded &quot; +
       &quot;description and/or contents of the post. Either retry here, or use &quot; +
       &quot;your browser's 'Back' button to go back to the data you originally &quot; +
       &quot;entered (risking a duplicate entry).&quot;
@@ -143,7 +151,7 @@ class Admin::EntriesController &lt; Admin::BaseController
     end
 
     # Process meta fields
-    @params[:meta].each { |key, value|
+    params[:meta].each do |key, value|
       meta_field = @entry.metas.find_or_create_by_key(key)
 
       # If it's an uploaded file, first move it somewhere safe, at least if a
@@ -154,37 +162,38 @@ class Admin::EntriesController &lt; Admin::BaseController
           if value.content_type and !value.content_type.split(&quot;/&quot;)[1].empty?
             path += &quot;.&quot; + value.content_type.split(&quot;/&quot;)[1].strip
           end
-          File.open(RAILS_ROOT + &quot;/public/&quot; + path, &quot;wb&quot;) { |file|
+          File.open(RAILS_ROOT + &quot;/public/&quot; + path, &quot;wb&quot;) do |file|
             file.write(value.read)
-          }
+          end
           value = path
         else
-          value = meta_field.value # This is empty if the meta field doesn't exist.
+          value = meta_field.value # This is empty if the meta field doesn't
+                                   # exist.
         end
       end
 
       meta_field.value = value
       meta_field.entry_id = @entry.id
       meta_field.save()
-    }
+    end
 
     # Get ready to return
     case @entry.state
       when &quot;P&quot;
-        @flash[:notice] = &quot;The post '&quot; + html_escape(@entry.title) + &quot;' was published. &quot; +
-        &quot;&lt;a href='&quot; + url_for({:controller =&gt; &quot;/blog&quot;, :action =&gt; &quot;show&quot;, :slug =&gt; @entry.slug}) + &quot;'&gt;View it here.&lt;/a&gt;&quot;
+        flash[:notice] = &quot;The post '#{h(@entry.title)}' was published. &quot; +
+          &quot;&lt;a href='#{@entry.permalink}'&gt;View it here.&lt;/a&gt;&quot;
       when &quot;D&quot;
-        @flash[:notice] = &quot;The post '&quot; + html_escape(@entry.title) + &quot;' was saved.&quot;
+        flash[:notice] = &quot;The post '#{h(@entry.title)}' was saved.&quot;
       when &quot;W&quot;
-        @flash[:notice] = &quot;The post '&quot; + html_escape(@entry.title) + &quot;' was saved and quequed.&quot;
+        flash[:notice] = &quot;The post '#{h(@entry.title)}' was saved and quequed.&quot;
     end
 
-    case @params[:submit]
+    case params[:submit]
       when &quot;entry_submit_publish&quot;
-        redirect_to :controller =&gt; &quot;/blog&quot;, :action =&gt; &quot;show&quot;, :slug =&gt; @entry.slug
+        redirect_to @entry.permalink
       when &quot;entry_submit_save&quot;
         if @entry.state == &quot;P&quot;
-          redirect_to :controller =&gt; &quot;/blog&quot;, :action =&gt; &quot;show&quot;, :slug =&gt; @entry.slug
+          redirect_to @entry.permalink
         else
           redirect_to :action =&gt; &quot;manage&quot;
         end
@@ -216,10 +225,10 @@ class Admin::EntriesController &lt; Admin::BaseController
     if @entry = find_entry()
       title = html_escape @entry.title
       if @entry.destroy
-        @flash[:notice] = &quot;The post '&quot; + title + &quot;' was deleted.&quot;
+        flash[:notice] = &quot;The post '&quot; + title + &quot;' was deleted.&quot;
         redirect_to :action =&gt; &quot;manage&quot;
       else
-        @flash[:notice] = &quot;The post could not be deleted.&quot;
+        flash[:notice] = &quot;The post could not be deleted.&quot;
         flash_errors @entry
         redirect_to :back
       end
@@ -235,25 +244,25 @@ class Admin::EntriesController &lt; Admin::BaseController
   # &lt;tt&gt;flash[:notice]&lt;/tt&gt; if +flash+ is +true+ (default).
   def find_entry(flash = true)
     begin
-      if @params[:id]
-        entry = Entry.find(@params[:id])
-      elsif @params[:entry] and @params[:entry][:id]
-        entry = Entry.find(@params[:entry][:id])
-      elsif @params[:slug]
-        entry = Entry.find_by_slug(@params[:slug])
-      elsif @params[:entry] and @params[:entry][:slug]
-        entry = Entry.find_by_slug(@params[:entry][:slug])
+      if params[:id]
+        entry = Entry.find(params[:id])
+      elsif params[:entry] and params[:entry][:id]
+        entry = Entry.find(params[:entry][:id])
+      elsif params[:slug]
+        entry = Entry.find_by_slug(params[:slug])
+      elsif params[:entry] and params[:entry][:slug]
+        entry = Entry.find_by_slug(params[:entry][:slug])
       else
-        @flash[:notice] = &quot;No entry was found with the given ID or slug.&quot; if flash
+        flash[:notice] = &quot;No entry was found with the given ID or slug.&quot; if flash
         return false
       end
     rescue ActiveRecord::RecordNotFound
-      @flash[:notice] = &quot;No entry was found with the given ID or slug.&quot; if flash
+      flash[:notice] = &quot;No entry was found with the given ID or slug.&quot; if flash
       return false
     end
     if entry.nil? # ActiveRecord doesn't seem to always return an exception,
                   # so we need to double-check.
-      @flash[:notice] = &quot;No entry was found with the given ID or slug.&quot; if flash
+      flash[:notice] = &quot;No entry was found with the given ID or slug.&quot; if flash
       return false
     end
     return entry</diff>
      <filename>app/controllers/admin/entries_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,7 +12,11 @@ class Admin::SettingsController &lt; Admin::BaseController
 
   # View the settings.
   def settings
-    @section = Option.get_section(@params[:section])
+    @section = Option.get_section(params[:section]) if params[:section]
+    if !@section
+      flash[:notice] = &quot;The selected setting section does not exist.&quot;
+      redirect_to :back
+    end
   end
 
   # Save edited settings.
@@ -20,11 +24,11 @@ class Admin::SettingsController &lt; Admin::BaseController
     section_to = &quot;general&quot;
 
     # Run through all sections to see if there are settings provided for them in
-    # @params.
+    # params.
     Option.settings.each do |section|
-      if @params.keys.include? section[&quot;key&quot;]
+      if params[section[&quot;key&quot;]].kind_of?(Hash)
         section_to = section[&quot;key&quot;]
-        Option[section[&quot;key&quot;]] = @params[section[&quot;key&quot;]]
+        Option[section[&quot;key&quot;]] = params[section[&quot;key&quot;]]
       end
     end
 </diff>
      <filename>app/controllers/admin/settings_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,12 +20,17 @@ class Admin::ThemesController &lt; Admin::BaseController
   # Select another theme.
   def select
     begin
-      new_theme = Theme.find(@params[&quot;theme&quot;][&quot;id&quot;])
-      Option[&quot;theming_themeid&quot;] = @params[&quot;theme&quot;][&quot;id&quot;]
-      @flash[:notice] = &quot;You are now using the theme '#{new_theme.name}'.&quot;
-      redirect_to :action =&gt; &quot;manage&quot;
+      if params[&quot;theme&quot;] and params[&quot;theme&quot;][&quot;id&quot;]
+        new_theme = Theme.find(params[&quot;theme&quot;][&quot;id&quot;])
+        Option[&quot;theming_themeid&quot;] = params[&quot;theme&quot;][&quot;id&quot;]
+        flash[:notice] = &quot;You are now using the theme '#{new_theme.name}'.&quot;
+        redirect_to :action =&gt; &quot;manage&quot;
+      else
+        flash[:notice] = &quot;The selected theme does not exist.&quot;
+        redirect_to :back
+      end
     rescue ActiveRecord::RecordNotFound
-      @flash[:notice] = &quot;The selected theme does not exist.&quot;
+      flash[:notice] = &quot;The selected theme does not exist.&quot;
       redirect_to :back
     end
   end</diff>
      <filename>app/controllers/admin/themes_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,101 +12,110 @@ class Admin::UsersController &lt; Admin::BaseController
     redirect_to :action =&gt; &quot;manage&quot;
   end
 
+  # Show the manage users page.
+  def manage
+    @user_pages, @users = paginate :users, :per_page =&gt; 10
+  end
+
   # Show the new user page.
   def new
   end
 
   # Create a new user
   def create
-    unless @params[:newpassword1] == @params[:newpassword2]
-      @flash[:notice] = &quot;The user was not created, because the entered &quot; +
+    unless params[:newpassword1] == params[:newpassword2]
+      flash[:notice] = &quot;The user was not created, because the entered &quot; +
                         &quot;password did not match its confirmation.&quot;
       redirect_to :back
       return
     end
 
-    @params[:user][:password] = @params[:newpassword1]
-    @user = User.create(@params[:user])
+    params[:user][:password] = params[:newpassword1]
+    @user = User.create(params[:user])
     if @user.save
-      @flash[:notice] = &quot;The user #{@user.username} was created.&quot;
+      flash[:notice] = &quot;The user #{@user.username} was created.&quot;
       redirect_to :action =&gt; &quot;manage&quot;
     else
-      @flash[:notice] = &quot;The user could not be created.&quot;
+      flash[:notice] = &quot;The user could not be created.&quot;
       flash_errors @user
       redirect_to :back
     end
   end
 
-  # Show the manage users page.
-  def manage
-    @user_pages, @users = paginate :users, :per_page =&gt; 10
-  end
-
   # Edit a user.
   def edit
-    if @params[:id]
-      @user = User.find(@params[:id])
-      @latest_entries = Entry.find(:all,
-        :conditions =&gt; &quot;user_id = '#{@user.id}' AND state = 'P'&quot;,
-        :order =&gt; &quot;date_published DESC&quot;, :limit =&gt; 5)
+    begin
+      if params[:id]
+        @user = User.find(params[:id])
+        @latest_entries = @user.latest_entries
+      else
+        flash[:notice] = &quot;The user could not be found.&quot;
+        redirect_to :action =&gt; &quot;manage&quot;
+      end
+    rescue ActiveRecord::RecordNotFound
+      flash[:notice] = &quot;The user could not be found.&quot;
+      redirect_to :action =&gt; &quot;manage&quot;
     end
   end
 
   # Save an edited user.
   def save
     begin
-      if @params[:user][:id]
-        @user = User.find(@params[:user][:id])
+      if params[:user] and params[:user][:id]
+        @user = User.find(params[:user][:id])
 
         # First, update attributes (one by one, we can't use update_attributes()
-        # because not all attributes were included in the form.
-        @params[:user].each do |key, value|
+        # because not all attributes were included in the form). Skip username
+        # and password if they would be in here for some reason or another.
+        params[:user].delete(&quot;username&quot;)
+        params[:user].delete(&quot;password&quot;)
+        params[:user].each do |key, value|
           @user.send(key + &quot;=&quot;, value)
         end
         if @user.save
-          @flash[:notice] = &quot;The properties of the user  &quot; + @user.username +
-                            &quot;  were saved.&quot;
+          flash[:notice] = &quot;The settings for the user #{@user.username} were &quot; +
+                           &quot;saved.&quot;
         else
-          @flash[:notice] = &quot;The user could not be edited.&quot;
+          flash[:notice] = &quot;The user could not be edited.&quot;
           flash_errors @user
           redirect_to :back
           return
         end
-        if @params[:newpassword1].empty?
+        if params[:newpassword1].empty?
           redirect_to :action =&gt; &quot;manage&quot;
           return
         end
 
         # Then, change the password if a new password was requested.
-        unless User.authenticate(@user.username, @params[:currentpassword])
-          @flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password was not changed, because &quot; +
+        unless User.authenticate(@user.username, params[:currentpassword])
+          flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password was not changed, because &quot; +
                              &quot;the entered (current) password was not correct.&quot;
           redirect_to :back
           return
         end
-        unless @params[:newpassword1] == @params[:newpassword2]
-          @flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password was not changed, because &quot; +
+        unless params[:newpassword1] == params[:newpassword2]
+          flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password was not changed, because &quot; +
                              &quot;the new password did not match its confirmation.&quot;
           redirect_to :back
           return
         end
-        @user.password = @params[:newpassword1]
+        @user.password = params[:newpassword1]
         if @user.save
-          @flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password was changed.&quot;
+          flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password was changed.&quot;
           redirect_to :action =&gt; &quot;manage&quot;
           return
         else
-          @flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password, however, could not be updated.&quot;
+          flash[:notice] &lt;&lt; &quot;&lt;br /&gt;The password, however, could not be updated.&quot;
           flash_errors @user
           redirect_to :back
           return
         end
       else
-        @flash[:notice] = &quot;The user could not be found and as a result was not edited.&quot;
+        flash[:notice] = &quot;The user could not be found and as a result was not edited.&quot;
         redirect_to :back
       end
     rescue ActiveRecord::RecordNotFound
-      @flash[:notice] = &quot;The user could not be found and as a result was not edited.&quot;
+      flash[:notice] = &quot;The user could not be found and as a result was not edited.&quot;
       redirect_to :back
     end
   end</diff>
      <filename>app/controllers/admin/users_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 AUTHOR = &quot;JW (JW00000@gmail.com)&quot;                   if !defined? AUTHOR
-APP_VERSION = &quot;0.6.1.1&quot;                             if !defined? APP_VERSION
+APP_VERSION = &quot;Development version&quot;                 if !defined? APP_VERSION
 COPYRIGHT = &quot;Copyright (c) 2006, 2007 by JW&quot;        if !defined? COPYRIGHT
 LICENSE = &quot;MIT License&quot;                             if !defined? LICENSE
 
@@ -11,6 +11,5 @@ require_dependency &quot;chameleon&quot;
 class ApplicationController &lt; ActionController::Base
   include LoginSystem
   include ThemingSystem
-  model :user
 
 end
\ No newline at end of file</diff>
      <filename>app/controllers/application.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,15 @@ class BlogController &lt; ApplicationController
   set_theme Option[&quot;theming_themeid&quot;]
   layout &quot;blog&quot;
 
+  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
+  verify :method =&gt; :post, :only =&gt; [:comment, :trackback],
+         :redirect_to =&gt; {:action =&gt; :index},
+         :add_flash =&gt; {:notice =&gt; &quot;The page was requested with a GET &quot; +
+                                   &quot;request, while a POST request was expected.&quot;}
+
   # Show the homepage.
   def index
     home()
-    render :action =&gt; &quot;home&quot;
   end
 
   # Rendered when a page was not found.
@@ -34,6 +39,7 @@ class BlogController &lt; ApplicationController
     #  entry = Entry.find(e[&quot;id&quot;])
     #  @popular_posts &lt;&lt; entry if entry.state == &quot;P&quot;
     #end
+    render :action =&gt; &quot;home&quot;
   end
 
   # Show an entry.
@@ -54,7 +60,7 @@ class BlogController &lt; ApplicationController
       return
     end
 
-    if @entry.state != &quot;P&quot; and @session[&quot;user&quot;].nil?
+    if @entry.state != &quot;P&quot; and session[&quot;user&quot;].nil?
       notfound()
       return
     end
@@ -70,7 +76,7 @@ class BlogController &lt; ApplicationController
       entry_template = Liquid::Template.parse(template_file.read.to_s)
       @content_for_entry = entry_template.render(
         {&quot;entry&quot; =&gt; @entry, &quot;site&quot; =&gt; Liquid::SiteDrop.new(self),
-         &quot;option&quot; =&gt; Liquid::OptionDrop.new, &quot;user&quot; =&gt; @session[&quot;user&quot;]},
+         &quot;option&quot; =&gt; Liquid::OptionDrop.new, &quot;user&quot; =&gt; session[&quot;user&quot;]},
                             # FIXME: These variables are currently hard-coded from #load_liquid_variables.
         :filters =&gt; [self.master_helper_module],
         :registers =&gt; {:controller =&gt; self})
@@ -92,6 +98,7 @@ class BlogController &lt; ApplicationController
       :conditions =&gt; &quot;comment_type = 'T' AND approved = 1&quot;,
       :order =&gt; &quot;date&quot;)
 
+    render :action =&gt; &quot;show&quot;
   end
 
   # Receive a trackback.
@@ -153,9 +160,26 @@ class BlogController &lt; ApplicationController
 
   # Post a comment.
   def comment
+    if params[:comment].nil?
+      notfound()
+      return
+    end
+
+    begin
+      @entry = Entry.find(params[:comment][:entry_id])
+    rescue ActiveRecord::RecordNotFound
+      notfound()
+      return
+    end
+    if @entry.nil? # ActiveRecord doesn't seem to always return an exception,
+                   # so we need to double-check.
+      notfound()
+      return
+    end
+
     params[:comment][:date] = Time.now.strftime(&quot;%Y-%m-%dT%H:%M:%SZ&quot;)
     params[:comment][:ip] = request.remote_ip
-    params[:comment][:user_agent] = request.user_agent
+    params[:comment][:user_agent] = request.env['HTTP_USER_AGENT']
     params[:comment][:referer] = request.env['HTTP_REFERER']
     @comment = Comment.new(params[:comment])
     if @comment.save
@@ -169,7 +193,6 @@ class BlogController &lt; ApplicationController
       end
     else
       show()
-      render :action =&gt; &quot;show&quot;
       return
     end
   end
@@ -191,6 +214,7 @@ class BlogController &lt; ApplicationController
     @entries = @total_entries[@from, @entry_pages.items_per_page]
     @archive_type = &quot;month&quot;
     @time = Time.mktime(params[:year].to_i, params[:month].to_i)
+    render :action =&gt; &quot;archive&quot;
   end
 
   # Show a tag archive.
@@ -217,6 +241,7 @@ class BlogController &lt; ApplicationController
     end
     @entries = @total_entries[@from, @entry_pages.items_per_page]
     @archive_type = &quot;tag&quot;
+    render :action =&gt; &quot;tag&quot;
   end
 
   # Show a search query.
@@ -235,6 +260,7 @@ class BlogController &lt; ApplicationController
     end
     @entries = @total_entries[@from, @entry_pages.items_per_page]
     @archive_type = &quot;search&quot;
+    render :action =&gt; &quot;search&quot;
   end
 
   protected
@@ -243,7 +269,7 @@ class BlogController &lt; ApplicationController
   def load_liquid_variables
     @site = Liquid::SiteDrop.new(self)
     @option = Liquid::OptionDrop.new
-    @user = @session[&quot;user&quot;]
+    @user = session[&quot;user&quot;]
   end
 
 end</diff>
      <filename>app/controllers/blog_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,40 +2,21 @@ class JsonController &lt; ApplicationController
 
   # Return a Textile-formatted text in HTML.
   def textile
+    params[:callback] = &quot;json_reponse&quot; if params[:callback].nil? or params[:callback].empty?
     if params[:text].nil? or params[:text].empty?
-      render :text =&gt; prepare_json(params[:callback], &quot;&quot;)
+      render :json =&gt; '&quot;&quot;', :callback =&gt; params[:callback]
     else
       textilized = RedCloth.new(params[:text], [:hard_breaks, :filter_html]).to_html
-      render :text =&gt; prepare_json(params[:callback], textilized.to_json)
+      render :json =&gt; '&quot;' + textilized + '&quot;', :callback =&gt; params[:callback]
     end
   end
 
   # Return all tags.
   def gettags
+    params[:callback] = &quot;json_reponse&quot; if params[:callback].nil? or params[:callback].empty?
     tags = Tag.find(:all)
     tags.collect! { |tag| tag.name }
-    render :text =&gt; prepare_json(params[:callback], tags.to_json)
-  end
-
-  protected
-
-  # Prepare a JSON request with the given callback function name (defaults to
-  # params[:callback], next to json_reponse) and the parameters (converted using
-  # to_s).
-  def prepare_json(callback = &quot;&quot;, parameters = [])
-    if callback.nil? or callback.empty?
-      if params[:callback]
-        callback = params[:callback]
-      else
-        callback = &quot;json_reponse&quot;
-      end
-    end
-    if parameters.is_a? Array
-      parameters = parameters.join(&quot;, &quot;)
-    else
-      parameters = parameters.to_s
-    end
-    callback + &quot;(&quot; + parameters + &quot;);&quot;
+    render :json =&gt; tags.to_json, :callback =&gt; params[:callback]
   end
 
 end</diff>
      <filename>app/controllers/json_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,10 +11,8 @@ module Admin::BaseHelper
           args[0] = {} unless args[0]
           args[0][:checked] = &quot;checked&quot; if @entry.metas.find_by_key(name).value == &quot;1&quot;
         when &quot;radio_button&quot;
-          args_given = args
-          args = []
-          args[0] = @entry.metas.find_by_key(name).value
-          args[1] = args_given
+          args[1] = {} unless args[1]
+          args[1][:checked] = &quot;checked&quot; if @entry.metas.find_by_key(name).value == args[0]
         when &quot;select&quot;
           args[1] = {} unless args[1]
           args[1][:selected] = @entry.metas.find_by_key(name).value
@@ -26,7 +24,11 @@ module Admin::BaseHelper
     if respond_to? type
       send type, &quot;meta&quot;, name, *args
     else
-      options[:value]
+      if args[0] and args[0][:value]
+        args[0][:value]
+      else
+        &quot;&quot;
+      end
     end
   end
 </diff>
      <filename>app/helpers/admin/base_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,5 @@
+require 'erb'
+
 module Admin::SettingsHelper
 
   # Display the option, with label, input field and optional tip.
@@ -12,7 +14,7 @@ module Admin::SettingsHelper
     r &lt;&lt; &quot;  &lt;label for='#{section[&quot;key&quot;]}_#{option[&quot;key&quot;]}'&gt;#{option[&quot;name&quot;]}&lt;/label&gt;\n&quot;
     r &lt;&lt; &quot;  &quot; + option_field({:section =&gt; section[&quot;key&quot;], :key =&gt; option[&quot;key&quot;]},
                              option[&quot;type&quot;], option[&quot;value&quot;], html_options) + &quot;\n&quot;
-    r &lt;&lt; &quot;  &lt;p class='tip'&gt;#{render :inline =&gt; option[&quot;description&quot;]}&lt;/p&gt;\n&quot;
+    r &lt;&lt; &quot;  &lt;p class='tip'&gt;#{ERB.new(option[&quot;description&quot;]).result()}&lt;/p&gt;\n&quot;
     r &lt;&lt; &quot;&lt;/li&gt;\n&quot;
 
     return r
@@ -28,6 +30,8 @@ module Admin::SettingsHelper
   # +style+:: A hash of other HTML attributes for this option. For example,
   #           &lt;tt&gt;{:class =&gt; &quot;whatever&quot;, :style =&gt; &quot;width: 1em;&quot;}&lt;/tt&gt; or
   #           &lt;tt&gt;{:cols =&gt; 14, :rows =&gt; 80}&lt;/tt&gt;
+  #
+  # FIXME: Large parts of this code look too hackish, and should be reworked.
   def option_field(key, type, value, options = {})
     html = &quot;&quot;
 
@@ -43,21 +47,29 @@ module Admin::SettingsHelper
       when &quot;radio&quot;
         options = options.update({:type =&gt; &quot;radio&quot;, :name =&gt; name})
         # [[1, &quot;One&quot;], [2, &quot;Two&quot;]]
-        options[&quot;possibilities&quot;].each do |key, possibility|
-          options = options.update({:id =&gt; id + &quot;_&quot; + key, :value =&gt; possibility[0].to_s})
-          options[:checked] = &quot;checked&quot; if possibility[0].to_s == value
+        possibilities = options[:possibilities]
+        options.delete(:possibilities)
+        possibilities.each do |key, possibility|
+          key = key.to_s
+          options = options.update({:id =&gt; id + &quot;_&quot; + key, :value =&gt; key})
+          if key == value
+            options[:checked] = &quot;checked&quot;
+          else
+            options.delete(:checked)
+          end
           html &lt;&lt; tag(:input, options)
-          html &lt;&lt; content_tag(:label, possibility[1], {:for =&gt; id})
+          html &lt;&lt; content_tag(:label, possibility, {:for =&gt; id + &quot;_&quot; + key})
         end
       when &quot;select&quot;
         options = options.update({:name =&gt; name, :id =&gt; id})
         # [[1, &quot;One&quot;], [2, &quot;Two&quot;]]
-        possibilities = options[&quot;possibilities&quot;].collect do |possibility|
-          possibility_options = {:value =&gt; possibility[0]}
-          possibility_options[:checked] = &quot;checked&quot; if possibility[0].to_s == value
-          content_tag(:option, possibility[1], possibility_options)
+        possibilities = options[:possibilities].collect do |key, possibility|
+          key = key.to_s
+          possibility_options = {:value =&gt; key}
+          possibility_options[:checked] = &quot;checked&quot; if key == value
+          content_tag(:option, possibility, possibility_options)
         end
-        options.delete(&quot;options&quot;)
+        options.delete(:possibilities)
         html &lt;&lt; content_tag(:select, possibilities.join(&quot;\n&quot;), options)
       when &quot;textarea&quot;
         options = options.update({:name =&gt; name, :id =&gt; id})</diff>
      <filename>app/helpers/admin/settings_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,26 +1,36 @@
-COMMENT_TYPE = {&quot;Reply&quot; =&gt; &quot;C&quot;, &quot;Pingback&quot; =&gt; &quot;P&quot;, &quot;Trackback&quot; =&gt; &quot;T&quot;} if !defined? COMMENT_TYPE
-
 class Comment &lt; ActiveRecord::Base
   belongs_to              :entry
 
-  validates_presence_of :author,
-                        :message =&gt; &quot;Please enter a name.&quot;
-  validates_presence_of :text,
-                        :message =&gt; &quot;Please enter a comment text.&quot;
-  validates_length_of   :author,  :within =&gt; 0..50,
-                        :message =&gt; &quot;Please enter a name shorter than 50 characters.&quot;
-  validates_length_of   :email,   :within =&gt; 0..50,
-                        :message =&gt; &quot;Please enter an e-mail address shorter than 50 characters, or none at all.&quot;
-  validates_length_of   :website, :within =&gt; 0..50,
-                        :message =&gt; &quot;Please enter a website shorter than 50 characters, or none at all.&quot;
-  validates_length_of   :text,    :within =&gt; 0..65536,
-                        :message =&gt; &quot;The comment has to be within 1 and 65536 characters of size.&lt;br /&gt;\n&quot; +
-                                    &quot;If your comment is too long, please don't post it, as most of its content will be redundant.&quot;
-  validates_format_of   :email,   :with =&gt; /^(([a-zA-Z0-9%_+-]+\.?)+@([a-zA-Z0-9%_+-]+\.)+[a-zA-Z0-9]{2,})?$/,
-                        :message =&gt; &quot;Please enter a valid e-mail address, or none at all.&quot;
-  validates_associated  :entry,
-                        :message =&gt; &quot;This comment is not connected to an entry. &quot; +
-                        &quot;Please reload the page and try again, and don't fool with the system!&quot;
+  validates_presence_of  :author,
+    :message =&gt; &quot;Please enter a name.&quot;
+  validates_presence_of  :text,
+    :message =&gt; &quot;Please enter a comment text.&quot;
+  validates_length_of    :author,  :within =&gt; 0..50,
+    :too_long =&gt; &quot;Please enter a name shorter than 50 characters.&quot;
+  validates_length_of    :email,   :within =&gt; 0..50,
+    :too_long =&gt; &quot;Please enter an e-mail address shorter than 50 characters, &quot; +
+                 &quot;or none at all.&quot;
+  validates_length_of    :website, :within =&gt; 0..50,
+    :too_long =&gt; &quot;Please enter a website shorter than 50 characters, or none &quot; +
+                 &quot;at all.&quot;
+  validates_length_of    :text,    :within =&gt; 0..65536,
+    :too_long =&gt; &quot;The comment has to be within 1 and 65536 characters of &quot; +
+                 &quot;size.&lt;br /&gt;\nIf your comment is too long, please don't &quot; +
+                 &quot;post it, as most of its content will be redundant.&quot;
+  validates_format_of    :email,
+    :with =&gt; /^([a-zA-Z0-9.%_+-]+@[a-zA-Z0-9.%_+-]+\.[a-zA-Z0-9]{2,})?$/,
+    :message =&gt; &quot;Please enter a valid e-mail address, or none at all.&quot;
+  validates_presence_of  :entry_id,
+    :message =&gt; &quot;This comment is not connected to an entry. Please reload &quot; +
+                &quot;the page and try again, and don't fool with the system!&quot;
+  validates_existence_of :entry,
+    :message =&gt; &quot;This comment is not connected to an existing entry. Please &quot; +
+                &quot;reload the page and try again, and don't fool with the system!&quot;
+  validates_associated   :entry,
+    :message =&gt; &quot;This comment is not connected to a valid entry. Please &quot; +
+                &quot;reload the page and try again, and don't fool with the system!&quot;
+
+  TYPES = {&quot;Reply&quot; =&gt; &quot;C&quot;, &quot;Pingback&quot; =&gt; &quot;P&quot;, &quot;Trackback&quot; =&gt; &quot;T&quot;}
 
   # Extends ActiveRecord's default initialize function to check the comment for
   # spam.
@@ -40,7 +50,7 @@ class Comment &lt; ActiveRecord::Base
   # Returns the full type of the comment, as a word (for example, &quot;reply&quot; or
   # &quot;pingback&quot;).
   def full_type
-    COMMENT_TYPE.index(self.comment_type).downcase
+    Comment::TYPES.index(self.comment_type).downcase
   end
 
   # Returns the text of the comment, as it is in the database (with the Textile</diff>
      <filename>app/models/comment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,126 +1,179 @@
+require &quot;time&quot;
 require_dependency &quot;search&quot;
 
-STATES = {&quot;Draft&quot; =&gt; &quot;D&quot;, &quot;Published&quot; =&gt; &quot;P&quot;}                     if !defined? STATES
-         # &quot;To be published&quot; =&gt; &quot;W&quot;
-         # TODO: Enable &quot;To be published&quot; state
-FORMATTING = {&quot;None&quot; =&gt; &quot;N&quot;, &quot;Textile&quot; =&gt; &quot;T&quot;, &quot;Markdown&quot; =&gt; &quot;M&quot;} if !defined? FORMATTING
-         # TODO: This will have to be moved into a plugin.
-
 class Entry &lt; ActiveRecord::Base
+  ## Relationships
   belongs_to              :user
   belongs_to              :type
   has_and_belongs_to_many :tags
-  has_many                :metas, :dependent =&gt; :destroy
+  has_many                :metas,    :dependent =&gt; :destroy
   has_many                :comments, :dependent =&gt; :destroy
+
+  ## For search
   searches_on             :title, :description, :contents
 
-  validates_presence_of :title,
-                        :message =&gt; &quot;Please enter a title.&quot;
-  validates_presence_of :slug,
-                        :message =&gt; &quot;Please enter a slug.&quot;
+  ## Constants
+  STATES = {&quot;Draft&quot; =&gt; &quot;D&quot;, &quot;Published&quot; =&gt; &quot;P&quot;}
+         # &quot;To be published&quot; =&gt; &quot;W&quot;
+         # TODO: Enable &quot;To be published&quot; state
+  FORMATTING = {&quot;None&quot; =&gt; &quot;N&quot;, &quot;Textile&quot; =&gt; &quot;T&quot;, &quot;Markdown&quot; =&gt; &quot;M&quot;}
+         # TODO: This will have to be moved into a plugin.
+
+  ## Validations
+  # Title
+  validates_presence_of   :title,
+    :message =&gt; &quot;Please enter a title.&quot;
+  validates_length_of     :title,  :within =&gt; 0..255,
+    :too_long =&gt; &quot;Please enter a title shorter than 255 characters.&quot;
+  # Slug
+  validates_presence_of   :slug,
+    :message =&gt; &quot;Please enter a slug.&quot;
+  validates_length_of     :slug,  :within =&gt; 0..255,
+    :too_long =&gt; &quot;Please enter a slug shorter than 255 characters.&quot;
+  validates_format_of     :slug,    :with =&gt; /^[a-zA-Z0-9+_-]*$/,
+    :message =&gt; &quot;Please enter a valid slug. The slug cannot contain any &quot; +
+                &quot;other characters than letters, numbers and dashes.&quot;
   validates_uniqueness_of :slug,
-                        :message =&gt; &quot;The same slug is already taken by another entry. Change it to something unique, by for example adding \&quot;-2\&quot; behind it.&quot;
-  validates_associated  :type,
-                        :message =&gt; &quot;The entry should have a type. Please select one.&quot;
-  validates_format_of   :slug,    :with =&gt; /^[a-zA-Z0-9+_-]*$/,
-                        :message =&gt; &quot;The slug cannot contain any other characters than letters, numbers and dashes.&quot;
+    :message =&gt; 'The same slug is already taken by another entry. Change it ' +
+                'to something unique, for example by appending &quot;-2&quot; to it.'
+  # Description
+  validates_length_of     :description, :within =&gt; 0..65535,
+    :too_long =&gt; &quot;The description of the entry has to be shorter than 65535 &quot; +
+                 &quot;characters.&quot;
+  # Contents
+  validates_length_of     :contents, :within =&gt; 0..65535,
+    :too_long =&gt; &quot;The contents of the entry have to be shorter than 65535 &quot; +
+                 &quot;bytes or characters.&quot;
+  # State
+  validates_inclusion_of  :state, :in =&gt; Entry::STATES.values,
+    :message =&gt; &quot;The state has to be either P (Published) or D (Draft).&quot;
+  # Date published: see #validate_date
+  # Relationships:
+  message = &quot;This entry is not connected to %s. Please reload the page and &quot; +
+            &quot;try again, and don't fool with the system!&quot;
+  # User
+  validates_presence_of   :user_id, :message =&gt; message % &quot;a user&quot;
+  validates_existence_of  :user,    :message =&gt; message % &quot;an existing user&quot;
+  validates_associated    :user,    :message =&gt; message % &quot;a valid user&quot;
+  # Type
+  validates_presence_of   :type_id, :message =&gt; message % &quot;a type&quot;
+  validates_existence_of  :type,    :message =&gt; message % &quot;an existing type&quot;
+  validates_associated    :type,    :message =&gt; message % &quot;a valid type&quot;
+
+
+  ## Functions definitions for extra attributes
 
   # Returns the permalink of an entry.
   def permalink
     Option[&quot;general_bloguri&quot;] + self.slug
   end
 
-  # Returns true if this entry is featured, false otherwise.
-  # An entry is featured if it has as meta field with key &quot;featured&quot; and value &quot;1&quot;.
-  # 
-  # TODO: Move this to the Featured Posts plugin.
-  def is_featured?
-    self.metas.find_by_key(&quot;featuredposts_featured&quot;) and self.metas.find_by_key(&quot;featuredposts_featured&quot;).value == 0
-  end
-
-  # Returns true if this entry is recent, false otherwise.
-  # An entry is recent if it has been published in the last 3 days.
-  # 
-  # TODO: Move this to a plugin.
-  def is_recent?
-    if self.date_published
-      self.date_published + (60 * 60 * 24 * 3) &gt; Time.now
+  # Returns the value of a meta field, or +false+ if the meta field was not
+  # found.
+  def meta(key)
+    if metas.find_by_key(key) and !metas.find_by_key(key).value.empty?
+      metas.find_by_key(key).value
     else
       false
     end
   end
 
-  # Returns true if this entry is popular, false otherwise.
-  # An entry is popular if it is among the 10 entries with the most comments.
-  # 
-  # TODO: Move this to a plugin.
-  def is_popular?
-    popular = false
-    popular_entries = Entry.popular_entries()
-    for entry in popular_entries
-      if entry[&quot;id&quot;] == self.id
-        popular = true
-      end
-    end
-    popular
-  end
-
-  # Returns the contents of the post, formatted according to the formatting meta
+  # Returns the description of the post, formatted according to the formatting meta
   # field and the given parameter.
   # 
   # There are three options for the formatting parameter:
-  # +none+:: The content is returned as it's stored in the database. (default)
-  # +text+:: The content is formatted as plain text. Use this to filter out
+  # +none+:: The description is returned as it's stored in the database. (default)
+  # +text+:: The description is formatted as plain text. Use this to filter out
   #          special formatting (like Textile/Markdown formatting). This can be
   #          used in in +alt+ tags for images, in feeds...
-  # +html+:: The content is converted to HTML.
+  # +html+:: The description is converted to HTML.
   # 
   # TODO: Use this in conjunction with real plugins.
-  def contents(formatting = &quot;none&quot;)
+  def description(formatting = &quot;none&quot;)
     case formatting
       when &quot;html&quot;
-        contents_html()
+        description_html()
       when &quot;text&quot;
-        contents_text()
+        description_text()
       else
-        contents_none()
+        description_none()
     end
   end
 
-  # Returns the description of the post, formatted according to the formatting meta
+  # Returns the contents of the post, formatted according to the formatting meta
   # field and the given parameter.
   # 
   # There are three options for the formatting parameter:
-  # +none+:: The description is returned as it's stored in the database. (default)
-  # +text+:: The description is formatted as plain text. Use this to filter out
+  # +none+:: The content is returned as it's stored in the database. (default)
+  # +text+:: The content is formatted as plain text. Use this to filter out
   #          special formatting (like Textile/Markdown formatting). This can be
   #          used in in +alt+ tags for images, in feeds...
-  # +html+:: The description is converted to HTML.
+  # +html+:: The content is converted to HTML.
   # 
   # TODO: Use this in conjunction with real plugins.
-  # FIXME: This needs to be tested more thoroughly, on all pages.
-  def description(formatting = &quot;none&quot;)
+  def contents(formatting = &quot;none&quot;)
     case formatting
       when &quot;html&quot;
-        description_html()
+        contents_html()
       when &quot;text&quot;
-        description_text()
+        contents_text()
       else
-        description_none()
+        contents_none()
     end
   end
 
-  # Returns the value of a meta field, or +false+ if the meta field was not
-  # found.
-  def meta(key)
-    if metas.find_by_key(key) and !metas.find_by_key(key).value.empty?
-      metas.find_by_key(key).value
-    else
-      false
+
+  ## More utility functions
+
+  # Returns an array of popular entries.
+  #
+  # Set the attribute +limit+ to limit the amount entries returned. Default is
+  # 10.
+  #
+  # This array is sorted on comment count (the entry with the most comments and
+  # thus the most popular entry at the top). It contains a set of hashes, with
+  # the following pairs:
+  # entry::         An Entry object representing the entry.
+  # comment_count:: The amount of comments on that entry.
+  #
+  # Please note that this function will also return non-published entries, if
+  # they have enough comments.
+  def self.popular_entries(limit = 10)
+    sql = ActiveRecord::Base.connection()
+    results = sql.select_all(&quot;SELECT entry_id, COUNT(*) AS count &quot; +
+      &quot;FROM comments WHERE approved = 1 GROUP BY entry_id &quot; +
+      &quot;ORDER BY COUNT(*) DESC LIMIT #{limit.to_i.to_s}&quot;)
+    popular_entries = []
+    results.each do |r|
+      popular_entries &lt;&lt; {&quot;entry&quot; =&gt; Entry.find_by_id(r[&quot;entry_id&quot;]),
+        &quot;comment_count&quot; =&gt; r[&quot;count&quot;].to_i}
     end
+    return popular_entries
   end
 
-  after_save :send_pings
+  # Returns an array of year/month combinations with the amount of entries in
+  # that month.
+  #
+  # This array is ordered chronologically, the first month is first in the
+  # array. It contains a set of hashes, with the following pairs:
+  # year::  The year of the archive.
+  # month:: The month of the archive.
+  # time::  A Time object for the month and year.
+  # count:: The amount of entries published that month.
+  def self.monthly_archives
+    sql = ActiveRecord::Base.connection()
+    results = sql.select_all(&quot;SELECT YEAR(date_published) AS year, &quot; +
+      &quot;MONTH(date_published) AS month, COUNT(*) AS count FROM entries &quot; +
+      &quot;WHERE state = 'P' GROUP BY YEAR(date_published), MONTH(date_published)&quot; +
+      &quot;ORDER BY date_published ASC&quot;)
+    monthly_archives = []
+    results.each do |r|
+      monthly_archives &lt;&lt; {&quot;year&quot; =&gt; r[&quot;year&quot;].to_i, &quot;month&quot; =&gt; r[&quot;month&quot;].to_i,
+        &quot;time&quot; =&gt; Time.mktime(r[&quot;year&quot;].to_i, r[&quot;month&quot;].to_i),
+        &quot;count&quot; =&gt; r[&quot;count&quot;].to_i}
+    end
+    return monthly_archives
+  end
 
   # Sends pings to the links in the document, if they haven't been pinged yet
   # and the entry is Published.
@@ -175,62 +228,37 @@ class Entry &lt; ActiveRecord::Base
     pinged_field.save()
   end
 
-  # Returns an array of popular entries.
-  #
-  # Set the attribute +limit+ to limit the amount entries returned. Default is
-  # 10.
-  #
-  # This array is sorted on comment count (the entry with the most comments and
-  # thus the most popular entry at the top). It contains a set of hashes, with
-  # the following pairs:
-  # entry::         An Entry object representing the entry.
-  # comment_count:: The amount of comments on that entry.
-  #
-  # Please note that this function will also return non-published entries, if
-  # they have enough comments.
-  def self.popular_entries(limit = 10)
-    # This returns an array of (Comment) objects, with two attributes: entry_id
-    # and count. For each entry_id, the count value represents the amount
-    # of comments on the entry. The array is ordered on the amount of comments,
-    # the highest value first. The executed SQL query is:
-    # SELECT entry_id, COUNT(*) AS count FROM comments WHERE approved = 1
-    # GROUP BY entry_id ORDER BY COUNT(*) DESC LIMIT 10
-    populars = Comment.find(:all, {:select =&gt; &quot;entry_id, COUNT(*)&quot;,
-      :conditions =&gt; &quot;approved = 1&quot;, :group =&gt; &quot;entry_id&quot;,
-      :order =&gt; &quot;COUNT(*) DESC&quot;, :limit =&gt; limit})
-    popular_entries = []
-    populars.each { |e|
-      popular_entries &lt;&lt; {&quot;entry&quot; =&gt; Entry.find_by_id(e.entry_id),
-        &quot;comment_count&quot; =&gt; e.attributes[&quot;count&quot;]}
-    }
-    return popular_entries
+  after_save :send_pings
+
+  # Returns true if this entry is featured, false otherwise.
+  # An entry is featured if it has as meta field with key &quot;featured&quot; and value &quot;1&quot;.
+  # 
+  # TODO: Move this to the Featured Posts plugin.
+  def is_featured?
+    meta(&quot;featuredposts_featured&quot;) == 1
   end
 
-  # Returns an array of year/month combinations with the amount of entries in
-  # that month.
-  #
-  # This array is ordered chronologically, the first month is first in the
-  # array. It contains a set of hashes, with the following pairs:
-  # year::  The year of the archive.
-  # month:: The month of the archive.
-  # time::  A Time object for the month and year.
-  # count:: The amount of entries published that month.
-  def self.monthly_archives
-    months = Entry.find_by_sql(&quot;SELECT YEAR(date_published), &quot; +
-      &quot;MONTH(date_published), COUNT(*) FROM entries WHERE state = 'P' &quot; +
-      &quot;GROUP BY YEAR(date_published), MONTH(date_published)&quot; +
-      &quot;ORDER BY date_published ASC&quot;)
-      # We represent this in an Entry object, but there's no reason for that. It
-      # just has to be an ActiveRecord::Base class, but not AR::Base itself.
-    monthly_archives = []
-    months.each do |m|
-      monthly_archives &lt;&lt; {&quot;year&quot; =&gt; m.attributes[&quot;YEAR(date_published)&quot;],
-        &quot;month&quot; =&gt; m.attributes[&quot;MONTH(date_published)&quot;],
-        &quot;time&quot; =&gt; Time.mktime(m.attributes[&quot;YEAR(date_published)&quot;].to_i,
-                              m.attributes[&quot;MONTH(date_published)&quot;].to_i),
-        &quot;count&quot; =&gt; m.attributes[&quot;COUNT(*)&quot;].to_i}
+  # Returns true if this entry is recent, false otherwise.
+  # An entry is recent if it has been published in the last 3 days.
+  # 
+  # TODO: Move this to a plugin.
+  def is_recent?
+    if self.date_published
+      self.date_published + (60 * 60 * 24 * 3) &gt; Time.now
+    else
+      false
     end
-    return monthly_archives
+  end
+
+  # Returns true if this entry is popular, false otherwise.
+  # An entry is popular if it is among the 10 entries with the most comments.
+  # 
+  # TODO: Move this to a plugin.
+  def is_popular?
+    for e in Entry.popular_entries()
+      return true if e[&quot;id&quot;] == self.id
+    end
+    return false
   end
 
   # Returns an EntryDrop for use in Liquid templates. An EntryDrop object is a
@@ -242,100 +270,115 @@ class Entry &lt; ActiveRecord::Base
     Liquid::EntryDrop.new(self, options)
   end
 
+
+  ## Protected functions
   protected
 
   # Returns the content formatted in HTML. See #contents.
   # 
   # TODO: Move this to the Text Control plugin.
   def contents_html
-    if self.metas and self.metas.find_by_key(&quot;formatting&quot;) and
-       self.metas.find_by_key(&quot;formatting&quot;).value
-      case self.metas.find_by_key(&quot;formatting&quot;).value
-        when &quot;T&quot;
-          return RedCloth.new(self.attributes[&quot;contents&quot;]).to_html
-        when &quot;M&quot;
-          return BlueCloth.new(self.attributes[&quot;contents&quot;]).to_html
-      end
+    case meta(&quot;formatting&quot;)
+      when &quot;T&quot;
+        RedCloth.new(read_attribute(&quot;contents&quot;)).to_html
+      when &quot;M&quot;
+        BlueCloth.new(read_attribute(&quot;contents&quot;)).to_html
+      else
+        read_attribute(&quot;contents&quot;)
     end
-    self.attributes[&quot;contents&quot;]
   end
 
   # Returns the content formatted as plain text. See #contents.
   # 
   # TODO: Move this to the Text Control plugin.
   def contents_text
-    if self.metas and self.metas.find_by_key(&quot;formatting&quot;) and
-       self.metas.find_by_key(&quot;formatting&quot;).value
-      case self.metas.find_by_key(&quot;formatting&quot;).value
-        when &quot;T&quot;
-          contents = RedCloth.new(self.attributes[&quot;contents&quot;])
-          contents = contents.to_html [:refs_textile, :block_textile_table,
-                    :block_textile_lists, :block_textile_prefix,
-                    :inline_textile_image, :inline_textile_link,
-                    :inline_textile_code, :inline_textile_span]
-          contents = contents.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
-          return contents
-        when &quot;M&quot;
-          contents = BlueCloth.new(self.attributes[&quot;contents&quot;]).to_html
-          contents = contents.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
-          return contents
-      end
+    case meta(&quot;formatting&quot;)
+      when &quot;T&quot;
+        contents = RedCloth.new(read_attribute(&quot;contents&quot;))
+        contents = contents.to_html([:refs_textile, :block_textile_table,
+          :block_textile_lists, :block_textile_prefix, :inline_textile_image,
+          :inline_textile_link, :inline_textile_code, :inline_textile_span])
+        contents = contents.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
+        return contents
+      when &quot;M&quot;
+        contents = BlueCloth.new(read_attribute(&quot;contents&quot;)).to_html
+        contents = contents.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
+        return contents
+      else
+        read_attribute(&quot;contents&quot;)
     end
-    self.attributes[&quot;contents&quot;]
   end
 
   # Returns the content as it's stored in the database. See #contents.
   # 
   # TODO: Move this to the Text Control plugin.
   def contents_none
-    self.attributes[&quot;contents&quot;]
+    read_attribute(&quot;contents&quot;)
   end
 
   # Returns the description formatted in HTML. See #description.
   # 
   # TODO: Move this to the Text Control plugin.
   def description_html
-    if self.metas and self.metas.find_by_key(&quot;formatting&quot;) and
-       self.metas.find_by_key(&quot;formatting&quot;).value
-      case self.metas.find_by_key(&quot;formatting&quot;).value
-        when &quot;T&quot;
-          return RedCloth.new(self.attributes[&quot;description&quot;]).to_html
-        when &quot;M&quot;
-          return BlueCloth.new(self.attributes[&quot;description&quot;]).to_html
-      end
+    case meta(&quot;formatting&quot;)
+      when &quot;T&quot;
+        RedCloth.new(read_attribute(&quot;description&quot;)).to_html
+      when &quot;M&quot;
+        BlueCloth.new(read_attribute(&quot;description&quot;)).to_html
+      else
+        read_attribute(&quot;description&quot;)
     end
-    self.attributes[&quot;description&quot;]
   end
 
   # Returns the description formatted as plain text. See #description.
   # 
   # TODO: Move this to the Text Control plugin.
   def description_text
-    if self.metas and self.metas.find_by_key(&quot;formatting&quot;) and
-       self.metas.find_by_key(&quot;formatting&quot;).value
-      case self.metas.find_by_key(&quot;formatting&quot;).value
-        when &quot;T&quot;
-          description = RedCloth.new(self.attributes[&quot;description&quot;])
-          description = description.to_html [:refs_textile, :block_textile_table,
-                    :block_textile_lists, :block_textile_prefix,
-                    :inline_textile_image, :inline_textile_link,
-                    :inline_textile_code, :inline_textile_span]
-          description = description.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
-          return description
-        when &quot;M&quot;
-          description = BlueCloth.new(self.attributes[&quot;description&quot;]).to_html
-          description = description.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
-          return description
-      end
+    case meta(&quot;formatting&quot;)
+      when &quot;T&quot;
+        description = RedCloth.new(read_attribute(&quot;description&quot;))
+        description = description.to_html([:refs_textile, :block_textile_table,
+          :block_textile_lists, :block_textile_prefix, :inline_textile_image,
+          :inline_textile_link, :inline_textile_code, :inline_textile_span])
+        description = description.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
+        return description
+      when &quot;M&quot;
+        description = BlueCloth.new(read_attribute(&quot;description&quot;)).to_html
+        description = description.gsub(/(&lt;[^&gt;]*&gt;)/, &quot;&quot;)
+        return description
+      else
+        read_attribute(&quot;description&quot;)
     end
-    self.attributes[&quot;description&quot;]
   end
 
   # Returns the description as it's stored in the database. See #description.
   # 
   # TODO: Move this to the Text Control plugin.
   def description_none
-    self.attributes[&quot;description&quot;]
+    read_attribute(&quot;description&quot;)
+  end
+
+
+  ## Extra validation functions
+
+  # Overwrite the validate method to validate the +date_published+ field.
+  def validate
+    validate_date()
+  end
+
+  # Validate the +date_published+ field: it can be empty, a valid Time object,
+  # or a string that can be parsed into a Time object (by the typecasting
+  # functions in the connection adapters).
+  def validate_date
+    # Nil and empty is OK, but only if it was subitted that way.
+    # (Typecasting Time's will change invalid times to nil, so it's only correct
+    # if it was already nil.)
+    return true if read_attribute_before_type_cast(&quot;date_published&quot;).nil? or
+                   read_attribute_before_type_cast(&quot;date_published&quot;) == &quot;&quot;
+    # A Time object is good too, or a string that can be parsed into a Time
+    # object by the typecasting.
+    return true if read_attribute(&quot;date_published&quot;).kind_of?(Time)
+    errors.add &quot;date_published&quot;, &quot;Please enter a correct date, or none at all.&quot;
   end
 
 end</diff>
      <filename>app/models/entry.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,7 +12,8 @@ class Tag &lt; ActiveRecord::Base
   # name::  The tag name
   # count:: The amount of entries with that tag.
   def self.popular_tags(limit = 10)
-    entries_tags = Tag.find_by_sql(
+    sql = ActiveRecord::Base.connection()
+    results = sql.select_all(
       &quot;SELECT tags.id, tags.name, count(entries_tags.entry_id) AS count &quot; +
       &quot;FROM tags &quot; +
       &quot;INNER JOIN entries_tags ON tags.id = entries_tags.tag_id &quot; +
@@ -20,19 +21,13 @@ class Tag &lt; ActiveRecord::Base
       &quot;WHERE entries.state = 'P' &quot; +
       &quot;GROUP BY tags.name &quot; +
       &quot;HAVING count &gt; 0 &quot; +
-      &quot;ORDER BY count DESC &quot; +
-      &quot;LIMIT #{limit}&quot;)
-      # We represent this in a Tag object, but there's no reason for that. It
-      # just has to be an ActiveRecord::Base class, but not AR::Base itself.
-      # This SQL is a slightly modified version of the one in Ultimate Tag
-      # Warrior (http://www.neato.co.nz/ultimate-tag-warrior), the tagging
-      # plugin for WordPress.
-    tags = []
-    entries_tags.each do |t|
-      tags &lt;&lt; {&quot;id&quot; =&gt; t.attributes[&quot;id&quot;], &quot;name&quot; =&gt; t.attributes[&quot;name&quot;],
-               &quot;count&quot; =&gt; t.attributes[&quot;count&quot;].to_i}
+      &quot;ORDER BY count DESC, tags.id ASC &quot; +
+      &quot;LIMIT #{limit.to_i.to_s}&quot;)
+    results.each do |r| # This makes it easier for anyone using this function.
+      r[&quot;id&quot;] = r[&quot;id&quot;].to_i
+      r[&quot;count&quot;] = r[&quot;count&quot;].to_i
     end
-    return tags
+    return results
   end
 
   # Parses a space-separated list of tags and returns an array of tags.
@@ -51,7 +46,7 @@ class Tag &lt; ActiveRecord::Base
     # then, get whatever's left
     tag_names.concat(list.split(/\s/))
 
-    # convert all names to lowerspace, and strip whitespace from the names
+    # convert all names to lowercase, and strip whitespace from the names
     tag_names = tag_names.map { |t| t.downcase.strip }
 
     # delete any blank tag names</diff>
      <filename>app/models/tag.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,26 +4,25 @@ class User &lt; ActiveRecord::Base
   has_many                :entries
 
   validates_presence_of     :username,
-                            :message =&gt; &quot;Please enter a username.&quot;
-  validates_presence_of     :password,
-                            :message =&gt; &quot;Please enter a password.&quot;
+    :message =&gt; &quot;Please enter a username.&quot;
   validates_presence_of     :nickname,
-                            :message =&gt; &quot;Please enter a nickname.&quot;
+    :message =&gt; &quot;Please enter a nickname.&quot;
   validates_length_of       :username, :within =&gt; 0..255,
-                            :message =&gt; &quot;Please enter a username smaller than 255 characters.&quot;
-  validates_length_of       :password, :within =&gt; 0..255,
-                            :message =&gt; &quot;Please enter a password smaller than 255 characters.&quot;
+    :too_long =&gt; &quot;Please enter a username shorter than 255 characters.&quot;
   validates_length_of       :nickname, :within =&gt; 0..255,
-                            :message =&gt; &quot;Please enter a nickname smaller than 255 characters.&quot;
+    :too_long =&gt; &quot;Please enter a nickname shorter than 255 characters.&quot;
   validates_length_of       :email,    :within =&gt; 0..255,
-                            :message =&gt; &quot;Please enter an e-mail smaller than 255 characters, or none at all.&quot;
+    :too_long =&gt; &quot;Please enter an e-mail address shorter than 255 &quot; +
+                 &quot;characters, or none at all.&quot;
   validates_length_of       :website,  :within =&gt; 0..255,
-                            :message =&gt; &quot;Please enter a website smaller than 255 characters, or none at all.&quot;
-  validates_format_of       :email,    :with =&gt; /^(([a-zA-Z0-9%_+-]+\.?)+@([a-zA-Z0-9%_+-]+\.)+[a-zA-Z0-9]{2,})?$/,
-                            :message =&gt; &quot;Please enter a valid e-mail address, or none at all.&quot;
+    :too_long =&gt; &quot;Please enter a website shorter than 255 characters, or &quot; +
+                 &quot;none at all.&quot;
+  validates_format_of       :email,
+    :with =&gt; /^([a-zA-Z0-9.%_+-]+@[a-zA-Z0-9.%_+-]+\.[a-zA-Z0-9]{2,})?$/,
+    :message =&gt; &quot;Please enter a valid e-mail address, or none at all.&quot;
   validates_uniqueness_of   :username, :on =&gt; :create,
-                            :message =&gt; &quot;A user with the same username already exists. &quot; +
-                            &quot;Please try a different username.&quot;
+    :message =&gt; &quot;A user with the same username already exists. &quot; +
+                &quot;Please try a different username.&quot;
 
   # The salt is used to enhance security. Change it to any random value, as long
   # as it only contains numbers and the letters a-f and consists of exactly 50
@@ -32,9 +31,9 @@ class User &lt; ActiveRecord::Base
   @@salt = '150c4e29f02c1267d78ad1a73e1174fb9c85ea9d'
   cattr_accessor :salt
 
-  # Authenticate a user. (Returns the corresponding User object.)
+  # Authenticate a user. Returns the corresponding User object, nil otherwise.
   def self.authenticate(username, password)
-    find_first([&quot;username = ? AND password = ?&quot;, username, sha1(password)])
+    find(:first, :conditions =&gt; {:username =&gt; username, :password =&gt; sha1(password)})
   end
 
   # Change the password.
@@ -42,6 +41,13 @@ class User &lt; ActiveRecord::Base
     write_attribute &quot;password&quot;, self.class.sha1(pass)
   end
 
+  # Returns an array with the latest entries published by this user. Set the
+  # attribute +limit+ to limit the amount entries returned. Default is 5.
+  def latest_entries(limit = 5)
+    Entry.find(:all, :conditions =&gt; [&quot;user_id = ? AND state = 'P'&quot;, id],
+               :order =&gt; &quot;date_published DESC&quot;, :limit =&gt; limit)
+  end
+
   # Returns an UserDrop for use in Liquid templates. An UserDrop object is a
   # wrapper around this object, it makes sure all input is validated before it
   # is presented.</diff>
      <filename>app/models/user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,7 +15,7 @@
 &lt;p id=&quot;notice&quot;&gt;&lt;%= flash[:notice] %&gt;&lt;/p&gt;
 &lt;%- end -%&gt;
 
-&lt;%= start_form_tag :action =&gt; &quot;login&quot; %&gt;
+&lt;% form_tag({:action =&gt; &quot;login&quot;}) do %&gt;
 
 &lt;p&gt;&lt;label for=&quot;username&quot;&gt;&lt;span class=&quot;accesskey&quot;&gt;U&lt;/span&gt;sername:&lt;/label&gt;&lt;br/&gt;
 &lt;input type=&quot;text&quot; name=&quot;username&quot; id=&quot;login_username&quot; accesskey=&quot;u&quot; /&gt;&lt;/p&gt;
@@ -25,7 +25,7 @@
 
 &lt;p&gt;&lt;input type=&quot;submit&quot; name=&quot;login&quot; id=&quot;login_submit&quot; value=&quot;Login&quot; accesskey=&quot;l&quot; /&gt;&lt;/p&gt;
 
-&lt;%= end_form_tag %&gt;
+&lt;% end %&gt;
 
 &lt;/div&gt;
 </diff>
      <filename>app/views/account/login.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@
 
 &lt;h2&gt;Edit comment&lt;/h2&gt;
 
-&lt;%= form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) %&gt;
+&lt;% form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) do %&gt;
 
 &lt;fieldset id=&quot;comment_info&quot;&gt;
 &lt;legend&gt;Information about the comment&lt;/legend&gt;
@@ -24,7 +24,7 @@
   &lt;/li&gt;
   &lt;li class=&quot;right&quot;&gt;
     &lt;label for=&quot;comment_comment_type&quot;&gt;T&lt;span class=&quot;accesskey&quot;&gt;y&lt;/span&gt;pe:&lt;/label&gt;
-    &lt;%= select &quot;comment&quot;, &quot;comment_type&quot;, COMMENT_TYPE, {}, {:accesskey =&gt; &quot;y&quot;} %&gt;
+    &lt;%= select &quot;comment&quot;, &quot;comment_type&quot;, Comment::TYPES, {}, {:accesskey =&gt; &quot;y&quot;} %&gt;
   &lt;/li&gt;
   &lt;li class=&quot;right&quot;&gt;
     &lt;label for=&quot;comment_website&quot;&gt;&lt;span class=&quot;accesskey&quot;&gt;W&lt;/span&gt;ebsite:&lt;/label&gt;
@@ -47,7 +47,7 @@
 &lt;%= tag :input, {:type =&gt; &quot;button&quot;, :name =&gt; &quot;commit&quot;, :value =&gt; &quot;Cancel&quot;, :id =&gt; &quot;comment_submit_cancel&quot;, :onclick =&gt; &quot;window.history.back();&quot;} %&gt;
 &lt;/div&gt;
 
-&lt;%= end_form_tag %&gt;
+&lt;% end %&gt;
 
 &lt;div style=&quot;clear: both;&quot; /&gt;
 &lt;/div&gt;</diff>
      <filename>app/views/admin/comments/edit.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -40,7 +40,7 @@
       &lt;%= link_to &quot;Edit&quot;, :action =&gt; &quot;edit&quot;, :id =&gt; comment.id %&gt;
       &lt;%= link_to &quot;Delete&quot;, {:action =&gt; &quot;delete&quot;, :id =&gt; comment.id},
           :confirm =&gt; &quot;Are you sure you want to delete the comment by &quot; +
-          comment.author + &quot; on '&quot; + comment.entry.title + &quot;'?&quot;, :post =&gt; true %&gt;
+          h(comment.author) + &quot; on '&quot; + comment.entry.title + &quot;'?&quot;, :post =&gt; true %&gt;
     &lt;/p&gt;
   &lt;/li&gt;
 &lt;% end %&gt;</diff>
      <filename>app/views/admin/comments/manage.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -74,11 +74,11 @@ frequently:&lt;/p&gt;
   &lt;li&gt;The most active commenter on your blog is &lt;%= comment_author @most_active_commenter %&gt;.&lt;/li&gt;
   &lt;%- end -%&gt;
 
-  &lt;%- if @most_used_tags and @most_used_tags.length &gt; 0 -%&gt;
+  &lt;%- if @popular_tags and @popular_tags.length &gt; 0 -%&gt;
   &lt;li&gt;The most used tags are:
     &lt;ul&gt;
-      &lt;%- for tag in @most_used_tags -%&gt;
-      &lt;li&gt;&lt;%= tag[&quot;name&quot;] %&gt;&lt;/li&gt;
+      &lt;%- for tag in @popular_tags -%&gt;
+      &lt;li&gt;&lt;%= tag[&quot;name&quot;] %&gt; (&lt;%= tag[&quot;count&quot;] %&gt;)&lt;/li&gt;
       &lt;%- end -%&gt;
     &lt;/ul&gt;
   &lt;/li&gt;</diff>
      <filename>app/views/admin/dashboard/welcome.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,7 @@
                     else
                       &quot;N&quot;
                     end -%&gt;
-    &lt;%= select &quot;meta&quot;, &quot;formatting&quot;, FORMATTING, {:selected =&gt; @selected} %&gt;
+    &lt;%= select &quot;meta&quot;, &quot;formatting&quot;, Entry::FORMATTING, {:selected =&gt; @selected} %&gt;
   &lt;/li&gt;
 &lt;/ol&gt;
 &lt;/fieldset&gt;</diff>
      <filename>app/views/admin/entries/_plugin_textcontrol.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -9,9 +9,6 @@
   &lt;li&gt;
     &lt;label for=&quot;entry_contents_3&quot;&gt;Bod&lt;span class=&quot;accesskey&quot;&gt;y&lt;/span&gt;:&lt;/label&gt;
     &lt;%= text_area_tag &quot;entry_contents_3&quot;, nil, {:size =&gt; &quot;100x20&quot;} %&gt;
-    &lt;p class=&quot;tip&quot;&gt;This can contain
-    &lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/index.html&quot;&gt;ERB&lt;/a&gt;,
-    inline Ruby code.&lt;/p&gt;
   &lt;/li&gt;
 &lt;/ol&gt;
 &lt;/fieldset&gt;</diff>
      <filename>app/views/admin/entries/_type_page.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@
       (&lt;span class=&quot;entry_type&quot;&gt;&lt;%=h entry.type.name.capitalize %&gt;&lt;/span&gt;)
     &lt;/p&gt;
     &lt;p class=&quot;entry_info&quot;&gt;
-      &lt;%= STATES.index(entry.state) %&gt;
+      &lt;%= Entry::STATES.index(entry.state) %&gt;
       &lt;%= &quot;on #{date entry}&quot; if entry.state == &quot;P&quot; or entry.state == &quot;T&quot; %&gt;
       &lt;%=h &quot;by &quot; + entry.user.nickname %&gt;
     &lt;/p&gt;</diff>
      <filename>app/views/admin/entries/manage.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@
 
 &lt;h2&gt;Write new entry&lt;/h2&gt;
 
-&lt;%= start_form_tag({:action =&gt; &quot;save&quot;},{:method =&gt; &quot;post&quot;, :multipart =&gt; true, :id =&gt;&quot;entry-form&quot;}) %&gt;
+&lt;% form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true, :id =&gt;&quot;entry-form&quot;}) do %&gt;
 
 &lt;!-- Basic information about the entry --&gt;
 &lt;fieldset id=&quot;entry_basic&quot;&gt;
@@ -26,7 +26,7 @@
   &lt;/li&gt;
   &lt;li class=&quot;right&quot;&gt;
     &lt;label for=&quot;entry_state&quot;&gt;Stat&lt;span class=&quot;accesskey&quot;&gt;e&lt;/span&gt;:&lt;/label&gt;
-    &lt;%= select &quot;entry&quot;, &quot;state&quot;, STATES, {}, {:accesskey =&gt; &quot;e&quot;} %&gt;
+    &lt;%= select &quot;entry&quot;, &quot;state&quot;, Entry::STATES, {}, {:accesskey =&gt; &quot;e&quot;} %&gt;
   &lt;/li&gt;
   &lt;li class=&quot;left&quot; id=&quot;entry_suggest&quot;&gt;
     &lt;label&gt;Suggestions:&lt;/label&gt;
@@ -107,7 +107,7 @@ if($(&quot;entry_type_id&quot;).value != &quot;&quot;) {
 &lt;div id=&quot;live-preview&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;
 &lt;/fieldset&gt;
 
-&lt;!-- Plugins  --&gt;
+&lt;!-- Plugins --&gt;
 &lt;div id=&quot;entry_pluginbased&quot; class=&quot;tabs&quot;&gt;
 &lt;ul id=&quot;entry_plugins&quot; class=&quot;tablist&quot;&gt;
 &lt;%- for plugin in @plugins -%&gt;
@@ -134,7 +134,7 @@ loadPlugin(&quot;&lt;%= @plugins[0]['key'] %&gt;&quot;);
 &lt;%= tag :input, {:type =&gt; &quot;button&quot;, :name =&gt; &quot;commit&quot;, :value =&gt; &quot;Cancel&quot;, :id =&gt; &quot;entry_submit_cancel&quot;, :onclick =&gt; &quot;window.history.back();&quot;} %&gt;
 &lt;/div&gt;
 
-&lt;%= end_form_tag %&gt;
+&lt;% end %&gt;
 
 &lt;div style=&quot;clear: both;&quot; /&gt;
 &lt;/div&gt;
\ No newline at end of file</diff>
      <filename>app/views/admin/entries/write.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@
 
 &lt;%= &quot;&lt;p class='intro'&gt;#{@section[&quot;description&quot;]}&lt;/p&gt;&quot; if @section[&quot;description&quot;] and !@section[&quot;description&quot;].empty? %&gt;
 
-&lt;%= form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) %&gt;
+&lt;% form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) do %&gt;
 
 &lt;fieldset id=&quot;settings&quot;&gt;
 
@@ -23,7 +23,7 @@
 &lt;%= tag :input, {:type =&gt; &quot;button&quot;, :name =&gt; &quot;commit&quot;, :value =&gt; &quot;Cancel&quot;, :id =&gt; &quot;settings_submit_cancel&quot;, :onclick =&gt; &quot;window.history.back();&quot;} %&gt;
 &lt;/div&gt;
 
-&lt;%= end_form_tag %&gt;
+&lt;% end %&gt;
 
 &lt;div style=&quot;clear: both;&quot; /&gt;
 &lt;/div&gt;</diff>
      <filename>app/views/admin/settings/settings.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -50,10 +50,10 @@
     &lt;p class=&quot;theme_description&quot;&gt;
       &lt;%= theme.description %&gt;
     &lt;/p&gt;
-    &lt;%= form_tag({:action =&gt; &quot;select&quot;}, {:method =&gt; &quot;post&quot;, :class =&gt; &quot;theme_form&quot;}) %&gt;
+    &lt;% form_tag({:action =&gt; &quot;select&quot;}, {:method =&gt; &quot;post&quot;, :class =&gt; &quot;theme_form&quot;}) do %&gt;
       &lt;input type=&quot;hidden&quot; name=&quot;theme[id]&quot; value=&quot;&lt;%= theme.id %&gt;&quot; /&gt;
       &lt;input type=&quot;submit&quot; class=&quot;theme_select&quot; value=&quot;Select this theme&quot; /&gt;
-    &lt;%= end_form_tag %&gt;
+    &lt;% end %&gt;
   &lt;/li&gt;
 &lt;% end %&gt;
 &lt;/ol&gt;</diff>
      <filename>app/views/admin/themes/manage.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@
 
 &lt;h2&gt;Edit user&lt;/h2&gt;
 
-&lt;%= form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) %&gt;
+&lt;% form_tag({:action =&gt; &quot;save&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) do %&gt;
 
 &lt;fieldset id=&quot;user_info&quot;&gt;
 &lt;legend&gt;Basic information about the user&lt;/legend&gt;
@@ -69,7 +69,7 @@
 &lt;%= tag :input, {:type =&gt; &quot;button&quot;, :name =&gt; &quot;commit&quot;, :value =&gt; &quot;Cancel&quot;, :id =&gt; &quot;user_submit_cancel&quot;, :onclick =&gt; &quot;window.history.back();&quot;} %&gt;
 &lt;/div&gt;
 
-&lt;%= end_form_tag %&gt;
+&lt;% end %&gt;
 
 &lt;div style=&quot;clear: both;&quot; /&gt;
 &lt;/div&gt;
\ No newline at end of file</diff>
      <filename>app/views/admin/users/edit.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@
 
 &lt;h2&gt;New user&lt;/h2&gt;
 
-&lt;%= form_tag({:action =&gt; &quot;create&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) %&gt;
+&lt;% form_tag({:action =&gt; &quot;create&quot;}, {:method =&gt; &quot;post&quot;, :multipart =&gt; true}) do %&gt;
 
 &lt;fieldset id=&quot;user_info&quot;&gt;
 &lt;legend&gt;Basic information about the user&lt;/legend&gt;
@@ -41,7 +41,7 @@
 &lt;%= tag :input, {:type =&gt; &quot;button&quot;, :name =&gt; &quot;commit&quot;, :value =&gt; &quot;Cancel&quot;, :id =&gt; &quot;user_submit_cancel&quot;, :onclick =&gt; &quot;window.history.back();&quot;} %&gt;
 &lt;/div&gt;
 
-&lt;%= end_form_tag %&gt;
+&lt;% end %&gt;
 
 &lt;div style=&quot;clear: both;&quot; /&gt;
 &lt;/div&gt;
\ No newline at end of file</diff>
      <filename>app/views/admin/users/new.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-xml.instruct! :xml, :version =&gt; &quot;1.0&quot;, :encoding =&gt; &quot;iso-8859-1&quot;
+xml.instruct! :xml, :version =&gt; &quot;1.0&quot;, :encoding =&gt; &quot;utf-8&quot;
 xml.response do
   xml.error(if @error.blank? then 0 else 1 end)
   xml.message(@error) if @error</diff>
      <filename>app/views/blog/trackback.rxml</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ xml.instruct!
 
 xml.feed &quot;xmlns&quot; =&gt; &quot;http://www.w3.org/2005/Atom&quot; do
 
-  xml.title   &quot;Recent Posts&quot;
+  xml.title   Option[&quot;general_blogname&quot;]
   xml.id      url_for(:only_path =&gt; false, :controller =&gt; &quot;blog&quot;)
   xml.updated @entries[0].date_published.strftime(&quot;%Y-%m-%dT%H:%M:%SZ&quot;) if @entries.any?
   xml.link    &quot;rel&quot; =&gt; &quot;self&quot;,</diff>
      <filename>app/views/feed/atom/entries.rxml</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ xml.instruct!
 xml.rss &quot;version&quot; =&gt; &quot;2.0&quot; do
   xml.channel do
 
-    xml.title       &quot;Recent Posts&quot;
+    xml.title       Option[&quot;general_blogname&quot;]
     xml.link        url_for(:only_path =&gt; false, :controller =&gt; &quot;blog&quot;)
     xml.pubDate     CGI.rfc1123_date(@entries[0].date_published) if @entries.any?
     xml.description &quot;Get the latest entries published on this blog right in your RSS reader.&quot;</diff>
      <filename>app/views/feed/rss2/entries.rxml</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@
 # ENV['RAILS_ENV'] ||= 'production'
 
 # Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '1.1.6'
+RAILS_GEM_VERSION = '1.2.5'
 
 # Bootstrap the Rails environment, frameworks, and default configuration
 require File.join(File.dirname(__FILE__), 'boot')</diff>
      <filename>config/environment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -53,13 +53,13 @@ ActionController::Routing::Routes.draw do |map|
 
   ## ThemingSystem
   # Stylesheets
-  map.connect &quot;:controller/stylesheets/:file&quot;, :action =&gt; &quot;stylesheets&quot;
+  map.connect &quot;:controller/stylesheets/:file.:format&quot;, :action =&gt; &quot;stylesheets&quot;
 
   # JavaScripts
-  map.connect &quot;:controller/javascripts/:file&quot;, :action =&gt; &quot;javascripts&quot;
+  map.connect &quot;:controller/javascripts/:file.:format&quot;, :action =&gt; &quot;javascripts&quot;
 
   # Images
-  map.connect &quot;:controller/images/:file&quot;, :action =&gt; &quot;images&quot;
+  map.connect &quot;:controller/images/:file.:format&quot;, :action =&gt; &quot;images&quot;
 
   ## Default routes
   # Default route for a controller and an action</diff>
      <filename>config/routes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ class CreateTypes &lt; ActiveRecord::Migration
   def self.up
     create_table &quot;types&quot;, :force =&gt; true do |t|
       t.column &quot;name&quot;,        :string, :default =&gt; &quot;&quot;, :null =&gt; false
-      t.column &quot;description&quot;, :text,   :default =&gt; &quot;&quot;, :null =&gt; false
+      t.column &quot;description&quot;, :text,                   :null =&gt; false
     end
   end
 </diff>
      <filename>db/migrate/005_create_types.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ class CreateMetas &lt; ActiveRecord::Migration
   def self.up
     create_table &quot;metas&quot;, :force =&gt; true do |t|
       t.column &quot;key&quot;,      :string, :default =&gt; &quot;&quot;, :null =&gt; false
-      t.column &quot;value&quot;,    :text,   :default =&gt; &quot;&quot;, :null =&gt; false
+      t.column &quot;value&quot;,    :text,                   :null =&gt; false
       t.column &quot;entry_id&quot;, :integer
     end
 </diff>
      <filename>db/migrate/006_create_metas.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ class CreateOptions &lt; ActiveRecord::Migration
     create_table &quot;options&quot;, :force =&gt; true do |t|
       t.column &quot;section&quot;,     :string, :default =&gt; &quot;&quot;, :null =&gt; false
       t.column &quot;key&quot;,         :string, :default =&gt; &quot;&quot;, :null =&gt; false
-      t.column &quot;value&quot;,       :text,   :default =&gt; &quot;&quot;, :null =&gt; false
+      t.column &quot;value&quot;,       :text,                   :null =&gt; false
     end
 
     add_index &quot;options&quot;, [&quot;section&quot;, &quot;key&quot;], :name =&gt; &quot;key&quot;, :unique =&gt; true</diff>
      <filename>db/migrate/007_create_options.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,31 +5,31 @@
 ActiveRecord::Schema.define(:version =&gt; 7) do
 
   create_table &quot;comments&quot;, :force =&gt; true do |t|
-    t.column &quot;author&quot;, :string, :limit =&gt; 50
-    t.column &quot;email&quot;, :string, :limit =&gt; 50
-    t.column &quot;website&quot;, :string, :limit =&gt; 50
-    t.column &quot;ip&quot;, :string, :limit =&gt; 15
-    t.column &quot;user_agent&quot;, :string
-    t.column &quot;referer&quot;, :string
-    t.column &quot;text&quot;, :text
-    t.column &quot;comment_type&quot;, :string, :limit =&gt; 1, :default =&gt; &quot;C&quot;, :null =&gt; false
-    t.column &quot;date&quot;, :datetime
-    t.column &quot;approved&quot;, :boolean, :default =&gt; true, :null =&gt; false
-    t.column &quot;entry_id&quot;, :integer
+    t.column &quot;author&quot;,       :string,   :limit =&gt; 50
+    t.column &quot;email&quot;,        :string,   :limit =&gt; 50
+    t.column &quot;website&quot;,      :string,   :limit =&gt; 50
+    t.column &quot;ip&quot;,           :string,   :limit =&gt; 15
+    t.column &quot;user_agent&quot;,   :string
+    t.column &quot;referer&quot;,      :string
+    t.column &quot;text&quot;,         :text
+    t.column &quot;comment_type&quot;, :string,   :limit =&gt; 1,  :default =&gt; &quot;C&quot;,  :null =&gt; false
+    t.column &quot;date&quot;,         :datetime
+    t.column &quot;approved&quot;,     :boolean,                :default =&gt; true, :null =&gt; false
+    t.column &quot;entry_id&quot;,     :integer
   end
 
   add_index &quot;comments&quot;, [&quot;entry_id&quot;], :name =&gt; &quot;entry_id&quot;
   add_index &quot;comments&quot;, [&quot;comment_type&quot;], :name =&gt; &quot;type&quot;
 
   create_table &quot;entries&quot;, :force =&gt; true do |t|
-    t.column &quot;title&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;slug&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;description&quot;, :text
-    t.column &quot;contents&quot;, :binary
-    t.column &quot;state&quot;, :string, :limit =&gt; 1
+    t.column &quot;title&quot;,          :string,                :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;slug&quot;,           :string,                :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;description&quot;,    :text
+    t.column &quot;contents&quot;,       :binary
+    t.column &quot;state&quot;,          :string,   :limit =&gt; 1
     t.column &quot;date_published&quot;, :datetime
-    t.column &quot;user_id&quot;, :integer
-    t.column &quot;type_id&quot;, :integer
+    t.column &quot;user_id&quot;,        :integer
+    t.column &quot;type_id&quot;,        :integer
   end
 
   add_index &quot;entries&quot;, [&quot;slug&quot;], :name =&gt; &quot;slug&quot;, :unique =&gt; true
@@ -37,14 +37,14 @@ ActiveRecord::Schema.define(:version =&gt; 7) do
 
   create_table &quot;entries_tags&quot;, :id =&gt; false, :force =&gt; true do |t|
     t.column &quot;entry_id&quot;, :integer, :default =&gt; 0, :null =&gt; false
-    t.column &quot;tag_id&quot;, :integer, :default =&gt; 0, :null =&gt; false
+    t.column &quot;tag_id&quot;,   :integer, :default =&gt; 0, :null =&gt; false
   end
 
   add_index &quot;entries_tags&quot;, [&quot;entry_id&quot;, &quot;tag_id&quot;], :name =&gt; &quot;entry_id&quot;
 
   create_table &quot;metas&quot;, :force =&gt; true do |t|
-    t.column &quot;key&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;value&quot;, :text, :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;key&quot;,      :string,  :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;value&quot;,    :text,    :default =&gt; &quot;&quot;, :null =&gt; false
     t.column &quot;entry_id&quot;, :integer
   end
 
@@ -52,8 +52,8 @@ ActiveRecord::Schema.define(:version =&gt; 7) do
 
   create_table &quot;options&quot;, :force =&gt; true do |t|
     t.column &quot;section&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;key&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;value&quot;, :text, :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;key&quot;,     :string, :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;value&quot;,   :text,   :default =&gt; &quot;&quot;, :null =&gt; false
   end
 
   add_index &quot;options&quot;, [&quot;section&quot;, &quot;key&quot;], :name =&gt; &quot;key&quot;, :unique =&gt; true
@@ -65,16 +65,16 @@ ActiveRecord::Schema.define(:version =&gt; 7) do
   add_index &quot;tags&quot;, [&quot;name&quot;], :name =&gt; &quot;name&quot;, :unique =&gt; true
 
   create_table &quot;types&quot;, :force =&gt; true do |t|
-    t.column &quot;name&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;description&quot;, :text, :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;name&quot;,        :string, :default =&gt; &quot;&quot;, :null =&gt; false
+    t.column &quot;description&quot;, :text,   :default =&gt; &quot;&quot;, :null =&gt; false
   end
 
   create_table &quot;users&quot;, :force =&gt; true do |t|
     t.column &quot;username&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
     t.column &quot;password&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
     t.column &quot;nickname&quot;, :string, :default =&gt; &quot;&quot;, :null =&gt; false
-    t.column &quot;email&quot;, :string
-    t.column &quot;website&quot;, :string
+    t.column &quot;email&quot;,    :string
+    t.column &quot;website&quot;,  :string
   end
 
   add_index &quot;users&quot;, [&quot;username&quot;, &quot;nickname&quot;, &quot;email&quot;], :name =&gt; &quot;username&quot;, :unique =&gt; true</diff>
      <filename>db/schema.rb</filename>
    </modified>
    <modified>
      <diff>@@ -183,13 +183,22 @@ module Liquid
       MetaFieldsDrop.new(@entry)
     end
 
-    # Returns the URL of the stylesheet for the type of this entry.
+    # Returns the URL of the stylesheet for the type of this entry, or ++false++
+    # if there is none.
     #
     # For example, if this entry's type is ++article++, it will return something
-    # like ++http://www.example.com/blog/stylesheets/type_article.css++.
+    # like ++http://example.com/blog/stylesheets/type_article.css++, or
+    # ++false++ if ++themes/current_theme/stylesheets/type_article.css++ doesn't
+    # exist.
     def url_for_type_stylesheet
-      @context.registers[:controller].url_for({:controller =&gt; &quot;blog&quot;,
-        :action =&gt; &quot;stylesheets&quot;, :file =&gt; &quot;type_#{@entry.type.name}.css&quot;})
+      if File.exists?(File.join(@context.registers[:controller].theme_path(),
+                                &quot;stylesheets&quot;, &quot;type_#{@entry.type.name}.css&quot;))
+        @context.registers[:controller].url_for({:controller =&gt; &quot;blog&quot;,
+          :action =&gt; &quot;stylesheets&quot;, :file =&gt; &quot;type_#{@entry.type.name}&quot;,
+          :format =&gt; &quot;css&quot;})
+      else
+        false
+      end
     end
 
   end</diff>
      <filename>lib/chameleon/liquid/entry_drop.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,8 +9,8 @@ module Liquid
         :action =&gt; &quot;show&quot;, :slug =&gt; entry.slug})
     end
 
-    # Link to an entry. The given +entry+ can either be an Entry or an
-    # EntryDrop, as long as it has a +slug+ function.
+    # Link to the admin page of entry. The given +entry+ can either be an Entry
+    # or an EntryDrop, as long as it has a +slug+ function.
     def url_for_editing_entry(entry)
       @context.registers[:controller].url_for({:controller =&gt; &quot;admin/entries&quot;,
         :action =&gt; &quot;write&quot;, :slug =&gt; entry.slug})
@@ -38,8 +38,8 @@ module Liquid
         :action =&gt; &quot;archive&quot;, :year =&gt; date.year, :month =&gt; date.month})
     end
 
-    # Link to a user. The given +user+ can either be an User or a UserDrop,
-    # as long as it has an +id+ function.
+    # Link to the admin page of a user. The given +user+ can either be an User
+    # or a UserDrop, as long as it has an +id+ function.
     def url_for_editing_user(user)
       @context.registers[:controller].url_for({:controller =&gt; &quot;admin/users&quot;,
         :action =&gt; &quot;edit&quot;, :id =&gt; user.id})
@@ -55,19 +55,22 @@ module Liquid
     # Link to an image within the current theme.
     def url_for_image(file)
       @context.registers[:controller].url_for({:controller =&gt; &quot;blog&quot;,
-        :action =&gt; &quot;images&quot;, :file =&gt; file})
+        :action =&gt; &quot;images&quot;, :file =&gt; file.split(&quot;.&quot;)[0..-2],
+        :format =&gt; file.split(&quot;.&quot;)[-1]})
     end
 
     # Link to a stylesheet within the current theme.
     def url_for_stylesheet(file)
       @context.registers[:controller].url_for({:controller =&gt; &quot;blog&quot;,
-        :action =&gt; &quot;stylesheets&quot;, :file =&gt; file})
+        :action =&gt; &quot;stylesheets&quot;, :file =&gt; file.split(&quot;.&quot;)[0..-2],
+        :format =&gt; file.split(&quot;.&quot;)[-1]})
     end
 
     # Link to a JavaScript file within the current theme.
     def url_for_javascript(file)
       @context.registers[:controller].url_for({:controller =&gt; &quot;blog&quot;,
-        :action =&gt; &quot;javascripts&quot;, :file =&gt; file})
+        :action =&gt; &quot;javascripts&quot;, :file =&gt; file.split(&quot;.&quot;)[0..-2],
+        :format =&gt; file.split(&quot;.&quot;)[-1]})
     end
 
   end</diff>
      <filename>lib/chameleon/liquid/url_for_filters.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,43 +9,46 @@ module ThemingSystem
     self.class.theme_id
   end
 
-  # This will render the stylesheet with the name in &lt;tt&gt;@params[:file]&lt;/tt&gt;
-  # from this theme.
+  # Get the theme path, on the local filesystem.
+  # For example, ++/var/www/chameleon/themes/current_theme++.
+  def theme_path
+    File.join(RAILS_ROOT, &quot;themes&quot;, theme_id())
+  end
+
+  # This will render the stylesheet with the name &lt;tt&gt;params[:file] + &quot;.&quot; +
+  # params[:format]&lt;/tt&gt; from this theme.
   #
   # For example, a request to
-  # http://www.example.com/themedcontroller/stylesheets/test.css will render the
-  # file at themes/theme_id/stylesheets/test.css.
+  # {{http://example.com/themed_controller/stylesheets/test.css}} will render
+  # the file at {{themes/&lt;theme_id&gt;/stylesheets/test.css}}.
   def stylesheets
-    headers['Content-Type'] = &quot;&quot; # Else Rails will use &quot;text/html&quot;, now it's
-                                 # calculated automatically.
-    render :file =&gt; File.join(RAILS_ROOT, &quot;themes&quot;, theme_id(), &quot;stylesheets&quot;,
-                              @params[:file])
+    render_asset(File.join(theme_path(), &quot;stylesheets&quot;,
+                           params[:file] + &quot;.&quot; + params[:format]),
+                 Mime::EXTENSION_LOOKUP[params[:format]])
   end
 
-  # This will render the javascript file with the name in
-  # &lt;tt&gt;@params[:file]&lt;/tt&gt; from this theme.
+  # This will render the javascript file with the name in &lt;tt&gt;params[:file] +
+  # &quot;.&quot; + params[:format]&lt;/tt&gt; from this theme.
   #
   # For example, a request to
-  # http://www.example.com/themedcontroller/javascripts/test.js will render the
-  # file at themes/theme_id/javascripts/test.js.
+  # {{http://example.com/themed_controller/javascripts/test.js}} will render the
+  # file at {{themes/&lt;theme_id&gt;/javascripts/test.js}}.
   def javascripts
-    headers['Content-Type'] = &quot;&quot; # Else Rails will use &quot;text/html&quot;, now it's
-                                 # calculated automatically.
-    render :file =&gt; File.join(RAILS_ROOT, &quot;themes&quot;, theme_id(), &quot;javascripts&quot;,
-                              @params[:file])
+    render_asset(File.join(theme_path(), &quot;javascripts&quot;,
+                           params[:file] + &quot;.&quot; + params[:format]),
+                 Mime::EXTENSION_LOOKUP[params[:format]])
   end
 
-  # This will render the image with the name in &lt;tt&gt;@params[:file]&lt;/tt&gt; from
-  # this theme.
+  # This will render the image with the name in &lt;tt&gt;params[:file] + &quot;.&quot; +
+  # params[:format]&lt;/tt&gt; from this theme.
   #
   # For example, a request to
-  # http://www.example.com/themedcontroller/images/test.png will render the file
-  # at themes/theme_id/images/test.png.
+  # {{http://example.com/themed_controller/images/test.png}} will render the
+  # file at {{themes/&lt;theme_id&gt;/images/test.png}}.
   def images
-    headers['Content-Type'] = &quot;&quot; # Else Rails will use &quot;text/html&quot;, now it's
-                                 # calculated automatically.
-    render :file =&gt; File.join(RAILS_ROOT, &quot;themes&quot;, theme_id(), &quot;images&quot;,
-                              @params[:file])
+    render_asset(File.join(theme_path(), &quot;images&quot;,
+                           params[:file] + &quot;.&quot; + params[:format]),
+                 Mime::EXTENSION_LOOKUP[params[:format]])
   end
 
   # This function will automatically be called by Ruby when this module gets
@@ -53,6 +56,13 @@ module ThemingSystem
   def self.append_features(base)
     super
     base.extend(ClassMethods)
+
+    # If the MIME type of a file you're using for your theme isn't supported,
+    # add it to this list.
+    Mime::Type.register &quot;text/css&quot;, :css
+    Mime::Type.register &quot;image/png&quot;, :png
+    Mime::Type.register &quot;image/gif&quot;, :gif
+    Mime::Type.register &quot;image/jpg&quot;, :jpg
   end
 
   # This set of class methods will be added to the class this module gets
@@ -86,4 +96,18 @@ module ThemingSystem
 
   end
 
+  protected
+
+  # Render an asset (image, javascript, stylesheet or possibly somthing else).
+  #
+  # {{path}} should be the path to the file on the local filesystem.
+  # {{type}} should be the content type of the file, preferably as a Mime::Type.
+  def render_asset(path, type)
+    if File.exists?(path)
+      send_file(path, :type =&gt; type.to_s, :disposition =&gt; &quot;inline&quot;)
+    else
+      render :text =&gt; &quot;File not found&quot;, :status =&gt; 404
+    end
+  end
+
 end</diff>
      <filename>lib/chameleon/theming_system.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,6 +10,8 @@ module LoginSystem
   #   def authorize?(user)
   #     user.login == &quot;admin&quot;
   #   end
+  #
+  # FIXME: This doesn't work, nor is it used anywhere at this moment.
   def authorize?(user)
      true
   end
@@ -26,6 +28,8 @@ module LoginSystem
   #        return true
   #     end
   #   end
+  #
+  # FIXME: This doesn't work, nor is it used anywhere at this moment.
   def protect?(action)
     true
   end
@@ -42,7 +46,7 @@ module LoginSystem
       return true
     end
 
-    if @session['user'] and authorize?(@session['user'])
+    if session['user'] and authorize?(session['user'])
       return true
     end
 
@@ -63,17 +67,17 @@ module LoginSystem
   # Store the current URI in the session, so we can return to it after logging
   # in.
   def store_location
-    @session['return-to'] = @request.request_uri
+    session['return-to'] = request.request_uri
   end
 
   # Redirect back to URI we were requesting when the access was denied (and the
   # login form was displayed), or go to a default location.
   def redirect_back_or_default(default)
-    if @session['return-to'].nil?
+    if session['return-to'].nil?
       redirect_to default
     else
-      redirect_to_url @session['return-to']
-      @session['return-to'] = nil
+      redirect_to session['return-to']
+      session['return-to'] = nil
     end
   end
 </diff>
      <filename>lib/login_system.rb</filename>
    </modified>
    <modified>
      <diff>@@ -266,12 +266,4 @@ function loadPlugin(key) {
  */
 function submitButton(button) {
     $(&quot;submit&quot;).value = button.id;
-    if($(&quot;entry_submit_publish&quot;)) {
-      $(&quot;entry_submit_publish&quot;).disabled = true; }
-    if($(&quot;entry_submit_save&quot;)) {
-      $(&quot;entry_submit_save&quot;).disabled = true; }
-    if($(&quot;entry_submit_saveandcontinue&quot;)) {
-      $(&quot;entry_submit_saveandcontinue&quot;).disabled = true; }
-    if($(&quot;entry_submit_cancel&quot;)) {
-      $(&quot;entry_submit_cancel&quot;).disabled = true; }
 }</diff>
      <filename>public/javascripts/admin.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-first_comment:
+first:
   id: 1
   author: Tester
   email: test@example.com
@@ -12,11 +12,29 @@ first_comment:
   date: 2006-01-01 01:01:20
   approved: 1
   entry_id: 1
-second_comment:
+second:
   id: 2
   author: Tester
-  text: This is the second comment.
+  text: This is the *second* comment.
   comment_type: C
   date: 2006-01-01 01:04:50
   approved: 1
   entry_id: 1
+third:
+  id: 3
+  text: This is the third comment overall, the first one on entry 2.
+  comment_type: C
+  approved: 1
+  entry_id: 2
+spam:
+  id: 4
+  author: viagra-test-123   # viagra-test-123 is always seen as spam by Akismet.
+  email: test@example.com
+  website: http://www.example.com
+  ip: 127.0.0.1
+  user_agent: Mozilla/5.0 Gecko/20061115 Firefox/1.5.0.8
+  text: This is a spam comment.
+  comment_type: C
+  date: 2006-01-01 02:15:45
+  approved: 0
+  entry_id: 1</diff>
      <filename>test/fixtures/comments.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,15 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-first_entry:
+first:
   id: 1
   title: Hello World!
   slug: hello-world
-  description: Welcome!
-  contents: Welcome to Chameleon!
+  description: Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!
+  contents: Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!
   state: P
   date_published: 2006-01-01 00:00:00
   user_id: 1
   type_id: 1
-second_entry:
+second:
   id: 2
   title: Hello there!
   slug: hello-there
@@ -33,3 +33,14 @@ second_entry:
   date_published: 2006-01-01 00:02:30
   user_id: 1
   type_id: 1
+third:
+  id: 3
+  title: Sample draft
+  slug: sample-draft
+  description: This is a sample draft that can only be viewed when logged in.
+  contents: This is a sample draft that can only be viewed when logged in. Feel
+            free to delete it.
+  state: D
+  date_published: 2006-01-01 00:03:12
+  user_id: 1
+  type_id: 1</diff>
      <filename>test/fixtures/entries.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,11 +1,6 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-first_meta:
+first_entry_is_textile:
   id: 1
-  key: meta_field
-  value: Test
+  key: formatting
+  value: T
   entry_id: 1
-scond_meta:
-  id: 2
-  key: meta_field
-  value: Test
-  entry_id: 2</diff>
      <filename>test/fixtures/metas.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,13 +1,16 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
 blogname:
+  id: 1
   section: general
   key: blogname
   value: Test
 bloguri:
+  id: 2
   section: general
   key: bloguri
   value: http://0.0.0.0:3000/
 themeid:
+  id: 3
   section: theming
   key: themeid
   value: default</diff>
      <filename>test/fixtures/options.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,13 @@
 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-first_tag:
+hello:
   id: 1
   name: first
-second_tag:
+world:
   id: 2
   name: second
+test:
+  id: 3
+  name: test
+chameleon:
+  id: 4
+  name: chameleon</diff>
      <filename>test/fixtures/tags.yml</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,7 @@ review:
 page:
   id: 3
   name: page
-  description: A page, possibly containing ERB (inline Ruby code).
+  description: A page.
 image:
   id: 5
   name: image</diff>
      <filename>test/fixtures/types.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,74 +1,72 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../controller_testcase'
 require 'account_controller'
 
 # Raise errors beyond the default web-based presentation
 class AccountController; def rescue_action(e) raise e end; end
 
-class AccountControllerTest &lt; Test::Unit::TestCase
-  self.use_instantiated_fixtures  = true
-  
-  fixtures :users
-  
+class AccountControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
 
     @controller = AccountController.new
     @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
     @request.host = &quot;localhost&quot;
   end
-  
-  def test_auth_bob
-    @request.session['return-to'] = &quot;/bogus/location&quot;
 
-    post :login, &quot;user_login&quot; =&gt; &quot;bob&quot;, &quot;user_password&quot; =&gt; &quot;test&quot;
-    assert_session_has &quot;user&quot;
+  def test_login
+    u = FixtureReplacement.create_user_admin
 
-    assert_equal @bob, @response.session[&quot;user&quot;]
-    
-    assert_redirected_to &quot;/bogus/location&quot;
-  end
-  
-  def test_signup
     @request.session['return-to'] = &quot;/bogus/location&quot;
+    post :login, {&quot;username&quot; =&gt; u.username,
+                  &quot;password&quot; =&gt; fixture_password(:user_admin)}
 
-    post :signup, &quot;user&quot; =&gt; { &quot;login&quot; =&gt; &quot;newbob&quot;, &quot;password&quot; =&gt; &quot;newpassword&quot;, &quot;password_confirmation&quot; =&gt; &quot;newpassword&quot; }
-    assert_session_has &quot;user&quot;
-    
     assert_redirected_to &quot;/bogus/location&quot;
+
+    assert session[&quot;user&quot;]
+    assert_equal u, session[&quot;user&quot;]
   end
 
-  def test_bad_signup
-    @request.session['return-to'] = &quot;/bogus/location&quot;
+  def test_incorrect_username
+    post :login, {&quot;username&quot; =&gt; &quot;incorrect&quot;, &quot;password&quot; =&gt; &quot;incorrect&quot;}
 
-    post :signup, &quot;user&quot; =&gt; { &quot;login&quot; =&gt; &quot;newbob&quot;, &quot;password&quot; =&gt; &quot;newpassword&quot;, &quot;password_confirmation&quot; =&gt; &quot;wrong&quot; }
-    assert_invalid_column_on_record &quot;user&quot;, &quot;password&quot;
-    assert_success
-    
-    post :signup, &quot;user&quot; =&gt; { &quot;login&quot; =&gt; &quot;yo&quot;, &quot;password&quot; =&gt; &quot;newpassword&quot;, &quot;password_confirmation&quot; =&gt; &quot;newpassword&quot; }
-    assert_invalid_column_on_record &quot;user&quot;, &quot;login&quot;
-    assert_success
-
-    post :signup, &quot;user&quot; =&gt; { &quot;login&quot; =&gt; &quot;yo&quot;, &quot;password&quot; =&gt; &quot;newpassword&quot;, &quot;password_confirmation&quot; =&gt; &quot;wrong&quot; }
-    assert_invalid_column_on_record &quot;user&quot;, [&quot;login&quot;, &quot;password&quot;]
-    assert_success
+    assert_nil session[&quot;user&quot;]
+    assert flash[:notice]
   end
 
-  def test_invalid_login
-    post :login, &quot;user_login&quot; =&gt; &quot;bob&quot;, &quot;user_password&quot; =&gt; &quot;not_correct&quot;
-     
-    assert_session_has_no &quot;user&quot;
-    
-    assert_template_has &quot;message&quot;
-    assert_template_has &quot;login&quot;
+  def test_incorrect_password
+    u = FixtureReplacement.create_user_admin
+
+    @request.session['return-to'] = &quot;/bogus/location&quot;
+    post :login, {&quot;username&quot; =&gt; u.username,
+                  &quot;password&quot; =&gt; fixture_password(:user_admin) + &quot;123&quot;}
+
+    assert_nil session[&quot;user&quot;]
+    assert flash[:notice]
+    assert_equal u.username, assigns[&quot;username&quot;]
   end
-  
-  def test_login_logoff
 
-    post :login, &quot;user_login&quot; =&gt; &quot;bob&quot;, &quot;user_password&quot; =&gt; &quot;test&quot;
-    assert_session_has &quot;user&quot;
+  def test_logout
+    u = FixtureReplacement.create_user_admin
+
+    post :login, {&quot;username&quot; =&gt; u.username,
+                  &quot;password&quot; =&gt; fixture_password(:user_admin)}
+    assert session[&quot;user&quot;]
+    assert_equal u, session[&quot;user&quot;]
 
     get :logout
-    assert_session_has_no &quot;user&quot;
+    assert_nil session[&quot;user&quot;]
+  end
+
+  private
 
+  # Get the password defined in a certain fixture, in plain text. Pass the
+  # fixture name as a symbol.
+  def fixture_password(fixture)
+    # Get the FixtureReplacementController::Attributes object representing the
+    # fixture. #hash will contain the attributes as set in the fixture.
+    FixtureReplacementController::Attributes.find_by_fixture_name(fixture).hash[:password]
   end
-  
+
 end</diff>
      <filename>test/functional/account_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,17 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/base_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::BaseController; def rescue_action(e) raise e end; end
 
-class Admin::BaseControllerTest &lt; Test::Unit::TestCase
+class Admin::BaseControllerTest &lt; ControllerTestCase
+
   def setup
+    super
+
     @controller = Admin::BaseController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
-  end
 end</diff>
      <filename>test/functional/admin/base_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,411 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/comments_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::CommentsController; def rescue_action(e) raise e end; end
 
-class Admin::CommentsControllerTest &lt; Test::Unit::TestCase
+class Admin::CommentsControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = Admin::CommentsController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
+
+    login()
+  end
+
+  ## Index
+
+  # The index action should redirect to the manage action.
+  def test_index
+    get :index
+    assert_redirected_to :action =&gt; &quot;manage&quot;
+  end
+
+  ## Manage
+
+  # Test the manage action, without a view parameter given.
+  def test_manage__no_view
+    2.times { FixtureReplacement.create_comment }
+    2.times { FixtureReplacement.create_comment_spam }
+
+    get :manage
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title         =&gt; &quot;Manage comments&quot;,
+                     :content_title =&gt; &quot;Manage approved comments&quot;
+
+    paginator_test :manage, assigns[&quot;comments_pages&quot;],
+                   :item_count =&gt; 2,
+                   :current_page =&gt; 1
+
+    assert_equal &quot;approved&quot;, assigns[&quot;params&quot;][:view]
+    assert assigns[&quot;comments&quot;].is_a?(Array)
+    assert_equal 2, assigns[&quot;comments&quot;].length
+  end
+
+  # Test the manage action, for page two.
+  def test_manage__page_two
+    15.times { FixtureReplacement.create_comment }
+    2.times { FixtureReplacement.create_comment_spam }
+
+    get :manage, {:page =&gt; 2}
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title         =&gt; &quot;Manage comments&quot;,
+                     :content_title =&gt; &quot;Manage approved comments&quot;
+
+    paginator_test :manage, assigns[&quot;comments_pages&quot;],
+                   :item_count =&gt; 15,
+                   :current_page =&gt; 2
+
+    assert_equal &quot;approved&quot;, assigns[&quot;params&quot;][:view]
+    assert assigns[&quot;comments&quot;].is_a?(Array)
+    assert_equal 5, assigns[&quot;comments&quot;].length
+  end
+
+  # Test the manage action, with as view parameter &quot;spam&quot;.
+  def test_manage__spam
+    2.times { FixtureReplacement.create_comment }
+    2.times { FixtureReplacement.create_comment_spam }
+
+    get :manage, {:view =&gt; &quot;spam&quot;}
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title         =&gt; &quot;Manage comments&quot;,
+                     :content_title =&gt; &quot;Manage spam comments&quot;
+
+    paginator_test :manage, assigns[&quot;comments_pages&quot;],
+                   :item_count =&gt; 2,
+                   :current_page =&gt; 1
+
+    assert_equal &quot;spam&quot;, assigns[&quot;params&quot;][:view]
+    assert assigns[&quot;comments&quot;].is_a?(Array)
+    assert_equal 2, assigns[&quot;comments&quot;].length
+  end
+
+  # Test the manage action, with as view parameter &quot;approved&quot;.
+  def test_manage__approved
+    2.times { FixtureReplacement.create_comment }
+    2.times { FixtureReplacement.create_comment_spam }
+
+    get :manage, {:view =&gt; &quot;approved&quot;}
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title         =&gt; &quot;Manage comments&quot;,
+                     :content_title =&gt; &quot;Manage approved comments&quot;
+
+    paginator_test :manage, assigns[&quot;comments_pages&quot;],
+                   :item_count =&gt; 2,
+                   :current_page =&gt; 1
+
+    assert_equal &quot;approved&quot;, assigns[&quot;params&quot;][:view]
+    assert assigns[&quot;comments&quot;].is_a?(Array)
+    assert_equal 2, assigns[&quot;comments&quot;].length
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  ## Edit
+
+  # Test the edit action, as it is requested normally.
+  def test_edit__normal
+    c = FixtureReplacement.create_comment
+
+    get :edit, {:id =&gt; c.id}
+    assert_response :success
+
+    admin_get_action :edit,
+                     :title         =&gt; &quot;Edit comment&quot;,
+                     :content_title =&gt; &quot;Edit comment&quot;
+
+    assert_equal c, assigns[&quot;comment&quot;]
+  end
+
+  # Test the edit action, without an ID provided.
+  def test_edit__no_id
+    get :edit
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert &quot;The comment could not be found.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;].nil?
+end
+
+  # Test the edit action, with a comment ID that doesn't exist.
+  def test_edit__wrong_id
+    get :edit, {:id =&gt; 1001}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert &quot;The comment could not be found.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;].nil?
   end
+
+  ## Save
+
+  # Test the save action, as it is requested normally.
+  def test_save__normal
+    original = FixtureReplacement.create_comment()
+    new      = FixtureReplacement.new_comment_author_and_text_changed()
+    new.id   = original.id
+
+    post :save, {:id =&gt; original.id,
+                 :comment =&gt; new.attributes_before_type_cast}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{new.author} on '#{new.entry.title}' was &quot; +
+                 &quot;edited.&quot;, flash[:notice]
+
+    assert_equal new.attributes, Comment.find(original.id).attributes
+    assert_equal new.attributes, assigns[&quot;comment&quot;].attributes
+    assert assigns[&quot;comment&quot;].errors.empty?
+  end
+
+  # Test the save action, without an ID or any parameters provided.
+  def test_save__no_parameters
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be found and as a result was not &quot; +
+                 &quot;edited.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;].nil?
+  end
+
+  # Test the save action, for a valid (!) comment with no parameters given
+  # except the ID. This is valid, because when a parameter is not given, it is
+  # not overwritten. So, the original comment and the edited one are the same.
+  def test_save__only_id
+    c = FixtureReplacement.create_comment()
+
+    post :save, {:id =&gt; c.id, :comment =&gt; {&quot;id&quot; =&gt; c.id}}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{c.author} on '#{c.entry.title}' was edited.&quot;,
+                 flash[:notice]
+
+    assert_equal c.attributes, assigns[&quot;comment&quot;].attributes
+    assert assigns[&quot;comment&quot;].errors.empty?
+  end
+
+  # Test the save action, with a comment ID that doesn't exist.
+  def test_save__wrong_id
+    c = FixtureReplacement.create_comment()
+    c.id = 1056
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save, {:id =&gt; c.id, :comment =&gt; c.attributes_before_type_cast}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be found and as a result was not &quot; +
+                 &quot;edited.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;].nil?
+  end
+
+  # Test the save action, with parameters that should cause a validation error.
+  # This tests whether errors are returned correctly, in the flash message.
+  def test_save__validation_errors
+    original = FixtureReplacement.create_comment()
+    new      = FixtureReplacement.new_comment_no_author_no_text()
+    new.id   = original.id
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save, {:id =&gt; original.id,
+                 :comment =&gt; new.attributes_before_type_cast}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be edited.\n&lt;br /&gt;Please enter a &quot; +
+                 &quot;comment text.\n&lt;br /&gt;Please enter a name.&quot;, flash[:notice]
+
+    assert_equal new.attributes, assigns[&quot;comment&quot;].attributes
+    assert_equal original.attributes, Comment.find(original.id).attributes
+    assert_equal 2, assigns[&quot;comment&quot;].errors.count
+    assert_equal([[&quot;author&quot;, &quot;Please enter a name.&quot;],
+                  [&quot;text&quot;, &quot;Please enter a comment text.&quot;]],
+                 assigns[&quot;comment&quot;].errors.entries.sort,
+                 &quot;The right error messages should be returned for the right &quot;+
+                 &quot;attributes.&quot;)
+  end
+
+  ## Mark
+
+  # Test the mark action, as it is requested normally for an approved comment to
+  # be marked as spam.
+  def test_mark__ham_as_spam
+    c = FixtureReplacement.create_comment()
+
+    post :mark, {:id =&gt; c.id, :mark =&gt; &quot;spam&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{c.author} on '#{c.entry.title}' was &quot; +
+      &quot;marked as spam.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert_equal false, assigns[&quot;comment&quot;].approved?
+  end
+
+  # Test the mark action, as it is requested normally for an approved comment to
+  # be marked as ham (which it already is).
+  def test_mark__ham_as_ham
+    c = FixtureReplacement.create_comment()
+
+    post :mark, {:id =&gt; c.id, :mark =&gt; &quot;ham&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{c.author} on '#{c.entry.title}' was &quot; +
+      &quot;marked as approved.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert_equal true, assigns[&quot;comment&quot;].approved?
+  end
+
+  # Test the mark action, as it is requested normally for a spam comment to be
+  # marked as ham.
+  def test_mark__spam_as_ham
+    c = FixtureReplacement.create_comment_spam()
+
+    post :mark, {:id =&gt; c.id, :mark =&gt; &quot;ham&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{c.author} on '#{c.entry.title}' was &quot; +
+      &quot;marked as approved.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert_equal true, assigns[&quot;comment&quot;].approved?
+  end
+
+  # Test the mark action, as it is requested normally for a spam comment to be
+  # marked as spam (which it already is).
+  def test_mark__spam_as_spam
+    c = FixtureReplacement.create_comment_spam()
+
+    post :mark, {:id =&gt; c.id, :mark =&gt; &quot;spam&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{c.author} on '#{c.entry.title}' was &quot; +
+      &quot;marked as spam.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert_equal false, assigns[&quot;comment&quot;].approved?
+  end
+
+  # Test the mark action, but with an invalid 'mark' parameter.
+  def test_mark__invalid_mark
+    c = FixtureReplacement.create_comment()
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :mark, {:id =&gt; c.id, :mark =&gt; &quot;invalid&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal 'An error occured while changing the &quot;approved&quot; state of a ' +
+                 'comment. Nothing was changed.', flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert_equal c.attributes, Comment.find(c.id).attributes
+  end
+
+  # Test the mark action, but with an no 'mark' parameter given.
+  def test_mark__no_mark
+    c = FixtureReplacement.create_comment()
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :mark, {:id =&gt; c.id}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal 'An error occured while changing the &quot;approved&quot; state of a ' +
+                 'comment. Nothing was changed.', flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert_equal c.attributes, Comment.find(c.id).attributes
+  end
+
+  # Test the mark action, but with no parameters given.
+  def test_mark__no_parameters
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :mark
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be found and as a result was not &quot; +
+                 &quot;updated.&quot;, flash[:notice]
+  end
+
+  # Test the mark action, but with a comment ID that doesn't exist.
+  def test_mark__wrong_id
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :mark, {:id =&gt; 1056, :mark =&gt; &quot;spam&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be found and as a result was not &quot; +
+                 &quot;updated.&quot;, flash[:notice]
+  end
+
+  ## Delete
+
+  # Test the delete action, as it is requested normally for a single comment.
+  def test_delete__normal_single
+    c = FixtureReplacement.create_comment()
+
+    post :delete, {:id =&gt; c.id}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The comment by #{c.author} on '#{c.entry.title}' was &quot; +
+      &quot;deleted.&quot;, flash[:notice]
+
+    assert assigns[&quot;comment&quot;]
+    assert assigns[&quot;comment&quot;].frozen?
+    assert_raises(ActiveRecord::RecordNotFound) { Comment.find(c.id) }
+  end
+
+  # Test the delete action, as it is requested normally for a all comments.
+  def test_delete__normal_all
+    3.times { FixtureReplacement.create_comment }
+    3.times { FixtureReplacement.create_comment_spam }
+
+    post :delete, {:id =&gt; &quot;spam&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;comments&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;All spam comments were deleted.&quot;, flash[:notice]
+
+    assert_equal 3, Comment.find(:all).length
+    assert_equal 0, Comment.find(:all, :conditions =&gt; &quot;approved = 0&quot;).length
+    assert_equal 3, Comment.find(:all, :conditions =&gt; &quot;approved = 1&quot;).length
+  end
+
+  # Test the delete action, but without a comment ID given.
+  def test_delete__no_id
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :delete
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be found and as a result was not &quot; +
+                 &quot;deleted.&quot;, flash[:notice]
+  end
+
+  # Test the delete action, but with a comment ID that doesn't exist.
+  def test_delete__wrong_id
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :delete, {:id =&gt; 1056}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The comment could not be found and as a result was not &quot; +
+                 &quot;deleted.&quot;, flash[:notice]
+  end
+
 end</diff>
      <filename>test/functional/admin/comments_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,36 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/dashboard_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::DashboardController; def rescue_action(e) raise e end; end
 
-class Admin::DashboardControllerTest &lt; Test::Unit::TestCase
+class Admin::DashboardControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = Admin::DashboardController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
+
+    login()
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # The index action should redirect to the welcome action.
+  def test_index
+    get :index
+    assert_redirected_to :action =&gt; &quot;welcome&quot;
   end
+
+  # Test the welcome action.
+  def test_welcome
+    get :welcome
+    assert_response :success
+
+    admin_get_action :welcome,
+                     :title         =&gt; &quot;Welcome&quot;,
+                     :content_title =&gt; &quot;Welcome!&quot;
+  end
+
 end</diff>
      <filename>test/functional/admin/dashboard_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,17 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/entries_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::EntriesController; def rescue_action(e) raise e end; end
 
-class Admin::EntriesControllerTest &lt; Test::Unit::TestCase
+class Admin::EntriesControllerTest &lt; ControllerTestCase
+
   def setup
+    super
+
     @controller = Admin::EntriesController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
-  end
 end</diff>
      <filename>test/functional/admin/entries_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,98 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/settings_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::SettingsController; def rescue_action(e) raise e end; end
 
-class Admin::SettingsControllerTest &lt; Test::Unit::TestCase
+class Admin::SettingsControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = Admin::SettingsController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
+
+    login()
+  end
+
+  ## Index
+
+  # The index action should redirect to the settings action, for the section
+  # general.
+  def test_index
+    get :index
+    assert_redirected_to :action =&gt; &quot;settings&quot;, :section =&gt; &quot;general&quot;
+  end
+
+  ## Settings
+
+  # Test the settings action, for the general section.
+  def test_settings__general
+    get :settings, {:section =&gt; &quot;general&quot;}
+    assert_response :success
+
+    admin_get_action :settings,
+                     :title         =&gt; &quot;General settings&quot;,
+                     :content_id    =&gt; &quot;settings_setting&quot;,
+                     :content_title =&gt; &quot;General settings&quot;
+
+    assert assigns[&quot;section&quot;]
+    assert_equal &quot;general&quot;, assigns[&quot;section&quot;][&quot;key&quot;]
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test the settings action, but without a section given.
+  def test_settings__no_section
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    get :settings
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The selected setting section does not exist.&quot;, flash[:notice]
   end
+
+  # Test the settings action, but with a section that doesn't exist.
+  def test_settings__wrong_section
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    get :settings, {:section =&gt; &quot;doesnotexist&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The selected setting section does not exist.&quot;, flash[:notice]
+  end
+
+  ## Save
+
+  # Test the save action as it is requested normally.
+  def test_save__normal
+    blogname = Option[&quot;general_blogname&quot;] + &quot;123&quot;
+    bloguri = Option[&quot;general_bloguri&quot;] + &quot;123&quot;
+    description = Option[&quot;general_description&quot;]
+
+    post :save, {&quot;general&quot; =&gt; {&quot;blogname&quot; =&gt; blogname,
+                               &quot;bloguri&quot;  =&gt; bloguri}}
+
+    assert_redirected_to :controller =&gt; &quot;settings&quot;, :action =&gt; &quot;settings&quot;,
+                         :section =&gt; &quot;general&quot;
+    assert_equal &quot;The settings were saved.&quot;, flash[:notice]
+
+    assert_equal blogname, Option[&quot;general_blogname&quot;]
+    assert_equal bloguri, Option[&quot;general_bloguri&quot;]
+    assert_equal description, Option[&quot;general_description&quot;]
+  end
+
+  # Test the save action, but without any settings changed.
+  def test_save__no_settings
+    blogname = Option[&quot;general_blogname&quot;]
+
+    post :save
+
+    assert_redirected_to :controller =&gt; &quot;settings&quot;, :action =&gt; &quot;settings&quot;,
+                         :section =&gt; &quot;general&quot;
+    assert_equal &quot;The settings were saved.&quot;, flash[:notice]
+
+    assert_equal blogname, Option[&quot;general_blogname&quot;]
+  end
+
 end</diff>
      <filename>test/functional/admin/settings_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,84 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/themes_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::ThemesController; def rescue_action(e) raise e end; end
 
-class Admin::ThemesControllerTest &lt; Test::Unit::TestCase
+class Admin::ThemesControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = Admin::ThemesController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
+
+    login()
+  end
+
+  ## Index
+
+  # The index action should redirect to the manage action.
+  def test_index
+    get :index
+    assert_redirected_to :action =&gt; &quot;manage&quot;
+  end
+
+  ## Manage
+
+  # Test the manage action.
+  def test_manage
+    get :manage
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title =&gt; &quot;Manage themes&quot;,
+                     :content_title =&gt; &quot;Manage themes&quot;
+
+    assert assigns[&quot;themes&quot;].is_a?(Array)
+  end
+
+  ## Select
+
+  # Test the select action, as it is requested normally.
+  def test_select__normal
+    t = Theme.find(:all).first
+
+    post :select, {&quot;theme&quot; =&gt; {&quot;id&quot; =&gt; t.id}}
+
+    assert_redirected_to :controller =&gt; &quot;themes&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;You are now using the theme '#{t.name}'.&quot;, flash[:notice]
+
+    assert_equal t.id, Option[&quot;theming_themeid&quot;]
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test the select action, without an ID provided.
+  def test_select__no_id
+    original = Option[&quot;theming_themeid&quot;]
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :select
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The selected theme does not exist.&quot;, flash[:notice]
+
+    assert_equal original, Option[&quot;theming_themeid&quot;]
   end
+
+  # Test the select action, with an ID that doesn't exist.
+  def test_select__wrong_id
+    original = Option[&quot;theming_themeid&quot;]
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :select, {&quot;theme&quot; =&gt; {&quot;id&quot; =&gt; 1001}}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The selected theme does not exist.&quot;, flash[:notice]
+
+    assert_equal original, Option[&quot;theming_themeid&quot;]
+  end
+
 end</diff>
      <filename>test/functional/admin/themes_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,401 @@
-require File.dirname(__FILE__) + '/../../test_helper'
+require File.dirname(__FILE__) + '/../../controller_testcase'
 require 'admin/users_controller'
 
 # Re-raise errors caught by the controller.
 class Admin::UsersController; def rescue_action(e) raise e end; end
 
-class Admin::UsersControllerTest &lt; Test::Unit::TestCase
+class Admin::UsersControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = Admin::UsersController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
+
+    login()
+  end
+
+  ## Index
+
+  # The index action should redirect to the manage action.
+  def test_index
+    get :index
+    assert_redirected_to :action =&gt; &quot;manage&quot;
+  end
+
+  ## Manage
+
+  # Test the manage action.
+  def test_manage
+    # An administrator was already created by #login.
+    2.times { FixtureReplacement.create_user }
+
+    get :manage
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title =&gt; &quot;Manage users&quot;,
+                     :content_title =&gt; &quot;Manage users&quot;
+
+    paginator_test :manage, assigns[&quot;user_pages&quot;],
+                   :item_count =&gt; 3,
+                   :current_page =&gt; 1
+
+    assert assigns[&quot;users&quot;].is_a?(Array)
+    assert_equal 3, assigns[&quot;users&quot;].length
+  end
+
+  # Test the manage action, for page two.
+  def test_manage__page_two
+    # An administrator was already created by #login.
+    15.times { FixtureReplacement.create_user }
+
+    get :manage, {:page =&gt; 2}
+    assert_response :success
+
+    admin_get_action :manage,
+                     :title =&gt; &quot;Manage users&quot;,
+                     :content_title =&gt; &quot;Manage users&quot;
+
+    paginator_test :manage, assigns[&quot;user_pages&quot;],
+                   :item_count =&gt; 16
+
+    assert assigns[&quot;users&quot;].is_a?(Array)
+    assert_equal 6, assigns[&quot;users&quot;].length
+  end
+
+  ## New
+
+  # The new action should render a form to create a new user.
+  def test_new
+    get :new
+    assert_response :success
+
+    admin_get_action :new,
+                     :title         =&gt; &quot;New user&quot;,
+                     :content_title =&gt; &quot;New user&quot;
+  end
+
+  ## Create
+
+  # Test the create action, as it is requested normally.
+  def test_create__normal
+    u = FixtureReplacement.new_user
+    request_attributes = u.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    post :create, {:user =&gt; request_attributes,
+                   :newpassword1 =&gt; fixture_password(:user),
+                   :newpassword2 =&gt; fixture_password(:user)}
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The user #{u.username} was created.&quot;, flash[:notice]
+
+    u.id = assigns[&quot;user&quot;].id
+    assert_equal u.attributes, assigns[&quot;user&quot;].attributes
+    assert_equal u.attributes, User.find(assigns[&quot;user&quot;].id).attributes
+    assert assigns[&quot;user&quot;].errors.empty?
+  end
+
+  # Test the create action, but with an incorrect confirmation password.
+  def test_create__incorrect_confirmation
+    u = FixtureReplacement.new_user
+    request_attributes = u.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :create, {:user =&gt; request_attributes,
+                   :newpassword1 =&gt; fixture_password(:user),
+                   :newpassword2 =&gt; fixture_password(:user) + &quot;123&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The user was not created, because the entered password did &quot; +
+                 &quot;not match its confirmation.&quot;, flash[:notice]
+
+    assert_nil assigns[&quot;user&quot;]
+  end
+
+  # Test the create action, with parameters that should cause a validation
+  # error. This tests whether errors are returned correctly, in the flash
+  # message.
+  def test_create__validation_errors
+    u = FixtureReplacement.new_user_no_nickname
+    request_attributes = u.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :create, {:user =&gt; request_attributes,
+                   :newpassword1 =&gt; fixture_password(:user_no_nickname),
+                   :newpassword2 =&gt; fixture_password(:user_no_nickname)}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The user could not be created.\n&lt;br /&gt;Please enter a &quot; +
+                 &quot;nickname.&quot;, flash[:notice]
+
+    assert_equal u.attributes, assigns[&quot;user&quot;].attributes
+    assert_equal 1, assigns[&quot;user&quot;].errors.count
+    assert_equal([[&quot;nickname&quot;, &quot;Please enter a nickname.&quot;]],
+                 assigns[&quot;user&quot;].errors.entries,
+                 &quot;The right error messages should be returned for the right &quot;+
+                 &quot;attributes.&quot;)
+  end
+
+  ## Edit
+
+  # Test the edit action, as it is requested normally.
+  def test_edit__normal
+    u = FixtureReplacement.create_user
+
+    get :edit, {:id =&gt; u.id}
+    assert_response :success
+
+    admin_get_action :edit,
+                     :title         =&gt; &quot;Edit user&quot;,
+                     :content_title =&gt; &quot;Edit user&quot;
+
+    assert_equal u, assigns[&quot;user&quot;]
+    assert_equal u.latest_entries, assigns[&quot;latest_entries&quot;]
+  end
+
+  # Test the edit action, without an ID provided.
+  def test_edit__no_id
+    get :edit
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert &quot;The user could not be found.&quot;, flash[:notice]
+
+    assert assigns[&quot;user&quot;].nil?
+    assert assigns[&quot;latest_entries&quot;].nil?
+end
+
+  # Test the edit action, with a user ID that doesn't exist.
+  def test_edit__wrong_id
+    get :edit, {:id =&gt; 1001}
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert &quot;The user could not be found.&quot;, flash[:notice]
+
+    assert assigns[&quot;user&quot;].nil?
+    assert assigns[&quot;latest_entries&quot;].nil?
+  end
+
+  ## Save
+
+  # Test the save action, as it is requested normally when changing user
+  # attributes.
+  def test_save__normal__change_attributes
+    original = FixtureReplacement.create_user
+    new      = FixtureReplacement.new_user_email_and_website_changed
+    new.id   = original.id
+    new.username = original.username # Because usernames are timebased.
+    request_attributes = new.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    post :save, {:id =&gt; original.id, :user =&gt; request_attributes}
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The settings for the user #{new.username} were saved.&quot;,
+                 flash[:notice]
+
+    assert_equal new.attributes, User.find(original.id).attributes
+    assert_equal new.attributes, assigns[&quot;user&quot;].attributes
+    assert assigns[&quot;user&quot;].errors.empty?
+  end
+
+  # Test the save action, as it is requested normally when changing the
+  # password.
+  def test_save__normal_change_password
+    original = FixtureReplacement.create_user
+    new      = FixtureReplacement.new_user_password_changed
+    new.id   = original.id
+    new.username = original.username # Because usernames are timebased.
+    request_attributes = new.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    post :save, {:id =&gt; original.id, :user =&gt; request_attributes,
+                 :currentpassword =&gt; fixture_password(:user),
+                 :newpassword1 =&gt; fixture_password(:user_password_changed),
+                 :newpassword2 =&gt; fixture_password(:user_password_changed)}
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The settings for the user #{new.username} were saved.&lt;br /&gt;&quot; +
+                 &quot;The password was changed.&quot;, flash[:notice]
+
+    assert_equal new.attributes, User.find(original.id).attributes
+    assert_equal new.attributes, assigns[&quot;user&quot;].attributes
+    assert assigns[&quot;user&quot;].errors.empty?
+  end
+
+  # Test the save action, as it is requested normally when changing user
+  # attributes and the password.
+  def test_save__normal_change_attributes_and_password
+    original = FixtureReplacement.create_user
+    new      = FixtureReplacement.new_user_email_and_password_changed
+    new.id   = original.id
+    new.username = original.username # Because usernames are timebased.
+    request_attributes = new.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    post :save, {:id =&gt; original.id, :user =&gt; request_attributes,
+                 :currentpassword =&gt; fixture_password(:user),
+                 :newpassword1 =&gt; fixture_password(:user_email_and_password_changed),
+                 :newpassword2 =&gt; fixture_password(:user_email_and_password_changed)}
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The settings for the user #{new.username} were saved.&lt;br /&gt;&quot; +
+                 &quot;The password was changed.&quot;, flash[:notice]
+
+    assert_equal new.attributes, User.find(original.id).attributes
+    assert_equal new.attributes, assigns[&quot;user&quot;].attributes
+    assert assigns[&quot;user&quot;].errors.empty?
+  end
+
+  # Test the save action, without an ID or any parameters provided.
+  def test_save__no_parameters
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The user could not be found and as a result was not edited.&quot;,
+                 flash[:notice]
+
+    assert assigns[&quot;user&quot;].nil?
+  end
+
+  # Test the save action, for a valid (!) user with no parameters given except
+  # the ID. This is valid, because when a parameter is not given, it is not
+  # overwritten. So, the original user and the edited one are the same.
+  def test_save__only_id
+    u = FixtureReplacement.create_user
+
+    post :save, {:id =&gt; u.id, :user =&gt; {&quot;id&quot; =&gt; u.id}}
+
+    assert_redirected_to :controller =&gt; &quot;users&quot;, :action =&gt; &quot;manage&quot;
+    assert_equal &quot;The settings for the user #{u.username} were saved.&quot;,
+                 flash[:notice]
+
+    assert_equal u.attributes, assigns[&quot;user&quot;].attributes
+    assert assigns[&quot;user&quot;].errors.empty?
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test the save action, with a user ID that doesn't exist.
+  def test_save__wrong_id
+    u = FixtureReplacement.create_user
+    u.id = 1056
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save, {:id =&gt; u.id, :user =&gt; u.attributes_before_type_cast}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The user could not be found and as a result was not edited.&quot;,
+                 flash[:notice]
+
+    assert assigns[&quot;user&quot;].nil?
   end
+
+  # Test the save action, to change the password, but with an incorrect current
+  # password.
+  def test_save__incorrect_currentpassword
+    original = FixtureReplacement.create_user
+    new      = FixtureReplacement.new_user_email_and_password_changed
+    new.id   = original.id
+    new.username = original.username # Because usernames are timebased.
+    request_attributes = new.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save, {:id =&gt; original.id, :user =&gt; request_attributes,
+                 :currentpassword =&gt; fixture_password(:user) + &quot;123&quot;,
+                 :newpassword1 =&gt; fixture_password(:user_email_and_password_changed),
+                 :newpassword2 =&gt; fixture_password(:user_email_and_password_changed)}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The settings for the user #{new.username} were saved.&lt;br /&gt;&quot; +
+                 &quot;The password was not changed, because the entered &quot; +
+                 &quot;(current) password was not correct.&quot;, flash[:notice]
+
+    # Compare the attributes, except for the &quot;password&quot; attribute.
+    attrs = new.attributes.reject { |k,| k == &quot;password&quot; }
+    assert_equal attrs,
+                 User.find(original.id).attributes.reject { |k,| k == &quot;password&quot; }
+    assert_equal attrs,
+                 assigns[&quot;user&quot;].attributes.reject { |k,| k == &quot;password&quot; }
+    assert assigns[&quot;user&quot;].errors.empty?
+  end
+
+  # Test the save action, to change the password, but with an incorrect
+  # confirmation for the new password.
+  def test_save__incorrect_confirmation
+    original = FixtureReplacement.create_user
+    new      = FixtureReplacement.new_user_email_and_password_changed
+    new.id   = original.id
+    new.username = original.username # Because usernames are timebased.
+    request_attributes = new.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save, {:id =&gt; original.id, :user =&gt; request_attributes,
+                 :currentpassword =&gt; fixture_password(:user),
+                 :newpassword1 =&gt; fixture_password(:user_email_and_password_changed),
+                 :newpassword2 =&gt; fixture_password(:user_email_and_password_changed) + &quot;123&quot;}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The settings for the user #{new.username} were saved.&lt;br /&gt;&quot; +
+                 &quot;The password was not changed, because the new password did &quot; +
+                 &quot;not match its confirmation.&quot;, flash[:notice]
+
+    # Compare the attributes, except for the &quot;password&quot; attribute.
+    attrs = new.attributes.reject { |k,| k == &quot;password&quot; }
+    assert_equal attrs,
+                 User.find(original.id).attributes.reject { |k,| k == &quot;password&quot; }
+    assert_equal attrs,
+                 assigns[&quot;user&quot;].attributes.reject { |k,| k == &quot;password&quot; }
+    assert assigns[&quot;user&quot;].errors.empty?
+  end
+
+  # Test the save action, with parameters that should cause a validation error.
+  # This tests whether errors are returned correctly, in the flash message.
+  def test_save__validation_errors
+    original = FixtureReplacement.create_user
+    new      = FixtureReplacement.new_user_no_nickname
+    new.id   = original.id
+    new.username = original.username # Because usernames are timebased.
+    request_attributes = new.attributes_before_type_cast
+    request_attributes.delete(&quot;password&quot;)
+
+    @request.env[&quot;HTTP_REFERER&quot;] = url_for(:controller =&gt; &quot;/admin/dashboard&quot;,
+      :action =&gt; &quot;welcome&quot;)
+    post :save, {:id =&gt; original.id, :user =&gt; request_attributes}
+
+    assert_redirected_to :controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;
+    assert_equal &quot;The user could not be edited.\n&lt;br /&gt;Please enter a &quot; +
+                 &quot;nickname.&quot;, flash[:notice]
+
+    assert_equal new.attributes, assigns[&quot;user&quot;].attributes
+    assert_equal original.attributes, User.find(original.id).attributes
+    assert_equal 1, assigns[&quot;user&quot;].errors.count
+    assert_equal([[&quot;nickname&quot;, &quot;Please enter a nickname.&quot;]],
+                 assigns[&quot;user&quot;].errors.entries,
+                 &quot;The right error messages should be returned for the right &quot;+
+                 &quot;attributes.&quot;)
+  end
+
+  private
+
+  # Get the password defined in a certain fixture, in plain text. Pass the
+  # fixture name as a symbol.
+  def fixture_password(fixture)
+    # Get the FixtureReplacementController::Attributes object representing the
+    # fixture. #hash will contain the attributes as set in the fixture.
+    FixtureReplacementController::Attributes.find_by_fixture_name(fixture).hash[:password]
+  end
+
 end</diff>
      <filename>test/functional/admin/users_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,16 +1,432 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../controller_testcase'
 require 'blog_controller'
 
 # Re-raise errors caught by the controller.
 class BlogController; def rescue_action(e) raise e end; end
 
-class BlogControllerTest &lt; Test::Unit::TestCase
-  fixtures :entries
+class BlogControllerTest &lt; ControllerTestCase
+  fixtures :entries, :types, :tags, :users
 
   def setup
+    super
+
     @controller = BlogController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
   end
 
+  ## Index
+
+  # The index action should equal to the home action.
+  def test_index
+    assert_equal_responses({:method =&gt; :get, :action =&gt; :home},
+                           {:method =&gt; :get, :action =&gt; :index})
+  end
+
+  ## Home
+
+  # Test the home action and its assigns.
+  def test_home
+    get :home
+    assert_response :success
+
+    assert assigns[&quot;entries&quot;].kind_of?(Array)
+    assert_equal [], assigns[&quot;entries&quot;].find_all { |obj| !obj.kind_of?(Entry) },
+           'Not all objects in assigns[&quot;entries&quot;] are an Entry.'
+  end
+
+  ## Notfound
+
+  # Test the notfound action.
+  def test_notfound
+    get :notfound
+    assert_response 404
+    assert_template &quot;notfound&quot;
+  end
+
+  ## Show
+
+  # Test the show action and its assigns.
+  def test_show__valid
+    get :show, {:id =&gt; 1}
+    assert_response :success
+
+    assert_not_nil assigns[&quot;content_for_entry&quot;]
+
+    {&quot;C&quot; =&gt; &quot;comments&quot;,
+     &quot;P&quot; =&gt; &quot;pingbacks&quot;,
+     &quot;T&quot; =&gt; &quot;trackbacks&quot;}.each do |key, type|
+      assert assigns[type].kind_of?(Array)
+      assert_equal [], assigns[type].find_all { |obj| !obj.kind_of?(Comment) },
+            &quot;Not all objects in assigns[\&quot;#{type}\&quot;] are a Comment.&quot;
+      assert_equal [], assigns[type].find_all { |obj| !obj.comment_type == key },
+            &quot;Not all objects in assigns[\&quot;#{type}\&quot;] are a Comment &quot; +
+            &quot;with comment_type #{key}.&quot;
+    end
+  end
+
+  # Test the show action using an ID, and using a slug: both should return the
+  # same response.
+  def test_show__id_or_slug
+    request1 = {:method =&gt; :get, :action =&gt; :show, :parameters =&gt; {:id =&gt; 1}}
+    request2 = {:method =&gt; :get, :action =&gt; :show,
+                :parameters =&gt; {:slug =&gt; &quot;hello-world&quot;}}
+    assert_equal_responses request1, request2
+  end
+
+  # Test the show action and its assigns, for an entry that doesn't exist.
+  def test_show__invalid_incorrect_entry
+    get :show, {:id =&gt; &quot;thisdoesntexist&quot;}
+    assert_response 404
+    assert_template &quot;notfound&quot;
+  end
+
+  # Test the show action and its assigns, with no parameters given.
+  def test_show__invalid_nothing_given
+    get :show
+    assert_response 404
+    assert_template &quot;notfound&quot;
+  end
+
+  # Test the show action and its assigns, to display a draft when not logged in.
+  def test_show__draft_not_logged_in
+    get :show, {:id =&gt; 3}
+    assert_response 404
+    assert_nil assigns[&quot;content_for_entry&quot;]
+  end
+
+  # Test the show action and its assigns, to display a draft when logged in.
+  def test_show__draft_logged_in
+    @request.session[&quot;user&quot;] = users(:admin)
+    get :show, {:id =&gt; 3}
+    assert_response :success
+    assert_not_nil assigns[&quot;content_for_entry&quot;]
+  end
+
+  ## Archive
+
+  # Test the archive action and its assigns.
+  def test_archive__normal
+    get :archive, {:year =&gt; 2007, :month =&gt; 1}
+    assert_response :success
+
+    # @archive_type
+    assert_equal &quot;month&quot;, assigns[&quot;archive_type&quot;]
+    # @time
+    assert_equal Time.mktime(2007, 1), assigns[&quot;time&quot;]
+    archive_asserts()
+  end
+
+  # Test the archive action and its assigns, for a month that doesn't contain
+  # entries.
+  def test_archive__no_entries
+    get :archive, {:year =&gt; 1980, :month =&gt; 10}
+    assert_response :success
+
+    # @archive_type
+    assert_equal &quot;month&quot;, assigns[&quot;archive_type&quot;]
+    # @time
+    assert_equal Time.mktime(1980, 10), assigns[&quot;time&quot;]
+    archive_asserts()
+    # @entries
+    assert_equal [], assigns[&quot;entries&quot;]
+    # @total
+    assert_equal 0, assigns[&quot;total&quot;]
+  end
+
+  ## Tag
+
+  # Test the tag action and its assigns.
+  def test_tag__valid
+    get :tag, {:name =&gt; &quot;test&quot;}
+    assert_response :success
+
+    # @archive_type
+    assert_equal &quot;tag&quot;, assigns[&quot;archive_type&quot;]
+    # @tag
+    assert_equal Tag.find_by_name(&quot;test&quot;), assigns[&quot;tag&quot;]
+    archive_asserts()
+  end
+
+  # Test the tag action and its assigns, for a tag that doesn't exist.
+  def test_tag__invalid_incorrect_tag
+    get :tag, {:name =&gt; &quot;thisdoesntexist&quot;}
+    assert_response 404
+    assert_template &quot;notfound&quot;
+  end
+
+  # Test the tag action and its assigns, with no parameters given.
+  def test_tag__invalid_nothing_given
+    get :tag
+    assert_response 404
+    assert_template &quot;notfound&quot;
+  end
+
+  ## Search
+
+  # Test the search action and its assigns.
+  def test_search__normal
+    get :search, {:q =&gt; &quot;t&quot;}
+    assert_response :success
+
+    # @archive_type
+    assert_equal &quot;search&quot;, assigns[&quot;archive_type&quot;]
+    archive_asserts()
+  end
+
+  # Test the search action and its assigns, with an empty query string.
+  def test_search__empty_query
+    get :search, {:q =&gt; &quot;&quot;}
+    assert_redirected_to :controller =&gt; &quot;blog&quot;, :action =&gt; &quot;home&quot;
+  end
+
+  ## Comment
+
+  # Test the comment action, its input and output, for a valid comment with all
+  # attributes given.
+  def test_comment__valid_all_given
+    attributes = {&quot;entry_id&quot; =&gt; 1, &quot;author&quot; =&gt; &quot;Author&quot;,
+                  &quot;email&quot; =&gt; &quot;mail@example.com&quot;,
+                  &quot;website&quot; =&gt; &quot;http://www.example.com&quot;,
+                  &quot;text&quot; =&gt; &quot;This is the comment.&quot;}
+    post :comment, {:id =&gt; 1, :comment =&gt; attributes}
+    assert_redirected_to assigns[&quot;comment&quot;].permalink
+
+    assert assigns[&quot;comment&quot;]
+    # For the following, you could also do
+    #  (assigns[&quot;comment&quot;].attributes.to_a &amp; attributes.to_a) == attributes.to_a
+    # but that's considerably less readable.
+    attributes.each do |key, value|
+      assert_equal value, assigns[&quot;comment&quot;].attributes[key],
+                   &quot;The attributes of the newly created comment should be &quot; +
+                   &quot;those given in the request.&quot;
+    end
+    assert assigns[&quot;comment&quot;].errors.empty?
+  end
+
+  # Test the comment action, its input and output, for an invalid comment
+  # without text.
+  def test_comment__invalid_without_text
+    attributes = {&quot;entry_id&quot; =&gt; 1, &quot;author&quot; =&gt; &quot;Author&quot;,
+                  &quot;email&quot; =&gt; &quot;mail@example.com&quot;,
+                  &quot;website&quot; =&gt; &quot;http://www.example.com&quot;,
+                  &quot;text&quot; =&gt; &quot;&quot;}
+    post :comment, {:id =&gt; 1, :comment =&gt; attributes}
+    assert_response :success
+    assert_template &quot;show&quot;
+
+    assert assigns[&quot;comment&quot;]
+    attributes.each do |key, value|
+      assert_equal value, assigns[&quot;comment&quot;].attributes[key],
+                   &quot;The attributes of the newly created comment should be &quot; +
+                   &quot;those given in the request.&quot;
+    end
+    assert_equal 1, assigns[&quot;comment&quot;].errors.count
+    assert !assigns[&quot;comment&quot;].errors[&quot;text&quot;].empty?
+  end
+
+  # Test the comment action, its input and output, for an invalid comment
+  # without author (which is invalid), and without e-mail (which is valid).
+  def test_comment__invalid_without_author_and_email
+    attributes = {&quot;entry_id&quot; =&gt; 1, &quot;author&quot; =&gt; &quot;&quot;, &quot;email&quot; =&gt; &quot;&quot;,
+                  &quot;website&quot; =&gt; &quot;http://www.example.com&quot;,
+                  &quot;text&quot; =&gt; &quot;This is the comment.&quot;}
+    post :comment, {:id =&gt; 1, :comment =&gt; attributes}
+    assert_response :success
+    assert_template &quot;show&quot;
+
+    assert assigns[&quot;comment&quot;]
+    attributes.each do |key, value|
+      assert_equal value, assigns[&quot;comment&quot;].attributes[key],
+                   &quot;The attributes of the newly created comment should be &quot; +
+                   &quot;those given in the request.&quot;
+    end
+    assert_equal 1, assigns[&quot;comment&quot;].errors.count
+    assert !assigns[&quot;comment&quot;].errors[&quot;author&quot;].empty?
+    assert assigns[&quot;comment&quot;].errors[&quot;email&quot;].empty?
+  end
+
+  # Test the comment action, its input and output, for a valid comment without
+  # e-mail and website.
+  def test_comment__valid_without_email_and_website
+    attributes = {&quot;entry_id&quot; =&gt; 1, &quot;author&quot; =&gt; &quot;Author&quot;,
+                  &quot;email&quot; =&gt; &quot;&quot;, &quot;website&quot; =&gt; &quot;&quot;,
+                  &quot;text&quot; =&gt; &quot;This is the comment.&quot;}
+    post :comment, {:id =&gt; 1, :comment =&gt; attributes}
+    assert_redirected_to assigns[&quot;comment&quot;].permalink
+
+    assert assigns[&quot;comment&quot;]
+    attributes.each do |key, value|
+      assert_equal value, assigns[&quot;comment&quot;].attributes[key],
+                   &quot;The attributes of the newly created comment should be &quot; +
+                   &quot;those given in the request.&quot;
+    end
+    assert assigns[&quot;comment&quot;].errors.empty?
+    assert assigns[&quot;comment&quot;].errors[&quot;e-mail&quot;].empty?
+    assert assigns[&quot;comment&quot;].errors[&quot;website&quot;].empty?
+  end
+
+  # Test the comment action, its input and output, for an invalid comment
+  # without any attributes given.
+  def test_comment__invalid_nothing_given
+    post :comment, {:id =&gt; 1}
+    assert_response 404
+  end
+
+  # Test the comment action, its input and output, for an invalid comment
+  # with an entry that doesn't exist.
+  def test_comment__invalid_incorrect_entry
+    attributes = {&quot;entry_id&quot; =&gt; 101, &quot;author&quot; =&gt; &quot;Author&quot;,
+                  &quot;email&quot; =&gt; &quot;mail@example.com&quot;,
+                  &quot;website&quot; =&gt; &quot;http://www.example.com&quot;,
+                  &quot;text&quot; =&gt; &quot;This is the comment.&quot;}
+    post :comment, {:id =&gt; 101, :comment =&gt; attributes}
+    assert_response 404
+  end
+
+  # Test the comment action, its input and output, when called with GET instead
+  # of POST.
+  def test_comment__get
+    attributes = {&quot;entry_id&quot; =&gt; 1, &quot;author&quot; =&gt; &quot;Author&quot;,
+                  &quot;email&quot; =&gt; &quot;mail@example.com&quot;,
+                  &quot;website&quot; =&gt; &quot;http://www.example.com&quot;,
+                  &quot;text&quot; =&gt; &quot;This is the comment.&quot;}
+    get :comment, {:id =&gt; 1, :comment =&gt; attributes}
+    assert_response :redirect
+
+    assert_equal &quot;The page was requested with a GET request, while a POST &quot; +
+                 &quot;request was expected.&quot;, flash[:notice]
+  end
+
+  ## Trackback
+
+  # Test the trackback action, its input and output, for a valid trackback with
+  # all attributes given.
+  def test_trackback__valid_all_given
+    attributes = {:id =&gt; 1,
+                  :title =&gt; &quot;Trackback title&quot;,
+                  :blog_name =&gt; &quot;Some Blog&quot;,
+                  :url =&gt; &quot;http://www.example.com/trackback&quot;,
+                  :excerpt =&gt; &quot;This is an excerpt.&quot;}
+    post :trackback, attributes
+    assert_response :success
+
+    expected_response = &lt;&lt;-RESPONSE
+    &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+    &lt;response&gt;
+    &lt;error&gt;0&lt;/error&gt;
+    &lt;/response&gt;
+    RESPONSE
+    assert_equal expected_response.gsub(/\s*/, &quot;&quot;), @response.body.gsub(/\s*/, &quot;&quot;)
+  end
+
+  # Test the trackback action using an ID, and using a slug: both should return
+  # the same response.
+  def test_trackback__id_or_slug
+    attributes = {:title =&gt; &quot;Trackback title&quot;,
+                  :blog_name =&gt; &quot;Some Blog&quot;,
+                  :url =&gt; &quot;http://www.example.com/trackback&quot;,
+                  :excerpt =&gt; &quot;This is an excerpt.&quot;}
+    request1 = {:method =&gt; :post, :action =&gt; :trackback,
+                :parameters =&gt; attributes.merge(:id =&gt; 1)}
+    request2 = {:method =&gt; :post, :action =&gt; :trackback,
+                :parameters =&gt; attributes.merge(:slug =&gt; &quot;hello-world&quot;)}
+    assert_equal_responses request1, request2
+  end
+
+  # Test the trackback action, its input and output, for a valid trackback with
+  # only the URL given.
+  def test_trackback__valid_only_url_given
+    attributes = {:id =&gt; 1,
+                  :url =&gt; &quot;http://www.example.com/trackback&quot;}
+    post :trackback, attributes
+    assert_response :success
+
+    expected_response = &lt;&lt;-RESPONSE
+    &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+    &lt;response&gt;
+    &lt;error&gt;0&lt;/error&gt;
+    &lt;/response&gt;
+    RESPONSE
+    assert_equal expected_response.gsub(/\s*/, &quot;&quot;), @response.body.gsub(/\s*/, &quot;&quot;)
+  end
+
+  # Test the trackback action, its input and output, for an invalid trackback
+  # without a URL.
+  def test_trackback__invalid_without_url
+    attributes = {:id =&gt; 1,
+                  :title =&gt; &quot;Trackback title&quot;,
+                  :blog_name =&gt; &quot;Some Blog&quot;,
+                  :excerpt =&gt; &quot;This is an excerpt.&quot;}
+    post :trackback, attributes
+    assert_response :success
+
+    expected_response = &lt;&lt;-RESPONSE
+    &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+    &lt;response&gt;
+    &lt;error&gt;1&lt;/error&gt;
+    &lt;message&gt;The url parameter is required.&lt;/message&gt;
+    &lt;/response&gt;
+    RESPONSE
+    assert_equal expected_response.gsub(/\s*/, &quot;&quot;), @response.body.gsub(/\s*/, &quot;&quot;)
+  end
+
+  # Test the trackback action, its input and output, for an invalid trackback
+  # with an entry that doesn't exist.
+  def test_trackback__invalid_incorrect_entry
+    attributes = {:id =&gt; 101,
+                  :title =&gt; &quot;Trackback title&quot;,
+                  :blog_name =&gt; &quot;Some Blog&quot;,
+                  :url =&gt; &quot;http://www.example.com/trackback&quot;,
+                  :excerpt =&gt; &quot;This is an excerpt.&quot;}
+    post :trackback, attributes
+    assert_response :success
+
+    expected_response = &lt;&lt;-RESPONSE
+    &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+    &lt;response&gt;
+    &lt;error&gt;1&lt;/error&gt;
+    &lt;message&gt;Requested entry not found.&lt;/message&gt;
+    &lt;/response&gt;
+    RESPONSE
+    assert_equal expected_response.gsub(/\s*/, &quot;&quot;), @response.body.gsub(/\s*/, &quot;&quot;)
+  end
+
+  # Test the trackback action, its input and output, when called with GET
+  # instead of POST.
+  def test_trackback__get
+    attributes = {:id =&gt; 1,
+                  :title =&gt; &quot;Trackback title&quot;,
+                  :blog_name =&gt; &quot;Some Blog&quot;,
+                  :url =&gt; &quot;http://www.example.com/trackback&quot;,
+                  :excerpt =&gt; &quot;This is an excerpt.&quot;}
+    get :trackback, attributes
+    assert_response :redirect
+
+    assert_equal &quot;The page was requested with a GET request, while a POST &quot; +
+                 &quot;request was expected.&quot;, flash[:notice]
+  end
+
+  protected
+
+  # Some asserts used for all archives.
+  def archive_asserts
+    # @entries
+    assert assigns[&quot;entries&quot;].kind_of?(Array)
+    assert_equal [], assigns[&quot;entries&quot;].find_all { |obj| !obj.kind_of?(Entry) },
+           'Not all objects in assigns[&quot;entries&quot;] are of the type Entry.'
+    # @total_entries
+    assert assigns[&quot;total_entries&quot;].kind_of?(Array)
+    assert_equal [], assigns[&quot;total_entries&quot;].find_all { |obj| !obj.kind_of?(Entry) },
+           'Not all objects in assigns[&quot;total_entries&quot;] are of the type Entry.'
+    # @entry_pages
+    assert assigns[&quot;entry_pages&quot;].kind_of?(ActionController::Pagination::Paginator)
+    # @from
+    assert assigns[&quot;from&quot;].kind_of?(Integer)
+    # @till
+    assert assigns[&quot;till&quot;].kind_of?(Integer)
+    # @total
+    assert assigns[&quot;total&quot;].kind_of?(Integer)
+  end
+
 end</diff>
      <filename>test/functional/blog_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,35 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../controller_testcase'
 require 'feed_controller'
 
 # Re-raise errors caught by the controller.
 class FeedController; def rescue_action(e) raise e end; end
 
-class FeedControllerTest &lt; Test::Unit::TestCase
+class FeedControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = FeedController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  def test_entries_rss2
+    5.times { FixtureReplacement.create_entry }
+
+    get :entries, {:format =&gt; &quot;rss2&quot;}
+
+    assert_equal &quot;&lt;?xml&quot;, @response.body[0..4]
+    assert @response.body.include?('&lt;rss version=&quot;2.0&quot;&gt;')
+  end
+
+  def test_entries_atom
+    5.times { FixtureReplacement.create_entry }
+
+    get :entries, {:format =&gt; &quot;atom&quot;}
+
+    assert_equal &quot;&lt;?xml&quot;, @response.body[0..4]
+    assert @response.body.include?('&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot;&gt;')
   end
 end</diff>
      <filename>test/functional/feed_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,18 +1,65 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../controller_testcase'
 require 'json_controller'
 
 # Re-raise errors caught by the controller.
 class JsonController; def rescue_action(e) raise e end; end
 
-class JsonControllerTest &lt; Test::Unit::TestCase
+class JsonControllerTest &lt; ControllerTestCase
+  include FixtureReplacement
+
   def setup
+    super
+
     @controller = JsonController.new
     @request    = ActionController::TestRequest.new
     @response   = ActionController::TestResponse.new
   end
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  ## Textile
+
+  # Test the textile action, normally.
+  def test_textile__normal
+    get :textile, {:callback =&gt; &quot;callback&quot;, :text =&gt; &quot;h1. This is some *text*.&quot;}
+
+    assert_equal 'callback(&quot;&lt;h1&gt;This is some &lt;strong&gt;text&lt;/strong&gt;.&lt;/h1&gt;&quot;)',
+                 @response.body
+  end
+
+  # Test the textile action, without a callback given.
+  def test_textile__no_callback
+    get :textile, {:text =&gt; &quot;h1. This is some *text*.&quot;}
+
+    assert_equal 'json_reponse(&quot;&lt;h1&gt;This is some &lt;strong&gt;text&lt;/strong&gt;.&lt;/h1&gt;&quot;)',
+                 @response.body
+  end
+
+  # Test the textile action, without a text given.
+  def test_textile__no_text
+    get :textile, {:callback =&gt; &quot;callback&quot;}
+
+    assert_equal 'callback(&quot;&quot;)', @response.body
+  end
+
+  ## Gettags
+
+  # Test the gettags action, normally.
+  def test_gettags__normal
+    5.times { FixtureReplacement.create_entry }
+
+    get :gettags, {:callback =&gt; &quot;gettags&quot;}
+
+    assert_equal 'gettags(' + Tag.find(:all).collect { |tag| tag.name }.to_json() + ')',
+                 @response.body
+  end
+
+  # Test the gettags action, without a callback given.
+  def test_gettags__no_callback
+    5.times { FixtureReplacement.create_entry }
+
+    get :gettags
+
+    assert_equal 'json_reponse(' + Tag.find(:all).collect { |tag| tag.name }.to_json() + ')',
+                 @response.body
   end
+
 end</diff>
      <filename>test/functional/json_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
-require &quot;#{File.dirname(__FILE__)}/../test_helper&quot;
+require &quot;#{File.dirname(__FILE__)}/../integration_testcase&quot;
 
-class AdminRoutesTest &lt; ActionController::IntegrationTest
+class AdminRoutesTest &lt; IntegrationTestCase
 
   fixtures :users
   fixtures :entries
@@ -11,24 +11,24 @@ class AdminRoutesTest &lt; ActionController::IntegrationTest
   fixtures :options
 
   def test_dashboardcontroller
-    login &quot;admin&quot;, &quot;admin&quot;
+    login
 
     # Dashboard
     assert_equal &quot;/admin&quot;, url_for(:controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;index&quot;, :only_path =&gt; true)
     assert_equal &quot;/admin/dashboard/welcome&quot;, url_for(:controller =&gt; &quot;/admin/dashboard&quot;, :action =&gt; &quot;welcome&quot;, :only_path =&gt; true)
     # /admin should render the welcome page
     get &quot;/admin&quot;
-    is_redirected_to &quot;admin/dashboard/welcome&quot;
+    assert_redirected_to_template &quot;admin/dashboard/welcome&quot;
     # /admin/dashboard should render the welcome page
     get &quot;/admin/dashboard&quot;
-    is_redirected_to &quot;admin/dashboard/welcome&quot;
+    assert_redirected_to_template &quot;admin/dashboard/welcome&quot;
     # /admin/dashboard/welcome should render the welcome page
     get &quot;/admin/dashboard/welcome&quot;
     assert_template &quot;admin/dashboard/welcome&quot;
   end
 
   def test_entriescontroller
-    login &quot;admin&quot;, &quot;admin&quot;
+    login
 
     # Write entry
     assert_equal &quot;/admin/entries/write&quot;, url_for(:controller =&gt; &quot;/admin/entries&quot;, :action =&gt; &quot;write&quot;, :only_path =&gt; true)
@@ -47,7 +47,7 @@ class AdminRoutesTest &lt; ActionController::IntegrationTest
     assert_equal &quot;/admin/entries/manage&quot;, url_for(:controller =&gt; &quot;/admin/entries&quot;, :action =&gt; &quot;manage&quot;, :only_path =&gt; true)
     # /admin/entries should render the manage action
     get &quot;/admin/entries&quot;
-    is_redirected_to &quot;admin/entries/manage&quot;
+    assert_redirected_to_template &quot;admin/entries/manage&quot;
     # /admin/entries/manage should render the manage action
     get &quot;/admin/entries/manage&quot;
     assert_template &quot;admin/entries/manage&quot;
@@ -56,11 +56,11 @@ class AdminRoutesTest &lt; ActionController::IntegrationTest
     assert_equal &quot;/admin/entries/delete/hello-world&quot;, url_for(:controller =&gt; &quot;/admin/entries&quot;, :action =&gt; &quot;delete&quot;, :slug =&gt; &quot;hello-world&quot;, :only_path =&gt; true)
     # /admin/entries/delete/hello-world should redirect to the manage action
     get &quot;/admin/entries/delete/hello-world&quot;
-    is_redirected_to &quot;admin/entries/manage&quot;
+    assert_redirected_to_template &quot;admin/entries/manage&quot;
   end
 
   def test_commentscontroller
-    login &quot;admin&quot;, &quot;admin&quot;
+    login
 
     # Edit comment
     assert_equal &quot;/admin/comments/edit/1&quot;, url_for(:controller =&gt; &quot;/admin/comments&quot;, :action =&gt; &quot;edit&quot;, :id =&gt; 1, :only_path =&gt; true)
@@ -72,20 +72,20 @@ class AdminRoutesTest &lt; ActionController::IntegrationTest
     assert_equal &quot;/admin/comments/mark/1?mark=spam&quot;, url_for(:controller =&gt; &quot;/admin/comments&quot;, :action =&gt; &quot;mark&quot;, :mark =&gt; &quot;spam&quot;, :id =&gt; 1, :only_path =&gt; true)
     # /admin/comments/mark/1?mark=spam should redirect to the manage action
     get &quot;/admin/comments/mark/1?mark=spam&quot;
-    is_redirected_to &quot;admin/comments/manage&quot;
+    assert_redirected_to_template &quot;admin/comments/manage&quot;
 
     # Mark a comment as ham
     assert_equal &quot;/admin/comments/mark/1?mark=ham&quot;, url_for(:controller =&gt; &quot;/admin/comments&quot;, :action =&gt; &quot;mark&quot;, :mark =&gt; &quot;ham&quot;, :id =&gt; 1, :only_path =&gt; true)
     # /admin/comments/mark/1?mark=ham should redirect to the manage action
     get &quot;/admin/comments/mark/1?mark=ham&quot;
-    is_redirected_to &quot;admin/comments/manage&quot;
+    assert_redirected_to_template &quot;admin/comments/manage&quot;
 
     # Manage comments
     assert_equal &quot;/admin/comments&quot;, url_for(:controller =&gt; &quot;/admin/comments&quot;, :action =&gt; &quot;index&quot;, :only_path =&gt; true)
     assert_equal &quot;/admin/comments/manage&quot;, url_for(:controller =&gt; &quot;/admin/comments&quot;, :action =&gt; &quot;manage&quot;, :only_path =&gt; true)
     # /admin/comments should render the manage action
     get &quot;/admin/comments&quot;
-    is_redirected_to &quot;admin/comments/manage&quot;
+    assert_redirected_to_template &quot;admin/comments/manage&quot;
     # /admin/comments/manage should render the manage action
     get &quot;/admin/comments/manage&quot;
     assert_template &quot;admin/comments/manage&quot;
@@ -94,11 +94,11 @@ class AdminRoutesTest &lt; ActionController::IntegrationTest
     assert_equal &quot;/admin/comments/delete/1&quot;, url_for(:controller =&gt; &quot;/admin/comments&quot;, :action =&gt; &quot;delete&quot;, :id =&gt; 1, :only_path =&gt; true)
     # /admin/comments/delete/1 should redirect to the manage action
     get &quot;/admin/comments/delete/1&quot;
-    is_redirected_to &quot;admin/comments/manage&quot;
+    assert_redirected_to_template &quot;admin/comments/manage&quot;
   end
 
   def test_userscontroller
-    login &quot;admin&quot;, &quot;admin&quot;
+    login
 
     # Add user
     assert_equal &quot;/admin/users/new&quot;, url_for(:controller =&gt; &quot;/admin/users&quot;, :action =&gt; &quot;new&quot;, :only_path =&gt; true)
@@ -117,20 +117,20 @@ class AdminRoutesTest &lt; ActionController::IntegrationTest
     assert_equal &quot;/admin/users/manage&quot;, url_for(:controller =&gt; &quot;/admin/users&quot;, :action =&gt; &quot;manage&quot;, :only_path =&gt; true)
     # /admin/users should render the manage action
     get &quot;/admin/users&quot;
-    is_redirected_to &quot;admin/users/manage&quot;
+    assert_redirected_to_template &quot;admin/users/manage&quot;
     # /admin/users/manage should render the manage action
     get &quot;/admin/users/manage&quot;
     assert_template &quot;admin/users/manage&quot;
   end
 
   def test_settingscontroller
-    login &quot;admin&quot;, &quot;admin&quot;
+    login
 
     # View section general
     assert_equal &quot;/admin/settings/general&quot;, url_for(:controller =&gt; &quot;/admin/settings&quot;, :action =&gt; &quot;settings&quot;, :section =&gt; &quot;general&quot;, :only_path =&gt; true)
     # /admin/settings should render the settings action
     get &quot;/admin/settings&quot;
-    is_redirected_to &quot;admin/settings/settings&quot;
+    assert_redirected_to_template &quot;admin/settings/settings&quot;
     # /admin/settings/general should render the settings action
     get &quot;/admin/settings/general&quot;
     assert_template &quot;admin/settings/settings&quot;</diff>
      <filename>test/integration/adminroutes_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
-require &quot;#{File.dirname(__FILE__)}/../test_helper&quot;
+require &quot;#{File.dirname(__FILE__)}/../integration_testcase&quot;
 
-class RoutesTest &lt; ActionController::IntegrationTest
+class RoutesTest &lt; IntegrationTestCase
 
   fixtures :users
   fixtures :entries
@@ -34,7 +34,7 @@ class RoutesTest &lt; ActionController::IntegrationTest
     assert_equal &quot;/hello-world/comment&quot;, url_for(:controller =&gt; &quot;blog&quot;, :action =&gt; &quot;comment&quot;, :slug =&gt; &quot;hello-world&quot;, :only_path =&gt; true)
     # /hello-world/comment should redirect to the show action
     #get &quot;/hello-world/comment&quot;
-    #is_redirected_to &quot;blog/show&quot;
+    #assert_redirected_to_template &quot;blog/show&quot;
 
     # Monthly archive
     assert_equal &quot;/archive/2006/1&quot;, url_for(:controller =&gt; &quot;blog&quot;, :action =&gt; &quot;archive&quot;, :year =&gt; 2006, :month =&gt; 1, :only_path =&gt; true)</diff>
      <filename>test/integration/routes_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -24,18 +24,10 @@ class Test::Unit::TestCase
   # then set this back to true.
   self.use_instantiated_fixtures  = false
 
-  # Log in.
-  def login(username, password)
-    post &quot;account/login&quot;, :username =&gt; username, :password =&gt; password
-    assert_response :redirect, &quot;The login with username #{username} and password #{password} failed.&quot;
-  end
-
-  # Check if a request is redirected to a certain template.
-  def is_redirected_to(template)
-    assert_response :redirect, &quot;The previous request was not a redirect.&quot;
-    follow_redirect!
-    assert_response :success, &quot;The request to #{path} failed.&quot;
-    assert_template template, &quot;#{path} does not have the template #{template}.&quot;
+  # Wrapper around the #url_for method.
+  def url_for(options)
+    url = ActionController::UrlRewriter.new(@request, nil)
+    url.rewrite(options)
   end
 
 end</diff>
      <filename>test/test_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,212 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class CommentTest &lt; Test::Unit::TestCase
-  fixtures :comments
+class CommentTest &lt; UnitTestCase
+  fixtures :comments, :entries, :users, :types, :options
+  # Users and types are in there because entries depend on them.
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test Comment#type and Comment#full_type functions.
+  def test_type
+    assert_equal &quot;C&quot;, comments(:first).type
+    assert_equal &quot;reply&quot;, comments(:first).full_type
   end
+
+  # Test Comment#text and Comment#get_text functions.
+  def test_text
+    assert_equal &quot;This is the *second* comment.&quot;, comments(:second).get_text
+    assert_equal &quot;&lt;p&gt;This is the &lt;strong&gt;second&lt;/strong&gt; comment.&lt;/p&gt;&quot;,
+                 comments(:second).text
+  end
+
+  # Test Comment#permalink.
+  def test_permalink
+    assert_equal Option[&quot;general_bloguri&quot;] + &quot;hello-world#comment-1&quot;,
+                 comments(:first).permalink
+  end
+
+  # Test Comment#is_approved? (which will set a comment as approved or not when
+  # creating it), based on the option &quot;comment_moderation&quot;.
+  def test_moderation
+    # Disable Akismet.
+    Option[&quot;comment_akismetkey&quot;] = &quot;&quot;
+    # Set comment moderation off.
+    Option[&quot;comment_moderation&quot;] = &quot;0&quot;
+    assert_equal true, Comment.new(comments(:first).attributes).approved?
+    # Set comment moderation on.
+    Option[&quot;comment_moderation&quot;] = &quot;1&quot;
+    assert_equal false, Comment.new(comments(:first).attributes).approved?
+  end
+
+  # Test Comment#is_approved? (which will set a comment as approved or not when
+  # creating it), using Akismet.
+  def test_akismet
+    # Enable Akismet.
+    flunk &quot;An Akismet key is needed to test Akismet. Please add one in &quot; +
+          &quot;test/unit/comment_test.rb.&quot;
+    #Option[&quot;comment_akismetkey&quot;] = &quot;Your Akismet API key&quot;
+    # Set comment moderation on, this shuld not affect the results.
+    Option[&quot;comment_moderation&quot;] = &quot;1&quot;
+    assert_equal true, Comment.new(comments(:first).attributes).approved?
+    assert_equal false, Comment.new(comments(:spam).attributes).approved?
+  end
+
+  # Test Comment#approved= which sets whether a comment is approved or not, and
+  # notifies Akismet if it is enabled (but for the purpose of this test we won't
+  # enable Akismet).
+  def test_approve
+    # Disable Akismet.
+    Option[&quot;comment_akismetkey&quot;] = &quot;&quot;
+    assert_equal true, comments(:first).approved?
+    comments(:first).approved = false
+    assert_equal false, comments(:first).approved?
+    comments(:first).approved = true
+    assert_equal true, comments(:first).approved?
+  end
+
+  # Test the validations on the author field: it can't be empty, and can't
+  # contain more than 50 characters.
+  def test_validation_author
+    # Empty author: invalid
+    comment = Comment.new(comments(:first).attributes.update(&quot;author&quot; =&gt; &quot;&quot;))
+    assert_errors_on comment, :author =&gt; &quot;Please enter a name.&quot;
+
+    # One character author: valid
+    comment = Comment.new(comments(:first).attributes.update(&quot;author&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on comment, :author
+
+    # 50 character author: valid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;author&quot; =&gt; &quot;HvwAxJbzc4aJjhCNfnEqNO23UYuLLU8j4K9AXZcektwjDuvhhk&quot;))
+    assert_no_errors_on comment, :author
+
+    # 51 character author: invalid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;author&quot; =&gt; &quot;HvwAxJbzc4aJjhCNfnEqNO23UYuLLU8j4K9AXZcektwjDuvhhko&quot;))
+    assert_errors_on comment, :author =&gt; &quot;Please enter a name shorter than &quot; +
+                                         &quot;50 characters.&quot;
+  end
+
+  # Test the validations on the email field: it can be empty, but can't contain
+  # more than 50 characters and has to be valid.
+  def test_validation_email
+    # Empty email: valid
+    comment = Comment.new(comments(:first).attributes.update(&quot;email&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on comment, :email
+
+    # 50 character email: valid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;email&quot; =&gt; &quot;HvwAxJbzc4aJjhCNfnEqNO23UYu@LU8j4K9AXZcektwjDu.com&quot;))
+    assert_no_errors_on comment, :email
+
+    # 51 character email: invalid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;email&quot; =&gt; &quot;oHvwAxJbzc4aJjhCNfnEqNO23UYu@LU8j4K9AXZcektwjDu.com&quot;))
+    assert_errors_on comment, :email =&gt; &quot;Please enter an e-mail address &quot; +
+      &quot;shorter than 50 characters, or none at all.&quot;
+
+    attributes = comments(:first).attributes
+    # A series of valid mail addresses.
+    ['test@example.com',
+     'john.doe@example.org',
+     'CapitalLetter@sub.example.info',
+     'test_002@example.it',
+     'ABC%21DEF@example.com',
+     '_somename@example.com',
+     'test+test@example.com',
+     '123+456%2138@subdomain1.test3.55.com'].each do |address|
+      comment = Comment.new(attributes.update(&quot;email&quot; =&gt; address))
+      assert_no_errors_on comment, :email
+    end
+    # A series of invalid mail addresses.
+    ['@example.com',
+     'john.doe@',
+     'no-at-sign.com',
+     '@',
+     't@com',
+     'test@example.c'].each do |address|
+      comment = Comment.new(attributes.update(&quot;email&quot; =&gt; address))
+      assert_errors_on comment, :email =&gt; &quot;Please enter a valid e-mail &quot; +
+        &quot;address, or none at all.&quot;
+    end
+  end
+
+  # Test the validations on the website field: it can be empty, but can't
+  # contain more than 50 characters
+  def test_validation_website
+    # Empty website: valid
+    comment = Comment.new(comments(:first).attributes.update(&quot;website&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on comment, :website
+
+    # One character website: valid
+    comment = Comment.new(comments(:first).attributes.update(&quot;website&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on comment, :website
+
+    # 50 character website: valid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;website&quot; =&gt; &quot;HvwAxJbzc4aJjhCNfnEqNO23UYuLLU8j4K9AXZcektwjDuvhhk&quot;))
+    assert_no_errors_on comment, :website
+
+    # 51 character website: invalid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;website&quot; =&gt; &quot;HvwAxJbzc4aJjhCNfnEqNO23UYuLLU8j4K9AXZcektwjDuvhhko&quot;))
+    assert_errors_on comment, :website =&gt; &quot;Please enter a website shorter &quot; +
+      &quot;than 50 characters, or none at all.&quot;
+  end
+
+  # Test the validations on the text field: it can't be empty, and can't
+  # contain more than 65536 characters.
+  def test_validation_text
+    # Empty text: invalid
+    comment = Comment.new(comments(:first).attributes.update(&quot;text&quot; =&gt; &quot;&quot;))
+    assert_errors_on comment, :text =&gt; &quot;Please enter a comment text.&quot;
+
+    # 8 character text: valid, the text is:
+    # &lt;p&gt;T&lt;/p&gt;
+    comment = Comment.new(comments(:first).attributes.update(&quot;text&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on comment, :text
+
+    # 65536 character text: valid, the text is:
+    # &lt;p&gt;(&quot;a&quot; * 65529)&lt;/p&gt;
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;text&quot; =&gt; (&quot;a&quot; * 65529)))
+    assert_no_errors_on comment, :text
+
+    # 65537 character text: valid, the text is:
+    # &lt;p&gt;(&quot;a&quot; * 65529)&lt;/p&gt;
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;text&quot; =&gt; (&quot;a&quot; * 65530)))
+    assert_errors_on comment, :text =&gt; &quot;The comment has to be within 1 and &quot; +
+      &quot;65536 characters of size.&lt;br /&gt;\nIf your comment is too long, please &quot; +
+      &quot;don't post it, as most of its content will be redundant.&quot;
+  end
+
+  # Test the validations on the entry_id field: a comment has to be connected to
+  # a valid entry.
+  def test_validation_entry
+    # No entry_id: invalid
+    attributes = comments(:first).attributes
+    attributes.delete(&quot;entry_id&quot;)
+    comment = Comment.new(attributes)
+    assert_errors_on comment, :entry_id =&gt; &quot;This comment is not connected to &quot; +
+      &quot;an entry. Please reload the page and try again, and don't fool with &quot; +
+      &quot;the system!&quot;
+
+    # Empty entry_id: invalid
+    comment = Comment.new(comments(:first).attributes.update(&quot;entry_id&quot; =&gt; &quot;&quot;))
+    assert_errors_on comment, :entry_id =&gt; &quot;This comment is not connected to &quot; +
+      &quot;an entry. Please reload the page and try again, and don't fool with &quot; +
+      &quot;the system!&quot;
+
+    # Invalid entry_id: invalid
+    comment = Comment.new(comments(:first).attributes.update(
+      &quot;entry_id&quot; =&gt; &quot;1056&quot;))
+    assert_errors_on comment, :entry =&gt; &quot;This comment is not connected to &quot; +
+      &quot;an existing entry. Please reload the page and try again, and don't &quot; +
+      &quot;fool with the system!&quot;
+
+    # Valid entry_id: valid
+    comment = Comment.new(comments(:first).attributes.update(&quot;entry_id&quot; =&gt; &quot;1&quot;))
+    assert_no_errors_on comment, :entry_id
+    assert_no_errors_on comment, :entry
+end
+
 end</diff>
      <filename>test/unit/comment_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,327 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class EntryTest &lt; Test::Unit::TestCase
-  fixtures :entries
+class EntryTest &lt; UnitTestCase
+  fixtures :entries, :metas, :types, :comments, :users
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test Entry#permalink.
+  def test_permalink
+    assert_equal Option[&quot;general_bloguri&quot;] + &quot;hello-world&quot;,
+                 entries(:first).permalink
   end
+
+  # Test Entry#meta, to get the contents of a meta field.
+  def test_meta
+    assert_equal &quot;T&quot;, entries(:first).meta(&quot;formatting&quot;)
+    assert_equal false, entries(:first).meta(&quot;does_not_exist&quot;)
+  end
+
+  # Test Entry#contents(&quot;none&quot;).
+  def test_contents_none
+    assert_equal 'Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!',
+                 entries(:first).contents(&quot;none&quot;)
+    assert_equal 'Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!',
+                 entries(:first).contents
+    assert_equal 'Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!',
+                 entries(:first).contents(&quot;invalid&quot;)
+  end
+
+  # Test Entry#contents(&quot;html&quot;).
+  def test_contents_html
+    assert_equal '&lt;p&gt;Welcome to &lt;a href=&quot;http://chameleon.wikidot.com&quot;&gt;Chameleon&lt;/a&gt;!&lt;/p&gt;',
+                 entries(:first).contents(&quot;html&quot;)
+  end
+
+  # Test Entry#contents(&quot;text&quot;).
+  def test_contents_text
+    assert_equal 'Welcome to Chameleon!',
+                 entries(:first).contents(&quot;text&quot;)
+  end
+
+  # Test Entry#description(&quot;none&quot;).
+  def test_description_none
+    assert_equal 'Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!',
+                 entries(:first).description(&quot;none&quot;)
+    assert_equal 'Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!',
+                 entries(:first).description
+    assert_equal 'Welcome to &quot;Chameleon&quot;:http://chameleon.wikidot.com!',
+                 entries(:first).description(&quot;invalid&quot;)
+  end
+
+  # Test Entry#description(&quot;html&quot;).
+  def test_description_html
+    assert_equal '&lt;p&gt;Welcome to &lt;a href=&quot;http://chameleon.wikidot.com&quot;&gt;Chameleon&lt;/a&gt;!&lt;/p&gt;',
+                 entries(:first).description(&quot;html&quot;)
+  end
+
+  # Test Entry#description(&quot;text&quot;).
+  def test_description_text
+    assert_equal 'Welcome to Chameleon!',
+                 entries(:first).description(&quot;text&quot;)
+  end
+
+  # Test Entry#popular_entries.
+  def test_popular_entries
+    # The first entry has three comments: two approved and one spam.
+    assert_equal [{&quot;entry&quot; =&gt; entries(:first), &quot;comment_count&quot; =&gt; 2},
+                  {&quot;entry&quot; =&gt; entries(:second), &quot;comment_count&quot; =&gt; 1}],
+                 Entry.popular_entries()
+    assert_equal [{&quot;entry&quot; =&gt; entries(:first), &quot;comment_count&quot; =&gt; 2}],
+                 Entry.popular_entries(1)
+  end
+
+  # Test Entry#monthly_archives.
+  def test_monthly_archives
+    # Not much to test here, there are only two entries both published in
+    # January 2006. Currently, there aren't any more entry fixtures because
+    # they're used as the default entries during the installation process too.
+    assert_equal [{&quot;year&quot; =&gt; 2006, &quot;month&quot; =&gt; 1,
+                   &quot;time&quot; =&gt; Time.mktime(2006, 1), &quot;count&quot; =&gt; 2}],
+                 Entry.monthly_archives()
+  end
+
+  # Test the validations on the title field: it can't be empty, and can't
+  # contain more than 255 characters.
+  def test_validation_title
+    # Empty title: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;title&quot; =&gt; &quot;&quot;))
+    assert_errors_on entry, :title =&gt; &quot;Please enter a title.&quot;
+
+    # One character title: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;title&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on entry, :title
+
+    # 255 character title: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;title&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on entry, :title
+
+    # 256 character title: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;title&quot; =&gt; (&quot;o&quot; * 256)))
+    assert_errors_on entry, :title =&gt; &quot;Please enter a title shorter than 255 &quot; +
+      &quot;characters.&quot;
+  end
+
+  # Test the validations on the slug field: it can't be empty, can't contain
+  # more than 255 characters, has to be unique, and valid.
+  def test_validation_slug
+    # Empty slug: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;slug&quot; =&gt; &quot;&quot;))
+    assert_errors_on entry, :slug =&gt; &quot;Please enter a slug.&quot;
+
+    # One character slug: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;slug&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on entry, :slug
+
+    # 255 character slug: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;slug&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on entry, :slug
+
+    # 256 character slug: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;slug&quot; =&gt; (&quot;o&quot; * 256)))
+    assert_errors_on entry, :slug =&gt; &quot;Please enter a slug shorter than 255 &quot; +
+      &quot;characters.&quot;
+
+    # A non-unique slug: invalid
+    entry = Entry.new(entries(:first).attributes) # Simply use the same attr's.
+    assert_errors_on entry, :slug =&gt; 'The same slug is already taken by ' +
+      'another entry. Change it to something unique, for example by ' +
+      'appending &quot;-2&quot; to it.'
+
+    attributes = entries(:first).attributes
+    # A series of valid slugs.
+    ['test', 'TeSt', 'TeSt001', 'TeSt-001', 'TeSt-0+01',
+     'TeSt-0+0_1'].each do |slug|
+      entry = Entry.new(attributes.update(&quot;slug&quot; =&gt; slug))
+      assert_no_errors_on entry, :slug
+    end
+    # A series of invalid slugs.
+    ['$Test', 't@st', 'a.dot', 'A space', 'This#is#wrong', 'This&amp;too', 'ac/dc',
+     'no!', 'star*', 't&#234;te', '&#233;l&#232;ve-2'].each do |slug|
+      entry = Entry.new(attributes.update(&quot;slug&quot; =&gt; slug))
+      assert_errors_on entry, :slug =&gt; &quot;Please enter a valid slug. The slug &quot; +
+        &quot;cannot contain any other characters than letters, numbers and dashes.&quot;
+    end
+  end
+
+  # Test the validations on the description field: it can be empty, but can't
+  # contain more than 65535 characters.
+  def test_validation_description
+    # Empty description: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;description&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on entry, :description
+
+    # One character description: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;description&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on entry, :description
+
+    # 65535 character description: valid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;description&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on entry, :description
+
+    # 65536 character description: invalid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;description&quot; =&gt; (&quot;o&quot; * 65536)))
+    assert_errors_on entry, :description =&gt; &quot;The description of the entry &quot; +
+      &quot;has to be shorter than 65535 characters.&quot;
+  end
+
+  # Test the validations on the contents field: it can be empty, but can't
+  # contain more than 65535 characters.
+  def test_validation_contents
+    # Empty contents: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;contents&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on entry, :contents
+
+    # One character contents: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;contents&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on entry, :contents
+
+    # 65535 character contents: valid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;contents&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on entry, :contents
+
+    # 65536 character contents: invalid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;contents&quot; =&gt; (&quot;o&quot; * 65536)))
+    assert_errors_on entry, :contents =&gt; &quot;The contents of the entry have to &quot; +
+      &quot;be shorter than 65535 bytes or characters.&quot;
+  end
+
+  # Test the validations on the state field: it has to be either P or D.
+  def test_validation_state
+    # Empty state: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;state&quot; =&gt; &quot;&quot;))
+    assert_errors_on entry, :state =&gt; &quot;The state has to be either P &quot; +
+      &quot;(Published) or D (Draft).&quot;
+
+    # State P: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;state&quot; =&gt; &quot;P&quot;))
+    assert_no_errors_on entry, :state
+
+    # State D: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;state&quot; =&gt; &quot;D&quot;))
+    assert_no_errors_on entry, :state
+
+    # State A: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;state&quot; =&gt; &quot;A&quot;))
+    assert_errors_on entry, :state =&gt; &quot;The state has to be either P &quot; +
+      &quot;(Published) or D (Draft).&quot;
+
+    # State Random: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;state&quot; =&gt; &quot;Random&quot;))
+    assert_errors_on entry, :state =&gt; &quot;The state has to be either P &quot; +
+      &quot;(Published) or D (Draft).&quot;
+  end
+
+  # Test the validations on the date_published field: it can be empty, or a
+  # valid date.
+  def test_validation_date_published
+    # Empty date: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;date_published&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on entry, :date_published
+
+    # Date nil: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;date_published&quot; =&gt; nil))
+    assert_no_errors_on entry, :date_published
+
+    # Date 2007-03-12 12:34:38: valid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;date_published&quot; =&gt; Time.local(2007, 3, 12, 12, 34, 38)))
+    assert_no_errors_on entry, :date_published
+
+    # Date now: valid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;date_published&quot; =&gt; Time.now))
+    assert_no_errors_on entry, :date_published
+
+    # Date 2007-13-12 12:34:38: invalid
+    assert_raise ArgumentError do
+      entry = Entry.new(entries(:first).attributes.update(
+        &quot;date_published&quot; =&gt; Time.local(2007, 13, 12, 12, 34, 38)))
+    end
+
+    # Date &quot;2007-03-12 12:34:38&quot;: valid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;date_published&quot; =&gt; &quot;2007-03-12 12:34:38&quot;))
+    assert_no_errors_on entry, :date_published
+    assert_equal Time.local(2007, 3, 12, 12, 34, 38), entry.date_published
+
+    # Date &quot;2007-13-12 12:34:38&quot;: invalid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;date_published&quot; =&gt; &quot;2007-13-12 12:34:38&quot;))
+    assert_errors_on entry, :date_published =&gt; &quot;Please enter a correct date, &quot; +
+                                               &quot;or none at all.&quot;
+
+    # Date &quot;2007-03-12&quot;: valid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;date_published&quot; =&gt; &quot;2007-03-12&quot;))
+    assert_no_errors_on entry, :date_published
+    assert_equal Time.local(2007, 3, 12, 0, 0, 0), entry.date_published
+
+    # Date &quot;2007&quot;: invalid
+    entry = Entry.new(entries(:first).attributes.update(
+      &quot;date_published&quot; =&gt; &quot;2007&quot;))
+    assert_errors_on entry, :date_published =&gt; &quot;Please enter a correct date, &quot; +
+                                               &quot;or none at all.&quot;
+  end
+
+  # Test the validations on the user_id field: an entry has to be connected to
+  # a valid user.
+  def test_validation_user
+    # No user_id: invalid
+    attributes = entries(:first).attributes
+    attributes.delete(&quot;user_id&quot;)
+    entry = Entry.new(attributes)
+    assert_errors_on entry, :user_id =&gt; &quot;This entry is not connected to a &quot; +
+      &quot;user. Please reload the page and try again, and don't fool with the &quot; +
+      &quot;system!&quot;
+
+    # Empty user_id: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;user_id&quot; =&gt; &quot;&quot;))
+    assert_errors_on entry, :user_id =&gt; &quot;This entry is not connected to a &quot; +
+      &quot;user. Please reload the page and try again, and don't fool with the &quot; +
+      &quot;system!&quot;
+
+    # Invalid user_id: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;user_id&quot; =&gt; &quot;688&quot;))
+    assert_errors_on entry, :user =&gt; &quot;This entry is not connected to an &quot; +
+      &quot;existing user. Please reload the page and try again, and don't fool &quot; +
+      &quot;with the system!&quot;
+
+    # Valid user_id: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;user_id&quot; =&gt; &quot;1&quot;))
+    assert_no_errors_on entry, :user_id
+    assert_no_errors_on entry, :user
+  end
+
+  # Test the validations on the type_id field: an entry has to be connected to
+  # a valid type.
+  def test_validation_type
+    # No type_id: invalid
+    attributes = entries(:first).attributes
+    attributes.delete(&quot;type_id&quot;)
+    entry = Entry.new(attributes)
+    assert_errors_on entry, :type_id =&gt; &quot;This entry is not connected to a &quot; +
+      &quot;type. Please reload the page and try again, and don't fool with the &quot; +
+      &quot;system!&quot;
+
+    # Empty type_id: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;type_id&quot; =&gt; &quot;&quot;))
+    assert_errors_on entry, :type_id =&gt; &quot;This entry is not connected to a &quot; +
+      &quot;type. Please reload the page and try again, and don't fool with the &quot; +
+      &quot;system!&quot;
+
+    # Invalid type_id: invalid
+    entry = Entry.new(entries(:first).attributes.update(&quot;type_id&quot; =&gt; &quot;688&quot;))
+    assert_errors_on entry, :type =&gt; &quot;This entry is not connected to an &quot; +
+      &quot;existing type. Please reload the page and try again, and don't fool &quot; +
+      &quot;with the system!&quot;
+
+    # Valid type_id: valid
+    entry = Entry.new(entries(:first).attributes.update(&quot;type_id&quot; =&gt; &quot;1&quot;))
+    assert_no_errors_on entry, :type_id
+    assert_no_errors_on entry, :type
+  end
+
 end</diff>
      <filename>test/unit/entry_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,6 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class MetaTest &lt; Test::Unit::TestCase
+class MetaTest &lt; UnitTestCase
   fixtures :metas
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
-  end
 end</diff>
      <filename>test/unit/meta_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,60 +1,59 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class OptionTest &lt; Test::Unit::TestCase
+class OptionTest &lt; UnitTestCase
   fixtures :options
 
-  # Try to get an option.
+  # Try to get an option, with both the Option#get and Option#[] functions.
   def test_get
-    assert_equal &quot;Test&quot;, Option.get(&quot;general&quot;, &quot;blogname&quot;)
+    # An existing option.
+    assert_equal options(:blogname).value, Option.get(&quot;general&quot;, &quot;blogname&quot;)
+    assert_equal options(:blogname).value, Option[&quot;general_blogname&quot;]
+    # A non-existing option in an existing section.
     assert_equal nil, Option.get(&quot;general&quot;, &quot;non-existing&quot;)
+    assert_equal nil, Option[&quot;general_non-existing&quot;]
+    # An option in a non-existing section.
     assert_equal nil, Option.get(&quot;non-existing&quot;, &quot;blogname&quot;)
+    assert_equal nil, Option[&quot;non-existing_blogname&quot;]
   end
 
-  # Test the get shortcut.
-  def test_getshortcut
-    assert_equal &quot;Test&quot;, Option[&quot;general_blogname&quot;]
-  end
-
-  # Try to get a whole section.
-  def test_getsection
+  # Try to get a whole section, with both the Option#get_section and Option#[]
+  # functions.
+  def test_get_section
+    # Get all settings from the config/settings.yml file.
     settings = YAML.load(File.open(&quot;#{RAILS_ROOT}/config/settings.yml&quot;))
     section_general = settings[0]
-    section_general[&quot;items&quot;][0][&quot;value&quot;] = &quot;Test&quot;
-    section_general[&quot;items&quot;][1][&quot;value&quot;] = &quot;http://0.0.0.0:3000/&quot;
+    # And merge with the fixtures.
+    section_general[&quot;items&quot;][0][&quot;value&quot;] = options(:blogname).value
+    section_general[&quot;items&quot;][1][&quot;value&quot;] = options(:bloguri).value
+    # Test if we can get a section.
     assert_equal section_general, Option.get_section(&quot;general&quot;)
-  end
-
-  # Test the get section shortcut.
-  def test_getsectionshortcut
-    settings = YAML.load(File.open(&quot;#{RAILS_ROOT}/config/settings.yml&quot;))
-    section_general = settings[0]
-    section_general[&quot;items&quot;][0][&quot;value&quot;] = &quot;Test&quot;
-    section_general[&quot;items&quot;][1][&quot;value&quot;] = &quot;http://0.0.0.0:3000/&quot;
     assert_equal section_general, Option[&quot;general&quot;]
   end
 
-  # Try the set function.
+  # Try the set function, with both the Option#set and Option#[]= functions.
   def test_set
-    Option.set(&quot;general&quot;, &quot;blogname&quot;, &quot;Test2&quot;)
-    assert_equal &quot;Test2&quot;, Option.get(&quot;general&quot;, &quot;blogname&quot;)
-  end
-
-  # Test the set shortcut.
-  def test_setshortcut
-    Option[&quot;general_blogname&quot;] = &quot;Test2&quot;
-    assert_equal &quot;Test2&quot;, Option[&quot;general_blogname&quot;]
-  end
-
-  # Try to set a whole section.
-  def test_setsection
-    Option.set_section(&quot;general&quot;, {&quot;blogname&quot; =&gt; &quot;Test2&quot;})
-    assert_equal &quot;Test2&quot;, Option[&quot;general_blogname&quot;]
-  end
-
-  # Test the set section shortcut.
-  def test_setsectionshortcut
-    Option[&quot;general&quot;] = {&quot;blogname&quot; =&gt; &quot;Test2&quot;}
-    assert_equal &quot;Test2&quot;, Option[&quot;general_blogname&quot;]
+    assert_equal options(:blogname).value, Option[&quot;general_blogname&quot;]
+    Option.set(&quot;general&quot;, &quot;blogname&quot;, &quot;Something else&quot;)
+    assert_equal &quot;Something else&quot;, Option[&quot;general_blogname&quot;]
+    Option[&quot;general_blogname&quot;] = &quot;Yet something else&quot;
+    assert_equal &quot;Yet something else&quot;, Option[&quot;general_blogname&quot;]
+  end
+
+  # Try to set a whole section, with both the Option#set_section and Option#[]=
+  # functions.
+  def test_set_section
+    assert_equal options(:blogname).value, Option[&quot;general_blogname&quot;]
+    Option.set_section(&quot;general&quot;, {&quot;blogname&quot; =&gt; &quot;Something else&quot;})
+    assert_equal &quot;Something else&quot;, Option[&quot;general_blogname&quot;]
+    Option[&quot;general&quot;] = {&quot;blogname&quot; =&gt; &quot;Yet something else&quot;}
+    assert_equal &quot;Yet something else&quot;, Option[&quot;general_blogname&quot;]
+  end
+
+  # Test invalid keys for the shortcut Option#[] and Option#[]= functions.
+  def test_invalid_shortcuts
+    assert_equal false, Option[&quot;this_is_invalid&quot;]
+    Option[&quot;this_is_invalid&quot;] = 5
+    assert_equal false, Option[&quot;this_is_invalid&quot;]
   end
 
 end</diff>
      <filename>test/unit/option_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,33 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class TagTest &lt; Test::Unit::TestCase
-  fixtures :tags
+class TagTest &lt; UnitTestCase
+  fixtures :tags, :entries, :entries_tags
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test the function to parse tag lists.
+  def test_parse
+    # Test separation by spaces
+    assert_equal [&quot;tag1&quot;, &quot;tag2&quot;, &quot;tag3&quot;], Tag.parse(&quot;tag1 tag2 tag3&quot;).sort
+    # Test separation by commas
+    assert_equal [&quot;tag1&quot;, &quot;tag2&quot;, &quot;tag3&quot;], Tag.parse(&quot;tag1,tag2,tag3&quot;).sort
+    # Test separation by both spaces and commas
+    assert_equal [&quot;tag1&quot;, &quot;tag2&quot;, &quot;tag3&quot;], Tag.parse(&quot;tag1, tag2 tag3,&quot;).sort
+    # Test quotes
+    assert_equal [&quot;tag 1&quot;, &quot;tag 3&quot;, &quot;tag2&quot;], Tag.parse(%q{&quot;tag 1&quot; tag2 'tag 3'}).sort
+    # Test conversion to lowercase
+    assert_equal [&quot;tag1&quot;, &quot;tag2&quot;, &quot;tag3&quot;], Tag.parse(&quot;Tag1 TAG2 tag3&quot;).sort
+    # Test removal of empty tags and tags only consisting of spaces
+    assert_equal [&quot;tag1&quot;, &quot;tag2&quot;, &quot;tag3&quot;], Tag.parse(%q{tag1   tag2, , tag3, &quot;  &quot;}).sort
+    # Test removal of duplicates
+    assert_equal [&quot;tag1&quot;, &quot;tag2&quot;, &quot;tag3&quot;], Tag.parse(%q{tag1 tag2 tag1 tag3 &quot;tag3&quot;}).sort
   end
+
+  # Test Tag#popular_tags.
+  def test_popular_tags
+    assert_equal [{&quot;id&quot; =&gt; 1, &quot;name&quot; =&gt; &quot;first&quot;,     &quot;count&quot; =&gt; 2},
+                  {&quot;id&quot; =&gt; 3, &quot;name&quot; =&gt; &quot;test&quot;,      &quot;count&quot; =&gt; 2},
+                  {&quot;id&quot; =&gt; 2, &quot;name&quot; =&gt; &quot;second&quot;,    &quot;count&quot; =&gt; 1},
+                  {&quot;id&quot; =&gt; 4, &quot;name&quot; =&gt; &quot;chameleon&quot;, &quot;count&quot; =&gt; 1}], Tag.popular_tags()
+    assert_equal [{&quot;id&quot; =&gt; 1, &quot;name&quot; =&gt; &quot;first&quot;,     &quot;count&quot; =&gt; 2}], Tag.popular_tags(1)
+  end
+
 end</diff>
      <filename>test/unit/tag_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class ThemeTest &lt; Test::Unit::TestCase
+class ThemeTest &lt; UnitTestCase
 
   # Create a new theme. This is in fact a virtual representation of a theme,
   # it doesn't actually exist.
@@ -12,6 +12,31 @@ class ThemeTest &lt; Test::Unit::TestCase
     assert_equal &quot;tester@example.com&quot;, test_theme.author[:email]
   end
 
+  # Check whether a theme is read-only by querying the Theme#readonly? function,
+  # and trying to change some of the attributes of a theme.
+  def test_readonly
+    test_theme = new_test_theme()
+    # #readonly? should return true
+    assert_equal true, test_theme.readonly?
+    # Try to re-assign an attribute, this should return a ReadOnlyRecord
+    # exception.
+    assert_raises(ActiveRecord::ReadOnlyRecord) {
+      test_theme.id = &quot;Test&quot;
+    }
+    assert_raises(ActiveRecord::ReadOnlyRecord) {
+      test_theme.license = &quot;GNU General Public License&quot;
+    }
+  end
+
+  # Test whether method_missing still works on missing methods, since we
+  # override it.
+  def test_method_missing
+    test_theme = new_test_theme()
+    assert_raises(NoMethodError) {
+      test_theme.does_not_exist()
+    }
+  end
+
   # Create a new theme. This should return a ReadOnlyRecord exception.
   def test_create
     assert_raises(ActiveRecord::ReadOnlyRecord) {
@@ -77,7 +102,7 @@ class ThemeTest &lt; Test::Unit::TestCase
   end
 
   # Find by id.
-  def test_findid
+  def test_find_id
     themes = existing_themes()
     if themes.length == 0
       flunk &quot;No themes in the directory themes/! Can't test!&quot;
@@ -96,7 +121,7 @@ class ThemeTest &lt; Test::Unit::TestCase
   end
 
   # Find all.
-  def test_findall
+  def test_find_all
     themes = existing_themes()
     if themes.length == 0
       flunk &quot;No themes in the directory themes/! Can't test!&quot;
@@ -156,7 +181,7 @@ class ThemeTest &lt; Test::Unit::TestCase
   end
 
   # Returns an array of all currently existing themes (in the {{themes}}
-  # directory.
+  # directory).
   # For example: &lt;tt&gt;[&quot;Kubrick&quot;, &quot;default&quot;]&lt;/tt&gt;
   def existing_themes
     Dir[&quot;themes/*/theme.yml&quot;].collect do |t|</diff>
      <filename>test/unit/theme_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,6 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class TypeTest &lt; Test::Unit::TestCase
+class TypeTest &lt; UnitTestCase
   fixtures :types
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
-  end
 end</diff>
      <filename>test/unit/type_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,196 @@
-require File.dirname(__FILE__) + '/../test_helper'
+require File.dirname(__FILE__) + '/../unit_testcase'
 
-class UserTest &lt; Test::Unit::TestCase
-  fixtures :users
+class UserTest &lt; UnitTestCase
+  fixtures :users, :entries
 
-  # Replace this with your real tests.
-  def test_truth
-    assert true
+  # Test the authentication.
+  def test_authenticate
+    assert_equal users(:admin), User.authenticate(&quot;admin&quot;, &quot;admin&quot;)
+    assert_equal nil, User.authenticate(&quot;admin&quot;, &quot;something else&quot;)
+    assert_equal nil, User.authenticate(&quot;something else&quot;, &quot;admin&quot;)
   end
+
+  # Test setting a password.
+  def test_set_password
+    # Set the password to &quot;admin&quot; and save.
+    users(:admin).password = &quot;admin&quot;
+    users(:admin).save()
+    # The password should never be stored in plain text.
+    assert_not_equal &quot;admin&quot;, users(:admin).password,
+                     &quot;The password should not be stored in plain text.&quot;
+    # Because we use a (variable) salt, we store the hashed version of &quot;admin&quot;.
+    hashed_admin = users(:admin).password
+    # Set the password to &quot;other&quot; and save.
+    users(:admin).password = &quot;other&quot;
+    users(:admin).save()
+    # The password should be changed, and we should be able to log in with the
+    # new password.
+    assert_not_equal hashed_admin, users(:admin).password,
+                     &quot;The password should be changed.&quot;
+    assert_equal users(:admin), User.authenticate(&quot;admin&quot;, &quot;other&quot;),
+                 &quot;We should be able to log in with the new password.&quot;
+    # Set the password back to &quot;admin&quot; and save.
+    users(:admin).password = &quot;admin&quot;
+    users(:admin).save()
+    # The password should be the hashed version of a&quot;dmin&quot;, and we should be
+    # able to log in with the new password.
+    assert_equal hashed_admin, users(:admin).password,
+                 &quot;The password should be the hashed version of 'admin'.&quot;
+    assert_equal users(:admin), User.authenticate(&quot;admin&quot;, &quot;admin&quot;),
+                 &quot;We should be able to log in with the new password.&quot;
+  end
+
+  # Test User#latest_entries.
+  def test_latest_entries
+    assert_equal [entries(:second), entries(:first)],
+                 users(:admin).latest_entries
+    assert_equal [entries(:second)],
+                 users(:admin).latest_entries(1)
+  end
+
+  # Test the validations on the username field: it can't be empty, can't contain
+  # more than 255 characters, and has to be unique.
+  def test_validation_username
+    # Empty username: invalid
+    user = User.new(users(:admin).attributes.update(&quot;username&quot; =&gt; &quot;&quot;))
+    assert_errors_on user, :username =&gt; &quot;Please enter a username.&quot;
+
+    # One character username: valid
+    user = User.new(users(:admin).attributes.update(&quot;username&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on user, :username
+
+    # 255 character username: valid
+    user = User.new(users(:admin).attributes.update(&quot;username&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on user, :username
+
+    # 256 character username: invalid
+    user = User.new(users(:admin).attributes.update(&quot;username&quot; =&gt; (&quot;o&quot; * 256)))
+    assert_errors_on user, :username =&gt; &quot;Please enter a username shorter &quot; +
+      &quot;than 255 characters.&quot;
+
+    # A non-unique username: invalid
+    user = User.new(users(:admin).attributes)
+    assert_errors_on user, :username =&gt; &quot;A user with the same username &quot; +
+      &quot;already exists. Please try a different username.&quot;
+  end
+
+  # Test the validations on the password field: it can be anything (empty, or
+  # any amount of characters) because it is hashed using SHA-1 anyway, which
+  # always results in a hash of 40 characters.
+  def test_validation_password
+    # Empty password: valid
+    user = User.new(users(:admin).attributes.update(&quot;password&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on user, :password
+
+    # One character password: valid
+    user = User.new(users(:admin).attributes.update(&quot;password&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on user, :password
+
+    # 256 character password: valid
+    user = User.new(users(:admin).attributes.update(&quot;password&quot; =&gt; (&quot;o&quot; * 256)))
+    assert_no_errors_on user, :password
+
+    # 65536 character password: valid
+    user = User.new(users(:admin).attributes.update(&quot;password&quot; =&gt; (&quot;d&quot; * 65536)))
+    assert_no_errors_on user, :password
+  end
+
+  # Test the validations on the nickname field: it can't be empty, and can't
+  # contain more than 255 characters.
+  def test_validation_nickname
+    # Empty nickname: invalid
+    user = User.new(users(:admin).attributes.update(&quot;nickname&quot; =&gt; &quot;&quot;))
+    assert_errors_on user, :nickname =&gt; &quot;Please enter a nickname.&quot;
+
+    # One character nickname: valid
+    user = User.new(users(:admin).attributes.update(&quot;nickname&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on user, :nickname
+
+    # 255 character nickname: valid
+    user = User.new(users(:admin).attributes.update(&quot;nickname&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on user, :nickname
+
+    # 256 character nickname: invalid
+    user = User.new(users(:admin).attributes.update(&quot;nickname&quot; =&gt; (&quot;o&quot; * 256)))
+    assert_errors_on user, :nickname =&gt; &quot;Please enter a nickname shorter &quot; +
+      &quot;than 255 characters.&quot;
+  end
+
+  # Test the validations on the email field: it can be empty, but can't contain
+  # more than 255 characters.
+  def test_validation_email
+    # Empty email: valid
+    user = User.new(users(:admin).attributes.update(&quot;email&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on user, :email
+
+    # 6 character email: valid
+    user = User.new(users(:admin).attributes.update(&quot;email&quot; =&gt; &quot;t@t.no&quot;))
+    assert_no_errors_on user, :email
+
+    # 255 character email: valid
+    user = User.new(users(:admin).attributes.update(
+      &quot;email&quot; =&gt; &quot;lcinn6PqfNiecZqdoS5XovBEtlJ4uquX9kmqUTnSI3vS.jyIUyknJRbJ4Kd&quot; +
+                 &quot;cJQnKysw7cviOPE7FHIhQoEJsa7th4A4fBOv1SEvAyoz1K43xR1iF2PJRcs&quot; +
+                 &quot;4Tc6XZdULH7Yur5tElVFoX7r02kns7e3PATvX8cIC8sws6S7bkHpSodj5.t&quot; +
+                 &quot;@q.UV90Kj5YUnnfXx54Gx.6PzErFNrVb3szyudpqvNWFcGGRWPBxnKkYknZ&quot; +
+                 &quot;GwcMhs6NetQsJ99.com&quot;))
+    assert_no_errors_on user, :email
+
+    # 256 character email: invalid
+    user = User.new(users(:admin).attributes.update(
+      &quot;email&quot; =&gt; &quot;lcinn6PqfNiecZqdoS5XovBEtlJ4uquX9kmqUTnSI3vS.jyIUyknJRbJ4Kd&quot; +
+                 &quot;cJQnKysw7cviOPE7FHIhQoEJsa7th4A4fBOv1SEvAyoz1K43xR1iF2PJRcs&quot; +
+                 &quot;4Tc6XZdULH7Yur5tElVFoX7r02kns7e3PATvX8cIC8sws6S7bkHpSodj5.t&quot; +
+                 &quot;@q.UV90Kj5YUnnfXx54Gx.6PzErFNrVb3szyudpqvNWFcGGRWPBxnKkYknZ&quot; +
+                 &quot;GwcMhs6NetQsJ989.com&quot;))
+    assert_errors_on user, :email =&gt; &quot;Please enter an e-mail address shorter &quot; +
+      &quot;than 255 characters, or none at all.&quot;
+
+    attributes = users(:admin).attributes
+    # A series of valid mail addresses.
+    ['test@example.com',
+     'john.doe@example.org',
+     'CapitalLetter@sub.example.info',
+     'test_002@example.it',
+     'ABC%21DEF@example.com',
+     '_somename@example.com',
+     'test+test@example.com',
+     '123+456%2138@subdomain1.test3.55.com'].each do |address|
+      user = User.new(attributes.update(&quot;email&quot; =&gt; address))
+      assert_no_errors_on user, :email
+    end
+    # A series of invalid mail addresses.
+    ['@example.com',
+     'john.doe@',
+     'no-at-sign.com',
+     '@',
+     't@com',
+     'test@example.c'].each do |address|
+      user = User.new(attributes.update(&quot;email&quot; =&gt; address))
+      assert_errors_on user, :email =&gt; &quot;Please enter a valid e-mail address, &quot; +
+        &quot;or none at all.&quot;
+    end
+  end
+
+  # Test the validations on the website field: it can be empty, but can't
+  # contain more than 255 characters.
+  def test_validation_website
+    # Empty website: valid
+    user = User.new(users(:admin).attributes.update(&quot;website&quot; =&gt; &quot;&quot;))
+    assert_no_errors_on user, :website
+
+    # One character website: valid
+    user = User.new(users(:admin).attributes.update(&quot;website&quot; =&gt; &quot;T&quot;))
+    assert_no_errors_on user, :website
+
+    # 255 character website: valid
+    user = User.new(users(:admin).attributes.update(&quot;website&quot; =&gt; (&quot;a&quot; * 255)))
+    assert_no_errors_on user, :website
+
+    # 256 character website: invalid
+    user = User.new(users(:admin).attributes.update(&quot;website&quot; =&gt; (&quot;o&quot; * 256)))
+    assert_errors_on user, :website =&gt; &quot;Please enter a website shorter &quot; +
+      &quot;than 255 characters, or none at all.&quot;
+  end
+
 end</diff>
      <filename>test/unit/user_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,57 @@
+*1.3.6* (October 12th, 2007)
+
+* Depend on Action Pack 1.13.6
+
+*1.3.5* (October 12th, 2007)
+
+* Depend on Action Pack 1.13.5
+
+
+*1.3.4* (October 4th, 2007)
+
+* Depend on Action Pack 1.13.4
+
+
+*1.3.3* (March 12th, 2007)
+
+* Depend on Action Pack 1.13.3
+
+
+*1.3.2* (February 5th, 2007)
+
+* Deprecate server_settings renaming it to smtp_settings,  add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Koz]
+
+
+*1.3.1* (January 16th, 2007)
+
+* Depend on Action Pack 1.13.1
+
+
+*1.3.0* (January 16th, 2007)
+
+* Make mime version default to 1.0. closes #2323 [ror@andreas-s.net]
+
+* Make sure quoted-printable text is decoded correctly when only portions of the text are encoded. closes #3154. [jon@siliconcircus.com]
+
+* Make sure DOS newlines in quoted-printable text are normalized to unix newlines before unquoting. closes #4166 and #4452. [Jamis Buck]
+
+* Fixed that iconv decoding should catch InvalidEncoding #3153 [jon@siliconcircus.com]
+
+* Tighten rescue clauses.  #5985 [james@grayproductions.net]
+
+* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [DHH]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Resolve action naming collision. #5520 [ssinghi@kreeti.com]
+
+* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
+
+* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.] 
+
+* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
+
+
 *1.2.5* (August 10th, 2006)
 
 * Depend on Action Pack 1.12.5
@@ -22,12 +76,12 @@
 * Depend on Action Pack 1.12.2
 
 
-*1.2.1* (April 6th, 2005)
+*1.2.1* (April 6th, 2006)
 
 * Be part of Rails 1.1.1
 
 
-*1.2.0* (March 27th, 2005)
+*1.2.0* (March 27th, 2006)
 
 * Nil charset caused subject line to be improperly quoted in implicitly multipart messages #2662 [ehalvorsen+rails@runbox.com]
 </diff>
      <filename>vendor/rails/actionmailer/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-Copyright (c) 2004 David Heinemeier Hansson
+Copyright (c) 2004-2006 David Heinemeier Hansson
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the</diff>
      <filename>vendor/rails/actionmailer/MIT-LICENSE</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 = Action Mailer -- Easy email delivery and testing
 
 Action Mailer is a framework for designing email-service layers. These layers
-are used to consolidate code for sending out forgotten passwords, welcoming
+are used to consolidate code for sending out forgotten passwords, welcome
 wishes on signup, invoices for billing, and any other use case that requires
 a written notification to either a person or another system.
 
@@ -136,13 +136,10 @@ Action Mailer is released under the MIT license.
 
 == Support
 
-The Action Mailer homepage is http://actionmailer.rubyonrails.org. You can find
+The Action Mailer homepage is http://www.rubyonrails.org. You can find
 the Action Mailer RubyForge page at http://rubyforge.org/projects/actionmailer.
 And as Jim from Rake says:
 
    Feel free to submit commits or feature requests.  If you send a patch,
    remember to update the corresponding unit tests.  If fact, I prefer
-   new feature to be submitted in the form of new unit tests.
-
-For other information, feel free to ask on the ruby-talk mailing list (which
-is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com.
+   new feature to be submitted in the form of new unit tests.
\ No newline at end of file</diff>
      <filename>vendor/rails/actionmailer/README</filename>
    </modified>
    <modified>
      <diff>@@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s|
   s.rubyforge_project = &quot;actionmailer&quot;
   s.homepage = &quot;http://www.rubyonrails.org&quot;
 
-  s.add_dependency('actionpack', '= 1.12.5' + PKG_BUILD)
+  s.add_dependency('actionpack', '= 1.13.6' + PKG_BUILD)
 
   s.has_rdoc = true
   s.requirements &lt;&lt; 'none'
@@ -85,11 +85,11 @@ end
 
 desc &quot;Publish the release files to RubyForge.&quot;
 task :release =&gt; [ :package ] do
-  `rubyforge login`
+  require 'rubyforge'
 
-  for ext in %w( gem tgz zip )
-    release_command = &quot;rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}&quot;
-    puts release_command
-    system(release_command)
-  end
+  packages = %w( gem tgz zip ).collect{ |ext| &quot;pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}&quot; }
+
+  rubyforge = RubyForge.new
+  rubyforge.login
+  rubyforge.add_release(PKG_NAME, PKG_NAME, &quot;REL #{PKG_VERSION}&quot;, *packages)
 end
\ No newline at end of file</diff>
      <filename>vendor/rails/actionmailer/Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 #--
-# Copyright (c) 2004 David Heinemeier Hansson
+# Copyright (c) 2004-2006 David Heinemeier Hansson
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
@@ -21,14 +21,13 @@
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #++
 
-begin
-  require 'action_controller'
-rescue LoadError
+unless defined?(ActionController)
   begin
-    require File.dirname(__FILE__) + '/../../actionpack/lib/action_controller'
+    $:.unshift &quot;#{File.dirname(__FILE__)}/../../actionpack/lib&quot;
+    require 'action_controller'
   rescue LoadError
     require 'rubygems'
-    require_gem 'actionpack', '&gt;= 1.9.1'
+    gem 'actionpack', '&gt;= 1.12.5'
   end
 end
 
@@ -48,4 +47,4 @@ ActionMailer::Base.class_eval do
   helper MailHelper
 end
 
-silence_warnings { TMail::Encoder.const_set(&quot;MAX_LINE_LEN&quot;, 200) }
\ No newline at end of file
+silence_warnings { TMail::Encoder.const_set(&quot;MAX_LINE_LEN&quot;, 200) }</diff>
      <filename>vendor/rails/actionmailer/lib/action_mailer.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,6 @@
 module ActionMailer
   module AdvAttrAccessor #:nodoc:
-    def self.append_features(base)
-      super
+    def self.included(base)
       base.extend(ClassMethods)
     end
 </diff>
      <filename>vendor/rails/actionmailer/lib/action_mailer/adv_attr_accessor.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,9 @@ require 'tmail/net'
 module ActionMailer #:nodoc:
   # ActionMailer allows you to send email from your application using a mailer model and views.
   #
+  #
   # = Mailer Models
+  #
   # To use ActionMailer, you need to create a mailer model.
   #   
   #   $ script/generate mailer Notifier
@@ -23,7 +25,7 @@ module ActionMailer #:nodoc:
   #      recipients recipient.email_address_with_name
   #      from       &quot;system@example.com&quot;
   #      subject    &quot;New account information&quot;
-  #      body       &quot;account&quot; =&gt; recipient
+  #      body       :account =&gt; recipient
   #    end
   #  end
   #
@@ -45,7 +47,9 @@ module ActionMailer #:nodoc:
   # in an instance variable &lt;tt&gt;@account&lt;/tt&gt; with the value of &lt;tt&gt;recipient&lt;/tt&gt; being accessible in the 
   # view.
   #
-  # = Mailer Views
+  #
+  # = Mailer views
+  #
   # Like ActionController, each mailer class has a corresponding view directory
   # in which each method of the class looks for a template with its name.
   # To define a template to be used with a mailing, create an &lt;tt&gt;.rhtml&lt;/tt&gt; file with the same name as the method
@@ -59,7 +63,30 @@ module ActionMailer #:nodoc:
   #   Hi &lt;%= @account.name %&gt;,
   #   Thanks for joining our service! Please check back often.
   #
-  # = Sending Mail
+  # You can even use Action Pack helpers in these views. For example:
+  #
+  #   You got a new note!
+  #   &lt;%= truncate(note.body, 25) %&gt;
+  # 
+  #
+  # = Generating URLs for mailer views
+  #
+  # If your view includes URLs from the application, you need to use url_for in the mailing method instead of the view.
+  # Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request. That's
+  # why you need to jump this little hoop and supply all the details needed for the URL. Example:
+  #
+  #    def signup_notification(recipient)
+  #      recipients recipient.email_address_with_name
+  #      from       &quot;system@example.com&quot;
+  #      subject    &quot;New account information&quot;
+  #      body       :account =&gt; recipient,
+  #                 :home_page =&gt; url_for(:host =&gt; &quot;example.com&quot;, :controller =&gt; &quot;welcome&quot;, :action =&gt; &quot;greeting&quot;)
+  #    end
+  #
+  # You can now access @home_page in the template and get http://example.com/welcome/greeting.
+  #
+  # = Sending mail
+  #
   # Once a mailer action and template are defined, you can deliver your message or create it and save it 
   # for delivery later:
   #
@@ -73,7 +100,9 @@ module ActionMailer #:nodoc:
   # like to deliver. The &lt;tt&gt;signup_notification&lt;/tt&gt; method defined above is
   # delivered by invoking &lt;tt&gt;Notifier.deliver_signup_notification&lt;/tt&gt;.
   #
-  # = HTML Email
+  #
+  # = HTML email
+  #
   # To send mail as HTML, make sure your view (the &lt;tt&gt;.rhtml&lt;/tt&gt; file) generates HTML and
   # set the content type to html.
   #
@@ -87,7 +116,9 @@ module ActionMailer #:nodoc:
   #     end
   #   end  
   #
-  # = Multipart Email
+  #
+  # = Multipart email
+  #
   # You can explicitly specify multipart messages:
   #
   #   class ApplicationMailer &lt; ActionMailer::Base
@@ -120,7 +151,9 @@ module ActionMailer #:nodoc:
   # with the corresponding content type. The same body hash is passed to
   # each template.
   #
+  #
   # = Attachments
+  #
   # Attachments can be added by using the +attachment+ method.
   #
   # Example:
@@ -141,6 +174,7 @@ module ActionMailer #:nodoc:
   #     end
   #   end 
   #
+  #
   # = Configuration options
   #
   # These options are specified on the class level, like &lt;tt&gt;ActionMailer::Base.template_root = &quot;/my/templates&quot;&lt;/tt&gt;
@@ -150,7 +184,7 @@ module ActionMailer #:nodoc:
   # * &lt;tt&gt;logger&lt;/tt&gt; - the logger is used for generating information on the mailing run if available.
   #   Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
   #
-  # * &lt;tt&gt;server_settings&lt;/tt&gt; -  Allows detailed configuration of the server:
+  # * &lt;tt&gt;smtp_settings&lt;/tt&gt; -  Allows detailed configuration for :smtp delivery method:
   #   * &lt;tt&gt;:address&lt;/tt&gt; Allows you to use a remote mail server. Just change it from its default &quot;localhost&quot; setting.
   #   * &lt;tt&gt;:port&lt;/tt&gt; On the off chance that your mail server doesn't run on port 25, you can change it.
   #   * &lt;tt&gt;:domain&lt;/tt&gt; If you need to specify a HELO domain, you can do it here.
@@ -159,10 +193,12 @@ module ActionMailer #:nodoc:
   #   * &lt;tt&gt;:authentication&lt;/tt&gt; If your mail server requires authentication, you need to specify the authentication type here. 
   #     This is a symbol and one of :plain, :login, :cram_md5
   #
+  # * &lt;tt&gt;sendmail_settings&lt;/tt&gt; - Allows you to override options for the :sendmail delivery method
+  #   * &lt;tt&gt;:location&lt;/tt&gt; The location of the sendmail executable, defaults to &quot;/usr/sbin/sendmail&quot;
+  #   * &lt;tt&gt;:arguments&lt;/tt&gt; The command line arguments
   # * &lt;tt&gt;raise_delivery_errors&lt;/tt&gt; - whether or not errors should be raised if the email fails to be delivered.
   #
   # * &lt;tt&gt;delivery_method&lt;/tt&gt; - Defines a delivery method. Possible values are :smtp (default), :sendmail, and :test.
-  #   Sendmail is assumed to be present at &quot;/usr/sbin/sendmail&quot;.
   #
   # * &lt;tt&gt;perform_deliveries&lt;/tt&gt; - Determines whether deliver_* methods are actually carried out. By default they are,
   #   but this can be turned off to help functional testing.
@@ -174,9 +210,8 @@ module ActionMailer #:nodoc:
   #   pick a different charset from inside a method with &lt;tt&gt;@charset&lt;/tt&gt;.
   # * &lt;tt&gt;default_content_type&lt;/tt&gt; - The default content type used for the main part of the message. Defaults to &quot;text/plain&quot;. You
   #   can also pick a different content type from inside a method with &lt;tt&gt;@content_type&lt;/tt&gt;. 
-  # * &lt;tt&gt;default_mime_version&lt;/tt&gt; - The default mime version used for the message. Defaults to nil. You
-  #   can also pick a different value from inside a method with &lt;tt&gt;@mime_version&lt;/tt&gt;. When multipart messages are in
-  #   use, &lt;tt&gt;@mime_version&lt;/tt&gt; will be set to &quot;1.0&quot; if it is not set inside a method.
+  # * &lt;tt&gt;default_mime_version&lt;/tt&gt; - The default mime version used for the message. Defaults to &quot;1.0&quot;. You
+  #   can also pick a different value from inside a method with &lt;tt&gt;@mime_version&lt;/tt&gt;.
   # * &lt;tt&gt;default_implicit_parts_order&lt;/tt&gt; - When a message is built implicitly (i.e. multiple parts are assembled from templates
   #   which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
   #   [&quot;text/html&quot;, &quot;text/enriched&quot;, &quot;text/plain&quot;]. Items that appear first in the array have higher priority in the mail client
@@ -184,17 +219,18 @@ module ActionMailer #:nodoc:
   #   &lt;tt&gt;@implicit_parts_order&lt;/tt&gt;.
   class Base
     include AdvAttrAccessor, PartContainer
+    include ActionController::UrlWriter
 
     # Action Mailer subclasses should be reloaded by the dispatcher in Rails
     # when Dependencies.mechanism = :load.
-    include Reloadable::Subclasses
+    include Reloadable::Deprecated
     
     private_class_method :new #:nodoc:
 
     class_inheritable_accessor :template_root
     cattr_accessor :logger
 
-    @@server_settings = { 
+    @@smtp_settings = { 
       :address        =&gt; &quot;localhost&quot;, 
       :port           =&gt; 25, 
       :domain         =&gt; 'localhost.localdomain', 
@@ -202,7 +238,13 @@ module ActionMailer #:nodoc:
       :password       =&gt; nil, 
       :authentication =&gt; nil
     }
-    cattr_accessor :server_settings
+    cattr_accessor :smtp_settings
+    
+    @@sendmail_settings = {
+      :location       =&gt; '/usr/sbin/sendmail',
+      :arguments      =&gt; '-i -t'
+    }
+    cattr_accessor :sendmail_settings
 
     @@raise_delivery_errors = true
     cattr_accessor :raise_delivery_errors
@@ -222,7 +264,7 @@ module ActionMailer #:nodoc:
     @@default_content_type = &quot;text/plain&quot;
     cattr_accessor :default_content_type
     
-    @@default_mime_version = nil
+    @@default_mime_version = &quot;1.0&quot;
     cattr_accessor :default_mime_version
 
     @@default_implicit_parts_order = [ &quot;text/html&quot;, &quot;text/enriched&quot;, &quot;text/plain&quot; ]
@@ -321,6 +363,18 @@ module ActionMailer #:nodoc:
       def deliver(mail)
         new.deliver!(mail)
       end
+      
+      # Server Settings is the old name for &lt;tt&gt;smtp_settings&lt;/tt&gt;
+      def server_settings
+        smtp_settings
+      end
+      deprecate :server_settings=&gt;&quot;It's now named smtp_settings&quot;
+      
+      def server_settings=(settings)
+        ActiveSupport::Deprecation.warn(&quot;server_settings has been renamed smtp_settings, this warning will be removed with rails 2.0&quot;, caller)
+        self.smtp_settings=settings
+      end
+      
     end
 
     # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
@@ -348,7 +402,7 @@ module ActionMailer #:nodoc:
           templates.each do |path|
             # TODO: don't hardcode rhtml|rxml
             basename = File.basename(path)
-            next unless md = /^([^\.]+)\.([^\.]+\.[^\+]+)\.(rhtml|rxml)$/.match(basename)
+            next unless md = /^([^\.]+)\.([^\.]+\.[^\.]+)\.(rhtml|rxml)$/.match(basename)
             template_name = basename
             content_type = md.captures[1].gsub('.', '/')
             @parts &lt;&lt; Part.new(:content_type =&gt; content_type,
@@ -395,7 +449,7 @@ module ActionMailer #:nodoc:
 
       begin
         send(&quot;perform_delivery_#{delivery_method}&quot;, mail) if perform_deliveries
-      rescue Object =&gt; e
+      rescue Exception =&gt; e  # Net::SMTP errors or sendmail pipe errors
         raise e if raise_delivery_errors
       end
 
@@ -508,14 +562,14 @@ module ActionMailer #:nodoc:
         destinations = mail.destinations
         mail.ready_to_send
 
-        Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain], 
-            server_settings[:user_name], server_settings[:password], server_settings[:authentication]) do |smtp|
+        Net::SMTP.start(smtp_settings[:address], smtp_settings[:port], smtp_settings[:domain], 
+            smtp_settings[:user_name], smtp_settings[:password], smtp_settings[:authentication]) do |smtp|
           smtp.sendmail(mail.encoded, mail.from, destinations)
         end
       end
 
       def perform_delivery_sendmail(mail)
-        IO.popen(&quot;/usr/sbin/sendmail -i -t&quot;,&quot;w+&quot;) do |sm|
+        IO.popen(&quot;#{sendmail_settings[:location]} #{sendmail_settings[:arguments]}&quot;,&quot;w+&quot;) do |sm|
           sm.print(mail.encoded.gsub(/\r/, ''))
           sm.flush
         end</diff>
      <filename>vendor/rails/actionmailer/lib/action_mailer/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,6 @@
 module ActionMailer
   module Helpers #:nodoc:
-    def self.append_features(base) #:nodoc:
-      super
-
+    def self.included(base) #:nodoc:
       # Initialize the base module to aggregate its helpers.
       base.class_inheritable_accessor :master_helper_module
       base.master_helper_module = Module.new
@@ -13,14 +11,12 @@ module ActionMailer
       base.class_eval do
         # Wrap inherited to create a new master helper module for subclasses.
         class &lt;&lt; self
-          alias_method :inherited_without_helper, :inherited
-          alias_method :inherited, :inherited_with_helper
+          alias_method_chain :inherited, :helper
         end
 
         # Wrap initialize_template_class to extend new template class
         # instances with the master helper module.
-        alias_method :initialize_template_class_without_helper, :initialize_template_class
-        alias_method :initialize_template_class, :initialize_template_class_with_helper
+        alias_method_chain :initialize_template_class, :helper
       end
     end
 </diff>
      <filename>vendor/rails/actionmailer/lib/action_mailer/helpers.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/text/format.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/address.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/base64.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/config.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/encode.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/facade.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/header.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/info.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/loader.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/mail.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/mailbox.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/mbox.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/net.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/obsolete.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/parser.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/port.rb</filename>
    </modified>
    <modified>
      <diff>@@ -49,25 +49,31 @@ module TMail
     class &lt;&lt; self
       def unquote_and_convert_to(text, to_charset, from_charset = &quot;iso-8859-1&quot;, preserve_underscores=false)
         return &quot;&quot; if text.nil?
-        if text =~ /^=\?(.*?)\?(.)\?(.*)\?=$/
-          from_charset = $1
-          quoting_method = $2
-          text = $3
-          case quoting_method.upcase
-            when &quot;Q&quot; then
-              unquote_quoted_printable_and_convert_to(text, to_charset, from_charset, preserve_underscores)
-            when &quot;B&quot; then
-              unquote_base64_and_convert_to(text, to_charset, from_charset)
-            else
-              raise &quot;unknown quoting method #{quoting_method.inspect}&quot;
-          end
-        else
-          convert_to(text, to_charset, from_charset)
+        text.gsub(/(.*?)(?:(?:=\?(.*?)\?(.)\?(.*?)\?=)|$)/) do
+          before = $1
+          from_charset = $2
+          quoting_method = $3
+          text = $4
+
+          before = convert_to(before, to_charset, from_charset) if before.length &gt; 0
+          before + case quoting_method
+              when &quot;q&quot;, &quot;Q&quot; then
+                unquote_quoted_printable_and_convert_to(text, to_charset, from_charset, preserve_underscores)
+              when &quot;b&quot;, &quot;B&quot; then
+                unquote_base64_and_convert_to(text, to_charset, from_charset)
+              when nil then
+                # will be nil at the end of the string, due to the nature of
+                # the regex used.
+                &quot;&quot;
+              else
+                raise &quot;unknown quoting method #{quoting_method.inspect}&quot;
+            end
         end
       end
  
       def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
         text = text.gsub(/_/, &quot; &quot;) unless preserve_underscores
+        text = text.gsub(/\r\n|\r/, &quot;\n&quot;) # normalize newlines
         convert_to(text.unpack(&quot;M*&quot;).first, to, from)
       end
  
@@ -80,7 +86,7 @@ module TMail
         def convert_to(text, to, from)
           return text unless to &amp;&amp; from
           text ? Iconv.iconv(to, from, text).first : &quot;&quot;
-        rescue Iconv::IllegalSequence, Errno::EINVAL
+        rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
           # the 'from' parameter specifies a charset other than what the text
           # actually is...not much we can do in this case but just return the
           # unconverted text.</diff>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/quoting.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/scanner.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/scanner_r.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/stringio.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/tmail.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionmailer/lib/action_mailer/vendor/tmail/utils.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,8 @@
 module ActionMailer
   module VERSION #:nodoc:
     MAJOR = 1
-    MINOR = 2
-    TINY  = 5
+    MINOR = 3
+    TINY  = 6
 
     STRING = [MAJOR, MINOR, TINY].join('.')
   end</diff>
      <filename>vendor/rails/actionmailer/lib/action_mailer/version.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1 +0,0 @@
-Have a lovely picture, from me. Enjoy!
\ No newline at end of file</diff>
      <filename>vendor/rails/actionmailer/test/fixtures/path.with.dots/multipart_with_template_path_with_dots.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,4 @@
-$:.unshift(File.dirname(__FILE__) + &quot;/../lib/&quot;)
-$:.unshift File.dirname(__FILE__) + &quot;/fixtures/helpers&quot;
-
-require 'test/unit'
-require 'action_mailer'
+require &quot;#{File.dirname(__FILE__)}/abstract_unit&quot;
 
 module MailerHelper
   def person_name
@@ -56,8 +52,6 @@ class HelperMailer &lt; ActionMailer::Base
     helper_method :name_of_the_mailer_class
 end
 
-HelperMailer.template_root = File.dirname(__FILE__) + &quot;/fixtures&quot;
-
 class MailerHelperTest &lt; Test::Unit::TestCase
   def new_mail( charset=&quot;utf-8&quot; )
     mail = TMail::Mail.new</diff>
      <filename>vendor/rails/actionmailer/test/mail_helper_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,4 @@
-$:.unshift(File.dirname(__FILE__) + &quot;/../lib/&quot;)
-
-require 'test/unit'
-require 'action_mailer'
+require &quot;#{File.dirname(__FILE__)}/abstract_unit&quot;
 
 class RenderMailer &lt; ActionMailer::Base
   def inline_template(recipient)
@@ -24,7 +21,21 @@ class RenderMailer &lt; ActionMailer::Base
   end
 end
 
-RenderMailer.template_root = File.dirname(__FILE__) + &quot;/fixtures&quot;
+class FirstMailer &lt; ActionMailer::Base
+  def share(recipient)
+    recipients recipient
+    subject    &quot;using helpers&quot;
+    from       &quot;tester@example.com&quot;
+  end
+end
+
+class SecondMailer &lt; ActionMailer::Base
+  def share(recipient)
+    recipients recipient
+    subject    &quot;using helpers&quot;
+    from       &quot;tester@example.com&quot;
+  end
+end
 
 class RenderHelperTest &lt; Test::Unit::TestCase
   def setup
@@ -46,3 +57,23 @@ class RenderHelperTest &lt; Test::Unit::TestCase
   end
 end
 
+class FirstSecondHelperTest &lt; Test::Unit::TestCase
+  def setup
+    ActionMailer::Base.delivery_method = :test
+    ActionMailer::Base.perform_deliveries = true
+    ActionMailer::Base.deliveries = []
+
+    @recipient = 'test@localhost'
+  end
+
+  def test_ordering
+    mail = FirstMailer.create_share(@recipient)
+    assert_equal &quot;first mail&quot;, mail.body.strip
+    mail = SecondMailer.create_share(@recipient)
+    assert_equal &quot;second mail&quot;, mail.body.strip
+    mail = FirstMailer.create_share(@recipient)
+    assert_equal &quot;first mail&quot;, mail.body.strip
+    mail = SecondMailer.create_share(@recipient)
+    assert_equal &quot;second mail&quot;, mail.body.strip
+  end
+end</diff>
      <filename>vendor/rails/actionmailer/test/mail_render_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,31 +1,8 @@
-$:.unshift(File.dirname(__FILE__) + &quot;/../lib/&quot;)
-
-require 'test/unit'
-require 'action_mailer'
-
-class MockSMTP
-  def self.deliveries
-    @@deliveries
-  end
-
-  def initialize
-    @@deliveries = []
-  end
-
-  def sendmail(mail, from, to)
-    @@deliveries &lt;&lt; [mail, from, to]
-  end
-end
-
-class Net::SMTP
-  def self.start(*args)
-    yield MockSMTP.new
-  end
-end
+require &quot;#{File.dirname(__FILE__)}/abstract_unit&quot;
 
 class FunkyPathMailer &lt; ActionMailer::Base
-  self.template_root = &quot;#{File.dirname(__FILE__)}/fixtures/path.with.dots&quot;
-
+  self.template_root = &quot;#{File.dirname(__FILE__)}/fixtures/path.with.dots&quot;
+
   def multipart_with_template_path_with_dots(recipient)
     recipients recipient
     subject    &quot;Have a lovely picture&quot;
@@ -33,14 +10,9 @@ class FunkyPathMailer &lt; ActionMailer::Base
     attachment :content_type =&gt; &quot;image/jpeg&quot;,
       :body =&gt; &quot;not really a jpeg, we're only testing, after all&quot;
   end
-
-  def template_path
-    &quot;#{File.dirname(__FILE__)}/fixtures/path.with.dots&quot;
-  end
 end
 
 class TestMailer &lt; ActionMailer::Base
-
   def signed_up(recipient)
     @recipients   = recipient
     @subject      = &quot;[Signed up] Welcome #{recipient}&quot;
@@ -222,7 +194,7 @@ class TestMailer &lt; ActionMailer::Base
     subject      &quot;nested multipart&quot;
     from         &quot;test@example.com&quot;
     content_type &quot;multipart/mixed&quot;
-    part :content_type =&gt; &quot;multipart/alternative&quot;, :content_disposition =&gt; &quot;inline&quot; do |p|
+    part :content_type =&gt; &quot;multipart/alternative&quot;, :content_disposition =&gt; &quot;inline&quot;, :headers =&gt; { &quot;foo&quot; =&gt; &quot;bar&quot; } do |p|
       p.part :content_type =&gt; &quot;text/plain&quot;, :body =&gt; &quot;test text\nline #2&quot;
       p.part :content_type =&gt; &quot;text/html&quot;, :body =&gt; &quot;&lt;b&gt;test&lt;/b&gt; HTML&lt;br/&gt;\nline #2&quot;
     end
@@ -273,8 +245,6 @@ class TestMailer &lt; ActionMailer::Base
   end
 end
 
-TestMailer.template_root = File.dirname(__FILE__) + &quot;/fixtures&quot;
-
 class ActionMailerTest &lt; Test::Unit::TestCase
   include ActionMailer::Quoting
 
@@ -284,6 +254,7 @@ class ActionMailerTest &lt; Test::Unit::TestCase
 
   def new_mail( charset=&quot;utf-8&quot; )
     mail = TMail::Mail.new
+    mail.mime_version = &quot;1.0&quot;
     if charset
       mail.set_content_type &quot;text&quot;, &quot;plain&quot;, { &quot;charset&quot; =&gt; charset }
     end
@@ -306,6 +277,7 @@ class ActionMailerTest &lt; Test::Unit::TestCase
     
     assert_equal &quot;multipart/mixed&quot;, created.content_type
     assert_equal &quot;multipart/alternative&quot;, created.parts.first.content_type
+    assert_equal &quot;bar&quot;, created.parts.first.header['foo'].to_s
     assert_equal &quot;text/plain&quot;, created.parts.first.parts.first.content_type
     assert_equal &quot;text/html&quot;, created.parts.first.parts[1].content_type
     assert_equal &quot;application/octet-stream&quot;, created.parts[1].content_type
@@ -324,7 +296,6 @@ class ActionMailerTest &lt; Test::Unit::TestCase
     expected.body    = &quot;Hello there, \n\nMr. #{@recipient}&quot;
     expected.from    = &quot;system@loudthinking.com&quot;
     expected.date    = Time.local(2004, 12, 12)
-    expected.mime_version = nil
 
     created = nil
     assert_nothing_raised { created = TestMailer.create_signed_up(@recipient) }
@@ -816,17 +787,30 @@ EOF
     assert_match %r{format=flowed}, mail['content-type'].to_s
     assert_match %r{charset=utf-8}, mail['content-type'].to_s
   end
+  
+  def test_deprecated_server_settings
+    old_smtp_settings = ActionMailer::Base.smtp_settings
+    assert_deprecated do
+      ActionMailer::Base.server_settings
+    end
+    assert_deprecated do
+      ActionMailer::Base.server_settings={}
+      assert_equal Hash.new, ActionMailer::Base.smtp_settings
+    end
+  ensure
+    ActionMailer::Base.smtp_settings=old_smtp_settings    
+  end
 end
 
-class InheritableTemplateRootTest &lt; Test::Unit::TestCase
-  def test_attr
-    expected = &quot;#{File.dirname(__FILE__)}/fixtures/path.with.dots&quot;
-    assert_equal expected, FunkyPathMailer.template_root
-
-    sub = Class.new(FunkyPathMailer)
-    sub.template_root = 'test/path'
-
-    assert_equal 'test/path', sub.template_root
-    assert_equal expected, FunkyPathMailer.template_root
-  end
-end
+class InheritableTemplateRootTest &lt; Test::Unit::TestCase
+  def test_attr
+    expected = &quot;#{File.dirname(__FILE__)}/fixtures/path.with.dots&quot;
+    assert_equal expected, FunkyPathMailer.template_root
+
+    sub = Class.new(FunkyPathMailer)
+    sub.template_root = 'test/path'
+
+    assert_equal 'test/path', sub.template_root
+    assert_equal expected, FunkyPathMailer.template_root
+  end
+end</diff>
      <filename>vendor/rails/actionmailer/test/mail_service_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,4 @@
-$:.unshift(File.dirname(__FILE__) + &quot;/../lib/&quot;)
-$:.unshift(File.dirname(__FILE__) + &quot;/../lib/action_mailer/vendor&quot;)
-
-require 'test/unit'
+require &quot;#{File.dirname(__FILE__)}/abstract_unit&quot;
 require 'tmail'
 require 'tempfile'
 
@@ -22,6 +19,18 @@ class QuotingTest &lt; Test::Unit::TestCase
     assert_equal unquoted, original
   end
 
+  # test an email that has been created using \r\n newlines, instead of
+  # \n newlines.
+  def test_email_quoted_with_0d0a
+    mail = TMail::Mail.parse(IO.read(&quot;#{File.dirname(__FILE__)}/fixtures/raw_email_quoted_with_0d0a&quot;))
+    assert_match %r{Elapsed time}, mail.body
+  end
+
+  def test_email_with_partially_quoted_subject
+    mail = TMail::Mail.parse(IO.read(&quot;#{File.dirname(__FILE__)}/fixtures/raw_email_with_partially_quoted_subject&quot;))
+    assert_equal &quot;Re: Test: \&quot;\346\274\242\345\255\227\&quot; mid \&quot;\346\274\242\345\255\227\&quot; tail&quot;, mail.subject
+  end
+
   private
 
     # This whole thing *could* be much simpler, but I don't think Tempfile,
@@ -40,7 +49,7 @@ class QuotingTest &lt; Test::Unit::TestCase
       end
 
       system(&quot;ruby #{test_name} &gt; #{res_name}&quot;) or raise &quot;could not run test in sandbox&quot;
-      File.read(res_name)
+      File.read(res_name).chomp
     ensure
       File.delete(test_name) rescue nil
       File.delete(res_name) rescue nil</diff>
      <filename>vendor/rails/actionmailer/test/quoting_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,4 @@
-$:.unshift(File.dirname(__FILE__) + &quot;/../lib/&quot;)
-$:.unshift File.dirname(__FILE__) + &quot;/fixtures/helpers&quot;
-
-require 'test/unit'
-require 'action_mailer'
+require &quot;#{File.dirname(__FILE__)}/abstract_unit&quot;
 
 class TMailMailTest &lt; Test::Unit::TestCase
   def test_body</diff>
      <filename>vendor/rails/actionmailer/test/tmail_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,27 +1,625 @@
-*1.12.5* (August 10th, 2006)
+*1.13.6* (November 24th, 2007)
 
-* Updated security fix
+* Correct Broken Fix for session_fixation attacks
 
+* Ensure that cookies handle array values correctly.  Closes #9937 [queso]
 
-*1.12.4* (August 8th, 2006)
+*1.13.5* (October 12th, 2007)
+
+* Backport: allow array and hash query parameters. Array route parameters are converted/to/a/path as before.  #6765, #7047, #7462 [bgipsy, Jeremy McAnally, Dan Kubb, brendan, Diego Algorta Casamayou]
+
+* Fix in place editor's setter action with non-string fields.  #7418 [Andreas]
+
+
+*1.13.4* (October 4th, 2007)
+
+* Only accept session ids from cookies, prevents session fixation attacks.  [bradediger]
+
+* Change the resource seperator from ; to / change the generated routes to use the new-style named routes.  e.g.  new_group_user_path(@group) instead of group_new_user_path(@group). [pixeltrix]
+
+* Integration tests: introduce methods for other HTTP methods.  #6353 [caboose]
+
+* Improve performance of action caching. Closes #8231 [skaes]
+
+* Fix errors with around_filters which do not yield, restore 1.1 behaviour with after filters. Closes #8891 [skaes]
+
+  After filters will *no longer* be run if an around_filter fails to yield, users relying on
+  this behaviour are advised to put the code in question after a yield statement in an around filter.
+
+* Allow you to delete cookies with options. Closes #3685 [josh, Chris Wanstrath]
+
+* Deprecate pagination. Install the classic_pagination plugin for forward compatibility, or move to the superior will_paginate plugin.  #8157 [Mislav Marohnic]
+
+* Fix filtered parameter logging with nil parameter values.  #8422 [choonkeat]
+
+* Integration tests: alias xhr to xml_http_request and add a request_method argument instead of always using POST.  #7124 [Nik Wakelin, Francois Beausoleil, Wizard]
+
+* Document caches_action.  #5419 [Jarkko Laine]
+
+* observe_form always sends the serialized form.  #5271 [manfred, normelton@gmail.com]
+
+* Update UrlWriter to accept :anchor parameter. Closes #6771. [octopod]
+
+* Replace the current block/continuation filter chain handling by an implementation based on a simple loop.  Closes #8226 [Stefan Kaes]
+
+* Return the string representation from an Xml Builder when rendering a partial.  #5044 [tpope]
+
+* Cleaned up, corrected, and mildly expanded ActionPack documentation.  Closes #7190 [jeremymcanally]
+
+* Small collection of ActionController documentation cleanups.  Closes #7319 [jeremymcanally]
+
+* Performance: patch cgi/session/pstore to require digest/md5 once rather than per #initialize.  #7583 [Stefan Kaes]
+
+* Deprecation: verification with :redirect_to =&gt; :named_route shouldn't be deprecated.  #7525 [Justin French]
+
+
+*1.13.3* (March 12th, 2007)
+
+* Fix a bug in Routing where a parameter taken from the path of the current request could not be used as a query parameter for the next.  #6752 [Nicholas Seckar]
+
+* session_enabled? works with session :off.  #6680 [Catfish]
+
+* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id.  [Stefan Kaes]
+
+
+*1.13.2* (February 5th, 2007)
+
+* Add much-needed html-scanner tests.  Fixed CDATA parsing bug. [Rick]
+
+* improve error message for Routing for named routes. [Rob Sanheim]
+
+* Added enhanced docs to routing assertions. [Rob Sanheim]
+
+* fix form_for example in ActionController::Resources documentation. [gnarg]
+
+* Add singleton resources from trunk [Rick Olson]
+
+* select :multiple =&gt; true suffixes the attribute name with [] unless already suffixed.  #6977 [nik.kakelin, ben, julik]
+
+* Improve routes documentation.  #7095 [zackchandler]
+
+* Resource member routes require :id, eliminating the ambiguous overlap with collection routes.  #7229 [dkubb]
+
+* Fixed NumberHelper#number_with_delimiter to use &quot;.&quot; always for splitting the original number, not the delimiter parameter #7389 [ceefour]
+
+* Autolinking recognizes trailing and embedded . , : ;  #7354 [Jarkko Laine]
+
+* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268.  [imajes]
+
+* Improved auto_link to match more valid urls correctly [Tobias Luetke]
+
+
+*1.13.1* (January 18th, 2007)
+
+* Fixed content-type bug in Prototype [sam]
+
+
+*1.13.0* (January 16th, 2007)
+
+* Modernize cookie testing code, and increase coverage (Heckle++) #7101 [Kevin Clark]
+
+* Heckling ActionController::Resources::Resource revealed that set_prefixes didn't break when :name_prefix was munged. #7081 [Kevin Clark]
+
+* Update to Prototype 1.5.0. [Sam Stephenson]
+
+* Allow exempt_from_layout :rhtml.  #6742, #7026 [dcmanges, Squeegy]
+
+* Fix parsing of array[] CGI parameters so extra empty values aren't included.  #6252 [Nicholas Seckar, aiwilliams, brentrowland]
+
+* link_to_unless_current works with full URLs as well as paths.  #6891 [Jarkko Laine, manfred, idrifter]
+
+* Fix HTML::Node to output double quotes instead of single quotes.  Closes #6845 [mitreandy]
+
+* Fix no method error with error_messages_on.  Closes #6935 [nik.wakelin Koz]
+
+* Slight doc tweak to the ActionView::Helpers::PrototypeHelper#replace docs.  Closes #6922 [Steven Bristol]
+
+* Slight doc tweak to #prepend_filter.  Closes #6493 [Jeremy Voorhis]
+
+* Add more extensive documentation to the AssetTagHelper.  Closes #6452 [Bob Silva]
+
+* Clean up multiple calls to #stringify_keys in TagHelper, add better documentation and testing for TagHelper.  Closes #6394 [Bob Silva]
+
+* [DOCS] fix reference to ActionController::Macros::AutoComplete for #text_field_with_auto_complete. Closes #2578 [Jan Prill]
+
+* Make sure html_document is reset between integration test requests. [ctm]
+
+* Set session to an empty hash if :new_session =&gt; false and no session cookie or param is present. CGI::Session was raising an unrescued ArgumentError.  [Josh Susser]
+
+* Fix assert_redirected_to bug where redirecting from a nested to to a top-level controller incorrectly added the current controller's nesting.  Closes #6128.  [Rick Olson]
+
+* Ensure render :json =&gt; ... skips the layout.  #6808 [Josh Peek]
+
+* Silence log_error deprecation warnings from inspecting deprecated instance variables.  [Nate Wiger]
+
+* Only cache GET requests with a 200 OK response.  #6514, #6743 [RSL, anamba]
+
+* Correctly report which filter halted the chain.  #6699 [Martin Emde]
+
+* respond_to recognizes JSON. render :json =&gt; @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument.  #4185 [Scott Raymond, eventualbuddha]
+    # application/json response with body 'Element.show({:name: &quot;David&quot;})'
+    respond_to do |format|
+      format.json { render :json =&gt; { :name =&gt; &quot;David&quot; }.to_json, :callback =&gt; 'Element.show' }
+    end
 
-* Documentation fix: integration test scripts don't require integration_test.  #4914 [Frederick Ros &lt;sl33p3r@free.fr&gt;]
+* Makes :discard_year work without breaking multi-attribute parsing in AR.  #1260, #3800 [sean@ardismg.com, jmartin@desertflood.com, stephen@touset.org, Bob Silva]
 
-* ActionController::Base Summary documentation rewrite.  #4900 [kevin.clark@gmail.com]
+* Adds html id attribute to date helper elements.  #1050, #1382 [mortonda@dgrmm.net, David North, Bob Silva]
 
-* Fix text_helper.rb documentation rendering.  #4725 [Frederick Ros]
+* Add :index and @auto_index capability to model driven date/time selects.  #847, #2655 [moriq, Doug Fales, Bob Silva]
 
-* Fixes bad rendering of JavaScriptMacrosHelper rdoc.  #4910 [Frederick Ros]
+* Add :order to datetime_select, select_datetime, and select_date.  #1427 [Timothee Peignier, patrick@lenz.sh, Bob Silva]
 
-* Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there.  #4079 [Rick Olson]
+* Added time_select to work with time values in models. Update scaffolding.  #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
 
-* Documentation for AbstractRequest.  #4895 [kevin.clark@gmail.com] 
+* Added :include_seconds to select_datetime, datetime_select and time_select.  #2998 [csn, Bob Silva]
 
-* Remove all remaining references to @params in the documentation. [Marcel Molina Jr.]
+* All date/datetime selects can now accept an array of month names with :use_month_names. Allows for localization.  #363 [tomasj, Bob Silva]
 
-* Add documentation for redirect_to :back's RedirectBackError exception.  [Marcel Molina Jr.]
+* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC.  #3811 [Bob Silva]
 
-* Update layout and content_for documentation to use yield rather than magic @content_for instance variables. [Marcel Molina Jr.]
+* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status.  [Jeremy Kemper]
+
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [DHH]
+
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers[&quot;REQUEST_METHOD&quot;] for get the &quot;real&quot; answer. Closes #6694 [DHH]
+
+
+*1.13.0 RC1* (r5619, November 22nd, 2006)
+
+* Update Routing to complain when :controller is not specified by a route. Closes #6669. [Nicholas Seckar]
+
+* Ensure render_to_string cleans up after itself when an exception is raised.  #6658 [rsanheim]
+
+* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs]
+
+* simple_format helper doesn't choke on nil.  #6644 [jerry426]
+
+* Reuse named route helper module between Routing reloads. Use remove_method to delete named route methods after each load. Since the module is never collected, this fixes a significant memory leak. [Nicholas Seckar]
+
+* Deprecate standalone components.  [Jeremy Kemper]
+
+* Always clear model associations from session.  #4795 [sd@notso.net, andylien@gmail.com]
+
+* Remove JavaScriptLiteral in favor of ActiveSupport::JSON::Variable. [Sam Stephenson]
+
+* Sync ActionController::StatusCodes::STATUS_CODES with http://www.iana.org/assignments/http-status-codes.  #6586 [dkubb]
+
+* Multipart form values may have a content type without being treated as uploaded files if they do not provide a filename.  #6401 [Andreas Schwarz, Jeremy Kemper]
+
+* assert_response supports symbolic status codes.  #6569 [Kevin Clark]
+    assert_response :ok
+    assert_response :not_found
+    assert_response :forbidden
+
+* Cache parsed query parameters.  #6559 [Stefan Kaes]
+
+* Deprecate JavaScriptHelper#update_element_function, which is superseeded by RJS [Thomas Fuchs]
+
+* Fix invalid test fixture exposed by stricter Ruby 1.8.5 multipart parsing.  #6524 [Bob Silva]
+
+* Set ActionView::Base.default_form_builder once rather than passing the :builder option to every form or overriding the form helper methods.  [Jeremy Kemper]
+
+* Deprecate expire_matched_fragments. Use expire_fragment instead.  #6535 [Bob Silva]
+
+* Deprecate start_form_tag and end_form_tag.  Use form_tag / '&lt;/form&gt;' from now on.  [Rick]
+
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick]
+
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs.  [Rick]
+
+* Added block-usage to TagHelper#content_tag [DHH]. Example:
+
+   &lt;% content_tag :div, :class =&gt; &quot;strong&quot; %&gt;
+     Hello world!
+   &lt;% end %&gt;
+  
+  Will output:
+    &lt;div class=&quot;strong&quot;&gt;Hello world!&lt;/div&gt;
+
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post =&gt; true #6409 [BobSilva]
+
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [BobSilva]
+
+* Fixed that setting RAILS_ASSET_ID to &quot;&quot; should not add a trailing slash after assets #6454 [BobSilva/chrismear]
+
+* Force *_url named routes to show the host in ActionView [Rick]
+
+  &lt;%= url_for ... %&gt; # no host
+  &lt;%= foo_path %&gt;    # no host
+  &lt;%= foo_url %&gt;     # host!
+
+* Add support for converting blocks into function arguments to JavaScriptGenerator#call and JavaScriptProxy#call. [Sam Stephenson]
+
+* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
+
+* Add &lt;%= escape_once html %&gt; to escape html while leaving any currently escaped entities alone.  Fix button_to double-escaping issue. [Rick]
+
+* Fix double-escaped entities, such as &amp;amp;amp;, &amp;amp;#123;, etc. [Rick]
+
+* Fix routing to correctly determine when generation fails. Closes #6300. [psross].
+
+* Fix broken assert_generates when extra keys are being checked. [Jamis Buck]
+
+* Replace KCODE checks with String#chars for truncate.  Closes #6385 [Manfred Stienstra]
+
+* Make page caching respect the format of the resource that is being requested even if the current route is the default route so that, e.g. posts.rss is not transformed by url_for to '/' and subsequently cached as '/index.html' when it should be cached as '/posts.rss'.  [Marcel Molina Jr.]
+
+* Use String#chars in TextHelper::excerpt. Closes #6386 [Manfred Stienstra]
+
+* Fix relative URL root matching problems. [Mark Imbriaco]
+
+* Fix filter skipping in controller subclasses.  #5949, #6297, #6299 [Martin Emde]
+
+* render_text may optionally append to the response body. render_javascript appends by default. This allows you to chain multiple render :update calls by setting @performed_render = false between them (awaiting a better public API).  [Jeremy Kemper]
+
+* Rename test assertion to prevent shadowing. Closes #6306. [psross]
+
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [phallstrom]
+
+* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
+
+* Added an html_options hash parameter to javascript_tag() and update_page_tag() helpers #6311 [tzaharia]. Example:
+
+    update_page_tag :defer =&gt; 'true' { |page| ... }
+
+  Gives:
+
+    &lt;script defer=&quot;true&quot; type=&quot;text/javascript&quot;&gt;...&lt;/script&gt;
+    
+  Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
+
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [mnaberez]
+
+* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven@c3d2.de]
+
+* Fixed that some 500 rescues would cause 500's themselves because the response had not yet been generated #6329 [cmselmer]
+
+* respond_to :html doesn't assume .rhtml.  #6281 [Hampton Catlin]
+
+* Fixed some deprecation warnings in ActionPack [Rick Olson]
+
+* assert_select_rjs decodes escaped unicode chars since the Javascript generators encode them.  #6240 [japgolly]
+
+* Deprecation: @cookies, @headers, @request, @response will be removed after 1.2. Use the corresponding method instead.  [Jeremy Kemper]
+
+* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. [Jamis Buck]. Examples:
+
+    head :status =&gt; 404        # expands to &quot;404 Not Found&quot;
+    head :status =&gt; :not_found # expands to &quot;404 Not Found&quot;
+    head :status =&gt; :created   # expands to &quot;201 Created&quot;
+
+* Add head(options = {}) for responses that have no body. [Jamis Buck]. Examples:
+
+    head :status =&gt; 404 # return an empty response with a 404 status
+    head :location =&gt; person_path(@person), :status =&gt; 201
+
+* Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain.  [Rick Olson]
+
+* strip_links is case-insensitive.  #6285 [tagoh, Bob Silva]
+
+* Clear the cache of possible controllers whenever Routes are reloaded. [Nicholas Seckar]
+
+* Filters overhaul including meantime filter support using around filters + blocks.  #5949 [Martin Emde, Roman Le Negrate, Stefan Kaes, Jeremy Kemper]
+
+* Update CGI process to allow sessions to contain namespaced models. Closes #4638. [dfelstead@site5.com]
+
+* Fix routing to respect user provided requirements and defaults when assigning default routing options (such as :action =&gt; 'index'). Closes #5950. [Nicholas Seckar]
+
+* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability.  #3368, #6226 [sdsykes, fhanshaw@vesaria.com]
+
+* Added that respond_to blocks will automatically set the content type to be the same as is requested [DHH]. Examples:
+
+    respond_to do |format|
+      format.html { render :text =&gt; &quot;I'm being sent as text/html&quot; }
+      format.rss  { render :text =&gt; &quot;I'm being sent as application/rss+xml&quot; }
+      format.atom { render :text =&gt; &quot;I'm being sent as application/xml&quot;, :content_type =&gt; Mime::XML }
+    end
+
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [DHH]
+
+* Added proper getters and setters for content type and charset [DHH]. Example of what we used to do:
+
+    response.headers[&quot;Content-Type&quot;] = &quot;application/atom+xml; charset=utf-8&quot;
+  
+  ...now:
+  
+    response.content_type = Mime::ATOM
+    response.charset      = &quot;utf-8&quot;
+
+* Declare file extensions exempt from layouts.  #6219 [brandon]
+    Example: ActionController::Base.exempt_from_layout 'rpdf'
+
+* Add chained replace/update support for assert_select_rjs [Rick Olson]
+
+    Given RJS like...
+
+      page['test1'].replace &quot;&lt;div id=\&quot;1\&quot;&gt;foo&lt;/div&gt;&quot;
+      page['test2'].replace_html &quot;&lt;div id=\&quot;2\&quot;&gt;foo&lt;/div&gt;&quot;
+
+    Test it with...
+
+      assert_select_rjs :chained_replace
+      assert_select_rjs :chained_replace, &quot;test1&quot;
+
+      assert_select_rjs :chained_replace_html
+      assert_select_rjs :chained_replace_html, &quot;test2&quot;
+
+* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency.  #6132, #6178 [choonkeat@gmail.com]
+
+* Skip params with empty names, such as the &amp;=Save query string from &lt;input type=&quot;submit&quot;/&gt;.  #2569 [manfred, raphinou@yahoo.com]
+
+* Fix assert_tag so that :content =&gt; &quot;foo&quot; does not match substrings, but only exact strings. Use :content =&gt; /foo/ to match substrings. #2799 [Eric Hodel]
+
+* Update JavaScriptGenerator#show/hide/toggle/remove to new Prototype syntax for multiple ids,  #6068 [petermichaux@gmail.com]
+
+* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
+
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [DHH]. So what used to require a nil, like this:
+
+    link_to(&quot;Hider&quot;, nil, :class =&gt; &quot;hider_link&quot;) { |p| p[:something].hide }
+  
+  ...can be written like this:
+
+    link_to(&quot;Hider&quot;, :class =&gt; &quot;hider_link&quot;) { |p| p[:something].hide }
+
+* Added access to nested attributes in RJS #4548 [richcollins@gmail.com]. Examples:
+
+    page['foo']['style']                  # =&gt; $('foo').style;
+    page['foo']['style']['color']         # =&gt; $('blank_slate').style.color;
+    page['foo']['style']['color'] = 'red' # =&gt; $('blank_slate').style.color = 'red';
+    page['foo']['style'].color = 'red'    # =&gt; $('blank_slate').style.color = 'red';
+
+* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule@space.ch]
+
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [DHH] 
+
+* Fixed FormOptionsHelper#select to respect :selected value #5813
+
+* Fixed TextHelper#simple_format to deal with multiple single returns within a single paragraph #5835 [moriq@moriq.com]
+
+* Fixed TextHelper#pluralize to handle 1 as a string #5909 [rails@bencurtis.com]
+
+* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
+
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static &quot;Application error (Rails)&quot; [DHH]
+
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 [DHH]
+
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [DHH]
+
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [DHH]
+
+* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin@gmail.com]
+
+* radio_button_tag generates unique id attributes.  #3353 [Bob Silva, somekool@gmail.com]
+
+* strip_tags passes through blank args such as nil or &quot;&quot;.  #2229, #6702 [duncan@whomwah.com, dharana]
+
+* Cleanup assert_tag :children counting.  #2181 [jamie@bravenet.com]
+
+* button_to accepts :method so you can PUT and DELETE with it.  #6005 [Dan Webb]
+
+* Update sanitize text helper to strip plaintext tags, and &lt;img src=&quot;javascript:bang&quot;&gt;.  [Rick Olson]
+
+* Add routing tests to assert that RoutingError is raised when conditions aren't met.  Closes #6016 [Nathan Witmer]
+
+* Make auto_link parse a greater subset of valid url formats. [Jamis Buck]
+
+* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. [Mike Clark]
+
+* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, DHH]
+
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Luetke]
+
+* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
+
+* Add UrlWriter to allow writing urls from Mailers and scripts. [Nicholas Seckar]
+
+* Relax Routing's anchor pattern warning; it was preventing use of [^/] inside restrictions. [Nicholas Seckar]
+
+* Add controller_paths variable to Routing. [Nicholas Seckar]
+
+* Fix assert_redirected_to issue with named routes for module controllers.  [Rick Olson]
+
+* Tweak RoutingError message to show option diffs, not just missing named route significant keys. [Rick Olson]
+
+* Invoke method_missing directly on hidden actions. Closes #3030. [Nicholas Seckar]
+
+* Add RoutingError exception when RouteSet fails to generate a path from a Named Route. [Rick Olson]
+
+* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
+
+* Deprecation: check whether instance variables have been monkeyed with before assigning them to deprecation proxies. Raises a RuntimeError if so. [Jeremy Kemper]
+
+* Add support for the param_name parameter to the auto_complete_field helper. #5026 [david.a.williams@gmail.com]
+
+* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
+
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [francois.beausoleil@gmail.com]
+
+* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that &quot;60 days ago&quot; becomes &quot;2 months ago&quot; #5611 [pjhyett@gmail.com]
+
+* Make controller_path available as an instance method.  #5724 [jmckible@gmail.com]
+
+* Update query parser to support adjacent hashes. [Nicholas Seckar]
+
+* Make action caching aware of different formats for the same action so that, e.g.  foo.xml is cached separately from foo.html. Implicitly set content type when reading in cached content with mime revealing extensions so the entire onous isn't on the webserver. [Marcel Molina Jr.]
+
+* Restrict Request Method hacking with ?_method to POST requests.  [Rick Olson]
+
+* Fixed the new_#{resource}_url route and added named route tests for Simply Restful.  [Rick Olson]
+
+* Added map.resources from the Simply Restful plugin [DHH]. Examples (the API has changed to use plurals!):
+
+    map.resources :messages
+    map.resources :messages, :comments
+    map.resources :messages, :new =&gt; { :preview =&gt; :post }
+
+* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
+
+* TestRequest#reset_session should restore a TestSession, not a hash [Koz]
+
+* Don't search a load-path of '.' for controller files [Jamis Buck]
+
+* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
+
+* Fixed that you can still access the flash after the flash has been reset in reset_session.  Closes #5584 [lmarlow@yahoo.com]
+
+* Allow form_for and fields_for to work with indexed form inputs.  [Jeremy Kemper, Matt Lyon]
+
+  &lt;% form_for 'post[]', @post do |f| -%&gt;
+  &lt;% end -%&gt;
+
+* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
+
+* Provide support for decimal columns to form helpers. Closes #5672. [dave@pragprog.com]
+
+* Pass :id =&gt; nil or :class =&gt; nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
+
+* Reset @html_document between requests so assert_tag works. #4810 [jarkko@jlaine.net, easleydp@gmail.com]
+
+* Integration tests behave well with render_component. #4632 [edward.frederick@revolution.com, dev.rubyonrails@maxdunn.com]
+
+* Added exception handling of missing layouts #5373 [chris@ozmm.org]
+
+* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary@panandscan.com]
+
+* Fixed that the flash should be reset when reset_session is called #5584 [shugo@ruby-lang.org]
+
+* Added special case for &quot;1 Byte&quot; in NumberHelper#number_to_human_size #5593 [murpyh@rubychan.de]
+
+* Fixed proper form-encoded parameter parsing for requests with &quot;Content-Type: application/x-www-form-urlencoded; charset=utf-8&quot; (note the presence of a charset directive) [DHH]
+
+* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
+
+* Avoid naming collision among compiled view methods. [Jeremy Kemper]
+
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [Mislav Marohnic]
+
+* Determine the correct template_root for deeply nested components.  #2841 [s.brink@web.de]
+
+* Fix that routes with *path segments in the recall can generate URLs. [Rick]
+
+* Fix strip_links so that it doesn't hang on multiline &lt;acronym&gt; tags [Jamis Buck]
+
+* Remove problematic control chars in rescue template. #5316 [Stefan Kaes]
+
+* Make sure passed routing options are not mutated by routing code. #5314 [Blair Zajac]
+
+* Make sure changing the controller from foo/bar to bing/bang does not change relative to foo. [Jamis Buck]
+
+* Escape the path before routing recognition. #3671
+
+* Make sure :id and friends are unescaped properly. #5275 [me@julik.nl]
+
+* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form).  #210 [mklame@atxeu.com, matthew@walker.wattle.id.au]
+
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [DHH]
+
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [DHH]. Example: Mime::Type.register(&quot;image/gif&quot;, :gif)
+
+* Added support for Mime objects in render :content_type option [DHH]. Example: render :text =&gt; some_atom, :content_type =&gt; Mime::ATOM
+
+* Add :status option to send_data and send_file. Defaults to '200 OK'.  #5243 [Manfred Stienstra &lt;m.stienstra@fngtps.com&gt;]
+
+* Routing rewrite. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes. [Nicholas Seckar, Jamis Buck]
+
+  map.connect '/foo/:id', :controller =&gt; '...', :action =&gt; '...'
+  map.connect '/foo/:id.:format', :controller =&gt; '...', :action =&gt; '...'
+  map.connect '/foo/:id', ..., :conditions =&gt; { :method =&gt; :get }
+
+* Cope with missing content type and length headers. Parse parameters from multipart and urlencoded request bodies only. [Jeremy Kemper]
+
+* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com]
+
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like &quot;rss&quot; or &quot;xml&quot;, that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [DHH]. Example:
+
+  class WeblogController &lt; ActionController::Base
+    def index
+      @posts = Post.find :all
+      
+      respond_to do |format|
+        format.html
+        format.xml { render :xml =&gt; @posts.to_xml }
+        format.rss { render :action =&gt; &quot;feed.rxml&quot; }
+      end
+    end
+  end
+  
+  # returns HTML when requested by a browser, since the browser
+  # has the HTML mimetype at the top of its priority list
+  Accept: text/html
+  GET /weblog 
+  
+  # returns the XML 
+  Accept: application/xml
+  GET /weblog 
+
+  # returns the HTML 
+  Accept: application/xml
+  GET /weblog.html
+
+  # returns the XML
+  Accept: text/html
+  GET /weblog.xml
+  
+  All this relies on the fact that you have a route that includes .:format.
+  
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [DHH]
+
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to &quot;Destroy&quot;, person_url(:id =&gt; person), :method =&gt; :delete [DHH]
+
+* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo@mk.ukrtelecom.ua]
+
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark kevin.clark@gmail.com]
+
+* Fix NoMethodError when parsing params like &amp;&amp;. [Adam Greenfield]
+
+* form.text_area handles the :size option just like the original text_area (:size =&gt; '60x10' becomes cols=&quot;60&quot; rows=&quot;10&quot;).  [Jeremy Kemper]
+
+* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
+
+* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick]
+
+    map.connect '*path', :controller =&gt; 'files', :action =&gt; 'show'
+
+* Use #flush between switching from #write to #syswrite. Closes #4907. [Blair Zajac &lt;blair@orcaware.com&gt;]
+
+* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. [andrew@redlinesoftware.com, Marcel Molina Jr.]
+  
+  error_messages_for :account, :user, :subscription, :object_name =&gt; :account
+
+* Fix assert_redirected_to tests according to real-world usage.  Also, don't fail if you add an extra :controller option: [Rick]
+
+    redirect_to :action =&gt; 'new'
+    assert_redirected_to :controller =&gt; 'monkeys', :action =&gt; 'new'
+
+* Diff compared routing options.  Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick]
+
+    assert_recognizes({:controller =&gt; 'users', :action =&gt; 'index'}, 'users')
+    assert_recognizes({:controller =&gt; 'users', :action =&gt; 'create'}, {:path =&gt; 'users', :method =&gt; :post})
+
+* Diff compared options with #assert_redirected_to [Rick]
+
+* Add support in routes for semicolon delimited &quot;subpaths&quot;, like /books/:id;:action [Jamis Buck]
+
+* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii@comcast.net, Sam Stephenson]
+
+* Modify routing so that you can say :require =&gt; { :method =&gt; :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. [Jamis Buck]
+
+* Added :add_headers option to verify which merges a hash of name/value pairs into the response's headers hash if the prerequisites cannot be satisfied. [Sam Stephenson]
+  ex. verify :only =&gt; :speak, :method =&gt; :post, 
+             :render =&gt; { :status =&gt; 405, :text =&gt; &quot;Must be post&quot; }, 
+             :add_headers =&gt; { &quot;Allow&quot; =&gt; &quot;POST&quot; }
+
+
+*1.12.5* (August 10th, 2006)
+
+* Updated security fix
+
+
+*1.12.4* (August 8th, 2006)
 
 * Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick]
 
@@ -1928,7 +2526,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
   Before:
 
     module WeblogHelper
-      def self.append_features(controller) #:nodoc:
+      def self.included(controller) #:nodoc:
         controller.ancestors.include?(ActionController::Base) ? controller.add_template_helper(self) : super
       end
     end
@@ -2286,9 +2884,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
 
 * Added pluralize method to the TextHelper that makes it easy to get strings like &quot;1 message&quot;, &quot;3 messages&quot;
 
-* Added proper escaping for the rescues [Andreas Schwartz]
+* Added proper escaping for the rescues [Andreas Schwarz]
 
-* Added proper escaping for the option and collection tags [Andreas Schwartz]
+* Added proper escaping for the option and collection tags [Andreas Schwarz]
 
 * Fixed NaN errors on benchmarking [Jim Weirich]
 </diff>
      <filename>vendor/rails/actionpack/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-Copyright (c) 2004 David Heinemeier Hansson
+Copyright (c) 2004-2006 David Heinemeier Hansson
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the</diff>
      <filename>vendor/rails/actionpack/MIT-LICENSE</filename>
    </modified>
    <modified>
      <diff>@@ -34,7 +34,7 @@ A short rundown of the major features:
   and can therefore share helper methods.
 
     BlogController &lt; ActionController::Base
-      def display
+      def show
         @customer = find_customer
       end
       
@@ -100,7 +100,7 @@ A short rundown of the major features:
       after_filter { |c| c.response.body = GZip::compress(c.response.body) }
       after_filter LocalizeFilter
       
-      def list
+      def index
         # Before this action is run, the user will be authenticated, the cache
         # will be examined to see if a valid copy of the results already
         # exists, and the action will be logged for auditing.
@@ -139,7 +139,7 @@ A short rundown of the major features:
     end
 
     Layout file (called weblog_layout):
-      &lt;html&gt;&lt;body&gt;&lt;%= @content_for_layout %&gt;&lt;/body&gt;&lt;/html&gt;
+      &lt;html&gt;&lt;body&gt;&lt;%= yield %&gt;&lt;/body&gt;&lt;/html&gt;
     
     Template for hello_world action:
       &lt;h1&gt;Hello world&lt;/h1&gt;
@@ -155,7 +155,7 @@ A short rundown of the major features:
     map.connect 'clients/:client_name/:project_name/:controller/:action'
 
     Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with
-    { &quot;client_name&quot; =&gt; &quot;37signals&quot;, &quot;project_name&quot; =&gt; &quot;basecamp&quot; } in @params[&quot;params&quot;]
+    { &quot;client_name&quot; =&gt; &quot;37signals&quot;, &quot;project_name&quot; =&gt; &quot;basecamp&quot; } in params[:params]
     
     From that URL, you can rewrite the redirect in a number of ways:
     
@@ -296,9 +296,8 @@ A short rundown of the major features:
   {Learn more}[link:classes/ActionController/Rescue.html]
 
 
-* Scaffolding for Action Record model objects
+* Scaffolding for Active Record model objects
 
-    require 'account' # must be an Active Record class
     class AccountController &lt; ActionController::Base
       scaffold :account
     end
@@ -306,7 +305,7 @@ A short rundown of the major features:
     The AccountController now has the full CRUD range of actions and default
     templates: list, show, destroy, new, create, edit, update
     
-  {Learn more}link:classes/ActionController/Scaffolding/ClassMethods.html
+  {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html]
 
 
 * Form building for Active Record model objects
@@ -338,10 +337,10 @@ A short rundown of the major features:
       &lt;input type=&quot;submit&quot; value=&quot;Create&quot;&gt;
     &lt;/form&gt;
 
-    This form generates a @params[&quot;post&quot;] array that can be used directly in a save action:
+    This form generates a params[:post] array that can be used directly in a save action:
     
     class WeblogController &lt; ActionController::Base
-      def save
+      def create
         post = Post.create(params[:post])
         redirect_to :action =&gt; &quot;display&quot;, :id =&gt; post.id
       end
@@ -350,10 +349,10 @@ A short rundown of the major features:
   {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html]
 
 
-* Runs on top of WEBrick, CGI, FCGI, and mod_ruby
+* Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby
 
 
-== Simple example
+== Simple example (from outside of Rails)
 
 This example will implement a simple weblog system using inline templates and
 an Active Record model. So let's build that WeblogController with just a few
@@ -366,11 +365,11 @@ methods:
     layout &quot;weblog/layout&quot;
   
     def index
-      @posts = Post.find_all
+      @posts = Post.find(:all)
     end
     
     def display
-      @post = Post.find(:params[:id])
+      @post = Post.find(params[:id])
     end
     
     def new
@@ -394,7 +393,7 @@ And the templates look like this:
 
   weblog/layout.rhtml:
     &lt;html&gt;&lt;body&gt;
-    &lt;%= @content_for_layout %&gt;
+    &lt;%= yield %&gt;
     &lt;/body&gt;&lt;/html&gt;
 
   weblog/index.rhtml:
@@ -431,6 +430,8 @@ template casing from content.
 Please note that you might need to change the &quot;shebang&quot; line to 
 #!/usr/local/env ruby, if your Ruby is not placed in /usr/local/bin/ruby
 
+Also note that these examples are all for demonstrating using Action Pack on
+its own. Not for when it's used inside of Rails.
 
 == Download
 
@@ -440,7 +441,7 @@ The latest version of Action Pack can be found at
 
 Documentation can be found at 
 
-* http://ap.rubyonrails.com
+* http://api.rubyonrails.com
 
 
 == Installation
@@ -459,13 +460,10 @@ Action Pack is released under the MIT license.
 
 == Support
 
-The Action Pack homepage is http://www.rubyonrails.com. You can find
+The Action Pack homepage is http://www.rubyonrails.org. You can find
 the Action Pack RubyForge page at http://rubyforge.org/projects/actionpack.
 And as Jim from Rake says:
 
    Feel free to submit commits or feature requests.  If you send a patch,
    remember to update the corresponding unit tests.  If fact, I prefer
-   new feature to be submitted in the form of new unit tests.
-
-For other information, feel free to ask on the ruby-talk mailing list (which
-is mirrored to comp.lang.ruby) or contact mailto:david@loudthinking.com.
+   new feature to be submitted in the form of new unit tests.
\ No newline at end of file</diff>
      <filename>vendor/rails/actionpack/README</filename>
    </modified>
    <modified>
      <diff>@@ -22,11 +22,14 @@ task :default =&gt; [ :test ]
 
 # Run the unit tests
 
-Rake::TestTask.new { |t|
+desc &quot;Run all unit tests&quot;
+task :test =&gt; [:test_action_pack, :test_active_record_integration]
+
+Rake::TestTask.new(:test_action_pack) { |t|
   t.libs &lt;&lt; &quot;test&quot;
 # make sure we include the controller tests (c*) first as on some systems
 # this will not happen automatically and the tests (as a whole) will error
-  t.test_files=Dir.glob( &quot;test/c*/*_test.rb&quot; ) + Dir.glob( &quot;test/[ft]*/*_test.rb&quot; )
+  t.test_files=Dir.glob( &quot;test/c*/**/*_test.rb&quot; ) + Dir.glob( &quot;test/[ft]*/*_test.rb&quot; )
 #  t.pattern = 'test/*/*_test.rb'
   t.verbose = true
 }
@@ -72,12 +75,12 @@ spec = Gem::Specification.new do |s|
   s.has_rdoc = true
   s.requirements &lt;&lt; 'none'
 
-  s.add_dependency('activesupport', '= 1.3.1' + PKG_BUILD)
+  s.add_dependency('activesupport', '= 1.4.4' + PKG_BUILD)
 
   s.require_path = 'lib'
   s.autorequire = 'action_controller'
 
-  s.files = [ &quot;Rakefile&quot;, &quot;install.rb&quot;, &quot;filler.txt&quot;, &quot;README&quot;, &quot;RUNNING_UNIT_TESTS&quot;, &quot;CHANGELOG&quot;, &quot;MIT-LICENSE&quot;, &quot;examples/.htaccess&quot; ]
+  s.files = [ &quot;Rakefile&quot;, &quot;install.rb&quot;, &quot;README&quot;, &quot;RUNNING_UNIT_TESTS&quot;, &quot;CHANGELOG&quot;, &quot;MIT-LICENSE&quot;, &quot;examples/.htaccess&quot; ]
   dist_dirs.each do |dir|
     s.files = s.files + Dir.glob( &quot;#{dir}/**/*&quot; ).delete_if { |item| item.include?( &quot;\.svn&quot; ) }
   end
@@ -141,11 +144,11 @@ end
 
 desc &quot;Publish the release files to RubyForge.&quot;
 task :release =&gt; [ :package ] do
-  `rubyforge login`
+  require 'rubyforge'
 
-  for ext in %w( gem tgz zip )
-    release_command = &quot;rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}&quot;
-    puts release_command
-    system(release_command)
-  end
-end
+  packages = %w( gem tgz zip ).collect{ |ext| &quot;pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}&quot; }
+
+  rubyforge = RubyForge.new
+  rubyforge.login
+  rubyforge.add_release(PKG_NAME, PKG_NAME, &quot;REL #{PKG_VERSION}&quot;, *packages)
+end
\ No newline at end of file</diff>
      <filename>vendor/rails/actionpack/Rakefile</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionpack/examples/address_book_controller.cgi</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionpack/examples/address_book_controller.fcgi</filename>
    </modified>
    <modified>
      <diff>@@ -28,11 +28,11 @@ class AddressBookController &lt; ActionController::Base
   end
   
   def person
-    @person = @address_book.find_person(@params[&quot;id&quot;])
+    @person = @address_book.find_person(params[:id])
   end
   
   def create_person
-    @address_book.create_person(@params[&quot;person&quot;])
+    @address_book.create_person(params[:person])
     redirect_to :action =&gt; &quot;index&quot;
   end
     
@@ -49,4 +49,4 @@ begin
   AddressBookController.process_cgi(CGI.new) if $0 == __FILE__
 rescue =&gt; e
   CGI.new.out { &quot;#{e.class}: #{e.message}&quot; }
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/rails/actionpack/examples/address_book_controller.rb</filename>
    </modified>
    <modified>
      <filename>vendor/rails/actionpack/examples/benchmark_with_ar.fcgi</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@ class BlogController &lt; ActionController::Base
     
     render_template &lt;&lt;-&quot;EOF&quot;
       &lt;html&gt;&lt;body&gt;
-      &lt;%= @flash[&quot;alert&quot;] %&gt;
+      &lt;%= flash[&quot;alert&quot;] %&gt;
       &lt;h1&gt;Posts&lt;/h1&gt;
       &lt;% @posts.each do |post| %&gt;
         &lt;p&gt;&lt;b&gt;&lt;%= post.title %&gt;&lt;/b&gt;&lt;br /&gt;&lt;%= post.body %&gt;&lt;/p&gt;
@@ -32,7 +32,7 @@ class BlogController &lt; ActionController::Base
   end
   
   def create
-    @session[&quot;posts&quot;].unshift(Post.new(@params[&quot;post&quot;][&quot;title&quot;], @params[&quot;post&quot;][&quot;body&quot;]))
+    @session[&quot;posts&quot;].unshift(Post.new(params[:post][:title], params[:post][:body]))
     flash[&quot;alert&quot;] = &quot;New post added!&quot;
     redirect_to :action =&gt; &quot;index&quot;
   end
@@ -50,4 +50,4 @@ begin
   BlogController.process_cgi(CGI.new) if $0 == __FILE__
 rescue =&gt; e
   CGI.new.out { &quot;#{e.class}: #{e.message}&quot; }
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/rails/actionpack/examples/blog_controller.cgi</filename>
    </modified>
    <modified>
      <diff>@@ -25,19 +25,19 @@ class DebateController &lt; ActionController::Base
   end
   
   def topic
-    @topic = @debate.find_topic(@params[&quot;id&quot;])
+    @topic = @debate.find_topic(params[:id])
   end
   
   # def new_topic() end &lt;-- This is not needed as the template doesn't require any assigns
 
   def create_topic
-    @debate.create_topic(@params[&quot;topic&quot;])
+    @debate.create_topic(params[:topic])
     redirect_to :action =&gt; &quot;index&quot;
   end
 
   def create_reply
-    @debate.create_reply(@params[&quot;reply&quot;])
-    redirect_to :action =&gt; &quot;topic&quot;, :path_params =&gt; { &quot;id&quot; =&gt; @params[&quot;reply&quot;][&quot;topic_id&quot;] }
+    @debate.create_reply(params[:reply])
+    redirect_to :action =&gt; &quot;topic&quot;, :path_params =&gt; { &quot;id&quot; =&gt; params[:reply][:topic_id] }
   end
     
   private
@@ -54,4 +54,4 @@ begin
   DebateController.process_cgi(CGI.new) if $0 == __FILE__
 rescue =&gt; e
   CGI.new.out { &quot;#{e.class}: #{e.message}&quot; }
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/rails/actionpack/examples/debate_controller.cgi</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 #--
-# Copyright (c) 2004 David Heinemeier Hansson
+# Copyright (c) 2004-2006 David Heinemeier Hansson
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
@@ -30,7 +30,7 @@ unless defined?(ActiveSupport)
     require 'active_support'  
   rescue LoadError
     require 'rubygems'
-    require_gem 'activesupport'
+    gem 'activesupport'
   end
 end
 
@@ -43,7 +43,7 @@ require 'action_controller/benchmarking'
 require 'action_controller/flash'
 require 'action_controller/filters'
 require 'action_controller/layout'
-require 'action_controller/dependencies'
+require 'action_controller/deprecated_dependencies'
 require 'action_controller/mime_responds'
 require 'action_controller/pagination'
 require 'action_controller/scaffolding'</diff>
      <filename>vendor/rails/actionpack/lib/action_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,320 +1,82 @@
 require 'test/unit'
 require 'test/unit/assertions'
-require 'rexml/document'
-require File.dirname(__FILE__) + &quot;/vendor/html-scanner/html/document&quot;
 
-module Test #:nodoc:
-  module Unit #:nodoc:
-    # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
-    # can be used against. These collections are:
-    #
-    # * assigns: Instance variables assigned in the action that are available for the view.
-    # * session: Objects being saved in the session.
-    # * flash: The flash objects currently in the session.
-    # * cookies: Cookies being sent to the user on this request.
-    # 
-    # These collections can be used just like any other hash:
-    #
-    #   assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
-    #   assert_equal &quot;Dave&quot;, cookies[:name] # makes sure that a cookie called :name was set as &quot;Dave&quot;
-    #   assert flash.empty? # makes sure that there's nothing in the flash
-    #
-    # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns[&quot;person&quot;] will. To
-    # appease our yearning for symbols, though, an alternative accessor has been deviced using a method call instead of index referencing.
-    # So assigns(:person) will work just like assigns[&quot;person&quot;], but again, assigns[:person] will not work.
-    #
-    # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
-    #
-    # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
-    # action call which can then be asserted against.
-    #
-    # == Manipulating the request collections
-    #
-    # The collections described above link to the response, so you can test if what the actions were expected to do happened. But
-    # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions
-    # and cookies, though. For sessions, you just do:
-    #
-    #   @request.session[:key] = &quot;value&quot;
-    #
-    # For cookies, you need to manually create the cookie, like this:
-    #
-    #   @request.cookies[&quot;key&quot;] = CGI::Cookie.new(&quot;key&quot;, &quot;value&quot;)
-    #
-    # == Testing named routes
-    #
-    # If you're using named routes, they can be easily tested using the original named routes methods straight in the test case.
-    # Example: 
-    #
-    #  assert_redirected_to page_url(:title =&gt; 'foo')
-    module Assertions
-      # Asserts that the response is one of the following types:
-      # 
-      # * &lt;tt&gt;:success&lt;/tt&gt;: Status code was 200
-      # * &lt;tt&gt;:redirect&lt;/tt&gt;: Status code was in the 300-399 range
-      # * &lt;tt&gt;:missing&lt;/tt&gt;: Status code was 404
-      # * &lt;tt&gt;:error&lt;/tt&gt;:  Status code was in the 500-599 range
-      #
-      # You can also pass an explicit status code number as the type, like assert_response(501)
-      def assert_response(type, message = nil)
-        clean_backtrace do
-          if [ :success, :missing, :redirect, :error ].include?(type) &amp;&amp; @response.send(&quot;#{type}?&quot;)
-            assert_block(&quot;&quot;) { true } # to count the assertion
-          elsif type.is_a?(Fixnum) &amp;&amp; @response.response_code == type
-            assert_block(&quot;&quot;) { true } # to count the assertion
-          else
-            assert_block(build_message(message, &quot;Expected response to be a &lt;?&gt;, but was &lt;?&gt;&quot;, type, @response.response_code)) { false }
-          end               
-        end
-      end
-
-      # Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial,
-      # such that assert_redirected_to(:controller =&gt; &quot;weblog&quot;) will also match the redirection of 
-      # redirect_to(:controller =&gt; &quot;weblog&quot;, :action =&gt; &quot;show&quot;) and so on.
-      def assert_redirected_to(options = {}, message=nil)
-        clean_backtrace do
-          assert_response(:redirect, message)
-
-          if options.is_a?(String)
-            msg = build_message(message, &quot;expected a redirect to &lt;?&gt;, found one to &lt;?&gt;&quot;, options, @response.redirect_url)
-            url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$}
-            eurl, epath, url, path = [options, @response.redirect_url].collect do |url|
-              u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url]
-              [u, (p[0..0] == '/') ? p : '/' + p]
-            end.flatten
-
-            assert_equal(eurl, url, msg) if eurl &amp;&amp; url
-            assert_equal(epath, path, msg) if epath &amp;&amp; path 
-          else
-            @response_diff = options.diff(@response.redirected_to) if options.is_a?(Hash) &amp;&amp; @response.redirected_to.is_a?(Hash)
-            msg = build_message(message, &quot;response is not a redirection to all of the options supplied (redirection is &lt;?&gt;)#{', difference: &lt;?&gt;' if @response_diff}&quot;, 
-                                @response.redirected_to || @response.redirect_url, @response_diff)
-
-            assert_block(msg) do
-              if options.is_a?(Symbol)
-                @response.redirected_to == options
-              else
-                options.keys.all? do |k|
-                  if k == :controller then options[k] == ActionController::Routing.controller_relative_to(@response.redirected_to[k], @controller.class.controller_path)
-                  else options[k] == (@response.redirected_to[k].respond_to?(:to_param) ? @response.redirected_to[k].to_param : @response.redirected_to[k] unless @response.redirected_to[k].nil?)
-                  end
-                end
-              end
-            end
-          end
-        end
-      end
-
-      # Asserts that the request was rendered with the appropriate template file.
-      def assert_template(expected = nil, message=nil)
-        clean_backtrace do
-          rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file
-          msg = build_message(message, &quot;expecting &lt;?&gt; but rendering with &lt;?&gt;&quot;, expected, rendered)
-          assert_block(msg) do
-            if expected.nil?
-              !@response.rendered_with_file?
-            else
-              expected == rendered
-            end
-          end               
-        end
-      end
-
-      # Asserts that the routing of the given path was handled correctly and that the parsed options match.
-      def assert_recognizes(expected_options, path, extras={}, message=nil)
-        clean_backtrace do 
-          path = &quot;/#{path}&quot; unless path[0..0] == '/'
-          # Load routes.rb if it hasn't been loaded.
-          ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? 
-      
-          # Assume given controller
-          request = ActionController::TestRequest.new({}, {}, nil)
-          request.path = path
-          ActionController::Routing::Routes.recognize!(request)
-      
-          expected_options = expected_options.clone
-          extras.each_key { |key| expected_options.delete key } unless extras.nil?
-      
-          expected_options.stringify_keys!
-          msg = build_message(message, &quot;The recognized options &lt;?&gt; did not match &lt;?&gt;&quot;, 
-              request.path_parameters, expected_options)
-          assert_block(msg) { request.path_parameters == expected_options }
-        end
-      end
-
-      # Asserts that the provided options can be used to generate the provided path.
-      def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
-        clean_backtrace do 
-          expected_path = &quot;/#{expected_path}&quot; unless expected_path[0] == ?/
-          # Load routes.rb if it hasn't been loaded.
-          ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? 
-      
-          generated_path, extra_keys = ActionController::Routing::Routes.generate(options, extras)
-          found_extras = options.reject {|k, v| ! extra_keys.include? k}
-
-          msg = build_message(message, &quot;found extras &lt;?&gt;, not &lt;?&gt;&quot;, found_extras, extras)
-          assert_block(msg) { found_extras == extras }
-      
-          msg = build_message(message, &quot;The generated path &lt;?&gt; did not match &lt;?&gt;&quot;, generated_path, 
-              expected_path)
-          assert_block(msg) { expected_path == generated_path }
-        end
-      end
-
-      # Asserts that path and options match both ways; in other words, the URL generated from 
-      # options is the same as path, and also that the options recognized from path are the same as options
-      def assert_routing(path, options, defaults={}, extras={}, message=nil)
-        assert_recognizes(options, path, extras, message)
-        
-        controller, default_controller = options[:controller], defaults[:controller] 
-        if controller &amp;&amp; controller.include?(?/) &amp;&amp; default_controller &amp;&amp; default_controller.include?(?/)
-          options[:controller] = &quot;/#{controller}&quot;
-        end
-         
-        assert_generates(path, options, defaults, extras, message)
+module ActionController #:nodoc:
+  # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
+  # can be used against. These collections are:
+  #
+  # * assigns: Instance variables assigned in the action that are available for the view.
+  # * session: Objects being saved in the session.
+  # * flash: The flash objects currently in the session.
+  # * cookies: Cookies being sent to the user on this request.
+  # 
+  # These collections can be used just like any other hash:
+  #
+  #   assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
+  #   assert_equal &quot;Dave&quot;, cookies[:name] # makes sure that a cookie called :name was set as &quot;Dave&quot;
+  #   assert flash.empty? # makes sure that there's nothing in the flash
+  #
+  # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns[&quot;person&quot;] will. To
+  # appease our yearning for symbols, though, an alternative accessor has been deviced using a method call instead of index referencing.
+  # So assigns(:person) will work just like assigns[&quot;person&quot;], but again, assigns[:person] will not work.
+  #
+  # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
+  #
+  # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
+  # action call which can then be asserted against.
+  #
+  # == Manipulating the request collections
+  #
+  # The collections described above link to the response, so you can test if what the actions were expected to do happened. But
+  # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions
+  # and cookies, though. For sessions, you just do:
+  #
+  #   @request.session[:key] = &quot;value&quot;
+  #
+  # For cookies, you need to manually create the cookie, like this:
+  #
+  #   @request.cookies[&quot;key&quot;] = CGI::Cookie.new(&quot;key&quot;, &quot;value&quot;)
+  #
+  # == Testing named routes
+  #
+  # If you're using named routes, they can be easily tested using the original named routes methods straight in the test case.
+  # Example: 
+  #
+  #  assert_redirected_to page_url(:title =&gt; 'foo')
+  module Assertions
+    def self.included(klass)
+      klass.class_eval do
+        include ActionController::Assertions::ResponseAssertions
+        include ActionController::Assertions::SelectorAssertions
+        include ActionController::Assertions::RoutingAssertions
+        include ActionController::Assertions::TagAssertions
+        include ActionController::Assertions::DomAssertions
+        include ActionController::Assertions::ModelAssertions
+        include ActionController::Assertions::DeprecatedAssertions
       end
+    end
 
-      # Asserts that there is a tag/node/element in the body of the response
-      # that meets all of the given conditions. The +conditions+ parameter must
-      # be a hash of any of the following keys (all are optional):
-      #
-      # * &lt;tt&gt;:tag&lt;/tt&gt;: the node type must match the corresponding value
-      # * &lt;tt&gt;:attributes&lt;/tt&gt;: a hash. The node's attributes must match the
-      #   corresponding values in the hash.
-      # * &lt;tt&gt;:parent&lt;/tt&gt;: a hash. The node's parent must match the
-      #   corresponding hash.
-      # * &lt;tt&gt;:child&lt;/tt&gt;: a hash. At least one of the node's immediate children
-      #   must meet the criteria described by the hash.
-      # * &lt;tt&gt;:ancestor&lt;/tt&gt;: a hash. At least one of the node's ancestors must
-      #   meet the criteria described by the hash.
-      # * &lt;tt&gt;:descendant&lt;/tt&gt;: a hash. At least one of the node's descendants
-      #   must meet the criteria described by the hash.
-      # * &lt;tt&gt;:sibling&lt;/tt&gt;: a hash. At least one of the node's siblings must
-      #   meet the criteria described by the hash.
-      # * &lt;tt&gt;:after&lt;/tt&gt;: a hash. The node must be after any sibling meeting
-      #   the criteria described by the hash, and at least one sibling must match.
-      # * &lt;tt&gt;:before&lt;/tt&gt;: a hash. The node must be before any sibling meeting
-      #   the criteria described by the hash, and at least one sibling must match.
-      # * &lt;tt&gt;:children&lt;/tt&gt;: a hash, for counting children of a node. Accepts
-      #   the keys:
-      #   * &lt;tt&gt;:count&lt;/tt&gt;: either a number or a range which must equal (or
-      #     include) the number of children that match.
-      #   * &lt;tt&gt;:less_than&lt;/tt&gt;: the number of matching children must be less
-      #     than this number.
-      #   * &lt;tt&gt;:greater_than&lt;/tt&gt;: the number of matching children must be
-      #     greater than this number.
-      #   * &lt;tt&gt;:only&lt;/tt&gt;: another hash consisting of the keys to use
-      #     to match on the children, and only matching children will be
-      #     counted.
-      # * &lt;tt&gt;:content&lt;/tt&gt;: the textual content of the node must match the
-      #     given value. This will not match HTML tags in the body of a
-      #     tag--only text.
-      #
-      # Conditions are matched using the following algorithm:
-      #
-      # * if the condition is a string, it must be a substring of the value.
-      # * if the condition is a regexp, it must match the value.
-      # * if the condition is a number, the value must match number.to_s.
-      # * if the condition is +true+, the value must not be +nil+.
-      # * if the condition is +false+ or +nil+, the value must be +nil+.
-      #
-      # Usage:
-      #
-      #   # assert that there is a &quot;span&quot; tag
-      #   assert_tag :tag =&gt; &quot;span&quot;
-      #
-      #   # assert that there is a &quot;span&quot; tag with id=&quot;x&quot;
-      #   assert_tag :tag =&gt; &quot;span&quot;, :attributes =&gt; { :id =&gt; &quot;x&quot; }
-      #
-      #   # assert that there is a &quot;span&quot; tag using the short-hand
-      #   assert_tag :span
-      #
-      #   # assert that there is a &quot;span&quot; tag with id=&quot;x&quot; using the short-hand
-      #   assert_tag :span, :attributes =&gt; { :id =&gt; &quot;x&quot; }
-      #
-      #   # assert that there is a &quot;span&quot; inside of a &quot;div&quot;
-      #   assert_tag :tag =&gt; &quot;span&quot;, :parent =&gt; { :tag =&gt; &quot;div&quot; }
-      #
-      #   # assert that there is a &quot;span&quot; somewhere inside a table
-      #   assert_tag :tag =&gt; &quot;span&quot;, :ancestor =&gt; { :tag =&gt; &quot;table&quot; }
-      #
-      #   # assert that there is a &quot;span&quot; with at least one &quot;em&quot; child
-      #   assert_tag :tag =&gt; &quot;span&quot;, :child =&gt; { :tag =&gt; &quot;em&quot; }
-      #
-      #   # assert that there is a &quot;span&quot; containing a (possibly nested)
-      #   # &quot;strong&quot; tag.
-      #   assert_tag :tag =&gt; &quot;span&quot;, :descendant =&gt; { :tag =&gt; &quot;strong&quot; }
-      #
-      #   # assert that there is a &quot;span&quot; containing between 2 and 4 &quot;em&quot; tags
-      #   # as immediate children
-      #   assert_tag :tag =&gt; &quot;span&quot;,
-      #              :children =&gt; { :count =&gt; 2..4, :only =&gt; { :tag =&gt; &quot;em&quot; } } 
-      #
-      #   # get funky: assert that there is a &quot;div&quot;, with an &quot;ul&quot; ancestor
-      #   # and an &quot;li&quot; parent (with &quot;class&quot; = &quot;enum&quot;), and containing a 
-      #   # &quot;span&quot; descendant that contains text matching /hello world/
-      #   assert_tag :tag =&gt; &quot;div&quot;,
-      #              :ancestor =&gt; { :tag =&gt; &quot;ul&quot; },
-      #              :parent =&gt; { :tag =&gt; &quot;li&quot;,
-      #                           :attributes =&gt; { :class =&gt; &quot;enum&quot; } },
-      #              :descendant =&gt; { :tag =&gt; &quot;span&quot;,
-      #                               :child =&gt; /hello world/ }
-      #
-      # &lt;strong&gt;Please note&lt;/strong: #assert_tag and #assert_no_tag only work
-      # with well-formed XHTML. They recognize a few tags as implicitly self-closing
-      # (like br and hr and such) but will not work correctly with tags
-      # that allow optional closing tags (p, li, td). &lt;em&gt;You must explicitly
-      # close all of your tags to use these assertions.&lt;/em&gt;
-      def assert_tag(*opts)
-        clean_backtrace do
-          opts = opts.size &gt; 1 ? opts.last.merge({ :tag =&gt; opts.first.to_s }) : opts.first
-          tag = find_tag(opts)
-          assert tag, &quot;expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}&quot;
-        end
-      end
-      
-      # Identical to #assert_tag, but asserts that a matching tag does _not_
-      # exist. (See #assert_tag for a full discussion of the syntax.)
-      def assert_no_tag(*opts)
-        clean_backtrace do
-          opts = opts.size &gt; 1 ? opts.last.merge({ :tag =&gt; opts.first.to_s }) : opts.first
-          tag = find_tag(opts)
-          assert !tag, &quot;expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}&quot;
-        end
-      end
+    def clean_backtrace(&amp;block)
+      yield
+    rescue Test::Unit::AssertionFailedError =&gt; e         
+      path = File.expand_path(__FILE__)
+      raise Test::Unit::AssertionFailedError, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
+    end
+  end
+end
 
-      # test 2 html strings to be equivalent, i.e. identical up to reordering of attributes
-      def assert_dom_equal(expected, actual, message=&quot;&quot;)
-        clean_backtrace do
-          expected_dom = HTML::Document.new(expected).root
-          actual_dom = HTML::Document.new(actual).root
-          full_message = build_message(message, &quot;&lt;?&gt; expected to be == to\n&lt;?&gt;.&quot;, expected_dom.to_s, actual_dom.to_s)
-          assert_block(full_message) { expected_dom == actual_dom }
-        end
-      end
-      
-      # negated form of +assert_dom_equivalent+
-      def assert_dom_not_equal(expected, actual, message=&quot;&quot;)
-        clean_backtrace do
-          expected_dom = HTML::Document.new(expected).root
-          actual_dom = HTML::Document.new(actual).root
-          full_message = build_message(message, &quot;&lt;?&gt; expected to be != to\n&lt;?&gt;.&quot;, expected_dom.to_s, actual_dom.to_s)
-          assert_block(full_message) { expected_dom != actual_dom }
-        end
-      end
+require File.dirname(__FILE__) + '/assertions/response_assertions'
+require File.dirname(__FILE__) + '/assertions/selector_assertions'
+require File.dirname(__FILE__) + '/assertions/tag_assertions'
+require File.dirname(__FILE__) + '/assertions/dom_assertions'
+require File.dirname(__FILE__) + '/assertions/routing_assertions'
+require File.dirname(__FILE__) + '/assertions/model_assertions'
+require File.dirname(__FILE__) + '/assertions/deprecated_assertions'
 
-      # ensures that the passed record is valid by active record standards. returns the error messages if not
-      def assert_valid(record)
-        clean_backtrace do
-          assert record.valid?, record.errors.full_messages.join(&quot;\n&quot;)
-        end
-      end             
-      
-      def clean_backtrace(&amp;block)
-        yield
-      rescue AssertionFailedError =&gt; e         
-        path = File.expand_path(__FILE__)
-        raise AssertionFailedError, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
-      end
+module Test #:nodoc:
+  module Unit #:nodoc:
+    class TestCase #:nodoc:
+      include ActionController::Assertions
     end
   end
-end
+end
\ No newline at end of file</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/assertions.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,8 +2,9 @@ require 'action_controller/mime_type'
 require 'action_controller/request'
 require 'action_controller/response'
 require 'action_controller/routing'
-require 'action_controller/code_generation'
+require 'action_controller/resources'
 require 'action_controller/url_rewriter'
+require 'action_controller/status_codes'
 require 'drb'
 require 'set'
 
@@ -27,6 +28,8 @@ module ActionController #:nodoc:
   end
   class MissingFile &lt; ActionControllerError #:nodoc:
   end
+  class RenderError &lt; ActionControllerError #:nodoc:
+  end
   class SessionOverflowError &lt; ActionControllerError #:nodoc:
     DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
 
@@ -35,23 +38,23 @@ module ActionController #:nodoc:
     end
   end
   class DoubleRenderError &lt; ActionControllerError #:nodoc:
-    DEFAULT_MESSAGE = &quot;Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and only once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \&quot;redirect_to(...) and return\&quot;. Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \&quot;render(...) and return false\&quot;.&quot; 
+    DEFAULT_MESSAGE = &quot;Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and only once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \&quot;redirect_to(...) and return\&quot;. Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \&quot;render(...) and return false\&quot;.&quot;
 
     def initialize(message = nil)
       super(message || DEFAULT_MESSAGE)
     end
   end
   class RedirectBackError &lt; ActionControllerError #:nodoc:
-    DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify @request.env[&quot;HTTP_REFERER&quot;].'
-  
+    DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env[&quot;HTTP_REFERER&quot;].'
+
     def initialize(message = nil)
       super(message || DEFAULT_MESSAGE)
     end
   end
 
-  # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed 
+  # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
   # on request and then either render a template or redirect to another action. An action is defined as a public method
-  # on the controller, which will automatically be made accessible to the web-server through Rails Routes. 
+  # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
   #
   # A sample controller could look like this:
   #
@@ -59,7 +62,7 @@ module ActionController #:nodoc:
   #     def index
   #       @entries = Entry.find(:all)
   #     end
-  #     
+  #
   #     def sign
   #       Entry.create(params[:entry])
   #       redirect_to :action =&gt; &quot;index&quot;
@@ -67,11 +70,11 @@ module ActionController #:nodoc:
   #   end
   #
   # Actions, by default, render a template in the &lt;tt&gt;app/views&lt;/tt&gt; directory corresponding to the name of the controller and action
-  # after executing code in the action. For example, the +index+ action of the +GuestBookController+  would render the 
+  # after executing code in the action. For example, the +index+ action of the +GuestBookController+  would render the
   # template &lt;tt&gt;app/views/guestbook/index.rhtml&lt;/tt&gt; by default after populating the &lt;tt&gt;@entries&lt;/tt&gt; instance variable.
   #
-  # Unlike index, the sign action will not render a template. After performing its main purpose (creating a 
-  # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external 
+  # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
+  # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
   # &quot;302 Moved&quot; HTTP response that takes the user to the index action.
   #
   # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
@@ -95,7 +98,7 @@ module ActionController #:nodoc:
   # == Parameters
   #
   # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
-  # which returns a hash. For example, an action that was performed through &lt;tt&gt;/weblog/list?category=All&amp;limit=5&lt;/tt&gt; will include 
+  # which returns a hash. For example, an action that was performed through &lt;tt&gt;/weblog/list?category=All&amp;limit=5&lt;/tt&gt; will include
   # &lt;tt&gt;{ &quot;category&quot; =&gt; &quot;All&quot;, &quot;limit&quot; =&gt; 5 }&lt;/tt&gt; in params.
   #
   # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
@@ -104,14 +107,14 @@ module ActionController #:nodoc:
   #   &lt;input type=&quot;text&quot; name=&quot;post[address]&quot; value=&quot;hyacintvej&quot;&gt;
   #
   # A request stemming from a form holding these inputs will include &lt;tt&gt;{ &quot;post&quot; =&gt; { &quot;name&quot; =&gt; &quot;david&quot;, &quot;address&quot; =&gt; &quot;hyacintvej&quot; } }&lt;/tt&gt;.
-  # If the address input had been named &quot;post[address][street]&quot;, the params would have included 
+  # If the address input had been named &quot;post[address][street]&quot;, the params would have included
   # &lt;tt&gt;{ &quot;post&quot; =&gt; { &quot;address&quot; =&gt; { &quot;street&quot; =&gt; &quot;hyacintvej&quot; } } }&lt;/tt&gt;. There's no limit to the depth of the nesting.
   #
   # == Sessions
   #
   # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
   # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
-  # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely 
+  # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
   # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
   #
   # You can place objects in the session by using the &lt;tt&gt;session&lt;/tt&gt; method, which accesses a hash:
@@ -125,13 +128,13 @@ module ActionController #:nodoc:
   # For removing objects from the session, you can either assign a single key to nil, like &lt;tt&gt;session[:person] = nil&lt;/tt&gt;, or you can
   # remove the entire session with reset_session.
   #
-  # By default, sessions are stored on the file system in &lt;tt&gt;RAILS_ROOT/tmp/sessions&lt;/tt&gt;. Any object can be placed in the session 
+  # By default, sessions are stored on the file system in &lt;tt&gt;RAILS_ROOT/tmp/sessions&lt;/tt&gt;. Any object can be placed in the session
   # (as long as it can be Marshalled). But remember that 1000 active sessions each storing a 50kb object could lead to a 50MB store on the filesystem.
   # In other words, think carefully about size and caching before resorting to the use of the session on the filesystem.
   #
   # An alternative to storing sessions on disk is to use ActiveRecordStore to store sessions in your database, which can solve problems
   # caused by storing sessions in the file system and may speed up your application. To use ActiveRecordStore, uncomment the line:
-  #   
+  #
   #   config.action_controller.session_store = :active_record_store
   #
   # in your &lt;tt&gt;environment.rb&lt;/tt&gt; and run &lt;tt&gt;rake db:sessions:create&lt;/tt&gt;.
@@ -205,9 +208,10 @@ module ActionController #:nodoc:
   #
   class Base
     DEFAULT_RENDER_STATUS_CODE = &quot;200 OK&quot;
-    
-    include Reloadable::Subclasses
-    
+
+    include Reloadable::Deprecated
+    include StatusCodes
+
     # Determines whether the view has access to controller internals @request, @response, @session, and @template.
     # By default, it does.
     @@view_controller_internals = true
@@ -217,8 +221,8 @@ module ActionController #:nodoc:
     @@protected_variables_cache = nil
     cattr_accessor :protected_variables_cache
 
-    # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets, 
-    # and images to a dedicated asset server away from the main web server. Example: 
+    # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
+    # and images to a dedicated asset server away from the main web server. Example:
     #   ActionController::Base.asset_host = &quot;http://assets.example.com&quot;
     @@asset_host = &quot;&quot;
     cattr_accessor :asset_host
@@ -228,7 +232,7 @@ module ActionController #:nodoc:
     # should instead be implemented in the controller to determine when debugging screens should be shown.
     @@consider_all_requests_local = true
     cattr_accessor :consider_all_requests_local
-    
+
     # Enable or disable the collection of failure information for RoutingErrors.
     # This information can be extremely useful when tweaking custom routes, but is
     # pointless once routes have been tested and verified.
@@ -241,84 +245,95 @@ module ActionController #:nodoc:
     @@allow_concurrency = false
     cattr_accessor :allow_concurrency
 
-    # Modern REST web services often need to submit complex data to the web application. 
-    # The param_parsers hash lets you register handlers wich will process the http body and add parameters to the 
+    # Modern REST web services often need to submit complex data to the web application.
+    # The param_parsers hash lets you register handlers which will process the http body and add parameters to the
     # &lt;tt&gt;params&lt;/tt&gt; hash. These handlers are invoked for post and put requests.
     #
-    # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated 
+    # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated
     # in the &lt;tt&gt;params&lt;/tt&gt;. This allows XML requests to mask themselves as regular form submissions, so you can have one
     # action serve both regular forms and web service requests.
-    # 
+    #
     # Example of doing your own parser for a custom content type:
     #
-    #   ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data| 
-    #      node = REXML::Document.new(post) 
+    #   ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
+    #      node = REXML::Document.new(post)
     #     { node.root.name =&gt; node.root }
     #   end
     #
-    # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the 
+    # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
     # root node for such requests. The new default is to keep the root, such that &quot;&lt;r&gt;&lt;name&gt;David&lt;/name&gt;&lt;/r&gt;&quot; results
-    # in params[:r][:name] for &quot;David&quot; instead of params[:name]. To get the old behavior, you can 
+    # in params[:r][:name] for &quot;David&quot; instead of params[:name]. To get the old behavior, you can
     # re-register XmlSimple as application/xml handler ike this:
     #
-    #   ActionController::Base.param_parsers[Mime::XML] = 
+    #   ActionController::Base.param_parsers[Mime::XML] =
     #     Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' =&gt; false) }
     #
     # A YAML parser is also available and can be turned on with:
     #
     #   ActionController::Base.param_parsers[Mime::YAML] = :yaml
     @@param_parsers = { Mime::XML =&gt; :xml_simple }
-    cattr_accessor :param_parsers 
+    cattr_accessor :param_parsers
+
+    # Controls the default charset for all renders.
+    @@default_charset = &quot;utf-8&quot;
+    cattr_accessor :default_charset
 
     # Template root determines the base from which template references will be made. So a call to render(&quot;test/template&quot;)
     # will be converted to &quot;#{template_root}/test/template.rhtml&quot;.
     class_inheritable_accessor :template_root
-    
+
     # The logger is used for generating information on the action run-time (including benchmarking) if available.
     # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
     cattr_accessor :logger
-    
+
     # Determines which template class should be used by ActionController.
     cattr_accessor :template_class
 
     # Turn on +ignore_missing_templates+ if you want to unit test actions without making the associated templates.
     cattr_accessor :ignore_missing_templates
 
+    # Controls the resource action separator
+    @@resource_action_separator = &quot;/&quot;
+    cattr_accessor :resource_action_separator
+
     # Holds the request object that's primarily used to get environment variables through access like
     # &lt;tt&gt;request.env[&quot;REQUEST_URI&quot;]&lt;/tt&gt;.
-    attr_accessor :request
-    
+    attr_internal :request
+
     # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like &lt;tt&gt;params[&quot;post_id&quot;]&lt;/tt&gt;
     # to get the post_id. No type casts are made, so all values are returned as strings.
-    attr_accessor :params
-    
-    # Holds the response object that's primarily used to set additional HTTP headers through access like 
+    attr_internal :params
+
+    # Holds the response object that's primarily used to set additional HTTP headers through access like
     # &lt;tt&gt;response.headers[&quot;Cache-Control&quot;] = &quot;no-cache&quot;&lt;/tt&gt;. Can also be used to access the final body HTML after a template
     # has been rendered through response.body -- useful for &lt;tt&gt;after_filter&lt;/tt&gt;s that wants to manipulate the output,
     # such as a OutputCompressionFilter.
-    attr_accessor :response
-    
+    attr_internal :response
+
     # Holds a hash of objects in the session. Accessed like &lt;tt&gt;session[:person]&lt;/tt&gt; to get the object tied to the &quot;person&quot;
     # key. The session will hold any type of object as values, but the key should be a string or symbol.
-    attr_accessor :session
-    
+    attr_internal :session
+
     # Holds a hash of header names and values. Accessed like &lt;tt&gt;headers[&quot;Cache-Control&quot;]&lt;/tt&gt; to get the value of the Cache-Control
     # directive. Values should always be specified as strings.
-    attr_accessor :headers
-    
+    attr_internal :headers
+
     # Holds the hash of variables that are passed on to the template class to be made available to the view. This hash
     # is generated by taking a snapshot of all the instance variables in the current scope just before a template is rendered.
     attr_accessor :assigns
 
     # Returns the name of the action this controller is processing.
     attr_accessor :action_name
-    
+
+    # Templates that are exempt from layouts
+    @@exempt_from_layout = Set.new([/\.rjs$/])
+
     class &lt;&lt; self
       # Factory for the standard create, process loop where the controller is discarded after processing.
       def process(request, response) #:nodoc:
         new.process(request, response)
       end
-      
+
       # Converts the class name from something like &quot;OneModule::TwoModule::NeatController&quot; to &quot;NeatController&quot;.
       def controller_class_name
         @controller_class_name ||= name.demodulize
@@ -328,7 +343,7 @@ module ActionController #:nodoc:
       def controller_name
         @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
       end
-      
+
       # Converts the class name from something like &quot;OneModule::TwoModule::NeatController&quot; to &quot;one_module/two_module/neat&quot;.
       def controller_path
         @controller_path ||= name.gsub(/Controller$/, '').underscore
@@ -346,7 +361,7 @@ module ActionController #:nodoc:
       def hide_action(*names)
         write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s })
       end
-      
+
       # Replace sensitive paramater data from the request log.
       # Filters paramaters that have any of the arguments as a substring.
       # Looks in all subhashes of the param hash for keys to filter.
@@ -368,7 +383,7 @@ module ActionController #:nodoc:
       #   =&gt; reverses the value to all keys matching /secret/i
       #
       #   filter_parameter_logging(:foo, &quot;bar&quot;) { |k,v| v.reverse! if k =~ /secret/i }
-      #   =&gt; reverses the value to all keys matching /secret/i, and 
+      #   =&gt; reverses the value to all keys matching /secret/i, and
       #      replaces the value to all keys matching /foo|bar/i with &quot;[FILTERED]&quot;
       def filter_parameter_logging(*filter_words, &amp;block)
         parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length &gt; 0
@@ -380,12 +395,13 @@ module ActionController #:nodoc:
             if key =~ parameter_filter
               filtered_parameters[key] = '[FILTERED]'
             elsif value.is_a?(Hash)
-              filtered_parameters[key] = filter_parameters(value) 
+              filtered_parameters[key] = filter_parameters(value)
             elsif block_given?
-              key, value = key.dup, value.dup
+              key = key.dup
+              value = value.dup if value
               yield key, value
               filtered_parameters[key] = value
-            else 
+            else
               filtered_parameters[key] = value
             end
           end
@@ -393,9 +409,20 @@ module ActionController #:nodoc:
           filtered_parameters
         end
       end
+
+      # Don't render layouts for templates with the given extensions.
+      def exempt_from_layout(*extensions)
+        @@exempt_from_layout.merge extensions.collect { |extension|
+          if extension.is_a?(Regexp)
+            extension
+          else
+            /\.#{Regexp.escape(extension.to_s)}$/
+          end
+        }
+      end
     end
 
-    public      
+    public
       # Extracts the action_name from the request parameters and performs that action.
       def process(request, response, method = :perform_action, *arguments) #:nodoc:
         initialize_template_class(response)
@@ -403,25 +430,26 @@ module ActionController #:nodoc:
         initialize_current_url
         assign_names
         forget_variables_added_to_assigns
-        
+
         log_processing
         send(method, *arguments)
-        
+
+        assign_default_content_type_and_charset
         response
       ensure
         process_cleanup
       end
 
-      # Returns a URL that has been rewritten according to the options hash and the defined Routes. 
+      # Returns a URL that has been rewritten according to the options hash and the defined Routes.
       # (For doing a complete redirect, use redirect_to).
       # &#160;
       # &lt;tt&gt;url_for&lt;/tt&gt; is used to:
       # &#160;
       # All keys given to url_for are forwarded to the Route module, save for the following:
-      # * &lt;tt&gt;:anchor&lt;/tt&gt; -- specifies the anchor name to be appended to the path. For example, 
-      #   &lt;tt&gt;url_for :controller =&gt; 'posts', :action =&gt; 'show', :id =&gt; 10, :anchor =&gt; 'comments'&lt;/tt&gt; 
+      # * &lt;tt&gt;:anchor&lt;/tt&gt; -- specifies the anchor name to be appended to the path. For example,
+      #   &lt;tt&gt;url_for :controller =&gt; 'posts', :action =&gt; 'show', :id =&gt; 10, :anchor =&gt; 'comments'&lt;/tt&gt;
       #   will produce &quot;/posts/show/10#comments&quot;.
-      # * &lt;tt&gt;:only_path&lt;/tt&gt; --  if true, returns the absolute URL (omitting the protocol, host name, and port)
+      # * &lt;tt&gt;:only_path&lt;/tt&gt; --  if true, returns the relative URL (omitting the protocol, host name, and port) (&lt;tt&gt;false&lt;/tt&gt; by default)
       # * &lt;tt&gt;:trailing_slash&lt;/tt&gt; --  if true, adds a trailing slash, as in &quot;/archive/2005/&quot;. Note that this
       #   is currently not recommended since it breaks caching.
       # * &lt;tt&gt;:host&lt;/tt&gt; -- overrides the default (current) host if provided
@@ -472,7 +500,7 @@ module ActionController #:nodoc:
       # displayed on:
       #
       #   url_for :controller =&gt; 'posts', :action =&gt; nil
-      #      
+      #
       # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the
       # :overwrite_params options. Say for your posts you have different views for showing and printing them.
       # Then, in the show view, you get the URL for the print view like this
@@ -483,9 +511,20 @@ module ActionController #:nodoc:
       # would have slashed-off the path components after the changed action.
       def url_for(options = {}, *parameters_for_method_reference) #:doc:
         case options
-          when String then options
-          when Symbol then send(options, *parameters_for_method_reference)
-          when Hash   then @url.rewrite(rewrite_options(options))
+          when String
+            options
+
+          when Symbol
+            ActiveSupport::Deprecation.warn(
+              &quot;You called url_for(:#{options}), which is a deprecated API call. Instead you should use the named &quot; +
+              &quot;route directly, like #{options}(). Using symbols and parameters with url_for will be removed from Rails 2.0.&quot;,
+              caller
+            )
+
+            send(options, *parameters_for_method_reference)
+
+          when Hash
+            @url.rewrite(rewrite_options(options))
         end
       end
 
@@ -499,15 +538,21 @@ module ActionController #:nodoc:
         self.class.controller_name
       end
 
+      # Converts the class name from something like &quot;OneModule::TwoModule::NeatController&quot; to &quot;one_module/two_module/neat&quot;.
+      def controller_path
+        self.class.controller_path
+      end
+
+      # Test whether the session is enabled for this request.
       def session_enabled?
-        request.session_options[:disabled] != false
+        request.session_options &amp;&amp; request.session_options[:disabled] != false
       end
 
     protected
       # Renders the content that will be returned to the browser as the response body.
       #
       # === Rendering an action
-      # 
+      #
       # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
       # specified. By default, actions are rendered within the current layout (if one exists).
       #
@@ -523,47 +568,54 @@ module ActionController #:nodoc:
       #   render :action =&gt; &quot;long_goal&quot;, :layout =&gt; &quot;spectacular&quot;
       #
       # _Deprecation_ _notice_: This used to have the signatures &lt;tt&gt;render_action(&quot;action&quot;, status = 200)&lt;/tt&gt;,
-      # &lt;tt&gt;render_without_layout(&quot;controller/action&quot;, status = 200)&lt;/tt&gt;, and 
+      # &lt;tt&gt;render_without_layout(&quot;controller/action&quot;, status = 200)&lt;/tt&gt;, and
       # &lt;tt&gt;render_with_layout(&quot;controller/action&quot;, status = 200, layout)&lt;/tt&gt;.
       #
       # === Rendering partials
-      # 
-      # Partial rendering is most commonly used together with Ajax calls that only update one or a few elements on a page
+      #
+      # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
       # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
       # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
       # controller action responding to Ajax calls). By default, the current layout is not used.
       #
-      #   # Renders the partial located at app/views/controller/_win.r(html|xml)
-      #   render :partial =&gt; &quot;win&quot;
+      #   # Renders the same partial with a local variable.
+      #   render :partial =&gt; &quot;person&quot;, :locals =&gt; { :name =&gt; &quot;david&quot; }
       #
-      #   # Renders the partial with a status code of 500 (internal error)
-      #   render :partial =&gt; &quot;broken&quot;, :status =&gt; 500
+      #   # Renders a collection of the same partial by making each element
+      #   # of @winners available through the local variable &quot;person&quot; as it
+      #   # builds the complete response.
+      #   render :partial =&gt; &quot;person&quot;, :collection =&gt; @winners
       #
-      #   # Renders the same partial but also makes a local variable available to it
-      #   render :partial =&gt; &quot;win&quot;, :locals =&gt; { :name =&gt; &quot;david&quot; }
+      #   # Renders the same collection of partials, but also renders the
+      #   # person_divider partial between each person partial.
+      #   render :partial =&gt; &quot;person&quot;, :collection =&gt; @winners, :spacer_template =&gt; &quot;person_divider&quot;
       #
-      #   # Renders a collection of the same partial by making each element of @wins available through 
-      #   # the local variable &quot;win&quot; as it builds the complete response
-      #   render :partial =&gt; &quot;win&quot;, :collection =&gt; @wins
+      #   # Renders a collection of partials located in a view subfolder
+      #   # outside of our current controller.  In this example we will be
+      #   # rendering app/views/shared/_note.r(html|xml)  Inside the partial
+      #   # each element of @new_notes is available as the local var &quot;note&quot;.
+      #   render :partial =&gt; &quot;shared/note&quot;, :collection =&gt; @new_notes
+      #
+      #   # Renders the partial with a status code of 500 (internal error).
+      #   render :partial =&gt; &quot;broken&quot;, :status =&gt; 500
       #
-      #   # Renders the same collection of partials, but also renders the win_divider partial in between
-      #   # each win partial.
-      #   render :partial =&gt; &quot;win&quot;, :collection =&gt; @wins, :spacer_template =&gt; &quot;win_divider&quot;
+      # Note that the partial filename must also be a valid Ruby variable name,
+      # so e.g. 2005 and register-user are invalid.
       #
-      # _Deprecation_ _notice_: This used to have the signatures 
+      # _Deprecation_ _notice_: This used to have the signatures
       # &lt;tt&gt;render_partial(partial_path = default_template_name, object = nil, local_assigns = {})&lt;/tt&gt; and
       # &lt;tt&gt;render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})&lt;/tt&gt;.
       #
       # === Rendering a template
-      # 
-      # Template rendering works just like action rendering except that it takes a path relative to the template root. 
+      #
+      # Template rendering works just like action rendering except that it takes a path relative to the template root.
       # The current layout is automatically applied.
       #
       #   # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.rhtml)
       #   render :template =&gt; &quot;weblog/show&quot;
       #
       # === Rendering a file
-      # 
+      #
       # File rendering works just like action rendering except that it takes a filesystem path. By default, the path
       # is assumed to be absolute, and the current layout is not applied.
       #
@@ -581,7 +633,7 @@ module ActionController #:nodoc:
       # _Deprecation_ _notice_: This used to have the signature &lt;tt&gt;render_file(path, status = 200)&lt;/tt&gt;
       #
       # === Rendering text
-      # 
+      #
       # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
       # rendering is not done within the active layout.
       #
@@ -589,17 +641,38 @@ module ActionController #:nodoc:
       #   render :text =&gt; &quot;hello world!&quot;
       #
       #   # Renders the clear text &quot;Explosion!&quot;  with status code 500
-      #   render :text =&gt; &quot;Explosion!&quot;, :status =&gt; 500 
+      #   render :text =&gt; &quot;Explosion!&quot;, :status =&gt; 500
       #
       #   # Renders the clear text &quot;Hi there!&quot; within the current active layout (if one exists)
       #   render :text =&gt; &quot;Explosion!&quot;, :layout =&gt; true
       #
-      #   # Renders the clear text &quot;Hi there!&quot; within the layout 
+      #   # Renders the clear text &quot;Hi there!&quot; within the layout
       #   # placed in &quot;app/views/layouts/special.r(html|xml)&quot;
       #   render :text =&gt; &quot;Explosion!&quot;, :layout =&gt; &quot;special&quot;
       #
+      # The :text option can also accept a Proc object, which can be used to manually control the page generation. This should
+      # generally be avoided, as it violates the separation between code and content, and because almost everything that can be
+      # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates.
+      #
+      #   # Renders &quot;Hello from code!&quot;
+      #   render :text =&gt; proc { |response, output| output.write(&quot;Hello from code!&quot;) }
+      #
       # _Deprecation_ _notice_: This used to have the signature &lt;tt&gt;render_text(&quot;text&quot;, status = 200)&lt;/tt&gt;
       #
+      # === Rendering JSON
+      #
+      # Rendering JSON sets the content type to text/x-json and optionally wraps the JSON in a callback. It is expected
+      # that the response will be eval'd for use as a data structure.
+      #
+      #   # Renders '{name: &quot;David&quot;}'
+      #   render :json =&gt; {:name =&gt; &quot;David&quot;}.to_json
+      #
+      # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
+      # so the callback option is provided for these cases.
+      #
+      #   # Renders 'show({name: &quot;David&quot;})'
+      #   render :json =&gt; {:name =&gt; &quot;David&quot;}.to_json, :callback =&gt; 'show'
+      #
       # === Rendering an inline template
       #
       # Rendering of an inline template works as a cross between text and action rendering where the source for the template
@@ -607,7 +680,7 @@ module ActionController #:nodoc:
       # and the current layout is not used.
       #
       #   # Renders &quot;hello, hello, hello, again&quot;
-      #   render :inline =&gt; &quot;&lt;%= 'hello, ' * 3 + 'again' %&gt;&quot; 
+      #   render :inline =&gt; &quot;&lt;%= 'hello, ' * 3 + 'again' %&gt;&quot;
       #
       #   # Renders &quot;&lt;p&gt;Good seeing you!&lt;/p&gt;&quot; using Builder
       #   render :inline =&gt; &quot;xml.p { 'Good seeing you!' }&quot;, :type =&gt; :rxml
@@ -640,17 +713,27 @@ module ActionController #:nodoc:
       def render(options = nil, deprecated_status = nil, &amp;block) #:doc:
         raise DoubleRenderError, &quot;Can only render or redirect once per action&quot; if performed?
 
-        # Backwards compatibility
-        unless options.is_a?(Hash)
-          if options == :update
-            options = {:update =&gt; true}
-          else
-            return render_file(options || default_template_name, deprecated_status, true)
+        if options.nil?
+          return render_file(default_template_name, deprecated_status, true)
+        else
+          # Backwards compatibility
+          unless options.is_a?(Hash)
+            if options == :update
+              options = { :update =&gt; true }
+            else
+              ActiveSupport::Deprecation.warn(
+                &quot;You called render('#{options}'), which is a deprecated API call. Instead you use &quot; +
+                &quot;render :file =&gt; #{options}. Calling render with just a string will be removed from Rails 2.0.&quot;,
+                caller
+              )
+
+              return render_file(options, deprecated_status, true)
+            end
           end
         end
 
         if content_type = options[:content_type]
-          headers[&quot;Content-Type&quot;] = content_type
+          response.content_type = content_type.to_s
         end
 
         if text = options[:text]
@@ -662,16 +745,21 @@ module ActionController #:nodoc:
 
           elsif template = options[:template]
             render_file(template, options[:status], true)
-            
+
           elsif inline = options[:inline]
             render_template(inline, options[:status], options[:type], options[:locals] || {})
-            
+
           elsif action_name = options[:action]
-            render_action(action_name, options[:status], options[:layout]) 
+            ActiveSupport::Deprecation.silence do
+              render_action(action_name, options[:status], options[:layout])
+            end
 
           elsif xml = options[:xml]
             render_xml(xml, options[:status])
 
+          elsif json = options[:json]
+            render_json(json, options[:callback], options[:status])
+
           elsif partial = options[:partial]
             partial = default_template_name if partial == true
             if collection = options[:collection]
@@ -683,17 +771,17 @@ module ActionController #:nodoc:
           elsif options[:update]
             add_variables_to_assigns
             @template.send :evaluate_assigns
-            
+
             generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &amp;block)
             render_javascript(generator.to_s)
 
           elsif options[:nothing]
             # Safari doesn't pass the headers of the return if the response is zero length
             render_text(&quot; &quot;, options[:status])
-            
+
           else
             render_file(default_template_name, options[:status], true)
-            
+
           end
         end
       end
@@ -701,21 +789,19 @@ module ActionController #:nodoc:
       # Renders according to the same rules as &lt;tt&gt;render&lt;/tt&gt;, but returns the result in a string instead
       # of sending it as the response body to the browser.
       def render_to_string(options = nil, &amp;block) #:doc:
-        result = render(options, &amp;block)
-
+        ActiveSupport::Deprecation.silence { render(options, &amp;block) }
+      ensure
         erase_render_results
         forget_variables_added_to_assigns
         reset_variables_added_to_assigns
-
-        result
-      end    
+      end
 
       def render_action(action_name, status = nil, with_layout = true) #:nodoc:
         template = default_template_name(action_name.to_s)
-        if with_layout &amp;&amp; !template_exempt_from_layout?(template) 
-          render_with_layout(template, status)
+        if with_layout &amp;&amp; !template_exempt_from_layout?(template)
+          render_with_layout(:file =&gt; template, :status =&gt; status, :use_full_path =&gt; true, :layout =&gt; true)
         else
-          render_without_layout(template, status)
+          render_without_layout(:file =&gt; template, :status =&gt; status, :use_full_path =&gt; true)
         end
       end
 
@@ -731,22 +817,36 @@ module ActionController #:nodoc:
         render_text(@template.render_template(type, template, nil, local_assigns), status)
       end
 
-      def render_text(text = nil, status = nil) #:nodoc:
+      def render_text(text = nil, status = nil, append_response = false) #:nodoc:
         @performed_render = true
-        @response.headers['Status'] = (status || DEFAULT_RENDER_STATUS_CODE).to_s
-        @response.body = text
+
+        response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
+
+        if append_response
+          response.body ||= ''
+          response.body &lt;&lt; text
+        else
+          response.body = text
+        end
       end
 
-      def render_javascript(javascript, status = nil) #:nodoc:
-        @response.headers['Content-Type'] = 'text/javascript; charset=UTF-8'
-        render_text(javascript, status)
+      def render_javascript(javascript, status = nil, append_response = true) #:nodoc:
+        response.content_type = Mime::JS
+        render_text(javascript, status, append_response)
       end
 
       def render_xml(xml, status = nil) #:nodoc:
-        @response.headers['Content-Type'] = 'application/xml'
+        response.content_type = Mime::XML
         render_text(xml, status)
       end
 
+      def render_json(json, callback = nil, status = nil) #:nodoc:
+        json = &quot;#{callback}(#{json})&quot; unless callback.blank?
+
+        response.content_type = Mime::JSON
+        render_text(json, status)
+      end
+
       def render_nothing(status = nil) #:nodoc:
         render_text(' ', status)
       end
@@ -770,13 +870,52 @@ module ActionController #:nodoc:
       end
 
 
+      # Return a response that has no content (merely headers). The options
+      # argument is interpreted to be a hash of header names and values.
+      # This allows you to easily return a response that consists only of
+      # significant headers:
+      #
+      #   head :created, :location =&gt; person_path(@person)
+      #
+      # It can also be used to return exceptional conditions:
+      #
+      #   return head(:method_not_allowed) unless request.post?
+      #   return head(:bad_request) unless valid_request?
+      #   render
+      def head(*args)
+        if args.length &gt; 2
+          raise ArgumentError, &quot;too many arguments to head&quot;
+        elsif args.empty?
+          raise ArgumentError, &quot;too few arguments to head&quot;
+        elsif args.length == 2
+          status = args.shift
+          options = args.shift
+        elsif args.first.is_a?(Hash)
+          options = args.first
+        else
+          status = args.first
+          options = {}
+        end
+
+        raise ArgumentError, &quot;head requires an options hash&quot; if !options.is_a?(Hash)
+
+        status = interpret_status(status || options.delete(:status) || :ok)
+
+        options.each do |key, value|
+          headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join(&quot;-&quot;)] = value.to_s
+        end
+
+        render :nothing =&gt; true, :status =&gt; status
+      end
+
+
       # Clears the rendered results, allowing for another render to be performed.
       def erase_render_results #:nodoc:
-        @response.body = nil
+        response.body = nil
         @performed_render = false
       end
-      
-      # Clears the redirected results from the headers, resets the status to 200 and returns 
+
+      # Clears the redirected results from the headers, resets the status to 200 and returns
       # the URL that was used to redirect or nil if there was no redirected URL
       # Note that +redirect_to+ will change the body of the response to indicate a redirection.
       # The response body is not reset here, see +erase_render_results+
@@ -785,7 +924,7 @@ module ActionController #:nodoc:
         response.redirected_to = nil
         response.redirected_to_method_params = nil
         response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
-        response.headers.delete('location')
+        response.headers.delete('Location')
       end
 
       # Erase both render and redirect results
@@ -801,7 +940,7 @@ module ActionController #:nodoc:
           options
         end
       end
-      
+
       # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
       # the form of a hash, just like the one you would use for url_for directly. Example:
       #
@@ -814,7 +953,7 @@ module ActionController #:nodoc:
       # by this method.
       def default_url_options(options) #:doc:
       end
-      
+
       # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
       #
       # * &lt;tt&gt;Hash&lt;/tt&gt;: The URL will be generated by calling url_for with the +options+.
@@ -822,7 +961,7 @@ module ActionController #:nodoc:
       # * &lt;tt&gt;String not containing a protocol&lt;/tt&gt;: The current protocol and host is prepended to the string.
       # * &lt;tt&gt;:back&lt;/tt&gt;: Back to the page that issued the request. Useful for forms that are triggered from multiple places.
       #   Short-hand for redirect_to(request.env[&quot;HTTP_REFERER&quot;])
-      # 
+      #
       # Examples:
       #   redirect_to :action =&gt; &quot;show&quot;, :id =&gt; 5
       #   redirect_to &quot;http://www.rubyonrails.org&quot;
@@ -845,7 +984,7 @@ module ActionController #:nodoc:
 
           when String
             redirect_to(request.protocol + request.host_with_port + options)
-          
+
           when :back
             request.env[&quot;HTTP_REFERER&quot;] ? redirect_to(request.env[&quot;HTTP_REFERER&quot;]) : raise(RedirectBackError)
 
@@ -854,12 +993,13 @@ module ActionController #:nodoc:
               redirect_to(url_for(options))
               response.redirected_to = options
             else
+              # TOOD: Deprecate me!
               redirect_to(url_for(options, *parameters_for_method_reference))
               response.redirected_to, response.redirected_to_method_params = options, parameters_for_method_reference
             end
         end
       end
-      
+
       # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a &quot;private&quot; instruction, so that
       # intermediate caches shouldn't cache the response.
       #
@@ -867,29 +1007,29 @@ module ActionController #:nodoc:
       #   expires_in 20.minutes
       #   expires_in 3.hours, :private =&gt; false
       #   expires in 3.hours, 'max-stale' =&gt; 5.hours, :private =&gt; nil, :public =&gt; true
-      # 
+      #
       # This method will overwrite an existing Cache-Control header.
       # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
       def expires_in(seconds, options = {}) #:doc:
         cache_options = { 'max-age' =&gt; seconds, 'private' =&gt; true }.symbolize_keys.merge!(options.symbolize_keys)
         cache_options.delete_if { |k,v| v.nil? or v == false }
         cache_control = cache_options.map{ |k,v| v == true ? k.to_s : &quot;#{k.to_s}=#{v.to_s}&quot;}
-        @response.headers[&quot;Cache-Control&quot;] = cache_control.join(', ')
+        response.headers[&quot;Cache-Control&quot;] = cache_control.join(', ')
       end
-      
+
       # Sets a HTTP 1.1 Cache-Control header of &quot;no-cache&quot; so no caching should occur by the browser or
       # intermediate caches (like caching proxy servers).
       def expires_now #:doc:
-        @response.headers[&quot;Cache-Control&quot;] = &quot;no-cache&quot;
+        response.headers[&quot;Cache-Control&quot;] = &quot;no-cache&quot;
       end
 
       # Resets the session by clearing out all the objects stored within and initializing a new session object.
       def reset_session #:doc:
-        @request.reset_session
-        @session = @request.session
-        @response.session = @session
+        request.reset_session
+        @_session = request.session
+        response.session = @_session
       end
-    
+
     private
       def self.view_class
         @view_class ||=
@@ -905,48 +1045,71 @@ module ActionController #:nodoc:
 
       def initialize_template_class(response)
         raise &quot;You must assign a template class through ActionController.template_class= before processing a request&quot; unless @@template_class
-        
+
         response.template = self.class.view_class.new(self.class.view_root, {}, self)
         response.redirected_to = nil
         @performed_render = @performed_redirect = false
       end
-    
+
       def assign_shortcuts(request, response)
-        @request, @params, @cookies = request, request.parameters, request.cookies
+        @_request, @_params, @_cookies = request, request.parameters, request.cookies
+
+        @_response         = response
+        @_response.session = request.session
 
-        @response         = response
-        @response.session = request.session
+        @_session = @_response.session
+        @template = @_response.template
+        @assigns  = @_response.template.assigns
 
-        @session  = @response.session
-        @template = @response.template
-        @assigns  = @response.template.assigns
-  
-        @headers  = @response.headers
+        @_headers = @_response.headers
+
+        assign_deprecated_shortcuts(request, response)
+      end
+
+
+      # TODO: assigns cookies headers params request response template
+      DEPRECATED_INSTANCE_VARIABLES = %w(cookies flash headers params request response session)
+
+      # Gone after 1.2.
+      def assign_deprecated_shortcuts(request, response)
+        DEPRECATED_INSTANCE_VARIABLES.each do |method|
+          var = &quot;@#{method}&quot;
+          if instance_variables.include?(var)
+            value = instance_variable_get(var)
+            unless ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy === value
+              raise &quot;Deprecating #{var}, but it's already set to #{value.inspect}! Use the #{method}= writer method instead of setting #{var} directly.&quot;
+            end
+          end
+          instance_variable_set var, ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, method)
+        end
       end
-      
+
       def initialize_current_url
-        @url = UrlRewriter.new(@request, @params.clone())
+        @url = UrlRewriter.new(request, params.clone)
       end
 
       def log_processing
         if logger
           logger.info &quot;\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]&quot;
-          logger.info &quot;  Session ID: #{@session.session_id}&quot; if @session and @session.respond_to?(:session_id)
-          logger.info &quot;  Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(@params).inspect : @params.inspect}&quot;
+          logger.info &quot;  Session ID: #{@_session.session_id}&quot; if @_session and @_session.respond_to?(:session_id)
+          logger.info &quot;  Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}&quot;
         end
       end
-    
+
       def perform_action
-        if self.class.action_methods.include?(action_name) || self.class.action_methods.include?('method_missing')
+        if self.class.action_methods.include?(action_name)
           send(action_name)
           render unless performed?
+        elsif respond_to? :method_missing
+          send(:method_missing, action_name)
+          render unless performed?
         elsif template_exists? &amp;&amp; template_public?
           render
         else
           raise UnknownAction, &quot;No action responded to #{action_name}&quot;, caller
         end
       end
-      
+
       def performed?
         @performed_render || @performed_redirect
       end
@@ -954,7 +1117,16 @@ module ActionController #:nodoc:
       def assign_names
         @action_name = (params['action'] || 'index')
       end
-      
+
+      def assign_default_content_type_and_charset
+        response.content_type ||= Mime::HTML
+        response.charset      ||= self.class.default_charset unless sending_file?
+      end
+
+      def sending_file?
+        response.headers[&quot;Content-Transfer-Encoding&quot;] == &quot;binary&quot;
+      end
+
       def action_methods
         self.class.action_methods
       end
@@ -970,17 +1142,17 @@ module ActionController #:nodoc:
           @variables_added = true
         end
       end
-      
+
       def forget_variables_added_to_assigns
         @variables_added = nil
       end
-      
+
       def reset_variables_added_to_assigns
         @template.instance_variable_set(&quot;@assigns_added&quot;, nil)
       end
 
       def add_instance_variables_to_assigns
-        @@protected_variables_cache ||= protected_instance_variables.inject({}) { |h, k| h[k] = true; h }
+        @@protected_variables_cache ||= Set.new(protected_instance_variables)
         instance_variables.each do |var|
           next if @@protected_variables_cache.include?(var)
           @assigns[var[1..-1]] = instance_variable_get(var)
@@ -988,33 +1160,36 @@ module ActionController #:nodoc:
       end
 
       def add_class_variables_to_assigns
-        %w( template_root logger template_class ignore_missing_templates ).each do |cvar|
+        %w(template_root logger template_class ignore_missing_templates).each do |cvar|
           @assigns[cvar] = self.send(cvar)
         end
       end
 
       def protected_instance_variables
         if view_controller_internals
-          [ &quot;@assigns&quot;, &quot;@performed_redirect&quot;, &quot;@performed_render&quot; ]
+          %w(@assigns @performed_redirect @performed_render)
         else
-          [ &quot;@assigns&quot;, &quot;@performed_redirect&quot;, &quot;@performed_render&quot;, &quot;@request&quot;, &quot;@response&quot;, &quot;@session&quot;, &quot;@cookies&quot;, &quot;@template&quot;, &quot;@request_origin&quot;, &quot;@parent_controller&quot; ]
+          %w(@assigns @performed_redirect @performed_render
+             @_request @request @_response @response @_params @params
+             @_session @session @_cookies @cookies
+             @template @request_origin @parent_controller)
         end
       end
 
       def request_origin
         # this *needs* to be cached!
         # otherwise you'd get different results if calling it more than once
-        @request_origin ||= &quot;#{@request.remote_ip} at #{Time.now.to_s(:db)}&quot;
+        @request_origin ||= &quot;#{request.remote_ip} at #{Time.now.to_s(:db)}&quot;
       end
-      
+
       def complete_request_uri
-        &quot;#{@request.protocol}#{@request.host}#{@request.request_uri}&quot;
+        &quot;#{request.protocol}#{request.host}#{request.request_uri}&quot;
       end
 
       def close_session
-        @session.close unless @session.nil? || Hash === @session
+        @_session.close if @_session &amp;&amp; @_session.respond_to?(:close)
       end
-      
+
       def template_exists?(template_name = default_template_name)
         @template.file_exists?(template_name)
       end
@@ -1024,7 +1199,9 @@ module ActionController #:nodoc:
       end
 
       def template_exempt_from_layout?(template_name = default_template_name)
-        template_name =~ /\.rjs$/ || (@template.pick_template_extension(template_name) == :rjs rescue false)
+        extension = @template.pick_template_extension(template_name) rescue nil
+        name_with_extension = !template_name.include?('.') &amp;&amp; extension ? &quot;#{template_name}.#{extension}&quot; : template_name
+        extension == :rjs || @@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
       end
 
       def assert_existence_of_template_file(template_name)
@@ -1044,11 +1221,11 @@ module ActionController #:nodoc:
         end
         &quot;#{self.class.controller_path}/#{action_name}&quot;
       end
-      
+
       def strip_out_controller(path)
         path.split('/', 2).last
       end
-      
+
       def template_path_includes_controller?(path)
         self.class.controller_path.split('/')[-1] == path.split('/')[0]
       end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,11 +8,8 @@ module ActionController #:nodoc:
       base.extend(ClassMethods)
 
       base.class_eval do
-        alias_method :perform_action_without_benchmark, :perform_action
-        alias_method :perform_action, :perform_action_with_benchmark
-
-        alias_method :render_without_benchmark, :render
-        alias_method :render, :render_with_benchmark
+        alias_method_chain :perform_action, :benchmark
+        alias_method_chain :render, :benchmark
       end
     end
 
@@ -68,7 +65,7 @@ module ActionController #:nodoc:
       else
         runtime = [Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001].max
         log_message  = &quot;Completed in #{sprintf(&quot;%.5f&quot;, runtime)} (#{(1 / runtime).floor} reqs/sec)&quot;
-        log_message &lt;&lt; rendering_runtime(runtime) if @rendering_runtime
+        log_message &lt;&lt; rendering_runtime(runtime) if defined?(@rendering_runtime)
         log_message &lt;&lt; active_record_runtime(runtime) if Object.const_defined?(&quot;ActiveRecord&quot;) &amp;&amp; ActiveRecord::Base.connected?
         log_message &lt;&lt; &quot; | #{headers[&quot;Status&quot;]}&quot;
         log_message &lt;&lt; &quot; [#{complete_request_uri rescue &quot;unknown&quot;}]&quot;</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/benchmarking.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,6 @@
 require 'fileutils'
+require 'uri'
+require 'set'
 
 module ActionController #:nodoc:
   # Caching is a cheap way of speeding up slow applications by keeping the result of calculations, renderings, and database calls
@@ -117,24 +119,24 @@ module ActionController #:nodoc:
         return unless perform_caching
         if options[:action].is_a?(Array)
           options[:action].dup.each do |action|
-            self.class.expire_page(url_for(options.merge({ :only_path =&gt; true, :skip_relative_url_root =&gt; true, :action =&gt; action })))
+            self.class.expire_page(url_for(options.merge(:only_path =&gt; true, :skip_relative_url_root =&gt; true, :action =&gt; action)))
           end
         else
-          self.class.expire_page(url_for(options.merge({ :only_path =&gt; true, :skip_relative_url_root =&gt; true })))
+          self.class.expire_page(url_for(options.merge(:only_path =&gt; true, :skip_relative_url_root =&gt; true)))
         end
       end
 
-      # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of @response.body is used
+      # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
       # If no options are provided, the current +options+ for this action is used. Example:
       #   cache_page &quot;I'm the cached content&quot;, :controller =&gt; &quot;lists&quot;, :action =&gt; &quot;show&quot;
       def cache_page(content = nil, options = {})
         return unless perform_caching &amp;&amp; caching_allowed
-        self.class.cache_page(content || @response.body, url_for(options.merge({ :only_path =&gt; true, :skip_relative_url_root =&gt; true })))
+        self.class.cache_page(content || response.body, url_for(options.merge(:only_path =&gt; true, :skip_relative_url_root =&gt; true, :format =&gt; params[:format])))
       end
 
       private
         def caching_allowed
-          !@request.post? &amp;&amp; @response.headers['Status'] &amp;&amp; @response.headers['Status'].to_i &lt; 400
+          request.get? &amp;&amp; response.headers['Status'].to_i == 200
         end
     end
 
@@ -155,17 +157,31 @@ module ActionController #:nodoc:
     # the current host and the path. So a page that is accessed at http://david.somewhere.com/lists/show/1 will result in a fragment named
     # &quot;david.somewhere.com/lists/show/1&quot;. This allows the cacher to differentiate between &quot;david.somewhere.com/lists/&quot; and
     # &quot;jamis.somewhere.com/lists/&quot; -- which is a helpful way of assisting the subdomain-as-account-key pattern.
+    #
+    # Different representations of the same resource, e.g. &lt;tt&gt;http://david.somewhere.com/lists&lt;/tt&gt; and &lt;tt&gt;http://david.somewhere.com/lists.xml&lt;/tt&gt;
+    # are treated like separate requests and so are cached separately. Keep in mind when expiring an action cache that &lt;tt&gt;:action =&gt; 'lists'&lt;/tt&gt; is not the same
+    # as &lt;tt&gt;:action =&gt; 'list', :format =&gt; :xml&lt;/tt&gt;.
     module Actions
-      def self.append_features(base) #:nodoc:
-        super
+      def self.included(base) #:nodoc:
         base.extend(ClassMethods)
-        base.send(:attr_accessor, :rendered_action_cache)
+        base.class_eval do
+          attr_accessor :rendered_action_cache, :action_cache_path
+          alias_method_chain :protected_instance_variables, :action_caching
+        end
       end
 
-      module ClassMethods #:nodoc:
+      def protected_instance_variables_with_action_caching
+        protected_instance_variables_without_action_caching + %w(@action_cache_path)
+      end
+
+      module ClassMethods
+        # Declares that +actions+ should be cached.
+        # See ActionController::Caching::Actions for details.
         def caches_action(*actions)
           return unless perform_caching
-          around_filter(ActionCacheFilter.new(*actions))
+          action_cache_filter = ActionCacheFilter.new(*actions)
+          before_filter action_cache_filter
+          after_filter action_cache_filter
         end
       end
 
@@ -173,31 +189,74 @@ module ActionController #:nodoc:
         return unless perform_caching
         if options[:action].is_a?(Array)
           options[:action].dup.each do |action|
-            expire_fragment(url_for(options.merge({ :action =&gt; action })).split(&quot;://&quot;).last)
+            expire_fragment(ActionCachePath.path_for(self, options.merge({ :action =&gt; action })))
           end
         else
-          expire_fragment(url_for(options).split(&quot;://&quot;).last)
+          expire_fragment(ActionCachePath.path_for(self, options))
         end
       end
 
       class ActionCacheFilter #:nodoc:
         def initialize(*actions)
-          @actions = actions
+          @actions = Set.new actions
         end
 
         def before(controller)
-          return unless @actions.include?(controller.action_name.intern)
-          if cache = controller.read_fragment(controller.url_for.split(&quot;://&quot;).last)
+          return unless @actions.include?(controller.action_name.to_sym)
+          cache_path = ActionCachePath.new(controller, {})
+          if cache = controller.read_fragment(cache_path.path)
             controller.rendered_action_cache = true
+            set_content_type!(controller, cache_path.extension)
             controller.send(:render_text, cache)
             false
+          else
+            controller.action_cache_path = cache_path
           end
         end
 
         def after(controller)
-          return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache
-          controller.write_fragment(controller.url_for.split(&quot;://&quot;).last, controller.response.body)
+          return if !@actions.include?(controller.action_name.to_sym) || controller.rendered_action_cache
+          controller.write_fragment(controller.action_cache_path.path, controller.response.body)
+        end
+        
+        private
+          def set_content_type!(controller, extension)
+            controller.response.content_type = Mime::EXTENSION_LOOKUP[extension].to_s if extension
+          end
+          
+      end
+      
+      class ActionCachePath
+        attr_reader :path, :extension
+        
+        class &lt;&lt; self
+          def path_for(controller, options)
+            new(controller, options).path
+          end
+        end
+        
+        def initialize(controller, options = {})
+          @extension = extract_extension(controller.request.path)
+          path = controller.url_for(options).split('://').last
+          normalize!(path)
+          add_extension!(path, @extension)
+          @path = URI.unescape(path)
         end
+        
+        private
+          def normalize!(path)
+            path &lt;&lt; 'index' if path[-1] == ?/
+          end
+        
+          def add_extension!(path, extension)
+            path &lt;&lt; &quot;.#{extension}&quot; if extension
+          end
+          
+          def extract_extension(file_path)
+            # Don't want just what comes after the last '.' to accomodate multi part extensions
+            # such as tar.gz.
+            file_path[/^[^.]+\.(.+)$/, 1]
+          end
       end
     end
 
@@ -208,7 +267,7 @@ module ActionController #:nodoc:
     #   &lt;b&gt;Hello &lt;%= @name %&gt;&lt;/b&gt;
     #   &lt;% cache do %&gt;
     #     All the topics in the system:
-    #     &lt;%= render_collection_of_partials &quot;topic&quot;, Topic.find_all %&gt;
+    #     &lt;%= render :partial =&gt; &quot;topic&quot;, :collection =&gt; Topic.find(:all) %&gt;
     #   &lt;% end %&gt;
     #
     # This cache will bind to the name of action that called it. So you would be able to invalidate it using
@@ -246,8 +305,7 @@ module ActionController #:nodoc:
     #   ActionController::Base.fragment_cache_store = :mem_cache_store, &quot;localhost&quot;
     #   ActionController::Base.fragment_cache_store = MyOwnStore.new(&quot;parameter&quot;)
     module Fragments
-      def self.append_features(base) #:nodoc:
-        super
+      def self.included(base) #:nodoc:
         base.class_eval do
           @@fragment_cache_store = MemoryStore.new
           cattr_reader :fragment_cache_store
@@ -306,7 +364,12 @@ module ActionController #:nodoc:
       # Name can take one of three forms:
       # * String: This would normally take the form of a path like &quot;pages/45/notes&quot;
       # * Hash: Is treated as an implicit call to url_for, like { :controller =&gt; &quot;pages&quot;, :action =&gt; &quot;notes&quot;, :id =&gt; 45 }
-      # * Regexp: Will destroy all the matched fragments, example: %r{pages/\d*/notes} Ensure you do not specify start and finish in the regex (^$) because the actual filename matched looks like ./cache/filename/path.cache
+      # * Regexp: Will destroy all the matched fragments, example:
+      #     %r{pages/\d*/notes}
+      #   Ensure you do not specify start and finish in the regex (^$) because
+      #   the actual filename matched looks like ./cache/filename/path.cache
+      #   Regexp expiration is not supported on caches which can't iterate over
+      #   all keys, such as memcached.
       def expire_fragment(name, options = nil)
         return unless perform_caching
 
@@ -327,6 +390,7 @@ module ActionController #:nodoc:
       def expire_matched_fragments(matcher = /.*/, options = nil) #:nodoc:
         expire_fragment(matcher, options)
       end
+      deprecate :expire_matched_fragments =&gt; :expire_fragment
 
 
       class UnthreadedMemoryStore #:nodoc:
@@ -430,7 +494,7 @@ module ActionController #:nodoc:
             if f =~ matcher
               begin
                 File.delete(f)
-              rescue Object =&gt; e
+              rescue SystemCallError =&gt; e
                 # If there's no cache, then there's nothing to complain about
               end
             end
@@ -493,8 +557,7 @@ module ActionController #:nodoc:
     #
     # In the example above, four actions are cached and three actions are responsible for expiring those caches.
     module Sweeping
-      def self.append_features(base) #:nodoc:
-        super
+      def self.included(base) #:nodoc:
         base.extend(ClassMethods)
       end
 
@@ -503,8 +566,7 @@ module ActionController #:nodoc:
           return unless perform_caching
           configuration = sweepers.last.is_a?(Hash) ? sweepers.pop : {}
           sweepers.each do |sweeper|
-            observer(sweeper)
-
+            ActiveRecord::Base.observers &lt;&lt; sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
             sweeper_instance = Object.const_get(Inflector.classify(sweeper)).instance
 
             if sweeper_instance.is_a?(Sweeper)
@@ -523,7 +585,7 @@ module ActionController #:nodoc:
 
         # ActiveRecord::Observer will mark this class as reloadable even though it should not be.
         # However, subclasses of ActionController::Caching::Sweeper should be Reloadable
-        include Reloadable::Subclasses
+        include Reloadable::Deprecated
         
         def before(controller)
           self.controller = controller</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/caching.rb</filename>
    </modified>
    <modified>
      <diff>@@ -27,13 +27,6 @@ class CGI #:nodoc:
   def request_parameters
     CGIMethods.parse_request_parameters(params, env_table)
   end
-
-  def redirect(where)
-     header({ 
-       &quot;Status&quot; =&gt; &quot;302 Moved&quot;, 
-       &quot;location&quot; =&gt; &quot;#{where}&quot;
-    })
-  end
   
   def session(parameters = nil)
     parameters = {} if parameters.nil?</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/cgi_ext/cgi_ext.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,217 +1,211 @@
 require 'cgi'
-require 'action_controller/vendor/xml_simple'
 require 'action_controller/vendor/xml_node'
+require 'strscan'
 
 # Static methods for parsing the query and request parameters that can be used in
 # a CGI extension class or testing in isolation.
 class CGIMethods #:nodoc:
-  public
-    # Returns a hash with the pairs from the query string. The implicit hash construction that is done in
-    # parse_request_params is not done here.
-    def CGIMethods.parse_query_parameters(query_string)
-      parsed_params = {}
-  
-      query_string.split(/[&amp;;]/).each { |p| 
-        # Ignore repeated delimiters.
-        next if p.empty?
-
-        k, v = p.split('=',2)
-        v = nil if (v &amp;&amp; v.empty?)
-
-        k = CGI.unescape(k) if k
-        v = CGI.unescape(v) if v
-
-        unless k.include?(?[)
-          parsed_params[k] = v
-        else
-          keys = split_key(k)
-          last_key = keys.pop
-          last_key = keys.pop if (use_array = last_key.empty?)
-          parent = keys.inject(parsed_params) {|h, k| h[k] ||= {}}
-          
-          if use_array then (parent[last_key] ||= []) &lt;&lt; v
-          else parent[last_key] = v
-          end
-        end
-      }
-  
-      parsed_params
+  class &lt;&lt; self
+    # DEPRECATED: Use parse_form_encoded_parameters
+    def parse_query_parameters(query_string)
+      pairs = query_string.split('&amp;').collect do |chunk|
+        next if chunk.empty?
+        key, value = chunk.split('=', 2)
+        next if key.empty?
+        value = (value.nil? || value.empty?) ? nil : CGI.unescape(value)
+        [ CGI.unescape(key), value ]
+      end.compact
+
+      FormEncodedPairParser.new(pairs).result
     end
 
-    # Returns the request (POST/GET) parameters in a parsed form where pairs such as &quot;customer[address][street]&quot; / 
-    # &quot;Somewhere cool!&quot; are translated into a full hash hierarchy, like
-    # { &quot;customer&quot; =&gt; { &quot;address&quot; =&gt; { &quot;street&quot; =&gt; &quot;Somewhere cool!&quot; } } }
-    def CGIMethods.parse_request_parameters(params)
-      parsed_params = {}
-
-      for key, value in params
-        value = [value] if key =~ /.*\[\]$/
-        unless key.include?('[')
-          # much faster to test for the most common case first (GET)
-          # and avoid the call to build_deep_hash
-          parsed_params[key] = get_typed_value(value[0])
-        else
-          build_deep_hash(get_typed_value(value[0]), parsed_params, get_levels(key))
+    # DEPRECATED: Use parse_form_encoded_parameters
+    def parse_request_parameters(params)
+      parser = FormEncodedPairParser.new
+
+      params = params.dup
+      until params.empty?
+        for key, value in params
+          if key.blank?
+            params.delete key
+          elsif !key.include?('[')
+            # much faster to test for the most common case first (GET)
+            # and avoid the call to build_deep_hash
+            parser.result[key] = get_typed_value(value[0])
+            params.delete key
+          elsif value.is_a?(Array)
+            parser.parse(key, get_typed_value(value.shift))
+            params.delete key if value.empty?
+          else
+            raise TypeError, &quot;Expected array, found #{value.inspect}&quot;
+          end
         end
       end
     
-      parsed_params
+      parser.result
     end
 
-    def self.parse_formatted_request_parameters(mime_type, raw_post_data)
-      params = case strategy = ActionController::Base.param_parsers[mime_type]
+    def parse_formatted_request_parameters(mime_type, raw_post_data)
+      case strategy = ActionController::Base.param_parsers[mime_type]
         when Proc
           strategy.call(raw_post_data)
         when :xml_simple
-          raw_post_data.blank? ? nil :
-            typecast_xml_value(XmlSimple.xml_in(raw_post_data,
-              'forcearray'   =&gt; false,
-              'forcecontent' =&gt; true,
-              'keeproot'     =&gt; true,
-              'contentkey'   =&gt; '__content__'))
+          raw_post_data.blank? ? {} : Hash.from_xml(raw_post_data)
         when :yaml
           YAML.load(raw_post_data)
         when :xml_node
           node = XmlNode.from_xml(raw_post_data)
           { node.node_name =&gt; node }
       end
-      
-      dasherize_keys(params || {})
-    rescue Object =&gt; e
+    rescue Exception =&gt; e # YAML, XML or Ruby code block errors
       { &quot;exception&quot; =&gt; &quot;#{e.message} (#{e.class})&quot;, &quot;backtrace&quot; =&gt; e.backtrace, 
         &quot;raw_post_data&quot; =&gt; raw_post_data, &quot;format&quot; =&gt; mime_type }
     end
 
-    def self.typecast_xml_value(value)
-      case value
-      when Hash
-        if value.has_key?(&quot;__content__&quot;)
-          content = translate_xml_entities(value[&quot;__content__&quot;])
-          case value[&quot;type&quot;]
-          when &quot;integer&quot;  then content.to_i
-          when &quot;boolean&quot;  then content == &quot;true&quot;
-          when &quot;datetime&quot; then Time.parse(content)
-          when &quot;date&quot;     then Date.parse(content)
-          else                 content
-          end
-        else
-          value.empty? ? nil : value.inject({}) do |h,(k,v)|
-            h[k] = typecast_xml_value(v)
-            h
-          end
-        end
-      when Array
-        value.map! { |i| typecast_xml_value(i) }
-        case value.length
-        when 0 then nil
-        when 1 then value.first
-        else value
+    private
+      def get_typed_value(value)
+        case value
+          when String
+            value
+          when NilClass
+            ''
+          when Array
+            value.map { |v| get_typed_value(v) }
+          else
+            # Uploaded file provides content type and filename.
+            if value.respond_to?(:content_type) &amp;&amp;
+                  !value.content_type.blank? &amp;&amp;
+                  !value.original_filename.blank?
+              unless value.respond_to?(:full_original_filename)
+                class &lt;&lt; value
+                  alias_method :full_original_filename, :original_filename
+
+                  # Take the basename of the upload's original filename.
+                  # This handles the full Windows paths given by Internet Explorer
+                  # (and perhaps other broken user agents) without affecting
+                  # those which give the lone filename.
+                  # The Windows regexp is adapted from Perl's File::Basename.
+                  def original_filename
+                    if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
+                      md.captures.first
+                    else
+                      File.basename full_original_filename
+                    end
+                  end
+                end
+              end
+
+              # Return the same value after overriding original_filename.
+              value
+
+            # Multipart values may have content type, but no filename.
+            elsif value.respond_to?(:read)
+              result = value.read
+              value.rewind
+              result
+
+            # Unknown value, neither string nor multipart.
+            else
+              raise &quot;Unknown form value: #{value.inspect}&quot;
+            end
         end
-      else
-        raise &quot;can't typecast #{value.inspect}&quot;
       end
-    end
+  end
 
-  private
+  class FormEncodedPairParser &lt; StringScanner #:nodoc:
+    attr_reader :top, :parent, :result
 
-    def self.translate_xml_entities(value)
-      value.gsub(/&amp;lt;/,   &quot;&lt;&quot;).
-            gsub(/&amp;gt;/,   &quot;&gt;&quot;).
-            gsub(/&amp;quot;/, '&quot;').
-            gsub(/&amp;apos;/, &quot;'&quot;).
-            gsub(/&amp;amp;/,  &quot;&amp;&quot;)
+    def initialize(pairs = [])
+      super('')
+      @result = {}
+      pairs.each { |key, value| parse(key, value) }
     end
-
-    def self.dasherize_keys(params)
-      case params.class.to_s
-      when &quot;Hash&quot;
-        params.inject({}) do |h,(k,v)|
-          h[k.to_s.tr(&quot;-&quot;, &quot;_&quot;)] = dasherize_keys(v)
-          h
-        end
-      when &quot;Array&quot;
-        params.map { |v| dasherize_keys(v) }
-      else
-        params
+     
+    KEY_REGEXP = %r{([^\[\]=&amp;]+)}
+    BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&amp;]+)\]}
+    
+    # Parse the query string
+    def parse(key, value)
+      self.string = key
+      @top, @parent = result, nil
+      
+      # First scan the bare key
+      key = scan(KEY_REGEXP) or return
+      key = post_key_check(key)
+            
+      # Then scan as many nestings as present
+      until eos? 
+        r = scan(BRACKETED_KEY_REGEXP) or return
+        key = self[1]
+        key = post_key_check(key)
       end
+ 
+      bind(key, value)
     end
 
-    # Splits the given key into several pieces. Example keys are 'name', 'person[name]',
-    # 'person[name][first]', and 'people[]'. In each instance, an Array instance is returned.
-    # 'person[name][first]' produces ['person', 'name', 'first']; 'people[]' produces ['people', '']
-    def CGIMethods.split_key(key)
-      if /^([^\[]+)((?:\[[^\]]*\])+)$/ =~ key
-        keys = [$1]
-        
-        keys.concat($2[1..-2].split(']['))
-        keys &lt;&lt; '' if key[-2..-1] == '[]' # Have to add it since split will drop empty strings
-        
-        keys
-      else
-        [key]
+    private
+      # After we see a key, we must look ahead to determine our next action. Cases:
+      # 
+      #   [] follows the key. Then the value must be an array.
+      #   = follows the key. (A value comes next)
+      #   &amp; or the end of string follows the key. Then the key is a flag.
+      #   otherwise, a hash follows the key. 
+      def post_key_check(key)
+        if scan(/\[\]/) # a[b][] indicates that b is an array
+          container(key, Array)
+          nil
+        elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
+          container(key, Hash)
+          nil
+        else # End of key? We do nothing.
+          key
+        end
       end
-    end
     
-    def CGIMethods.get_typed_value(value)
-      # test most frequent case first
-      if value.is_a?(String)
-        value
-      elsif value.respond_to?(:content_type) &amp;&amp; ! value.content_type.blank?
-        # Uploaded file
-        unless value.respond_to?(:full_original_filename)
-          class &lt;&lt; value
-            alias_method :full_original_filename, :original_filename
-
-            # Take the basename of the upload's original filename.
-            # This handles the full Windows paths given by Internet Explorer
-            # (and perhaps other broken user agents) without affecting
-            # those which give the lone filename.
-            # The Windows regexp is adapted from Perl's File::Basename.
-            def original_filename
-              if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
-                md.captures.first
-              else
-                File.basename full_original_filename
-              end
+      # Add a container to the stack.
+      # 
+      def container(key, klass)
+        type_conflict! klass, top[key] if top.is_a?(Hash) &amp;&amp; top.key?(key) &amp;&amp; ! top[key].is_a?(klass)
+        value = bind(key, klass.new)
+        type_conflict! klass, value unless value.is_a?(klass)
+        push(value)
+      end
+    
+      # Push a value onto the 'stack', which is actually only the top 2 items.
+      def push(value)
+        @parent, @top = @top, value
+      end
+    
+      # Bind a key (which may be nil for items in an array) to the provided value.
+      def bind(key, value)
+        if top.is_a? Array
+          if key
+            if top[-1].is_a?(Hash) &amp;&amp; ! top[-1].key?(key)
+              top[-1][key] = value
+            else
+              top &lt;&lt; {key =&gt; value}.with_indifferent_access
+              push top.last
             end
+          else
+            top &lt;&lt; value
           end
+        elsif top.is_a? Hash
+          key = CGI.unescape(key)
+          parent &lt;&lt; (@top = {}) if top.key?(key) &amp;&amp; parent.is_a?(Array)
+          return top[key] ||= value
+        else
+          raise ArgumentError, &quot;Don't know what to do: top is #{top.inspect}&quot;
         end
 
-        # Return the same value after overriding original_filename.
-        value
-
-      elsif value.respond_to?(:read)
-        # Value as part of a multipart request
-        value.read
-      elsif value.class == Array
-        value.collect { |v| CGIMethods.get_typed_value(v) }
-      else
-        # other value (neither string nor a multipart request)
-        value.to_s
-      end
-    end
-  
-    PARAMS_HASH_RE = /^([^\[]+)(\[.*\])?(.)?.*$/
-    def CGIMethods.get_levels(key)
-      all, main, bracketed, trailing = PARAMS_HASH_RE.match(key).to_a
-      if main.nil?
-        []
-      elsif trailing
-        [key]
-      elsif bracketed
-        [main] + bracketed.slice(1...-1).split('][')
-      else
-        [main]
+        return value
       end
-    end
-
-    def CGIMethods.build_deep_hash(value, hash, levels)
-      if levels.length == 0
-        value
-      elsif hash.nil?
-        { levels.first =&gt; CGIMethods.build_deep_hash(value, nil, levels[1..-1]) }
-      else
-        hash.update({ levels.first =&gt; CGIMethods.build_deep_hash(value, hash[levels.first], levels[1..-1]) })
+      
+      def type_conflict!(klass, value)
+        raise TypeError, 
+          &quot;Conflicting types for parameter containers. &quot; +
+          &quot;Expected an instance of #{klass}, but found an instance of #{value.class}. &quot; +
+          &quot;This can be caused by passing Array and Hash based paramters qs[]=value&amp;qs[key]=value. &quot;
       end
+      
     end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,27 +1,48 @@
 class CGI #:nodoc:
-  # Add @request.env['RAW_POST_DATA'] for the vegans.
   module QueryExtension
     # Initialize the data from the query.
     #
     # Handles multipart forms (in particular, forms that involve file uploads).
     # Reads query parameters in the @params field, and cookies into @cookies.
-    def initialize_query()
+    def initialize_query
       @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
 
-      #fix some strange request environments
+      # Fix some strange request environments.
       if method = env_table['REQUEST_METHOD']
         method = method.to_s.downcase.intern
       else
         method = :get
       end
 
-      if method == :post &amp;&amp; (boundary = multipart_form_boundary)
-        @multipart = true
-        @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
-      else
-        @multipart = false
-        @params = CGI::parse(read_query_params(method) || &quot;&quot;)
+      # POST assumes missing Content-Type is application/x-www-form-urlencoded.
+      content_type = env_table['CONTENT_TYPE']
+      if content_type.blank? &amp;&amp; method == :post
+        content_type = 'application/x-www-form-urlencoded'
       end
+
+      # Force content length to zero if missing.
+      content_length = env_table['CONTENT_LENGTH'].to_i
+
+      # Set multipart to false by default.
+      @multipart = false
+
+      # POST and PUT may have params in entity body. If content type is
+      # missing for POST, assume urlencoded. If content type is missing
+      # for PUT, don't assume anything and don't parse the parameters:
+      # it's likely binary data.
+      #
+      # The other HTTP methods have their params in the query string.
+      if method == :post || method == :put
+        if boundary = extract_multipart_form_boundary(content_type)
+          @multipart = true
+          @params = read_multipart(boundary, content_length)
+        elsif content_type.blank? || content_type !~ %r{application/x-www-form-urlencoded}i
+          read_params(method, content_length)
+          @params = {}
+        end
+      end
+
+      @params ||= CGI.parse(read_params(method, content_length))
     end
 
     private
@@ -29,45 +50,45 @@ class CGI #:nodoc:
         MULTIPART_FORM_BOUNDARY_RE = %r|\Amultipart/form-data.*boundary=\&quot;?([^\&quot;;,]+)\&quot;?|n #&quot;
       end
 
-      def multipart_form_boundary
-        MULTIPART_FORM_BOUNDARY_RE.match(env_table['CONTENT_TYPE']).to_a.pop
+      def extract_multipart_form_boundary(content_type)
+        MULTIPART_FORM_BOUNDARY_RE.match(content_type).to_a.pop
       end
 
       if defined? MOD_RUBY
-        def read_params_from_query
+        def read_query
           Apache::request.args || ''
         end
       else
-        def read_params_from_query
+        def read_query
           # fixes CGI querystring parsing for lighttpd
           env_qs = env_table['QUERY_STRING']
           if env_qs.blank? &amp;&amp; !(uri = env_table['REQUEST_URI']).blank?
             uri.split('?', 2)[1] || ''
           else
-            env_qs
+            env_qs || ''
           end
         end
       end
 
-      def read_params_from_post
+      def read_body(content_length)
         stdinput.binmode if stdinput.respond_to?(:binmode)
-        content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
-        # fix for Safari Ajax postings that always append \000
+        content = stdinput.read(content_length) || ''
+        # Fix for Safari Ajax postings that always append \000
         content.chop! if content[-1] == 0
-        content.gsub! /&amp;_=$/, ''
+        content.gsub!(/&amp;_=$/, '')
         env_table['RAW_POST_DATA'] = content.freeze
       end
 
-      def read_query_params(method)
+      def read_params(method, content_length)
         case method
           when :get
-            read_params_from_query
+            read_query
           when :post, :put
-            read_params_from_post
+            read_body(content_length)
           when :cmd
             read_from_cmdline
-          else # when :head, :delete, :options
-            read_params_from_query
+          else # :head, :delete, :options, :trace, :connect
+            read_query
         end
       end
   end # module QueryExtension</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,8 @@
 require 'action_controller/cgi_ext/cgi_ext'
 require 'action_controller/cgi_ext/cookie_performance_fix'
 require 'action_controller/cgi_ext/raw_post_data_fix'
+require 'action_controller/cgi_ext/session_performance_fix'
+require 'action_controller/cgi_ext/pstore_performance_fix'
 
 module ActionController #:nodoc:
   class Base
@@ -8,13 +10,13 @@ module ActionController #:nodoc:
     # sessions (large performance increase if sessions are not needed). The &lt;tt&gt;session_options&lt;/tt&gt; are the same as for CGI::Session:
     #
     # * &lt;tt&gt;:database_manager&lt;/tt&gt; - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore
-    #   (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in 
+    #   (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in
     #   lib/action_controller/session.
     # * &lt;tt&gt;:session_key&lt;/tt&gt; - the parameter name used for the session id. Defaults to '_session_id'.
-    # * &lt;tt&gt;:session_id&lt;/tt&gt; - the session id to use.  If not provided, then it is retrieved from the +session_key+ parameter
-    #   of the request, or automatically generated for a new session.
+    # * &lt;tt&gt;:session_id&lt;/tt&gt; - the session id to use.  If not provided, then it is retrieved from the +session_key+ cookie, or 
+    #   automatically generated for a new session.
     # * &lt;tt&gt;:new_session&lt;/tt&gt; - if true, force creation of a new session.  If not set, a new session is only created if none currently
-    #   exists.  If false, a new session is never created, and if none currently exists and the +session_id+ option is not set, 
+    #   exists.  If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
     #   an ArgumentError is raised.
     # * &lt;tt&gt;:session_expires&lt;/tt&gt; - the time the current session expires, as a +Time+ object.  If not set, the session will continue
     #   indefinitely.
@@ -22,10 +24,12 @@ module ActionController #:nodoc:
     #   server.
     # * &lt;tt&gt;:session_secure&lt;/tt&gt; - if +true+, this session will only work over HTTPS.
     # * &lt;tt&gt;:session_path&lt;/tt&gt; - the path for which this session applies.  Defaults to the directory of the CGI script.
-    def self.process_cgi(cgi = CGI.new, session_options = {}) 
+    # * &lt;tt&gt;:cookie_only&lt;/tt&gt; - if +true+ (the default), session IDs will only be accepted from cookies and not from
+    #   the query string or POST parameters. This protects against session fixation attacks.
+    def self.process_cgi(cgi = CGI.new, session_options = {})
       new.process_cgi(cgi, session_options)
     end
-  
+
     def process_cgi(cgi, session_options = {}) #:nodoc:
       process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
     end
@@ -33,11 +37,14 @@ module ActionController #:nodoc:
 
   class CgiRequest &lt; AbstractRequest #:nodoc:
     attr_accessor :cgi, :session_options
+    class SessionFixationAttempt &lt; StandardError; end #:nodoc:
 
     DEFAULT_SESSION_OPTIONS = {
       :database_manager =&gt; CGI::Session::PStore,
       :prefix           =&gt; &quot;ruby_sess.&quot;,
-      :session_path     =&gt; &quot;/&quot;
+      :session_path     =&gt; &quot;/&quot;,
+      :session_key      =&gt; &quot;_session_id&quot;,
+      :cookie_only      =&gt; true
     } unless const_defined?(:DEFAULT_SESSION_OPTIONS)
 
     def initialize(cgi, session_options = {})
@@ -47,11 +54,15 @@ module ActionController #:nodoc:
       super()
     end
 
+    def cookie_only?
+      session_options_with_string_keys['cookie_only']
+    end
+
     def query_string
       if (qs = @cgi.query_string) &amp;&amp; !qs.empty?
         qs
       elsif uri = @env['REQUEST_URI']
-        parts = uri.split('?')  
+        parts = uri.split('?')
         parts.shift
         parts.join('?')
       else
@@ -60,7 +71,8 @@ module ActionController #:nodoc:
     end
 
     def query_parameters
-      (qs = self.query_string).empty? ? {} : CGIMethods.parse_query_parameters(qs)
+      @query_parameters ||=
+        (qs = self.query_string).empty? ? {} : CGIMethods.parse_query_parameters(qs)
     end
 
     def request_parameters
@@ -71,7 +83,7 @@ module ActionController #:nodoc:
           CGIMethods.parse_request_parameters(@cgi.params)
         end
     end
-   
+
     def cookies
       @cgi.cookies.freeze
     end
@@ -101,15 +113,29 @@ module ActionController #:nodoc:
     end
 
     def session
-      unless @session
+      unless defined?(@session)
         if @session_options == false
           @session = Hash.new
         else
           stale_session_check! do
-            if session_options_with_string_keys['new_session'] == true
-              @session = new_session
-            else
-              @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+            if cookie_only? &amp;&amp; request_parameters[session_options_with_string_keys['session_key']]
+              raise SessionFixationAttempt
+            end
+            case value = session_options_with_string_keys['new_session']
+              when true
+                @session = new_session
+              when false
+                begin
+                  @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+                # CGI::Session raises ArgumentError if 'new_session' == false
+                # and no session cookie or query param is present.
+                rescue ArgumentError
+                  @session = Hash.new
+                end
+              when nil
+                @session = CGI::Session.new(@cgi, session_options_with_string_keys)
+              else
+                raise ArgumentError, &quot;Invalid new_session option: #{value}&quot;
             end
             @session['__valid_session']
           end
@@ -119,7 +145,7 @@ module ActionController #:nodoc:
     end
 
     def reset_session
-      @session.delete if CGI::Session === @session
+      @session.delete if defined?(@session) &amp;&amp; @session.is_a?(CGI::Session)
       @session = new_session
     end
 
@@ -141,11 +167,11 @@ module ActionController #:nodoc:
       def stale_session_check!
         yield
       rescue ArgumentError =&gt; argument_error
-        if argument_error.message =~ %r{undefined class/module (\w+)}
+        if argument_error.message =~ %r{undefined class/module ([\w:]+)}
           begin
             Module.const_missing($1)
           rescue LoadError, NameError =&gt; const_error
-            raise ActionController::SessionRestoreError, &lt;&lt;end_msg
+            raise ActionController::SessionRestoreError, &lt;&lt;-end_msg
 Session contains objects whose class definition isn\'t available.
 Remember to require the classes for all objects kept in the session.
 (Original exception: #{const_error.message} [#{const_error.class}])
@@ -159,7 +185,7 @@ end_msg
       end
 
       def session_options_with_string_keys
-        @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).inject({}) { |options, (k,v)| options[k.to_s] = v; options }
+        @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
       end
   end
 
@@ -170,38 +196,49 @@ end_msg
     end
 
     def out(output = $stdout)
-      convert_content_type!(@headers)
+      convert_content_type!
+      set_content_length!
+
       output.binmode      if output.respond_to?(:binmode)
       output.sync = false if output.respond_to?(:sync=)
-      
+
       begin
         output.write(@cgi.header(@headers))
 
         if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
           return
         elsif @body.respond_to?(:call)
+          # Flush the output now in case the @body Proc uses
+          # #syswrite.
+          output.flush if output.respond_to?(:flush)
           @body.call(self, output)
         else
           output.write(@body)
         end
 
         output.flush if output.respond_to?(:flush)
-      rescue Errno::EPIPE =&gt; e
-        # lost connection to the FCGI process -- ignore the output, then
+      rescue Errno::EPIPE, Errno::ECONNRESET
+        # lost connection to parent process, ignore output
       end
     end
 
     private
-      def convert_content_type!(headers)
-        if header = headers.delete(&quot;Content-Type&quot;)
-          headers[&quot;type&quot;] = header
+      def convert_content_type!
+        if content_type = @headers.delete(&quot;Content-Type&quot;)
+          @headers[&quot;type&quot;] = content_type
         end
-        if header = headers.delete(&quot;Content-type&quot;)
-          headers[&quot;type&quot;] = header
+        if content_type = @headers.delete(&quot;Content-type&quot;)
+          @headers[&quot;type&quot;] = content_type
         end
-        if header = headers.delete(&quot;content-type&quot;)
-          headers[&quot;type&quot;] = header
+        if content_type = @headers.delete(&quot;content-type&quot;)
+          @headers[&quot;type&quot;] = content_type
         end
       end
+      
+      # Don't set the Content-Length for block-based bodies as that would mean reading it all into memory. Not nice
+      # for, say, a 2GB streaming file.
+      def set_content_length!
+        @headers[&quot;Content-Length&quot;] = @body.size unless @body.respond_to?(:call)
+      end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/cgi_process.rb</filename>
    </modified>
    <modified>
      <diff>@@ -50,14 +50,9 @@ module ActionController #:nodoc:
       base.send :attr_accessor, :parent_controller
       
       base.class_eval do
-        alias_method :process_cleanup_without_components, :process_cleanup
-        alias_method :process_cleanup, :process_cleanup_with_components
-        
-        alias_method :set_session_options_without_components, :set_session_options
-        alias_method :set_session_options, :set_session_options_with_components
-        
-        alias_method :flash_without_components, :flash
-        alias_method :flash, :flash_with_components
+        alias_method_chain :process_cleanup, :components
+        alias_method_chain :set_session_options, :components
+        alias_method_chain :flash, :components
 
         alias_method :component_request?, :parent_controller       
       end
@@ -80,11 +75,13 @@ module ActionController #:nodoc:
       #     will also use /code/weblog/components as template root 
       #     and find templates in /code/weblog/components/admin/parties/users/
       def uses_component_template_root
-        path_of_calling_controller = File.dirname(caller[0].split(/:\d+:/).first)
-        path_of_controller_root    = path_of_calling_controller.sub(/#{controller_path.split(&quot;/&quot;)[0..-2]}$/, &quot;&quot;) # &quot; (for ruby-mode)
+        path_of_calling_controller = File.dirname(caller[1].split(/:\d+:/, 2).first)
+        path_of_controller_root    = path_of_calling_controller.sub(/#{Regexp.escape(File.dirname(controller_path))}$/, &quot;&quot;)
 
         self.template_root = path_of_controller_root
       end
+
+      deprecate :uses_component_template_root =&gt; 'Components are deprecated and will be removed in Rails 2.0.'
     end
 
     module InstanceMethods
@@ -116,27 +113,26 @@ module ActionController #:nodoc:
         end
 
         def flash_with_components(refresh = false) #:nodoc:
-          if @flash.nil? || refresh
-            @flash = 
-              if @parent_controller
+          if !defined?(@_flash) || refresh
+            @_flash =
+              if defined?(@parent_controller)
                 @parent_controller.flash
               else
                 flash_without_components
               end
           end
-          
-          @flash
+          @_flash
         end
 
       private
         def component_response(options, reuse_response)
           klass    = component_class(options)
           request  = request_for_component(klass.controller_name, options)
-          response = reuse_response ? @response : @response.dup
+          new_response = reuse_response ? response : response.dup
 
-          klass.process_with_components(request, response, self)
+          klass.process_with_components(request, new_response, self)
         end
-        
+
         # determine the controller class for the component request
         def component_class(options)
           if controller = options[:controller]
@@ -145,22 +141,22 @@ module ActionController #:nodoc:
             self.class
           end
         end
-        
+
         # Create a new request object based on the current request.
         # The new request inherits the session from the current request,
         # bypassing any session options set for the component controller's class
         def request_for_component(controller_name, options)
-          request         = @request.dup
-          request.session = @request.session
-        
-          request.instance_variable_set(
+          new_request         = request.dup
+          new_request.session = request.session
+
+          new_request.instance_variable_set(
             :@parameters,
             (options[:params] || {}).with_indifferent_access.update(
               &quot;controller&quot; =&gt; controller_name, &quot;action&quot; =&gt; options[:action], &quot;id&quot; =&gt; options[:id]
             )
           )
-          
-          request
+
+          new_request
         end
 
         def component_logging(options)</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/components.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,13 +4,14 @@ module ActionController #:nodoc:
   # itself back -- just the value it holds). Examples for writing:
   #
   #   cookies[:user_name] = &quot;david&quot; # =&gt; Will set a simple session cookie
-  #   cookies[:login] = { :value =&gt; &quot;XJ-122&quot;, :expires =&gt; Time.now + 360} # =&gt; Will set a cookie that expires in 1 hour
-  #   
+  #   cookies[:login] = { :value =&gt; &quot;XJ-122&quot;, :expires =&gt; 1.hour.from_now }
+  #   # =&gt; Will set a cookie that expires in 1 hour
+  #
   # Examples for reading:
   #
   #   cookies[:user_name] # =&gt; &quot;david&quot;
   #   cookies.size         # =&gt; 2
-  # 
+  #
   # Example for deleting:
   #
   #   cookies.delete :user_name
@@ -32,13 +33,13 @@ module ActionController #:nodoc:
 
       # Deprecated cookie writer method
       def cookie(*options)
-        @response.headers[&quot;cookie&quot;] &lt;&lt; CGI::Cookie.new(*options)
+        response.headers['cookie'] &lt;&lt; CGI::Cookie.new(*options)
       end
   end
-  
+
   class CookieJar &lt; Hash #:nodoc:
     def initialize(controller)
-      @controller, @cookies = controller, controller.instance_variable_get(&quot;@cookies&quot;)
+      @controller, @cookies = controller, controller.request.cookies
       super()
       update(@cookies)
     end
@@ -46,9 +47,12 @@ module ActionController #:nodoc:
     # Returns the value of the cookie by +name+ -- or nil if no such cookie exists. You set new cookies using either the cookie method
     # or cookies[]= (for simple name/value cookies without options).
     def [](name)
-      @cookies[name.to_s].value.first if @cookies[name.to_s] &amp;&amp; @cookies[name.to_s].respond_to?(:value)
+      cookie = @cookies[name.to_s]
+      if cookie &amp;&amp; cookie.respond_to?(:value)
+        cookie.size &gt; 1 ? cookie.value : cookie.value.to_s
+      end 
     end
-    
+
     def []=(name, options)
       if options.is_a?(Hash)
         options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
@@ -56,14 +60,16 @@ module ActionController #:nodoc:
       else
         options = { &quot;name&quot; =&gt; name.to_s, &quot;value&quot; =&gt; options }
       end
-      
+
       set_cookie(options)
     end
-    
+
     # Removes the cookie on the client machine by setting the value to an empty string
-    # and setting its expiration date into the past
-    def delete(name)
-      set_cookie(&quot;name&quot; =&gt; name.to_s, &quot;value&quot; =&gt; &quot;&quot;, &quot;expires&quot; =&gt; Time.at(0))
+    # and setting its expiration date into the past.  Like []=, you can pass in an options
+    # hash to delete cookies with extra data such as a +path+.
+    def delete(name, options = {})
+      options.stringify_keys!
+      set_cookie(options.merge(&quot;name&quot; =&gt; name.to_s, &quot;value&quot; =&gt; &quot;&quot;, &quot;expires&quot; =&gt; Time.at(0)))
     end
 
     private</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/cookies.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,19 +5,14 @@ module ActionController #:nodoc:
       base.send(:include, ActionController::Filters::InstanceMethods)
     end
 
-    # Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do 
-    # authentication, caching, or auditing before the intended action is performed. Or to do localization or output 
-    # compression after the action has been performed.
-    #
-    # Filters have access to the request, response, and all the instance variables set by other filters in the chain
-    # or by the action (in the case of after filters). Additionally, it's possible for a pre-processing &lt;tt&gt;before_filter&lt;/tt&gt;
-    # to halt the processing before the intended action is processed by returning false or performing a redirect or render. 
-    # This is especially useful for filters like authentication where you're not interested in allowing the action to be 
-    # performed if the proper credentials are not in order.
+    # Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do
+    # authentication, caching, or auditing before the intended action is performed. Or to do localization or output
+    # compression after the action has been performed. Filters have access to the request, response, and all the instance
+    # variables set by other filters in the chain or by the action (in the case of after filters).
     #
     # == Filter inheritance
     #
-    # Controller inheritance hierarchies share filters downwards, but subclasses can also add new filters without
+    # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without
     # affecting the superclass. For example:
     #
     #   class BankController &lt; ActionController::Base
@@ -39,7 +34,7 @@ module ActionController #:nodoc:
     #   end
     #
     # Now any actions performed on the BankController will have the audit method called before. On the VaultController,
-    # first the audit method is called, then the verify_credentials method. If the audit method returns false, then 
+    # first the audit method is called, then the verify_credentials method. If the audit method returns false, then
     # verify_credentials and the intended action are never called.
     #
     # == Filter types
@@ -64,7 +59,7 @@ module ActionController #:nodoc:
     # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can
     # manipulate them as it sees fit.
     #
-    # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation. 
+    # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation.
     # Or just as a quick test. It works like this:
     #
     #   class WeblogController &lt; ActionController::Base
@@ -76,6 +71,9 @@ module ActionController #:nodoc:
     # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call
     # and returns 1 or -1 on arity will do (such as a Proc or an Method object).
     #
+    # Please note that around_filters function a little differently than the normal before and after filters with regard to filter
+    # types. Please see the section dedicated to around_filters below.
+    #
     # == Filter chain ordering
     #
     # Using &lt;tt&gt;before_filter&lt;/tt&gt; and &lt;tt&gt;after_filter&lt;/tt&gt; appends the specified filters to the existing chain. That's usually
@@ -83,14 +81,14 @@ module ActionController #:nodoc:
     # can use &lt;tt&gt;prepend_before_filter&lt;/tt&gt; and &lt;tt&gt;prepend_after_filter&lt;/tt&gt;. Filters added by these methods will be put at the
     # beginning of their respective chain and executed before the rest. For example:
     #
-    #   class ShoppingController
+    #   class ShoppingController &lt; ActionController::Base
     #     before_filter :verify_open_shop
     #
-    #   class CheckoutController
+    #   class CheckoutController &lt; ShoppingController
     #     prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
     #
     # The filter chain for the CheckoutController is now &lt;tt&gt;:ensure_items_in_cart, :ensure_items_in_stock,&lt;/tt&gt;
-    # &lt;tt&gt;:verify_open_shop&lt;/tt&gt;. So if either of the ensure filters return false, we'll never get around to see if the shop 
+    # &lt;tt&gt;:verify_open_shop&lt;/tt&gt;. So if either of the ensure filters return false, we'll never get around to see if the shop
     # is open or not.
     #
     # You may pass multiple filter arguments of each type as well as a filter block.
@@ -98,347 +96,671 @@ module ActionController #:nodoc:
     #
     # == Around filters
     #
-    # In addition to the individual before and after filters, it's also possible to specify that a single object should handle
-    # both the before and after call. That's especially useful when you need to keep state active between the before and after,
-    # such as the example of a benchmark filter below:
-    # 
-    #   class WeblogController &lt; ActionController::Base
-    #     around_filter BenchmarkingFilter.new
-    #     
-    #     # Before this action is performed, BenchmarkingFilter#before(controller) is executed
-    #     def index
+    # Around filters wrap an action, executing code both before and after.
+    # They may be declared as method references, blocks, or objects responding
+    # to #filter or to both #before and #after.
+    #
+    # To use a method as an around_filter, pass a symbol naming the Ruby method.
+    # Yield (or block.call) within the method to run the action.
+    #
+    #   around_filter :catch_exceptions
+    #
+    #   private
+    #     def catch_exceptions
+    #       yield
+    #     rescue =&gt; exception
+    #       logger.debug &quot;Caught exception! #{exception}&quot;
+    #       raise
     #     end
-    #     # After this action has been performed, BenchmarkingFilter#after(controller) is executed
+    #
+    # To use a block as an around_filter, pass a block taking as args both
+    # the controller and the action block. You can't call yield directly from
+    # an around_filter block; explicitly call the action block instead:
+    #
+    #   around_filter do |controller, action|
+    #     logger.debug &quot;before #{controller.action_name}&quot;
+    #     action.call
+    #     logger.debug &quot;after #{controller.action_name}&quot;
     #   end
     #
+    # To use a filter object with around_filter, pass an object responding
+    # to :filter or both :before and :after. With a filter method, yield to
+    # the block as above:
+    #
+    #   around_filter BenchmarkingFilter
+    #
     #   class BenchmarkingFilter
-    #     def initialize
-    #       @runtime
+    #     def self.filter(controller, &amp;block)
+    #       Benchmark.measure(&amp;block)
     #     end
-    #     
-    #     def before
-    #       start_timer
+    #   end
+    #
+    # With before and after methods:
+    #
+    #   around_filter Authorizer.new
+    #
+    #   class Authorizer
+    #     # This will run before the action. Returning false aborts the action.
+    #     def before(controller)
+    #       if user.authorized?
+    #         return true
+    #       else
+    #         redirect_to login_url
+    #         return false
+    #       end
     #     end
-    #     
-    #     def after
-    #       stop_timer
-    #       report_result
+    #
+    #     # This will run after the action if and only if before returned true.
+    #     def after(controller)
     #     end
     #   end
     #
+    # If the filter has before and after methods, the before method will be
+    # called before the action. If before returns false, the filter chain is
+    # halted and after will not be run. See Filter Chain Halting below for
+    # an example.
+    #
     # == Filter chain skipping
     #
-    # Some times its convenient to specify a filter chain in a superclass that'll hold true for the majority of the 
-    # subclasses, but not necessarily all of them. The subclasses that behave in exception can then specify which filters
-    # they would like to be relieved of. Examples
+    # Declaring a filter on a base class conveniently applies to its subclasses,
+    # but sometimes a subclass should skip some of its superclass' filters:
     #
     #   class ApplicationController &lt; ActionController::Base
     #     before_filter :authenticate
+    #     around_filter :catch_exceptions
     #   end
     #
     #   class WeblogController &lt; ApplicationController
-    #     # will run the :authenticate filter
+    #     # Will run the :authenticate and :catch_exceptions filters.
     #   end
     #
     #   class SignupController &lt; ApplicationController
-    #     # will not run the :authenticate filter
+    #     # Skip :authenticate, run :catch_exceptions.
     #     skip_before_filter :authenticate
     #   end
     #
+    #   class ProjectsController &lt; ApplicationController
+    #     # Skip :catch_exceptions, run :authenticate.
+    #     skip_filter :catch_exceptions
+    #   end
+    #
+    #   class ClientsController &lt; ApplicationController
+    #     # Skip :catch_exceptions and :authenticate unless action is index.
+    #     skip_filter :catch_exceptions, :authenticate, :except =&gt; :index
+    #   end
+    #
     # == Filter conditions
     #
-    # Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to
-    # exclude or the actions to include when executing the filter. Available conditions are +:only+ or +:except+, both 
-    # of which accept an arbitrary number of method references. For example:
+    # Filters may be limited to specific actions by declaring the actions to
+    # include or exclude. Both options accept single actions (:only =&gt; :index)
+    # or arrays of actions (:except =&gt; [:foo, :bar]).
     #
     #   class Journal &lt; ActionController::Base
-    #     # only require authentication if the current action is edit or delete
-    #     before_filter :authorize, :only =&gt; [ :edit, :delete ]
-    #    
+    #     # Require authentication for edit and delete.
+    #     before_filter :authorize, :only =&gt; [:edit, :delete]
+    #
+    #     # Passing options to a filter with a block.
+    #     around_filter(:except =&gt; :index) do |controller, action_block|
+    #       results = Profiler.run(&amp;action_block)
+    #       controller.response.sub! &quot;&lt;/body&gt;&quot;, &quot;#{results}&lt;/body&gt;&quot;
+    #     end
+    #
     #     private
     #       def authorize
-    #         # redirect to login unless authenticated
+    #         # Redirect to login unless authenticated.
     #       end
     #   end
-    # 
-    # When setting conditions on inline method (proc) filters the condition must come first and be placed in parentheses.
-    #
-    #   class UserPreferences &lt; ActionController::Base
-    #     before_filter(:except =&gt; :new) { # some proc ... }
-    #     # ...
-    #   end
     #
+    # == Filter Chain Halting
+    #
+    # &lt;tt&gt;before_filter&lt;/tt&gt; and &lt;tt&gt;around_filter&lt;/tt&gt; may halt the request
+    # before a controller action is run. This is useful, for example, to deny
+    # access to unauthenticated users or to redirect from http to https.
+    # Simply return false from the filter or call render or redirect.
+    # After filters will not be executed if the filter chain is halted.
+    #
+    # Around filters halt the request unless the action block is called.
+    # Given these filters
+    #   after_filter :after
+    #   around_filter :around
+    #   before_filter :before
+    #
+    # The filter chain will look like:
+    #
+    #   ...
+    #   . \
+    #   .  #around (code before yield)
+    #   .  .  \
+    #   .  .  #before (actual filter code is run)
+    #   .  .  .  \
+    #   .  .  .  execute controller action
+    #   .  .  .  /
+    #   .  .  ...
+    #   .  .  /
+    #   .  #around (code after yield)
+    #   . /
+    #   #after (actual filter code is run, unless the around filter does not yield)
+    #
+    # If #around returns before yielding, #after will still not be run. The #before
+    # filter and controller action will not be run. If #before returns false,
+    # the second half of #around and will still run but #after and the
+    # action will not. If #around does not yield, #after will not be run.
     module ClassMethods
-      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be appended to the array of filters that's run _before_ actions
-      # on this controller are performed.
+      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be appended to the filter_chain and
+      # will execute before the action on this controller is performed.
       def append_before_filter(*filters, &amp;block)
-        conditions = extract_conditions!(filters)
-        filters &lt;&lt; block if block_given?
-        add_action_conditions(filters, conditions)
-        append_filter_to_chain('before', filters)
+        append_filter_to_chain(filters, :before, &amp;block)
       end
 
-      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be prepended to the array of filters that's run _before_ actions
-      # on this controller are performed.
+      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be prepended to the filter_chain and
+      # will execute before the action on this controller is performed.
       def prepend_before_filter(*filters, &amp;block)
-        conditions = extract_conditions!(filters) 
-        filters &lt;&lt; block if block_given?
-        add_action_conditions(filters, conditions)
-        prepend_filter_to_chain('before', filters)
+        prepend_filter_to_chain(filters, :before, &amp;block)
       end
 
-      # Short-hand for append_before_filter since that's the most common of the two.
+      # Shorthand for append_before_filter since it's the most common.
       alias :before_filter :append_before_filter
-      
-      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be appended to the array of filters that's run _after_ actions
-      # on this controller are performed.
+
+      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be appended to the array of filters
+      # that run _after_ actions on this controller are performed.
       def append_after_filter(*filters, &amp;block)
-        conditions = extract_conditions!(filters) 
-        filters &lt;&lt; block if block_given?
-        add_action_conditions(filters, conditions)
-        append_filter_to_chain('after', filters)
+        append_filter_to_chain(filters, :after, &amp;block)
       end
 
-      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be prepended to the array of filters that's run _after_ actions
-      # on this controller are performed.
+      # The passed &lt;tt&gt;filters&lt;/tt&gt; will be prepended to the array of filters
+      # that run _after_ actions on this controller are performed.
       def prepend_after_filter(*filters, &amp;block)
-        conditions = extract_conditions!(filters) 
-        filters &lt;&lt; block if block_given?
-        add_action_conditions(filters, conditions)
-        prepend_filter_to_chain(&quot;after&quot;, filters)
+        prepend_filter_to_chain(filters, :after, &amp;block)
       end
 
-      # Short-hand for append_after_filter since that's the most common of the two.
+      # Shorthand for append_after_filter since it's the most common.
       alias :after_filter :append_after_filter
-      
-      # The passed &lt;tt&gt;filters&lt;/tt&gt; will have their +before+ method appended to the array of filters that's run both before actions
-      # on this controller are performed and have their +after+ method prepended to the after actions. The filter objects must all 
-      # respond to both +before+ and +after+. So if you do append_around_filter A.new, B.new, the callstack will look like:
+
+
+      # If you append_around_filter A.new, B.new, the filter chain looks like
       #
       #   B#before
       #     A#before
+      #       # run the action
       #     A#after
       #   B#after
-      def append_around_filter(*filters)
-        conditions = extract_conditions!(filters) 
-        for filter in filters.flatten
-          ensure_filter_responds_to_before_and_after(filter)
-          append_before_filter(conditions || {}) { |c| filter.before(c) }
-          prepend_after_filter(conditions || {}) { |c| filter.after(c) }
-        end
-      end        
-
-      # The passed &lt;tt&gt;filters&lt;/tt&gt; will have their +before+ method prepended to the array of filters that's run both before actions
-      # on this controller are performed and have their +after+ method appended to the after actions. The filter objects must all 
-      # respond to both +before+ and +after+. So if you do prepend_around_filter A.new, B.new, the callstack will look like:
+      #
+      # With around filters which yield to the action block, #before and #after
+      # are the code before and after the yield.
+      def append_around_filter(*filters, &amp;block)
+        filters, conditions = extract_conditions(filters, &amp;block)
+        filters.map { |f| proxy_before_and_after_filter(f) }.each do |filter|
+          append_filter_to_chain([filter, conditions])
+        end
+      end
+
+      # If you prepend_around_filter A.new, B.new, the filter chain looks like:
       #
       #   A#before
       #     B#before
+      #       # run the action
       #     B#after
       #   A#after
-      def prepend_around_filter(*filters)
-        for filter in filters.flatten
-          ensure_filter_responds_to_before_and_after(filter)
-          prepend_before_filter { |c| filter.before(c) }
-          append_after_filter   { |c| filter.after(c) }
+      #
+      # With around filters which yield to the action block, #before and #after
+      # are the code before and after the yield.
+      def prepend_around_filter(*filters, &amp;block)
+        filters, conditions = extract_conditions(filters, &amp;block)
+        filters.map { |f| proxy_before_and_after_filter(f) }.each do |filter|
+          prepend_filter_to_chain([filter, conditions])
         end
-      end     
+      end
 
-      # Short-hand for append_around_filter since that's the most common of the two.
+      # Shorthand for append_around_filter since it's the most common.
       alias :around_filter :append_around_filter
-      
-      # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference 
+
+      # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
       # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
       # of many sub-controllers need a different hierarchy.
       #
-      # You can control the actions to skip the filter for with the &lt;tt&gt;:only&lt;/tt&gt; and &lt;tt&gt;:except&lt;/tt&gt; options, 
+      # You can control the actions to skip the filter for with the &lt;tt&gt;:only&lt;/tt&gt; and &lt;tt&gt;:except&lt;/tt&gt; options,
       # just like when you apply the filters.
       def skip_before_filter(*filters)
-        if conditions = extract_conditions!(filters)
-          remove_contradicting_conditions!(filters, conditions)
-          conditions[:only], conditions[:except] = conditions[:except], conditions[:only]
-          add_action_conditions(filters, conditions)
-        else
-          for filter in filters.flatten
-            write_inheritable_attribute(&quot;before_filters&quot;, read_inheritable_attribute(&quot;before_filters&quot;) - [ filter ])
-          end
-        end
+        skip_filter_in_chain(*filters, &amp;:before?)
       end
 
-      # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference 
+      # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
       # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
       # of many sub-controllers need a different hierarchy.
       #
-      # You can control the actions to skip the filter for with the &lt;tt&gt;:only&lt;/tt&gt; and &lt;tt&gt;:except&lt;/tt&gt; options, 
+      # You can control the actions to skip the filter for with the &lt;tt&gt;:only&lt;/tt&gt; and &lt;tt&gt;:except&lt;/tt&gt; options,
       # just like when you apply the filters.
       def skip_after_filter(*filters)
-        if conditions = extract_conditions!(filters)
-          remove_contradicting_conditions!(filters, conditions)
-          conditions[:only], conditions[:except] = conditions[:except], conditions[:only]
-          add_action_conditions(filters, conditions)
-        else
-          for filter in filters.flatten
-            write_inheritable_attribute(&quot;after_filters&quot;, read_inheritable_attribute(&quot;after_filters&quot;) - [ filter ])
-          end
-        end
+        skip_filter_in_chain(*filters, &amp;:after?)
       end
-      
+
+      # Removes the specified filters from the filter chain. This only works for method reference (symbol)
+      # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that
+      # it will match any before, after or yielding around filter.
+      #
+      # You can control the actions to skip the filter for with the &lt;tt&gt;:only&lt;/tt&gt; and &lt;tt&gt;:except&lt;/tt&gt; options,
+      # just like when you apply the filters.
+      def skip_filter(*filters)
+        skip_filter_in_chain(*filters)
+      end
+
+      # Returns an array of Filter objects for this controller.
+      def filter_chain
+        read_inheritable_attribute(&quot;filter_chain&quot;) || []
+      end
+
       # Returns all the before filters for this class and all its ancestors.
+      # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
       def before_filters #:nodoc:
-        @before_filters ||= read_inheritable_attribute(&quot;before_filters&quot;) || []
+        filter_chain.select(&amp;:before?).map(&amp;:filter)
       end
-      
+
       # Returns all the after filters for this class and all its ancestors.
+      # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
       def after_filters #:nodoc:
-        @after_filters ||= read_inheritable_attribute(&quot;after_filters&quot;) || []
+        filter_chain.select(&amp;:after?).map(&amp;:filter)
       end
-      
+
       # Returns a mapping between filters and the actions that may run them.
       def included_actions #:nodoc:
         @included_actions ||= read_inheritable_attribute(&quot;included_actions&quot;) || {}
       end
-      
+
       # Returns a mapping between filters and actions that may not run them.
       def excluded_actions #:nodoc:
         @excluded_actions ||= read_inheritable_attribute(&quot;excluded_actions&quot;) || {}
       end
-      
-      private
-        def append_filter_to_chain(condition, filters)
-          write_inheritable_array(&quot;#{condition}_filters&quot;, filters)
+
+      # Find a filter in the filter_chain where the filter method matches the _filter_ param
+      # and (optionally) the passed block evaluates to true (mostly used for testing before?
+      # and after? on the filter). Useful for symbol filters.
+      #
+      # The object of type Filter is passed to the block when yielded, not the filter itself.
+      def find_filter(filter, &amp;block) #:nodoc:
+        filter_chain.select { |f| f.filter == filter &amp;&amp; (!block_given? || yield(f)) }.first
+      end
+
+      # Returns true if the filter is excluded from the given action
+      def filter_excluded_from_action?(filter,action) #:nodoc:
+        case
+        when ia = included_actions[filter]
+          !ia.include?(action)
+        when ea = excluded_actions[filter]
+          ea.include?(action)
         end
+      end
 
-        def prepend_filter_to_chain(condition, filters)
-          old_filters = read_inheritable_attribute(&quot;#{condition}_filters&quot;) || []
-          write_inheritable_attribute(&quot;#{condition}_filters&quot;, filters + old_filters)
+      # Filter class is an abstract base class for all filters. Handles all of the included/excluded actions but
+      # contains no logic for calling the actual filters.
+      class Filter #:nodoc:
+        attr_reader :filter, :included_actions, :excluded_actions
+
+        def initialize(filter)
+          @filter = filter
         end
 
-        def ensure_filter_responds_to_before_and_after(filter)
-          unless filter.respond_to?(:before) &amp;&amp; filter.respond_to?(:after)
-            raise ActionControllerError, &quot;Filter object must respond to both before and after&quot;
-          end
+        def type
+          :around
         end
 
-        def extract_conditions!(filters)
-          return nil unless filters.last.is_a? Hash
-          filters.pop
+        def before?
+          type == :before
         end
 
-        def add_action_conditions(filters, conditions)
-          return unless conditions
-          included, excluded = conditions[:only], conditions[:except]
-          write_inheritable_hash('included_actions', condition_hash(filters, included)) &amp;&amp; return if included
-          write_inheritable_hash('excluded_actions', condition_hash(filters, excluded)) if excluded
+        def after?
+          type == :after
         end
 
-        def condition_hash(filters, *actions)
-          filters.inject({}) {|hash, filter| hash.merge(filter =&gt; actions.flatten.map {|action| action.to_s})}
-        end
-        
-        def remove_contradicting_conditions!(filters, conditions)
-          return unless conditions[:only]
-          filters.each do |filter|
-            next unless included_actions_for_filter = (read_inheritable_attribute('included_actions') || {})[filter]
-            [*conditions[:only]].each do |conditional_action|
-              conditional_action = conditional_action.to_s
-              included_actions_for_filter.delete(conditional_action) if included_actions_for_filter.include?(conditional_action)
-            end
+        def around?
+          type == :around
+        end
+
+        def run(controller)
+          raise ActionControllerError, 'No filter type: Nothing to do here.'
+        end
+
+        def call(controller, &amp;block)
+          run(controller)
+        end
+      end
+
+      # Abstract base class for filter proxies. FilterProxy objects are meant to mimic the behaviour of the old
+      # before_filter and after_filter by moving the logic into the filter itself.
+      class FilterProxy &lt; Filter #:nodoc:
+        def filter
+          @filter.filter
+        end
+      end
+
+      class BeforeFilterProxy &lt; FilterProxy #:nodoc:
+        def type
+          :before
+        end
+
+        def run(controller)
+          # only filters returning false are halted.
+          if false == @filter.call(controller)
+            controller.send :halt_filter_chain, @filter, :returned_false
           end
         end
-    end
 
-    module InstanceMethods # :nodoc:
-      def self.included(base)
-        base.class_eval do
-          alias_method :perform_action_without_filters, :perform_action
-          alias_method :perform_action, :perform_action_with_filters
+        def call(controller)
+          yield unless run(controller)
+        end
+      end
+
+      class AfterFilterProxy &lt; FilterProxy #:nodoc:
+        def type
+          :after
+        end
 
-          alias_method :process_without_filters, :process
-          alias_method :process, :process_with_filters
+        def run(controller)
+          @filter.call(controller)
+        end
 
-          alias_method :process_cleanup_without_filters, :process_cleanup
-          alias_method :process_cleanup, :process_cleanup_with_filters
+        def call(controller)
+          yield
+          run(controller)
         end
       end
 
-      def perform_action_with_filters
-        before_action_result = before_action
+      class SymbolFilter &lt; Filter #:nodoc:
+        def call(controller, &amp;block)
+          controller.send(@filter, &amp;block)
+        end
+      end
+
+      class ProcFilter &lt; Filter #:nodoc:
+        def call(controller)
+          @filter.call(controller)
+        rescue LocalJumpError # a yield from a proc... no no bad dog.
+          raise(ActionControllerError, 'Cannot yield from a Proc type filter. The Proc must take two arguments and execute #call on the second argument.')
+        end
+      end
 
-        unless before_action_result == false || performed?
-          perform_action_without_filters
-          after_action
+      class ProcWithCallFilter &lt; Filter #:nodoc:
+        def call(controller, &amp;block)
+          @filter.call(controller, block)
+        rescue LocalJumpError # a yield from a proc... no no bad dog.
+          raise(ActionControllerError, 'Cannot yield from a Proc type filter. The Proc must take two arguments and execute #call on the second argument.')
         end
+      end
 
-        @before_filter_chain_aborted = (before_action_result == false)
+      class MethodFilter &lt; Filter #:nodoc:
+        def call(controller, &amp;block)
+          @filter.call(controller, &amp;block)
+        end
       end
 
-      def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
-        @before_filter_chain_aborted = false
-        process_without_filters(request, response, method, *arguments)
+      class ClassFilter &lt; Filter #:nodoc:
+        def call(controller, &amp;block)
+          @filter.filter(controller, &amp;block)
+        end
       end
 
-      # Calls all the defined before-filter filters, which are added by using &quot;before_filter :method&quot;.
-      # If any of the filters return false, no more filters will be executed and the action is aborted.
-      def before_action #:doc:
-        call_filters(self.class.before_filters)
+      class ClassBeforeFilter &lt; Filter #:nodoc:
+        def call(controller, &amp;block)
+          @filter.before(controller)
+        end
       end
 
-      # Calls all the defined after-filter filters, which are added by using &quot;after_filter :method&quot;.
-      # If any of the filters return false, no more filters will be executed.
-      def after_action #:doc:
-        call_filters(self.class.after_filters)
+      class ClassAfterFilter &lt; Filter #:nodoc:
+        def call(controller, &amp;block)
+          @filter.after(controller)
+        end
       end
-      
-      private
-        def call_filters(filters)
-          filters.each do |filter| 
-            next if action_exempted?(filter)
-
-            filter_result = case
-              when filter.is_a?(Symbol)
-                self.send(filter)
-              when filter_block?(filter)
-                filter.call(self)
-              when filter_class?(filter)
-                filter.filter(self)
-              else
-                raise(
-                  ActionControllerError, 
-                  'Filters need to be either a symbol, proc/method, or class implementing a static filter method'
-                )
+
+      protected
+        def append_filter_to_chain(filters, filter_type = :around, &amp;block)
+          pos = find_filter_append_position(filters, filter_type)
+          update_filter_chain(filters, filter_type, pos, &amp;block)
+        end
+
+        def prepend_filter_to_chain(filters, filter_type = :around, &amp;block)
+          pos = find_filter_prepend_position(filters, filter_type)
+          update_filter_chain(filters, filter_type, pos, &amp;block)
+        end
+
+        def update_filter_chain(filters, filter_type, pos, &amp;block)
+          new_filters = create_filters(filters, filter_type, &amp;block)
+          new_chain = filter_chain.insert(pos, new_filters).flatten
+          write_inheritable_attribute('filter_chain', new_chain)
+        end
+
+        def find_filter_append_position(filters, filter_type)
+          # appending an after filter puts it at the end of the call chain
+          # before and around filters go before the first after filter in the chain
+          unless filter_type == :after
+            filter_chain.each_with_index do |f,i|
+              return i if f.after?
             end
+          end
+          return -1
+        end
 
-            if filter_result == false
-              logger.info &quot;Filter chain halted as [#{filter}] returned false&quot; if logger
-              return false 
+        def find_filter_prepend_position(filters, filter_type)
+          # prepending a before or around filter puts it at the front of the call chain
+          # after filters go before the first after filter in the chain
+          if filter_type == :after
+            filter_chain.each_with_index do |f,i|
+              return i if f.after?
             end
+            return -1
           end
+          return 0
         end
-        
-        def filter_block?(filter)
-          filter.respond_to?('call') &amp;&amp; (filter.arity == 1 || filter.arity == -1)
+
+        def create_filters(filters, filter_type, &amp;block) #:nodoc:
+          filters, conditions = extract_conditions(filters, &amp;block)
+          filters.map! { |filter| find_or_create_filter(filter, filter_type) }
+          update_conditions(filters, conditions)
+          filters
         end
-        
-        def filter_class?(filter)
-          filter.respond_to?('filter')
+
+        def find_or_create_filter(filter, filter_type)
+          if found_filter = find_filter(filter) { |f| f.type == filter_type }
+            found_filter
+          else
+            f = class_for_filter(filter, filter_type).new(filter)
+            # apply proxy to filter if necessary
+            case filter_type
+            when :before
+              BeforeFilterProxy.new(f)
+            when :after
+              AfterFilterProxy.new(f)
+            else
+              f
+            end
+          end
         end
 
-        def action_exempted?(filter)
+        # The determination of the filter type was once done at run time.
+        # This method is here to extract as much logic from the filter run time as possible
+        def class_for_filter(filter, filter_type) #:nodoc:
           case
-            when ia = self.class.included_actions[filter]
-              !ia.include?(action_name)
-            when ea = self.class.excluded_actions[filter] 
-              ea.include?(action_name)
+          when filter.is_a?(Symbol)
+            SymbolFilter
+          when filter.respond_to?(:call)
+            if filter.is_a?(Method)
+              MethodFilter
+            elsif filter.arity == 1
+              ProcFilter
+            else
+              ProcWithCallFilter
+            end
+          when filter.respond_to?(:filter)
+            ClassFilter
+          when filter.respond_to?(:before) &amp;&amp; filter_type == :before
+            ClassBeforeFilter
+          when filter.respond_to?(:after) &amp;&amp; filter_type == :after
+            ClassAfterFilter
+          else
+            raise(ActionControllerError, 'A filter must be a Symbol, Proc, Method, or object responding to filter, after or before.')
           end
         end
 
-        def process_cleanup_with_filters
-          if @before_filter_chain_aborted
-            close_session
+        def extract_conditions(*filters, &amp;block) #:nodoc:
+          filters.flatten!
+          conditions = filters.last.is_a?(Hash) ? filters.pop : {}
+          filters &lt;&lt; block if block_given?
+          return filters, conditions
+        end
+
+        def update_conditions(filters, conditions)
+          return if conditions.empty?
+          if conditions[:only]
+            write_inheritable_hash('included_actions', condition_hash(filters, conditions[:only]))
+          elsif conditions[:except]
+            write_inheritable_hash('excluded_actions', condition_hash(filters, conditions[:except]))
+          end
+        end
+
+        def condition_hash(filters, *actions)
+          actions = actions.flatten.map(&amp;:to_s)
+          filters.inject({}) { |h,f| h.update( f =&gt; (actions.blank? ? nil : actions)) }
+        end
+
+        def skip_filter_in_chain(*filters, &amp;test) #:nodoc:
+          filters, conditions = extract_conditions(filters)
+          filters.map! { |f| block_given? ? find_filter(f, &amp;test) : find_filter(f) }
+          filters.compact!
+
+          if conditions.empty?
+            delete_filters_in_chain(filters)
           else
-            process_cleanup_without_filters
+            remove_actions_from_included_actions!(filters,conditions[:only] || [])
+            conditions[:only], conditions[:except] = conditions[:except], conditions[:only]
+            update_conditions(filters,conditions)
+          end
+        end
+
+        def remove_actions_from_included_actions!(filters,*actions)
+          actions = actions.flatten.map(&amp;:to_s)
+          updated_hash = filters.inject(read_inheritable_attribute('included_actions')||{}) do |hash,filter|
+            ia = (hash[filter] || []) - actions
+            ia.empty? ? hash.delete(filter) : hash[filter] = ia
+            hash
           end
+          write_inheritable_attribute('included_actions', updated_hash)
         end
+
+        def delete_filters_in_chain(filters) #:nodoc:
+          write_inheritable_attribute('filter_chain', filter_chain.reject { |f| filters.include?(f) })
+        end
+
+        def filter_responds_to_before_and_after(filter) #:nodoc:
+          filter.respond_to?(:before) &amp;&amp; filter.respond_to?(:after)
+        end
+
+        def proxy_before_and_after_filter(filter) #:nodoc:
+          return filter unless filter_responds_to_before_and_after(filter)
+          Proc.new do |controller, action|
+            if filter.before(controller) == false
+              controller.send :halt_filter_chain, filter, :returned_false
+            else
+              begin
+                action.call
+              ensure
+                filter.after(controller)
+              end
+            end
+          end
+        end
+    end
+
+    module InstanceMethods # :nodoc:
+      def self.included(base)
+        base.class_eval do
+          alias_method_chain :perform_action, :filters
+          alias_method_chain :process, :filters
+          alias_method_chain :process_cleanup, :filters
+        end
+      end
+
+      protected
+
+      def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
+        @before_filter_chain_aborted = false
+        process_without_filters(request, response, method, *arguments)
+      end
+
+      def perform_action_with_filters
+        call_filters(self.class.filter_chain, 0, 0)
+      end
+
+      private
+
+      def call_filters(chain, index, nesting)
+        index = run_before_filters(chain, index, nesting)
+        aborted = @before_filter_chain_aborted
+        perform_action_without_filters unless performed? || aborted
+        return index if nesting != 0 || aborted
+        run_after_filters(chain, index)
+      end
+
+      def skip_excluded_filters(chain, index)
+        while (filter = chain[index]) &amp;&amp; self.class.filter_excluded_from_action?(filter, action_name)
+          index = index.next
+        end
+        [filter, index]
+      end
+
+      def run_before_filters(chain, index, nesting)
+        while chain[index]
+          filter, index = skip_excluded_filters(chain, index)
+          break unless filter # end of call chain reached
+          case filter.type
+          when :before
+            filter.run(self)  # invoke before filter
+            index = index.next
+            break if @before_filter_chain_aborted
+          when :around
+            yielded = false
+            filter.call(self) do
+              yielded = true
+              # all remaining before and around filters will be run in this call
+              index = call_filters(chain, index.next, nesting.next)
+            end
+            halt_filter_chain(filter, :did_not_yield) unless yielded
+            break
+          else
+            break  # no before or around filters left
+          end
+        end
+        index
+      end
+
+      def run_after_filters(chain, index)
+        seen_after_filter = false
+        while chain[index]
+          filter, index = skip_excluded_filters(chain, index)
+          break unless filter # end of call chain reached
+          case filter.type
+          when :after
+            seen_after_filter = true
+            filter.run(self)  # invoke after filter
+          else
+            # implementation error or someone has mucked with the filter chain
+            raise ActionControllerError, &quot;filter #{filter.inspect} was in the wrong place!&quot; if seen_after_filter
+          end
+          index = index.next
+        end
+        index.next
+      end
+
+      def halt_filter_chain(filter, reason)
+        @before_filter_chain_aborted = true
+        logger.info &quot;Filter chain halted as [#{filter.inspect}] #{reason}.&quot; if logger
+        false
+      end
+
+      def process_cleanup_with_filters
+        if @before_filter_chain_aborted
+          close_session
+        else
+          process_cleanup_without_filters
+        end
+      end
     end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/filters.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,7 +17,7 @@ module ActionController #:nodoc:
   #   end
   #
   #   display.rhtml
-  #     &lt;% if @flash[:notice] %&gt;&lt;div class=&quot;notice&quot;&gt;&lt;%= @flash[:notice] %&gt;&lt;/div&gt;&lt;% end %&gt;
+  #     &lt;% if flash[:notice] %&gt;&lt;div class=&quot;notice&quot;&gt;&lt;%= flash[:notice] %&gt;&lt;/div&gt;&lt;% end %&gt;
   #
   # This example just places a string in the flash, but you can put any object in there. And of course, you can put as many
   # as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
@@ -28,11 +28,9 @@ module ActionController #:nodoc:
       base.send :include, InstanceMethods
 
       base.class_eval do
-        alias_method :assign_shortcuts_without_flash, :assign_shortcuts
-        alias_method :assign_shortcuts, :assign_shortcuts_with_flash
-
-        alias_method :process_cleanup_without_flash, :process_cleanup
-        alias_method :process_cleanup, :process_cleanup_with_flash
+        alias_method_chain :assign_shortcuts, :flash
+        alias_method_chain :process_cleanup,  :flash
+        alias_method_chain :reset_session,    :flash
       end
     end
     
@@ -94,7 +92,7 @@ module ActionController #:nodoc:
       #
       #    flash.keep            # keeps the entire flash
       #    flash.keep(:notice)   # keeps only the &quot;notice&quot; entry, the rest of the flash is discarded
-      def keep(k=nil)
+      def keep(k = nil)
         use(k, false)
       end
     
@@ -102,7 +100,7 @@ module ActionController #:nodoc:
       #
       #     flash.keep                 # keep entire flash available for the next action
       #     flash.discard(:warning)    # discard the &quot;warning&quot; entry (it'll still be available for the current action)
-      def discard(k=nil)
+      def discard(k = nil)
         use(k)
       end
     
@@ -118,6 +116,7 @@ module ActionController #:nodoc:
             @used.delete(k)
           end
         end
+
         (@used.keys - keys).each{|k| @used.delete k } # clean up after keys that could have been left over by calling reject! or shift on the flash
       end
     
@@ -143,36 +142,41 @@ module ActionController #:nodoc:
       end
       
       def process_cleanup_with_flash
-        flash.sweep if @session
+        flash.sweep if @_session
         process_cleanup_without_flash
       end
+
+      def reset_session_with_flash
+        reset_session_without_flash
+        remove_instance_variable(:@_flash)
+        flash(:refresh)
+      end
       
       protected 
         # Access the contents of the flash. Use &lt;tt&gt;flash[&quot;notice&quot;]&lt;/tt&gt; to read a notice you put there or 
         # &lt;tt&gt;flash[&quot;notice&quot;] = &quot;hello&quot;&lt;/tt&gt; to put a new one.
         # Note that if sessions are disabled only flash.now will work.
         def flash(refresh = false) #:doc:
-          if @flash.nil? || refresh
-            @flash = 
-              if @session.is_a?(Hash)
-                # @session is a Hash, if sessions are disabled
-                # we don't put the flash in the session in this case
+          if !defined?(@_flash) || refresh
+            @_flash =
+              if session.is_a?(Hash)
+                # don't put flash in session if disabled
                 FlashHash.new
               else
-                # otherwise, @session is a CGI::Session or a TestSession
+                # otherwise, session is a CGI::Session or a TestSession
                 # so make sure it gets retrieved from/saved to session storage after request processing
-                @session[&quot;flash&quot;] ||= FlashHash.new
+                session[&quot;flash&quot;] ||= FlashHash.new
               end
           end
-          
-          @flash
+
+          @_flash
         end
 
         # deprecated. use &lt;tt&gt;flash.keep&lt;/tt&gt; instead
         def keep_flash #:doc:
-          warn 'keep_flash is deprecated; use flash.keep instead.'
+          ActiveSupport::Deprecation.warn 'keep_flash is deprecated; use flash.keep instead.', caller
           flash.keep
         end
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/flash.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,6 @@
 module ActionController #:nodoc:
   module Helpers #:nodoc:
-    def self.append_features(base)
-      super
-
+    def self.included(base)
       # Initialize the base module to aggregate its helpers.
       base.class_inheritable_accessor :master_helper_module
       base.master_helper_module = Module.new
@@ -13,8 +11,7 @@ module ActionController #:nodoc:
       base.class_eval do
         # Wrap inherited to create a new master helper module for subclasses.
         class &lt;&lt; self
-          alias_method :inherited_without_helper, :inherited
-          alias_method :inherited, :inherited_with_helper
+          alias_method_chain :inherited, :helper
         end
       end
     end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/helpers.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,7 @@
 require 'dispatcher'
 require 'stringio'
 require 'uri'
+require 'action_controller/test_process'
 
 module ActionController
   module Integration #:nodoc:
@@ -13,6 +14,7 @@ module ActionController
     # rather than instantiating Integration::Session directly.
     class Session
       include Test::Unit::Assertions
+      include ActionController::Assertions
       include ActionController::TestProcess
 
       # The integer HTTP status code of the last request.
@@ -65,7 +67,7 @@ module ActionController
         @https = false
         @cookies = {}
         @controller = @request = @response = nil
-      
+
         self.host        = &quot;www.example.com&quot;
         self.remote_addr = &quot;127.0.0.1&quot;
         self.accept      = &quot;text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5&quot;
@@ -73,11 +75,11 @@ module ActionController
         unless @named_routes_configured
           # install the named routes in this session instance.
           klass = class&lt;&lt;self; self; end
-          Routing::NamedRoutes.install(klass)
+          Routing::Routes.named_routes.install(klass)
 
           # the helpers are made protected by default--we make them public for
           # easier access during testing and troubleshooting.
-          klass.send(:public, *Routing::NamedRoutes::Helpers)
+          klass.send(:public, *Routing::Routes.named_routes.helpers)
           @named_routes_configured = true
         end
       end
@@ -87,7 +89,7 @@ module ActionController
       #   session.https!
       #   session.https!(false)
       def https!(flag=true)
-        @https = flag        
+        @https = flag
       end
 
       # Return +true+ if the session is mimicing a secure HTTPS request.
@@ -111,7 +113,7 @@ module ActionController
       # performed on the location header.
       def follow_redirect!
         raise &quot;not a redirect! #{@status} #{@status_message}&quot; unless redirect?
-        get(interpret_uri(headers[&quot;location&quot;].first))
+        get(interpret_uri(headers['location'].first))
         status
       end
 
@@ -141,32 +143,60 @@ module ActionController
       # Performs a GET request with the given parameters. The parameters may
       # be +nil+, a Hash, or a string that is appropriately encoded
       # (application/x-www-form-urlencoded or multipart/form-data).  The headers
-      # should be a hash.  The keys will automatically be upcased, with the 
+      # should be a hash.  The keys will automatically be upcased, with the
       # prefix 'HTTP_' added if needed.
+      #
+      # You can also perform POST, PUT, DELETE, and HEAD requests with #post,
+      # #put, #delete, and #head.
       def get(path, parameters=nil, headers=nil)
         process :get, path, parameters, headers
       end
 
-      # Performs a POST request with the given parameters. The parameters may
-      # be +nil+, a Hash, or a string that is appropriately encoded
-      # (application/x-www-form-urlencoded or multipart/form-data).  The headers
-      # should be a hash.  The keys will automatically be upcased, with the 
-      # prefix 'HTTP_' added if needed.
+      # Performs a POST request with the given parameters. See get() for more details.
       def post(path, parameters=nil, headers=nil)
         process :post, path, parameters, headers
       end
 
-      # Performs an XMLHttpRequest request with the given parameters, mimicing
-      # the request environment created by the Prototype library. The parameters
-      # may be +nil+, a Hash, or a string that is appropriately encoded
-      # (application/x-www-form-urlencoded or multipart/form-data).  The headers
-      # should be a hash.  The keys will automatically be upcased, with the 
-      # prefix 'HTTP_' added if needed.
-      def xml_http_request(path, parameters=nil, headers=nil)
-        headers = (headers || {}).merge(&quot;X-Requested-With&quot; =&gt; &quot;XMLHttpRequest&quot;)
-        post(path, parameters, headers)
+      # Performs a PUT request with the given parameters. See get() for more details.
+      def put(path, parameters=nil, headers=nil)
+        process :put, path, parameters, headers
+      end
+
+      # Performs a DELETE request with the given parameters. See get() for more details.
+      def delete(path, parameters=nil, headers=nil)
+        process :delete, path, parameters, headers
+      end
+
+      # Performs a HEAD request with the given parameters. See get() for more details.
+      def head(path, parameters=nil, headers=nil)
+        process :head, path, parameters, headers
       end
 
+      # Performs an XMLHttpRequest request with the given parameters, mirroring
+      # a request from the Prototype library.
+      #
+      # The request_method is :get, :post, :put, :delete or :head; the
+      # parameters are +nil+, a hash, or a url-encoded or multipart string;
+      # the headers are a hash.  Keys are automatically upcased and prefixed
+      # with 'HTTP_' if not already.
+      #
+      # This method used to omit the request_method parameter, assuming it
+      # was :post. This was deprecated in Rails 1.2.4. Always pass the request
+      # method as the first argument.
+      def xml_http_request(request_method, path, parameters = nil, headers = nil)
+        unless request_method.is_a?(Symbol)
+          ActiveSupport::Deprecation.warn 'xml_http_request now takes the request_method (:get, :post, etc.) as the first argument. It used to assume :post, so add the :post argument to your existing method calls to silence this warning.'
+          request_method, path, parameters, headers = :post, request_method, path, parameters
+        end
+
+        headers ||= {}
+        headers['X-Requested-With'] = 'XMLHttpRequest'
+        headers['Accept'] = 'text/javascript, text/html, application/xml, text/xml, */*'
+
+        process(request_method, path, parameters, headers)
+      end
+      alias xhr :xml_http_request
+
       # Returns the URL for the given options, according to the rules specified
       # in the application's routes.
       def url_for(options)
@@ -174,7 +204,6 @@ module ActionController
       end
 
       private
-
         class MockCGI &lt; CGI #:nodoc:
           attr_accessor :stdinput, :stdoutput, :env_table
 
@@ -224,7 +253,7 @@ module ActionController
 
           (headers || {}).each do |key, value|
             key = key.to_s.upcase.gsub(/-/, &quot;_&quot;)
-            key = &quot;HTTP_#{key}&quot; unless env.has_key?(key) || env =~ /^X|HTTP/
+            key = &quot;HTTP_#{key}&quot; unless env.has_key?(key) || key =~ /^HTTP_/
             env[key] = value
           end
 
@@ -247,6 +276,8 @@ module ActionController
           # tests.
           @response.extend(TestResponseBehavior)
 
+          @html_document = nil
+
           parse_result
           return status
         end
@@ -271,7 +302,7 @@ module ActionController
           @status = @status.to_i
         end
 
-        # Encode the cookies hash in a format suitable for passing to a 
+        # Encode the cookies hash in a format suitable for passing to a
         # request.
         def encode_cookies
           cookies.inject(&quot;&quot;) do |string, (name, value)|
@@ -317,9 +348,8 @@ module ActionController
       def self.included(base)
         base.extend(ClassMethods)
         base.class_eval do
-          class &lt;&lt;self
-            alias_method :new_without_capture, :new
-            alias_method :new, :new_with_capture
+          class &lt;&lt; self
+            alias_method_chain :new, :capture
           end
         end
       end
@@ -330,9 +360,11 @@ module ActionController
         def clear_last_instantiation!
           self.last_instantiation = nil
         end
-    
+
         def new_with_capture(*args)
-          self.last_instantiation ||= new_without_capture(*args)
+          controller = new_without_capture(*args)
+          self.last_instantiation ||= controller
+          controller
         end
       end
     end
@@ -428,7 +460,7 @@ module ActionController
     # without any test methods.
     def run(*args) #:nodoc:
       return if @method_name == &quot;default_test&quot;
-      super   
+      super
     end
 
     # Because of how use_instantiated_fixtures and use_transactional_fixtures
@@ -468,9 +500,11 @@ module ActionController
       @integration_session = open_session
     end
 
-    %w(get post cookies assigns xml_http_request).each do |method|
+    %w(get post put head delete cookies assigns xml_http_request).each do |method|
       define_method(method) do |*args|
         reset! unless @integration_session
+        # reset the html_document variable, but only for new get/post calls
+        @html_document = nil unless %w(cookies assigns).include?(method)
         returning @integration_session.send(method, *args) do
           copy_session_variables!
         end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/integration.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,12 +3,13 @@ module ActionController #:nodoc:
     def self.included(base)
       base.extend(ClassMethods)
       base.class_eval do
+        # NOTE: Can't use alias_method_chain here because +render_without_layout+ is already
+        # defined as a publicly exposed method
         alias_method :render_with_no_layout, :render
         alias_method :render, :render_with_a_layout
 
         class &lt;&lt; self
-          alias_method :inherited_without_layout, :inherited
-          alias_method :inherited, :inherited_with_layout
+          alias_method_chain :inherited, :layout
         end
       end
     end
@@ -26,9 +27,9 @@ module ActionController #:nodoc:
     # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
     # that the header and footer are only mentioned in one place, like this:
     #
-    #   &lt;!-- The header part of this layout --&gt;
+    #   // The header part of this layout
     #   &lt;%= yield %&gt;
-    #   &lt;!-- The footer part of this layout --&gt;
+    #   // The footer part of this layout --&gt;
     #
     # And then you have content pages that look like this:
     #
@@ -37,9 +38,9 @@ module ActionController #:nodoc:
     # Not a word about common structures. At rendering time, the content page is computed and then inserted in the layout, 
     # like this:
     #
-    #   &lt;!-- The header part of this layout --&gt;
+    #   // The header part of this layout
     #   hello world
-    #   &lt;!-- The footer part of this layout --&gt;
+    #   // The footer part of this layout --&gt;
     #
     # == Accessing shared variables
     #
@@ -182,7 +183,6 @@ module ActionController #:nodoc:
       private
         def inherited_with_layout(child)
           inherited_without_layout(child)
-          child.send :include, Reloadable
           layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
           child.layout(layout_match) unless layout_list.grep(%r{layouts/#{layout_match}\.[a-z][0-9a-z]*$}).empty?
         end
@@ -235,6 +235,8 @@ module ActionController #:nodoc:
       template_with_options = options.is_a?(Hash)
 
       if apply_layout?(template_with_options, options) &amp;&amp; (layout = pick_layout(template_with_options, options, deprecated_layout))
+        assert_existence_of_template_file(layout)
+
         options = options.merge :layout =&gt; false if template_with_options
         logger.info(&quot;Rendering #{options} within #{layout}&quot;) if logger
 
@@ -248,6 +250,7 @@ module ActionController #:nodoc:
         erase_render_results
         add_variables_to_assigns
         @template.instance_variable_set(&quot;@content_for_layout&quot;, content_for_layout)
+        response.layout = layout
         render_text(@template.render_file(layout, true), deprecated_status)
       else
         render_with_no_layout(options, deprecated_status, &amp;block)
@@ -263,7 +266,7 @@ module ActionController #:nodoc:
 
       def candidate_for_layout?(options)
         (options.has_key?(:layout) &amp;&amp; options[:layout] != false) || 
-        options.values_at(:text, :xml, :file, :inline, :partial, :nothing).compact.empty? &amp;&amp;
+        options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing).compact.empty? &amp;&amp;
         !template_exempt_from_layout?(default_template_name(options[:action] || options[:template]))
       end
 </diff>
      <filename>vendor/rails/actionpack/lib/action_controller/layout.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,11 +4,12 @@ module ActionController
   # backing.
   module Macros
     module AutoComplete #:nodoc:
-      def self.append_features(base) #:nodoc:
-        super
+      def self.included(base) #:nodoc:
         base.extend(ClassMethods)
       end
 
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Example:
       #
       #   # Controller</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/macros/auto_complete.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,11 +1,12 @@
 module ActionController
   module Macros
     module InPlaceEditing #:nodoc:
-      def self.append_features(base) #:nodoc:
-        super
+      def self.included(base) #:nodoc:
         base.extend(ClassMethods)
       end
 
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Example:
       #
       #   # Controller
@@ -23,7 +24,7 @@ module ActionController
           define_method(&quot;set_#{object}_#{attribute}&quot;) do
             @item = object.to_s.camelize.constantize.find(params[:id])
             @item.update_attribute(attribute, params[:value])
-            render :text =&gt; @item.send(attribute)
+            render :text =&gt; @item.send(attribute).to_s
           end
         end
       end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/macros/in_place_editing.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,18 +8,18 @@ module ActionController #:nodoc:
       # Without web-service support, an action which collects the data for displaying a list of people
       # might look something like this:
       #
-      #   def list
+      #   def index
       #     @people = Person.find(:all)
       #   end
       # 
       # Here's the same action, with web-service support baked in:
       # 
-      #   def list
+      #   def index
       #     @people = Person.find(:all)
       # 
-      #     respond_to do |wants|
-      #       wants.html
-      #       wants.xml { render :xml =&gt; @people.to_xml }
+      #     respond_to do |format|
+      #       format.html
+      #       format.xml { render :xml =&gt; @people.to_xml }
       #     end
       #   end
       # 
@@ -30,7 +30,7 @@ module ActionController #:nodoc:
       # Supposing you have an action that adds a new person, optionally creating their company 
       # (by name) if it does not already exist, without web-services, it might look like this:
       # 
-      #   def add
+      #   def create
       #     @company = Company.find_or_create_by_name(params[:company][:name])
       #     @person  = @company.people.create(params[:person])
       # 
@@ -39,15 +39,15 @@ module ActionController #:nodoc:
       # 
       # Here's the same action, with web-service support baked in:
       # 
-      #   def add
+      #   def create
       #     company  = params[:person].delete(:company)
       #     @company = Company.find_or_create_by_name(company[:name])
       #     @person  = @company.people.create(params[:person])
       # 
-      #     respond_to do |wants|
-      #       wants.html { redirect_to(person_list_url) }
-      #       wants.js
-      #       wants.xml  { render :xml =&gt; @person.to_xml(:include =&gt; @company) }
+      #     respond_to do |format|
+      #       format.html { redirect_to(person_list_url) }
+      #       format.js
+      #       format.xml  { render :xml =&gt; @person.to_xml(:include =&gt; @company) }
       #     end
       #   end
       # 
@@ -97,9 +97,8 @@ module ActionController #:nodoc:
       # environment.rb as follows.
       # 
       #   Mime::Type.register &quot;image/jpg&quot;, :jpg
-      # 
       def respond_to(*types, &amp;block)
-        raise ArgumentError, &quot;respond_to takes either types or a block, never bot&quot; unless types.any? ^ block
+        raise ArgumentError, &quot;respond_to takes either types or a block, never both&quot; unless types.any? ^ block
         block ||= lambda { |responder| types.each { |type| responder.send(type) } }
         responder = Responder.new(block.binding)
         block.call(responder)
@@ -108,15 +107,19 @@ module ActionController #:nodoc:
     end
     
     class Responder #:nodoc:
-      DEFAULT_BLOCKS = {
-        :html    =&gt; 'Proc.new { render }',
-        :js      =&gt; 'Proc.new { render :action =&gt; &quot;#{action_name}.rjs&quot; }',
-        :xml     =&gt; 'Proc.new { render :action =&gt; &quot;#{action_name}.rxml&quot; }'
-      }
+      DEFAULT_BLOCKS = [:html, :js, :xml].inject({}) do |blocks, ext|
+        template_extension = (ext == :html ? '' : &quot;.r#{ext}&quot;)
+        blocks.update ext =&gt; %(Proc.new { render :action =&gt; &quot;\#{action_name}#{template_extension}&quot;, :content_type =&gt; Mime::#{ext.to_s.upcase} })
+      end
       
       def initialize(block_binding)
         @block_binding = block_binding
-        @mime_type_priority = eval(&quot;request.accepts&quot;, block_binding)
+        @mime_type_priority = eval(
+          &quot;(params[:format] &amp;&amp; Mime::EXTENSION_LOOKUP[params[:format]]) ? &quot; +
+          &quot;[ Mime::EXTENSION_LOOKUP[params[:format]] ] : request.accepts&quot;, 
+          block_binding
+        )
+
         @order     = []
         @responses = {}
       end
@@ -127,24 +130,33 @@ module ActionController #:nodoc:
         @order &lt;&lt; mime_type
         
         if block_given?
-          @responses[mime_type] = block
+          @responses[mime_type] = Proc.new do
+            eval &quot;response.content_type = '#{mime_type.to_s}'&quot;, @block_binding
+            block.call
+          end
         else
-          @responses[mime_type] = eval(DEFAULT_BLOCKS[mime_type.to_sym], @block_binding)
-        end
-      end
-      
-      for mime_type in %w( all html js xml rss atom yaml )
-        eval &lt;&lt;-EOT
-          def #{mime_type}(&amp;block)
-            custom(Mime::#{mime_type.upcase}, &amp;block)
+          if source = DEFAULT_BLOCKS[mime_type.to_sym]
+            @responses[mime_type] = eval(source, @block_binding)
+          else
+            raise ActionController::RenderError, &quot;Expected a block but none was given for custom mime handler #{mime_type}&quot;
           end
-        EOT
+        end
       end
 
       def any(*args, &amp;block)
         args.each { |type| send(type, &amp;block) }
       end
       
+      def method_missing(symbol, &amp;block)
+        mime_constant = symbol.to_s.upcase
+        
+        if Mime::SET.include?(Mime.const_get(mime_constant))
+          custom(Mime.const_get(mime_constant), &amp;block)
+        else
+          super
+        end
+      end
+      
       def respond
         for priority in @mime_type_priority
           if priority == Mime::ALL</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/mime_responds.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,18 @@
 module Mime
-  class Type #:nodoc:
+  # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
+  #
+  #   class PostsController &lt; ActionController::Base
+  #     def show
+  #       @post = Post.find(params[:id])
+  #
+  #       respond_to do |format|
+  #         format.html
+  #         format.ics { render :text =&gt; post.to_ics, :mime_type =&gt; Mime::Type[&quot;text/calendar&quot;]  }
+  #         format.xml { render :xml =&gt; @people.to_xml }
+  #       end
+  #     end
+  #   end
+  class Type
     # A simple helper class used in parsing the accept header
     class AcceptItem #:nodoc:
       attr_accessor :order, :name, :q
@@ -31,14 +44,20 @@ module Mime
         LOOKUP[string]
       end
 
+      def register(string, symbol, synonyms = [])
+        Mime.send :const_set, symbol.to_s.upcase, Type.new(string, symbol, synonyms)
+        SET &lt;&lt; Mime.send(:const_get, symbol.to_s.upcase)
+        LOOKUP[string] = EXTENSION_LOOKUP[symbol.to_s] = SET.last        
+      end
+
       def parse(accept_header)
         # keep track of creation order to keep the subsequent sort stable
         index = 0
-        list = accept_header.split(/,/).
-          map! { |i| AcceptItem.new(index += 1, *i.split(/;\s*q=/)) }.sort!
+        list = accept_header.split(/,/).map! do |i| 
+          AcceptItem.new(index += 1, *i.split(/;\s*q=/))
+        end.sort!
 
         # Take care of the broken text/xml entry by renaming or deleting it
-  
         text_xml = list.index(&quot;text/xml&quot;)
         app_xml = list.index(&quot;application/xml&quot;)
 
@@ -112,31 +131,70 @@ module Mime
   end
 
   ALL   = Type.new &quot;*/*&quot;, :all
+  TEXT  = Type.new &quot;text/plain&quot;, :text
   HTML  = Type.new &quot;text/html&quot;, :html, %w( application/xhtml+xml )
   JS    = Type.new &quot;text/javascript&quot;, :js, %w( application/javascript application/x-javascript )
+  ICS   = Type.new &quot;text/calendar&quot;, :ics
+  CSV   = Type.new &quot;text/csv&quot;, :csv
   XML   = Type.new &quot;application/xml&quot;, :xml, %w( text/xml application/x-xml )
   RSS   = Type.new &quot;application/rss+xml&quot;, :rss
   ATOM  = Type.new &quot;application/atom+xml&quot;, :atom
   YAML  = Type.new &quot;application/x-yaml&quot;, :yaml, %w( text/yaml )
+  JSON  = Type.new &quot;application/json&quot;, :json, %w( text/x-json )
+
+  SET   = [ ALL, TEXT, HTML, JS, ICS, XML, RSS, ATOM, YAML, JSON ]
 
-  LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) }
+  LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k == &quot;&quot; }
 
   LOOKUP[&quot;*/*&quot;]                      = ALL
 
+  LOOKUP[&quot;text/plain&quot;]               = TEXT
+
   LOOKUP[&quot;text/html&quot;]                = HTML
   LOOKUP[&quot;application/xhtml+xml&quot;]    = HTML
 
-  LOOKUP[&quot;application/xml&quot;]          = XML
-  LOOKUP[&quot;text/xml&quot;]                 = XML
-  LOOKUP[&quot;application/x-xml&quot;]        = XML
-
   LOOKUP[&quot;text/javascript&quot;]          = JS
   LOOKUP[&quot;application/javascript&quot;]   = JS
   LOOKUP[&quot;application/x-javascript&quot;] = JS
 
+  LOOKUP[&quot;text/calendar&quot;]            = ICS
+
+  LOOKUP[&quot;text/csv&quot;]                 = CSV
+
+  LOOKUP[&quot;application/xml&quot;]          = XML
+  LOOKUP[&quot;text/xml&quot;]                 = XML
+  LOOKUP[&quot;application/x-xml&quot;]        = XML
+
   LOOKUP[&quot;text/yaml&quot;]                = YAML
   LOOKUP[&quot;application/x-yaml&quot;]       = YAML
 
   LOOKUP[&quot;application/rss+xml&quot;]      = RSS
   LOOKUP[&quot;application/atom+xml&quot;]     = ATOM
-end
\ No newline at end of file
+
+  LOOKUP[&quot;application/json&quot;]         = JSON
+  LOOKUP[&quot;text/x-json&quot;]              = JSON
+
+
+  EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k == &quot;&quot; }
+
+  EXTENSION_LOOKUP[&quot;html&quot;]  = HTML
+  EXTENSION_LOOKUP[&quot;xhtml&quot;] = HTML
+
+  EXTENSION_LOOKUP[&quot;txt&quot;]   = TEXT
+
+  EXTENSION_LOOKUP[&quot;xml&quot;]   = XML
+
+  EXTENSION_LOOKUP[&quot;js&quot;]    = JS
+
+  EXTENSION_LOOKUP[&quot;ics&quot;]   = ICS
+
+  EXTENSION_LOOKUP[&quot;csv&quot;]   = CSV
+
+  EXTENSION_LOOKUP[&quot;yml&quot;]   = YAML
+  EXTENSION_LOOKUP[&quot;yaml&quot;]  = YAML
+
+  EXTENSION_LOOKUP[&quot;rss&quot;]   = RSS
+  EXTENSION_LOOKUP[&quot;atom&quot;]  = ATOM
+
+  EXTENSION_LOOKUP[&quot;json&quot;]  = JSON
+end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/mime_type.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,10 @@
 module ActionController
   # === Action Pack pagination for Active Record collections
   #
+  # DEPRECATION WARNING: Pagination will be moved to a plugin in Rails 2.0.
+  # Install the classic_pagination plugin for forward compatibility:
+  #   script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination
+  #
   # The Pagination module aids in the process of paging large collections of
   # Active Record objects. It offers macro-style automatic fetching of your
   # model for multiple views, or explicit fetching for single actions. And if
@@ -104,8 +108,7 @@ module ActionController
     # ClassMethods#paginate.
     #
     # +options+ are:
-    # &lt;tt&gt;:singular_name&lt;/tt&gt;:: the singular name to use, if it can't be inferred by
-    #                        singularizing the collection name
+    # &lt;tt&gt;:singular_name&lt;/tt&gt;:: the singular name to use, if it can't be inferred by singularizing the collection name
     # &lt;tt&gt;:class_name&lt;/tt&gt;:: the class name to use, if it can't be inferred by
     #                        camelizing the singular name
     # &lt;tt&gt;:per_page&lt;/tt&gt;::   the maximum number of items to include in a 
@@ -129,6 +132,8 @@ module ActionController
       paginator_and_collection_for(collection_id, options)
     end
 
+    deprecate :paginate =&gt; 'Pagination is moving to a plugin in Rails 2.0: script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination'
+
     # These methods become class methods on any controller 
     module ClassMethods
       # Creates a +before_filter+ which automatically paginates an Active
@@ -147,6 +152,8 @@ module ActionController
           OPTIONS[self][collection_id] = options
         end
       end
+
+      deprecate :paginate =&gt; 'Pagination is moving to a plugin in Rails 2.0: script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination'
     end
 
     def create_paginators_and_retrieve_collections #:nodoc:
@@ -192,7 +199,7 @@ module ActionController
 
     def paginator_and_collection_for(collection_id, options) #:nodoc:
       klass = options[:class_name].constantize
-      page  = @params[options[:parameter]]
+      page  = params[options[:parameter]]
       count = count_collection_for_pagination(klass, options)
       paginator = Paginator.new(self, count, options[:per_page], page)
       collection = find_collection_for_pagination(klass, options, paginator)</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/pagination.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,12 +13,18 @@ module ActionController
       @parameters ||= request_parameters.update(query_parameters).update(path_parameters).with_indifferent_access
     end
 
-    # Returns the HTTP request method as a lowercase symbol (:get, for example)
+    # Returns the HTTP request method as a lowercase symbol (:get, for example). Note, HEAD is returned as :get
+    # since the two are supposedly to be functionaly equivilent for all purposes except that HEAD won't return a response
+    # body (which Rails also takes care of elsewhere).
     def method
-      @request_method ||= @env['REQUEST_METHOD'].downcase.to_sym
+      @request_method ||= (!parameters[:_method].blank? &amp;&amp; @env['REQUEST_METHOD'] == 'POST') ?
+        parameters[:_method].to_s.downcase.to_sym :
+        @env['REQUEST_METHOD'].downcase.to_sym
+      
+      @request_method == :head ? :get : @request_method
     end
 
-    # Is this a GET request?  Equivalent to request.method == :get
+    # Is this a GET (or HEAD) request?  Equivalent to request.method == :get
     def get?
       method == :get
     end
@@ -38,9 +44,10 @@ module ActionController
       method == :delete
     end
 
-    # Is this a HEAD request?  Equivalent to request.method == :head
+    # Is this a HEAD request?  HEAD is mapped as :get for request.method, so here we ask the 
+    # REQUEST_METHOD header directly. Thus, for head, both get? and head? will return true.
     def head?
-      method == :head
+      @env['REQUEST_METHOD'].downcase.to_sym == :head
     end
 
     # Determine whether the body of a HTTP call is URL-encoded (default)
@@ -128,19 +135,21 @@ module ActionController
       @env['RAW_POST_DATA']
     end
 
-    # Returns the request URI correctly, taking into account the idiosyncracies
-    # of the various servers.
+    # Return the request URI, accounting for server idiosyncracies.
+    # WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
     def request_uri
       if uri = @env['REQUEST_URI']
-        (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri # Remove domain, which webrick puts into the request_uri.
-      else  # REQUEST_URI is blank under IIS - get this from PATH_INFO and SCRIPT_NAME
+        # Remove domain, which webrick puts into the request_uri.
+        (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
+      else
+        # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
         script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
         uri = @env['PATH_INFO']
         uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
         unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
           uri &lt;&lt; '?' &lt;&lt; env_qs
         end
-        uri
+        @env['REQUEST_URI'] = uri
       end
     end
 
@@ -159,11 +168,10 @@ module ActionController
       path = (uri = request_uri) ? uri.split('?').first : ''
 
       # Cut off the path to the installation directory if given
-      root = relative_url_root
-      path[0, root.length] = '' if root
-      path || ''
+      path.sub!(%r/^#{relative_url_root}/, '')
+      path || ''      
     end
-
+    
     # Returns the path minus the web server relative installation directory.
     # This can be set with the environment variable RAILS_RELATIVE_URL_ROOT.
     # It can be automatically extracted for Apache setups. If the server is not</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/request.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,12 +6,10 @@ module ActionController #:nodoc:
   #
   # You can tailor the rescuing behavior and appearance by overwriting the following two stub methods.
   module Rescue
-    def self.append_features(base) #:nodoc:
-      super
+    def self.included(base) #:nodoc:
       base.extend(ClassMethods)
       base.class_eval do
-        alias_method :perform_action_without_rescue, :perform_action
-        alias_method :perform_action, :perform_action_with_rescue
+        alias_method_chain :perform_action, :rescue
       end
     end
 
@@ -36,23 +34,26 @@ module ActionController #:nodoc:
 
       # Overwrite to implement custom logging of errors. By default logs as fatal.
       def log_error(exception) #:doc:
-        if ActionView::TemplateError === exception
-          logger.fatal(exception.to_s)
-        else
-          logger.fatal(
-            &quot;\n\n#{exception.class} (#{exception.message}):\n    &quot; + 
-            clean_backtrace(exception).join(&quot;\n    &quot;) + 
-            &quot;\n\n&quot;
-          )
+        ActiveSupport::Deprecation.silence do
+          if ActionView::TemplateError === exception
+            logger.fatal(exception.to_s)
+          else
+            logger.fatal(
+              &quot;\n\n#{exception.class} (#{exception.message}):\n    &quot; +
+              clean_backtrace(exception).join(&quot;\n    &quot;) +
+              &quot;\n\n&quot;
+            )
+          end
         end
       end
 
       # Overwrite to implement public exception handling (for requests answering false to &lt;tt&gt;local_request?&lt;/tt&gt;).
       def rescue_action_in_public(exception) #:doc:
         case exception
-          when RoutingError, UnknownAction then
+          when RoutingError, UnknownAction
             render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), &quot;404 Not Found&quot;)
-          else render_text &quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;Application error (Rails)&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;&quot;
+          else
+            render_text(IO.read(File.join(RAILS_ROOT, 'public', '500.html')), &quot;500 Internal Error&quot;)
         end
       end
 
@@ -60,19 +61,19 @@ module ActionController #:nodoc:
       # the remote IP being 127.0.0.1. For example, this could include the IP of the developer machine when debugging
       # remotely.
       def local_request? #:doc:
-        [@request.remote_addr, @request.remote_ip] == [&quot;127.0.0.1&quot;] * 2
+        [request.remote_addr, request.remote_ip] == [&quot;127.0.0.1&quot;] * 2
       end
 
       # Renders a detailed diagnostics screen on action exceptions. 
       def rescue_action_locally(exception)
         add_variables_to_assigns
         @template.instance_variable_set(&quot;@exception&quot;, exception)
-        @template.instance_variable_set(&quot;@rescues_path&quot;, File.dirname(__FILE__) + &quot;/templates/rescues/&quot;)    
+        @template.instance_variable_set(&quot;@rescues_path&quot;, File.dirname(rescues_path(&quot;stub&quot;)))    
         @template.send(:assign_variables_from_controller)
 
         @template.instance_variable_set(&quot;@contents&quot;, @template.render_file(template_path_for_local_rescue(exception), false))
     
-        @headers[&quot;Content-Type&quot;] = &quot;text/html&quot;
+        response.content_type = Mime::HTML
         render_file(rescues_path(&quot;layout&quot;), response_code_for_rescue(exception))
       end
     
@@ -80,8 +81,8 @@ module ActionController #:nodoc:
       def perform_action_with_rescue #:nodoc:
         begin
           perform_action_without_rescue
-        rescue Object =&gt; exception
-          if defined?(Breakpoint) &amp;&amp; @params[&quot;BP-RETRY&quot;]
+        rescue Exception =&gt; exception  # errors from action performed
+          if defined?(Breakpoint) &amp;&amp; params[&quot;BP-RETRY&quot;]
             msg = exception.backtrace.first
             if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
               origin_file, origin_line = md[1], md[2].to_i
@@ -89,7 +90,7 @@ module ActionController #:nodoc:
               set_trace_func(lambda do |type, file, line, method, context, klass|
                 if file == origin_file and line == origin_line then
                   set_trace_func(nil)
-                  @params[&quot;BP-RETRY&quot;] = false
+                  params[&quot;BP-RETRY&quot;] = false
 
                   callstack = caller
                   callstack.slice!(0) if callstack.first[&quot;rescue.rb&quot;]
@@ -127,8 +128,10 @@ module ActionController #:nodoc:
       
       def response_code_for_rescue(exception)
         case exception
-          when UnknownAction, RoutingError then &quot;404 Page Not Found&quot;
-          else &quot;500 Internal Error&quot;
+          when UnknownAction, RoutingError 
+            &quot;404 Page Not Found&quot;
+          else
+            &quot;500 Internal Error&quot;
         end
       end
       </diff>
      <filename>vendor/rails/actionpack/lib/action_controller/rescue.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,33 @@
 module ActionController
   class AbstractResponse #:nodoc:
     DEFAULT_HEADERS = { &quot;Cache-Control&quot; =&gt; &quot;no-cache&quot; }
-    attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params
+    attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params, :layout
 
     def initialize
       @body, @headers, @session, @assigns = &quot;&quot;, DEFAULT_HEADERS.merge(&quot;cookie&quot; =&gt; []), [], []
     end
 
+    def content_type=(mime_type)
+      @headers[&quot;Content-Type&quot;] = charset ? &quot;#{mime_type}; charset=#{charset}&quot; : mime_type
+    end
+    
+    def content_type
+      content_type = String(@headers[&quot;Content-Type&quot;]).split(&quot;;&quot;)[0]
+      content_type.blank? ? nil : content_type
+    end
+    
+    def charset=(encoding)
+      @headers[&quot;Content-Type&quot;] = &quot;#{content_type || &quot;text/html&quot;}; charset=#{encoding}&quot;
+    end
+    
+    def charset
+      charset = String(@headers[&quot;Content-Type&quot;]).split(&quot;;&quot;)[1]
+      charset.blank? ? nil : charset.strip.split(&quot;=&quot;)[1]
+    end
+
     def redirect(to_url, permanently = false)
       @headers[&quot;Status&quot;]   = &quot;302 Found&quot; unless @headers[&quot;Status&quot;] == &quot;301 Moved Permanently&quot;
-      @headers[&quot;location&quot;] = to_url
+      @headers[&quot;Location&quot;] = to_url
 
       @body = &quot;&lt;html&gt;&lt;body&gt;You are being &lt;a href=\&quot;#{to_url}\&quot;&gt;redirected&lt;/a&gt;.&lt;/body&gt;&lt;/html&gt;&quot;
     end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/response.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,716 +1,1373 @@
+require 'cgi'
+
+class Object
+  def to_param
+    to_s
+  end
+end
+
+class TrueClass
+  def to_param
+    self
+  end
+end
+
+class FalseClass
+  def to_param
+    self
+  end
+end
+
+class NilClass
+  def to_param
+    self
+  end
+end
+
+class Regexp #:nodoc:
+  def number_of_captures
+    Regexp.new(&quot;|#{source}&quot;).match('').captures.length
+  end
+  
+  class &lt;&lt; self
+    def optionalize(pattern)
+      case unoptionalize(pattern)
+        when /\A(.|\(.*\))\Z/ then &quot;#{pattern}?&quot;
+        else &quot;(?:#{pattern})?&quot;
+      end
+    end
+    
+    def unoptionalize(pattern)
+      [/\A\(\?:(.*)\)\?\Z/, /\A(.|\(.*\))\?\Z/].each do |regexp|
+        return $1 if regexp =~ pattern
+      end
+      return pattern
+    end
+  end
+end
+
 module ActionController
-  module Routing #:nodoc:
+  # == Routing 
+  #
+  # The routing module provides URL rewriting in native Ruby. It's a way to
+  # redirect incoming requests to controllers and actions. This replaces
+  # mod_rewrite rules. Best of all Rails' Routing works with any web server. 
+  # Routes are defined in routes.rb in your RAILS_ROOT/config directory.
+  #
+  # Consider the following route, installed by Rails when you generate your 
+  # application:
+  #
+  #   map.connect ':controller/:action/:id'
+  #
+  # This route states that it expects requests to consist of a 
+  # :controller followed by an :action that in turns is fed by some :id 
+  #
+  # Suppose you get an incoming request for &lt;tt&gt;/blog/edit/22&lt;/tt&gt;, you'll end up 
+  # with:
+  #
+  #   params = { :controller =&gt; 'blog',
+  #              :action     =&gt; 'edit' 
+  #              :id         =&gt; '22'
+  #           }
+  #
+  # Think of creating routes as drawing a map for your requests. The map tells 
+  # them where to go based on some predefined pattern:
+  #
+  #  ActionController::Routing::Routes.draw do |map|
+  #   Pattern 1 tells some request to go to one place
+  #   Pattern 2 tell them to go to another
+  #   ...
+  #  end
+  #
+  # The following symbols are special:
+  #
+  #   :controller maps to your controller name
+  #   :action     maps to an action with your controllers
+  #   
+  # Other names simply map to a parameter as in the case of +:id+.
+  #    
+  # == Route priority
+  #
+  # Not all routes are created equally. Routes have priority defined by the 
+  # order of appearance of the routes in the routes.rb file. The priority goes
+  # from top to bottom. The last route in that file is at the lowest priority
+  # will be applied last. If no route matches, 404 is returned.
+  #
+  # Within blocks, the empty pattern goes first i.e. is at the highest priority.
+  # In practice this works out nicely:
+  #
+  #  ActionController::Routing::Routes.draw do |map| 
+  #    map.with_options :controller =&gt; 'blog' do |blog|
+  #      blog.show    '',  :action =&gt; 'list'
+  #    end
+  #    map.connect ':controller/:action/:view 
+  #  end
+  #
+  # In this case, invoking blog controller (with an URL like '/blog/') 
+  # without parameters will activate the 'list' action by default.
+  #
+  # == Defaults routes and default parameters
+  #
+  # Setting a default route is straightforward in Rails because by appending a
+  # Hash to the end of your mapping you can set default parameters.
+  #
+  # Example:
+  #  ActionController::Routing:Routes.draw do |map|
+  #    map.connect ':controller/:action/:id', :controller =&gt; 'blog'
+  #  end
+  #
+  # This sets up  +blog+ as the default controller if no other is specified. 
+  # This means visiting '/' would invoke the blog controller.
+  #
+  # More formally, you can define defaults in a route with the +:defaults+ key.
+  #   
+  #   map.connect ':controller/:id/:action', :action =&gt; 'show', :defaults =&gt; { :page =&gt; 'Dashboard' }
+  #
+  # == Named routes
+  #
+  # Routes can be named with the syntax &lt;tt&gt;map.name_of_route options&lt;/tt&gt;,
+  # allowing for easy reference within your source as +name_of_route_url+
+  # for the full URL and +name_of_route_path+ for the URI path.
+  #
+  # Example:
+  #   # In routes.rb
+  #   map.login 'login', :controller =&gt; 'accounts', :action =&gt; 'login'
+  #
+  #   # With render, redirect_to, tests, etc.
+  #   redirect_to login_url
+  #
+  # Arguments can be passed as well.
+  #
+  #   redirect_to show_item_path(:id =&gt; 25)
+  #
+  # Use &lt;tt&gt;map.root&lt;/tt&gt; as a shorthand to name a route for the root path &quot;&quot;
+  #
+  #   # In routes.rb
+  #   map.root :controller =&gt; 'blogs'
+  #
+  #   # would recognize http://www.example.com/ as
+  #   params = { :controller =&gt; 'blogs', :action =&gt; 'index' }
+  #
+  #   # and provide these named routes
+  #   root_url   # =&gt; 'http://www.example.com/'
+  #   root_path  # =&gt; ''
+  #
+  # Note: when using +with_options+, the route is simply named after the
+  # method you call on the block parameter rather than map.
+  #
+  #   # In routes.rb
+  #   map.with_options :controller =&gt; 'blog' do |blog|
+  #     blog.show    '',            :action  =&gt; 'list'
+  #     blog.delete  'delete/:id',  :action  =&gt; 'delete',
+  #     blog.edit    'edit/:id',    :action  =&gt; 'edit'
+  #   end
+  #
+  #   # provides named routes for show, delete, and edit
+  #   link_to @article.title, show_path(:id =&gt; @article.id) 
+  #
+  # == Pretty URLs
+  #
+  # Routes can generate pretty URLs. For example:
+  #
+  #  map.connect 'articles/:year/:month/:day',
+  #              :controller =&gt; 'articles', 
+  #              :action     =&gt; 'find_by_date',
+  #              :year       =&gt; /\d{4}/,
+  #              :month =&gt; /\d{1,2}/, 
+  #              :day   =&gt; /\d{1,2}/
+  #  
+  #  # Using the route above, the url below maps to:
+  #  # params = {:year =&gt; '2005', :month =&gt; '11', :day =&gt; '06'}
+  #  # http://localhost:3000/articles/2005/11/06
+  #
+  # == Regular Expressions and parameters
+  # You can specify a reqular expression to define a format for a parameter.
+  #
+  #  map.geocode 'geocode/:postalcode', :controller =&gt; 'geocode',
+  #              :action =&gt; 'show', :postalcode =&gt; /\d{5}(-\d{4})?/
+  #
+  # or  more formally:
+  #
+  #   map.geocode 'geocode/:postalcode', :controller =&gt; 'geocode', 
+  #                      :action =&gt; 'show', 
+  #                      :requirements { :postalcode =&gt; /\d{5}(-\d{4})?/ }
+  #
+  # == Route globbing
+  #
+  # Specifying &lt;tt&gt;*[string]&lt;/tt&gt; as part of a rule like :
+  #
+  #  map.connect '*path' , :controller =&gt; 'blog' , :action =&gt; 'unrecognized?'
+  #
+  # will glob all remaining parts of the route that were not recognized earlier. This idiom must appear at the end of the path. The globbed values are in &lt;tt&gt;params[:path]&lt;/tt&gt; in this case.  
+  #
+  # == Reloading routes
+  #
+  # You can reload routes if you feel you must:
+  #
+  #  Action::Controller::Routes.reload
+  #
+  # This will clear all named routes and reload routes.rb
+  #
+  # == Testing Routes
+  #
+  # The two main methods for testing your routes:
+  #
+  # === +assert_routing+
+  # 
+  #  def test_movie_route_properly_splits
+  #   opts = {:controller =&gt; &quot;plugin&quot;, :action =&gt; &quot;checkout&quot;, :id =&gt; &quot;2&quot;}
+  #   assert_routing &quot;plugin/checkout/2&quot;, opts
+  #  end
+  #  
+  # +assert_routing+ lets you test whether or not the route properly resolves into options.
+  #
+  # === +assert_recognizes+
+  #
+  #  def test_route_has_options
+  #   opts = {:controller =&gt; &quot;plugin&quot;, :action =&gt; &quot;show&quot;, :id =&gt; &quot;12&quot;}
+  #   assert_recognizes opts, &quot;/plugins/show/12&quot; 
+  #  end
+  # 
+  # Note the subtle difference between the two: +assert_routing+ tests that
+  # an URL fits options while +assert_recognizes+ tests that an URL
+  # breaks into parameters properly.
+  #
+  # In tests you can simply pass the URL or named route to +get+ or +post+.
+  #
+  #  def send_to_jail
+  #    get '/jail'
+  #    assert_response :success
+  #    assert_template &quot;jail/front&quot;
+  #  end
+  #
+  #  def goes_to_login
+  #    get login_url
+  #    #...
+  #  end
+  #
+  module Routing
+    SEPARATORS = %w( / ; . , ? )
+
+    # The root paths which may contain controller files
+    mattr_accessor :controller_paths
+    self.controller_paths = []
+
     class &lt;&lt; self
-      def expiry_hash(options, recall)
-        k = v = nil
-        expire_on = {}
-        options.each {|k, v| expire_on[k] = ((rcv = recall[k]) &amp;&amp; (rcv != v))}
-        expire_on
+      def with_controllers(names)
+        prior_controllers = @possible_controllers
+        use_controllers! names
+        yield
+      ensure
+        use_controllers! prior_controllers
+      end
+
+      def normalize_paths(paths)
+        # do the hokey-pokey of path normalization...
+        paths = paths.collect do |path|
+          path = path.
+            gsub(&quot;//&quot;, &quot;/&quot;).           # replace double / chars with a single
+            gsub(&quot;\\\\&quot;, &quot;\\&quot;).        # replace double \ chars with a single
+            gsub(%r{(.)[\\/]$}, '\1')  # drop final / or \ if path ends with it
+
+          # eliminate .. paths where possible
+          re = %r{\w+[/\\]\.\.[/\\]}
+          path.gsub!(%r{\w+[/\\]\.\.[/\\]}, &quot;&quot;) while path.match(re)
+          path
+        end
+
+        # start with longest path, first
+        paths = paths.uniq.sort_by { |path| - path.length }
       end
 
-      def extract_parameter_value(parameter) #:nodoc:
-        CGI.escape((parameter.respond_to?(:to_param) ? parameter.to_param : parameter).to_s) 
+      def possible_controllers
+        unless @possible_controllers
+          @possible_controllers = []
+        
+          paths = controller_paths.select { |path| File.directory?(path) &amp;&amp; path != &quot;.&quot; }
+
+          seen_paths = Hash.new {|h, k| h[k] = true; false}
+          normalize_paths(paths).each do |load_path|
+            Dir[&quot;#{load_path}/**/*_controller.rb&quot;].collect do |path|
+              next if seen_paths[path.gsub(%r{^\.[/\\]}, &quot;&quot;)]
+              
+              controller_name = path[(load_path.length + 1)..-1]
+              
+              controller_name.gsub!(/_controller\.rb\Z/, '')
+              @possible_controllers &lt;&lt; controller_name
+            end
+          end
+
+          # remove duplicates
+          @possible_controllers.uniq!
+        end
+        @possible_controllers
       end
+
+      def use_controllers!(controller_names)
+        @possible_controllers = controller_names
+      end
+
       def controller_relative_to(controller, previous)
         if controller.nil?           then previous
         elsif controller[0] == ?/    then controller[1..-1]
         elsif %r{^(.*)/} =~ previous then &quot;#{$1}/#{controller}&quot;
         else controller
-        end
+        end     
+      end     
+    end
+  
+    class Route #:nodoc:
+      attr_accessor :segments, :requirements, :conditions
+      
+      def initialize
+        @segments = []
+        @requirements = {}
+        @conditions = {}
       end
+  
+      # Write and compile a +generate+ method for this Route.
+      def write_generation
+        # Build the main body of the generation
+        body = &quot;expired = false\n#{generation_extraction}\n#{generation_structure}&quot;
+    
+        # If we have conditions that must be tested first, nest the body inside an if
+        body = &quot;if #{generation_requirements}\n#{body}\nend&quot; if generation_requirements
+        args = &quot;options, hash, expire_on = {}&quot;
+
+        # Nest the body inside of a def block, and then compile it.
+        raw_method = method_decl = &quot;def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend&quot;
+        instance_eval method_decl, &quot;generated code (#{__FILE__}:#{__LINE__})&quot;
 
-      def treat_hash(hash, keys_to_delete = [])
-        k = v = nil
-        hash.each do |k, v|
-          if v then hash[k] = (v.respond_to? :to_param) ? v.to_param.to_s : v.to_s
+        # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
+        # are the same as the keys that were recalled from the previous request. Thus,
+        # we can use the expire_on.keys to determine which keys ought to be used to build
+        # the query string. (Never use keys from the recalled request when building the
+        # query string.)
+
+        method_decl = &quot;def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend&quot;
+        instance_eval method_decl, &quot;generated code (#{__FILE__}:#{__LINE__})&quot;
+
+        method_decl = &quot;def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend&quot;
+        instance_eval method_decl, &quot;generated code (#{__FILE__}:#{__LINE__})&quot;
+        raw_method
+      end
+  
+      # Build several lines of code that extract values from the options hash. If any
+      # of the values are missing or rejected then a return will be executed.
+      def generation_extraction
+        segments.collect do |segment|
+          segment.extraction_code
+        end.compact * &quot;\n&quot;
+      end
+  
+      # Produce a condition expression that will check the requirements of this route
+      # upon generation.
+      def generation_requirements
+        requirement_conditions = requirements.collect do |key, req|
+          if req.is_a? Regexp
+            value_regexp = Regexp.new &quot;\\A#{req.source}\\Z&quot;
+            &quot;hash[:#{key}] &amp;&amp; #{value_regexp.inspect} =~ options[:#{key}]&quot;
           else
-            hash.delete k
-            keys_to_delete &lt;&lt; k
+            &quot;hash[:#{key}] == #{req.inspect}&quot;
           end
         end
-        hash
+        requirement_conditions * ' &amp;&amp; ' unless requirement_conditions.empty?
       end
-      
-      def test_condition(expression, condition)
-        case condition
-          when String then &quot;(#{expression} == #{condition.inspect})&quot;
-          when Regexp then
-            condition = Regexp.new(&quot;^#{condition.source}$&quot;) unless /^\^.*\$$/ =~ condition.source 
-            &quot;(#{condition.inspect} =~ #{expression})&quot;
-          when Array then
-            conds = condition.collect do |condition|
-              cond = test_condition(expression, condition)
-              (cond[0, 1] == '(' &amp;&amp; cond[-1, 1] == ')') ? cond : &quot;(#{cond})&quot;
-            end
-            &quot;(#{conds.join(' || ')})&quot;
-          when true then expression
-          when nil then &quot;! #{expression}&quot;
-          else
-            raise ArgumentError, &quot;Valid criteria are strings, regular expressions, true, or nil&quot;
-        end
+      def generation_structure
+        segments.last.string_structure segments[0..-2]
       end
-    end
-
-    class Component #:nodoc:
-      def dynamic?()  false end
-      def optional?() false end
-
-      def key() nil end
   
-      def self.new(string, *args)
-        return super(string, *args) unless self == Component
-        case string
-          when ':controller' then ControllerComponent.new(:controller, *args)
-          when /^:(\w+)$/    then DynamicComponent.new($1, *args)
-          when /^\*(\w+)$/   then PathComponent.new($1, *args)
-          else StaticComponent.new(string, *args)
-        end
-      end 
-    end
+      # Write and compile a +recognize+ method for this Route.
+      def write_recognition
+        # Create an if structure to extract the params from a match if it occurs.
+        body = &quot;params = parameter_shell.dup\n#{recognition_extraction * &quot;\n&quot;}\nparams&quot;
+        body = &quot;if #{recognition_conditions.join(&quot; &amp;&amp; &quot;)}\n#{body}\nend&quot;
+    
+        # Build the method declaration and compile it
+        method_decl = &quot;def recognize(path, env={})\n#{body}\nend&quot;
+        instance_eval method_decl, &quot;generated code (#{__FILE__}:#{__LINE__})&quot;
+        method_decl
+      end
 
-    class StaticComponent &lt; Component #:nodoc:
-      attr_reader :value
-  
-      def initialize(value)
-        @value = value
+      # Plugins may override this method to add other conditions, like checks on
+      # host, subdomain, and so forth. Note that changes here only affect route
+      # recognition, not generation.
+      def recognition_conditions
+        result = [&quot;(match = #{Regexp.new(recognition_pattern).inspect}.match(path))&quot;]
+        result &lt;&lt; &quot;conditions[:method] === env[:method]&quot; if conditions[:method]
+        result
       end
 
-      def write_recognition(g)
-        g.if_next_matches(value) do |gp|
-          gp.move_forward {|gpp| gpp.continue}
+      # Build the regular expression pattern that will match this route.
+      def recognition_pattern(wrap = true)
+        pattern = ''
+        segments.reverse_each do |segment|
+          pattern = segment.build_pattern pattern
         end
+        wrap ? (&quot;\\A&quot; + pattern + &quot;\\Z&quot;) : pattern
       end
-
-      def write_generation(g)
-        g.add_segment(value) {|gp| gp.continue }
+      
+      # Write the code to extract the parameters from a matched route.
+      def recognition_extraction
+        next_capture = 1
+        extraction = segments.collect do |segment|
+          x = segment.match_extraction next_capture
+          next_capture += Regexp.new(segment.regexp_chunk).number_of_captures
+          x
+        end
+        extraction.compact
       end
-    end
-
-    class DynamicComponent &lt; Component #:nodoc:
-      attr_reader :key, :default
-      attr_accessor :condition
   
-      def dynamic?()  true      end
-      def optional?() @optional end
-
-      def default=(default)
-        @optional = true
-        @default = default
-      end
-    
-      def initialize(key, options = {})
-        @key = key.to_sym
-        @optional = false
-        default, @condition = options[:default], options[:condition]
-        self.default = default if options.key?(:default)
+      # Write the real generation implementation and then resend the message.
+      def generate(options, hash, expire_on = {})
+        write_generation
+        generate options, hash, expire_on
       end
 
-      def default_check(g)
-        presence = &quot;#{g.hash_value(key, !! default)}&quot;
-        if default
-           &quot;!(#{presence} &amp;&amp; #{g.hash_value(key, false)} != #{default.to_s.inspect})&quot;
-        else
-          &quot;! #{presence}&quot;
-        end
+      def generate_extras(options, hash, expire_on = {})
+        write_generation
+        generate_extras options, hash, expire_on
       end
-  
-      def write_generation(g)
-        wrote_dropout = write_dropout_generation(g)
-        write_continue_generation(g, wrote_dropout)
+
+      # Generate the query string with any extra keys in the hash and append
+      # it to the given path, returning the new path.
+      def append_query_string(path, hash, query_keys=nil)
+        return nil unless path
+        query_keys ||= extra_keys(hash)
+        &quot;#{path}#{build_query_string(hash, query_keys)}&quot;
       end
 
-      def write_dropout_generation(g)
-        return false unless optional? &amp;&amp; g.after.all? {|c| c.optional?}
-    
-        check = [default_check(g)]
-        gp = g.dup # Use another generator to write the conditions after the first &amp;&amp;
-        # We do this to ensure that the generator will not assume x_value is set. It will
-        # not be set if it follows a false condition -- for example, false &amp;&amp; (x = 2)
-        
-        check += gp.after.map {|c| c.default_check gp}
-        gp.if(check.join(' &amp;&amp; ')) { gp.finish } # If this condition is met, we stop here
-        true 
+      # Determine which keys in the given hash are &quot;extra&quot;. Extra keys are
+      # those that were not used to generate a particular route. The extra
+      # keys also do not include those recalled from the prior request, nor
+      # do they include any keys that were implied in the route (like a
+      # :controller that is required, but not explicitly used in the text of
+      # the route.)
+      def extra_keys(hash, recall={})
+        (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
       end
 
-      def write_continue_generation(g, use_else)
-        test  = Routing.test_condition(g.hash_value(key, true, default), condition || true)
-        check = (use_else &amp;&amp; condition.nil? &amp;&amp; default) ? [:else] : [use_else ? :elsif : :if, test]
-    
-        g.send(*check) do |gp|
-          gp.expire_for_keys(key) unless gp.after.empty?
-          add_segments_to(gp) {|gpp| gpp.continue}
+      # Build a query string from the keys of the given hash. If +only_keys+
+      # is given (as an array), only the keys indicated will be used to build
+      # the query string. The query string will correctly build array parameter
+      # values.
+      def build_query_string(hash, only_keys = nil)
+        elements = []
+        
+        (only_keys || hash.keys).each do |key|
+          if value = hash[key]
+            elements &lt;&lt; value.to_query(key)
+          end
         end
+        elements.empty? ? '' : &quot;?#{elements.sort * '&amp;'}&quot;
       end
 
-      def add_segments_to(g)
-        g.add_segment(%(\#{CGI.escape(#{g.hash_value(key, true, default)})})) {|gp| yield gp}
+      # Write the real recognition implementation and then resend the message.
+      def recognize(path, environment={})
+        write_recognition
+        recognize path, environment
       end
   
-      def recognition_check(g)
-        test_type = [true, nil].include?(condition) ? :presence : :constraint
-    
-        prefix = condition.is_a?(Regexp) ? &quot;#{g.next_segment(true)} &amp;&amp; &quot; : ''
-        check = prefix + Routing.test_condition(g.next_segment(true), condition || true)
-    
-        g.if(check) {|gp| yield gp, test_type}
+      # A route's parameter shell contains parameter values that are not in the
+      # route's path, but should be placed in the recognized hash.
+      # 
+      # For example, +{:controller =&gt; 'pages', :action =&gt; 'show'} is the shell for the route:
+      # 
+      #   map.connect '/page/:id', :controller =&gt; 'pages', :action =&gt; 'show', :id =&gt; /\d+/
+      # 
+      def parameter_shell
+        @parameter_shell ||= returning({}) do |shell|
+          requirements.each do |key, requirement|
+            shell[key] = requirement unless requirement.is_a? Regexp
+          end
+        end
       end
   
-      def write_recognition(g)
-        test_type = nil
-        recognition_check(g) do |gp, test_type|
-          assign_result(gp) {|gpp| gpp.continue}
+      # Return an array containing all the keys that are used in this route. This
+      # includes keys that appear inside the path, and keys that have requirements
+      # placed upon them.
+      def significant_keys
+        @significant_keys ||= returning [] do |sk|
+          segments.each { |segment| sk &lt;&lt; segment.key if segment.respond_to? :key }
+          sk.concat requirements.keys
+          sk.uniq!
         end
-    
-        if optional? &amp;&amp; g.after.all? {|c| c.optional?}
-          call = (test_type == :presence) ? [:else] : [:elsif, &quot;! #{g.next_segment(true)}&quot;]
-       
-          g.send(*call) do |gp|
-            assign_default(gp)
-            gp.after.each {|c| c.assign_default(gp)}
-            gp.finish(false)
+      end
+
+      # Return a hash of key/value pairs representing the keys in the route that
+      # have defaults, or which are specified by non-regexp requirements.
+      def defaults
+        @defaults ||= returning({}) do |hash|
+          segments.each do |segment|
+            next unless segment.respond_to? :default
+            hash[segment.key] = segment.default unless segment.default.nil?
+          end
+          requirements.each do |key,req|
+            next if Regexp === req || req.nil?
+            hash[key] = req
           end
         end
       end
+  
+      def matches_controller_and_action?(controller, action)
+        unless @matching_prepared
+          @controller_requirement = requirement_for(:controller)
+          @action_requirement = requirement_for(:action)
+          @matching_prepared = true
+        end
 
-      def assign_result(g, with_default = false)
-        g.result key, &quot;CGI.unescape(#{g.next_segment(true, with_default ? default : nil)})&quot;
-        g.move_forward {|gp| yield gp}
+        (@controller_requirement.nil? || @controller_requirement === controller) &amp;&amp;
+        (@action_requirement.nil? || @action_requirement === action)
       end
 
-      def assign_default(g)
-        g.constant_result key, default unless default.nil?
+      def to_s
+        @to_s ||= begin
+          segs = segments.inject(&quot;&quot;) { |str,s| str &lt;&lt; s.to_s }
+          &quot;%-6s %-40s %s&quot; % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect]
+        end
+      end
+  
+    protected
+      def requirement_for(key)
+        return requirements[key] if requirements.key? key
+        segments.each do |segment|
+          return segment.regexp if segment.respond_to?(:key) &amp;&amp; segment.key == key
+        end
+        nil
       end
+  
     end
 
-    class ControllerComponent &lt; DynamicComponent #:nodoc:
-      def key() :controller end
+    class Segment #:nodoc:
+      attr_accessor :is_optional
+      alias_method :optional?, :is_optional
 
-      def add_segments_to(g)
-        g.add_segment(%(\#{#{g.hash_value(key, true, default)}})) {|gp| yield gp}
+      def initialize
+        self.is_optional = false
       end
-    
-      def recognition_check(g)
-        g &lt;&lt; &quot;controller_result = ::ActionController::Routing::ControllerComponent.traverse_to_controller(#{g.path_name}, #{g.index_name})&quot; 
-        g.if('controller_result') do |gp|
-          gp &lt;&lt; 'controller_value, segments_to_controller = controller_result'
-          if condition
-            gp &lt;&lt; &quot;controller_path = #{gp.path_name}[#{gp.index_name},segments_to_controller].join('/')&quot;
-            gp.if(Routing.test_condition(&quot;controller_path&quot;, condition)) do |gpp|
-              gpp.move_forward('segments_to_controller') {|gppp| yield gppp, :constraint}
-            end
-          else
-            gp.move_forward('segments_to_controller') {|gpp| yield gpp, :constraint}
-          end
+
+      def extraction_code
+        nil
+      end
+  
+      # Continue generating string for the prior segments.
+      def continue_string_structure(prior_segments)
+        if prior_segments.empty?
+          interpolation_statement(prior_segments)
+        else
+          new_priors = prior_segments[0..-2]
+          prior_segments.last.string_structure(new_priors)
         end
       end
-
-      def assign_result(g)
-        g.result key, 'controller_value'
-        yield g
+  
+      # Return a string interpolation statement for this segment and those before it.
+      def interpolation_statement(prior_segments)
+        chunks = prior_segments.collect { |s| s.interpolation_chunk }
+        chunks &lt;&lt; interpolation_chunk
+        &quot;\&quot;#{chunks * ''}\&quot;#{all_optionals_available_condition(prior_segments)}&quot;
       end
-
-      def assign_default(g)
-        ControllerComponent.assign_controller(g, default)
+  
+      def string_structure(prior_segments)
+        optional? ? continue_string_structure(prior_segments) : interpolation_statement(prior_segments)
       end
   
-      class &lt;&lt; self
-        def assign_controller(g, controller)
-          expr = &quot;::#{controller.split('/').collect {|c| c.camelize}.join('::')}Controller&quot;
-          g.result :controller, expr, true
-        end
+      # Return an if condition that is true if all the prior segments can be generated.
+      # If there are no optional segments before this one, then nil is returned.
+      def all_optionals_available_condition(prior_segments)
+        optional_locals = prior_segments.collect { |s| s.local_name if s.optional? &amp;&amp; s.respond_to?(:local_name) }.compact
+        optional_locals.empty? ? nil : &quot; if #{optional_locals * ' &amp;&amp; '}&quot;
+      end
+  
+      # Recognition
+  
+      def match_extraction(next_capture)
+        nil
+      end
+  
+      # Warning
+  
+      # Returns true if this segment is optional? because of a default. If so, then
+      # no warning will be emitted regarding this segment.
+      def optionality_implied?
+        false
+      end
+    end
 
-        def traverse_to_controller(segments, start_at = 0)
-          mod = ::Object
-          length = segments.length
-          index = start_at
-          mod_name = controller_name = segment = nil
-          while index &lt; length
-            return nil unless /\A[A-Za-z][A-Za-z\d_]*\Z/ =~ (segment = segments[index])
-            index += 1
-            
-            mod_name = segment.camelize
-            controller_name = &quot;#{mod_name}Controller&quot;
-            path_suffix = File.join(segments[start_at..(index - 1)])
-            next_mod = nil
-            
-            # If the controller is already present, or if we load it, return it.
-            if mod.const_defined?(controller_name) || attempt_load(mod, controller_name, path_suffix + &quot;_controller&quot;) == :defined
-              controller = mod.const_get(controller_name)
-              return nil unless controller.is_a?(Class) &amp;&amp; controller.ancestors.include?(ActionController::Base) # it's not really a controller?
-              return [controller, (index - start_at)]
-            end
-            
-            # No controller? Look for the module
-            if mod.const_defined? mod_name
-              next_mod = mod.send(:const_get, mod_name)
-              next_mod = nil unless next_mod.is_a?(Module)
-            else
-              # Try to load a file that defines the module we want.
-              case attempt_load(mod, mod_name, path_suffix)
-                when :defined then next_mod = mod.const_get mod_name
-                when :dir then # We didn't find a file, but there's a dir.
-                  next_mod = Module.new # So create a module for the directory
-                  mod.send :const_set, mod_name, next_mod
-                else
-                  return nil
-              end
-            end
-            mod = next_mod
-            
-            return nil unless mod &amp;&amp; mod.is_a?(Module)
-          end
-          nil
-        end
-        
-      protected
-        def safe_load_paths #:nodoc:
-          if defined?(RAILS_ROOT)
-            $LOAD_PATH.select do |base|
-              base = File.expand_path(base)
-              extended_root = File.expand_path(RAILS_ROOT)
-              # Exclude all paths that are not nested within app, lib, or components.
-              base.match(/\A#{Regexp.escape(extended_root)}\/*(app|lib|components)\/[a-z]/) || base =~ %r{rails-[\d.]+/builtin}
-            end
-          else
-            $LOAD_PATH
-          end
-        end
-        
-        def attempt_load(mod, const_name, path)
-          has_dir = false
-          safe_load_paths.each do |load_path|
-            full_path = File.join(load_path, path)
-            file_path = full_path + '.rb'
-            if File.file?(file_path) # Found a .rb file? Load it up
-              require_dependency(file_path)
-              return :defined if mod.const_defined? const_name
-            else
-              has_dir ||= File.directory?(full_path)
-            end
-          end
-          return (has_dir ? :dir : nil)
+    class StaticSegment &lt; Segment #:nodoc:
+      attr_accessor :value, :raw
+      alias_method :raw?, :raw
+  
+      def initialize(value = nil)
+        super()
+        self.value = value
+      end
+  
+      def interpolation_chunk
+        raw? ? value : CGI.escape(value)
+      end
+  
+      def regexp_chunk
+        chunk = Regexp.escape value
+        optional? ? Regexp.optionalize(chunk) : chunk
+      end
+  
+      def build_pattern(pattern)
+        escaped = Regexp.escape(value)
+        if optional? &amp;&amp; ! pattern.empty?
+          &quot;(?:#{Regexp.optionalize escaped}\\Z|#{escaped}#{Regexp.unoptionalize pattern})&quot;
+        elsif optional?
+          Regexp.optionalize escaped
+        else
+          escaped + pattern
         end
       end
+  
+      def to_s
+        value
+      end
     end
 
-    class PathComponent &lt; DynamicComponent #:nodoc:
-      def optional?() true end
-      def default()   []  end
-      def condition() nil  end
+    class DividerSegment &lt; StaticSegment #:nodoc:
+      def initialize(value = nil)
+        super(value)
+        self.raw = true
+        self.is_optional = true
+      end
+  
+      def optionality_implied?
+        true
+      end
+    end
 
-      def default=(value)
-        raise RoutingError, &quot;All path components have an implicit default of []&quot; unless value == []
+    class DynamicSegment &lt; Segment #:nodoc:
+      attr_accessor :key, :default, :regexp
+  
+      def initialize(key = nil, options = {})
+        super()
+        self.key = key
+        self.default = options[:default] if options.key? :default
+        self.is_optional = true if options[:optional] || options.key?(:default)
       end
   
-      def write_generation(g)
-        raise RoutingError, 'Path components must occur last' unless g.after.empty?
-        g.if(&quot;#{g.hash_value(key, true)} &amp;&amp; ! #{g.hash_value(key, true)}.empty?&quot;) do
-          g &lt;&lt; &quot;#{g.hash_value(key, true)} = #{g.hash_value(key, true)}.join('/') unless #{g.hash_value(key, true)}.is_a?(String)&quot;
-          g.add_segment(&quot;\#{CGI.escape_skipping_slashes(#{g.hash_value(key, true)})}&quot;) {|gp| gp.finish }
-        end
-        g.else { g.finish }
+      def to_s
+        &quot;:#{key}&quot;
       end
   
-      def write_recognition(g)
-        raise RoutingError, &quot;Path components must occur last&quot; unless g.after.empty?
-    
-        start = g.index_name
-        start = &quot;(#{start})&quot; unless /^\w+$/ =~ start
-    
-        value_expr = &quot;#{g.path_name}[#{start}..-1] || []&quot;
-        g.result key, &quot;ActionController::Routing::PathComponent::Result.new_escaped(#{value_expr})&quot;
-        g.finish(false)
+      # The local variable name that the value of this segment will be extracted to.
+      def local_name
+        &quot;#{key}_value&quot;
       end
   
-      class Result &lt; ::Array #:nodoc:
-        def to_s() join '/' end
-        def self.new_escaped(strings)
-          new strings.collect {|str| CGI.unescape str}
+      def extract_value
+        &quot;#{local_name} = hash[:#{key}] &amp;&amp; hash[:#{key}].to_param #{&quot;|| #{default.inspect}&quot; if default}&quot;
+      end
+      def value_check
+        if default # Then we know it won't be nil
+          &quot;#{value_regexp.inspect} =~ #{local_name}&quot; if regexp
+        elsif optional?
+          # If we have a regexp check that the value is not given, or that it matches.
+          # If we have no regexp, return nil since we do not require a condition.
+          &quot;#{local_name}.nil? || #{value_regexp.inspect} =~ #{local_name}&quot; if regexp
+        else # Then it must be present, and if we have a regexp, it must match too.
+          &quot;#{local_name} #{&quot;&amp;&amp; #{value_regexp.inspect} =~ #{local_name}&quot; if regexp}&quot;
         end
       end
-    end
-
-    class Route #:nodoc:
-      attr_accessor :components, :known
-      attr_reader :path, :options, :keys, :defaults
+      def expiry_statement
+        &quot;expired, hash = true, options if !expired &amp;&amp; expire_on[:#{key}]&quot;
+      end
   
-      def initialize(path, options = {})
-        @path, @options = path, options
-    
-        initialize_components path
-        defaults, conditions = initialize_hashes options.dup
-        @defaults = defaults.dup
-        configure_components(defaults, conditions)
-        add_default_requirements
-        initialize_keys
+      def extraction_code
+        s = extract_value
+        vc = value_check
+        s &lt;&lt; &quot;\nreturn [nil,nil] unless #{vc}&quot; if vc
+        s &lt;&lt; &quot;\n#{expiry_statement}&quot;
       end
   
-      def inspect
-        &quot;&lt;#{self.class} #{path.inspect}, #{options.inspect[1..-1]}&gt;&quot;
+      def interpolation_chunk
+        &quot;\#{CGI.escape(#{local_name}.to_s)}&quot;
       end
   
-      def write_generation(generator = CodeGeneration::GenerationGenerator.new)
-        generator.before, generator.current, generator.after = [], components.first, (components[1..-1] || [])
-
-        if known.empty? then generator.go
+      def string_structure(prior_segments)
+        if optional? # We have a conditional to do...
+          # If we should not appear in the url, just write the code for the prior
+          # segments. This occurs if our value is the default value, or, if we are
+          # optional, if we have nil as our value.
+          &quot;if #{local_name} == #{default.inspect}\n&quot; + 
+            continue_string_structure(prior_segments) + 
+          &quot;\nelse\n&quot; + # Otherwise, write the code up to here
+            &quot;#{interpolation_statement(prior_segments)}\nend&quot;
         else
-          # Alter the conditions to allow :action =&gt; 'index' to also catch :action =&gt; nil
-          altered_known = known.collect do |k, v|
-            if k == :action &amp;&amp; v== 'index' then [k, [nil, 'index']]
-            else [k, v]
-            end
-          end
-          generator.if(generator.check_conditions(altered_known)) {|gp| gp.go }
+          interpolation_statement(prior_segments)
         end
+      end
+  
+      def value_regexp
+        Regexp.new &quot;\\A#{regexp.source}\\Z&quot; if regexp
+      end
+      def regexp_chunk
+        regexp ? &quot;(#{regexp.source})&quot; : &quot;([^#{Routing::SEPARATORS.join}]+)&quot;
+      end
+  
+      def build_pattern(pattern)
+        chunk = regexp_chunk
+        chunk = &quot;(#{chunk})&quot; if Regexp.new(chunk).number_of_captures == 0
+        pattern = &quot;#{chunk}#{pattern}&quot;
+        optional? ? Regexp.optionalize(pattern) : pattern
+      end
+      def match_extraction(next_capture)
+        hangon = (default ? &quot;|| #{default.inspect}&quot; : &quot;if match[#{next_capture}]&quot;)
         
-        generator
+        # All non code-related keys (such as :id, :slug) have to be unescaped as other CGI params
+        &quot;params[:#{key}] = match[#{next_capture}] #{hangon}&quot;
       end
   
-      def write_recognition(generator = CodeGeneration::RecognitionGenerator.new)
-        g = generator.dup
-        g.share_locals_with generator
-        g.before, g.current, g.after = [], components.first, (components[1..-1] || [])
-    
-        known.each do |key, value|
-          if key == :controller then ControllerComponent.assign_controller(g, value)
-          else g.constant_result(key, value)
-          end
+      def optionality_implied?
+        [:action, :id].include? key
+      end
+  
+    end
+
+    class ControllerSegment &lt; DynamicSegment #:nodoc:
+      def regexp_chunk
+        possible_names = Routing.possible_controllers.collect { |name| Regexp.escape name }
+        &quot;(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))&quot;
+      end
+
+      # Don't CGI.escape the controller name, since it may have slashes in it,
+      # like admin/foo.
+      def interpolation_chunk
+        &quot;\#{#{local_name}.to_s}&quot;
+      end
+
+      # Make sure controller names like Admin/Content are correctly normalized to
+      # admin/content
+      def extract_value
+        &quot;#{local_name} = (hash[:#{key}] #{&quot;|| #{default.inspect}&quot; if default}).downcase&quot;
+      end
+
+      def match_extraction(next_capture)
+        if default
+          &quot;params[:#{key}] = match[#{next_capture}] ? match[#{next_capture}].downcase : '#{default}'&quot;
+        else
+          &quot;params[:#{key}] = match[#{next_capture}].downcase if match[#{next_capture}]&quot;
         end
-    
-        g.go
-    
-        generator
       end
+    end
+
+    class PathSegment &lt; DynamicSegment #:nodoc:
+      EscapedSlash = CGI.escape(&quot;/&quot;)
+      def interpolation_chunk
+        &quot;\#{CGI.escape(#{local_name}.to_s).gsub(#{EscapedSlash.inspect}, '/')}&quot;
+      end
+
+      def default
+        ''
+      end
+
+      def default=(path)
+        raise RoutingError, &quot;paths cannot have non-empty default values&quot; unless path.blank?
+      end
+
+      def match_extraction(next_capture)
+        &quot;params[:#{key}] = PathSegment::Result.new_escaped((match[#{next_capture}]#{&quot; || &quot; + default.inspect if default}).split('/'))#{&quot; if match[&quot; + next_capture + &quot;]&quot; if !default}&quot;
+      end
+
+      def regexp_chunk
+        regexp || &quot;(.*)&quot;
+      end
+
+      class Result &lt; ::Array #:nodoc:
+        def to_s() join '/' end 
+        def self.new_escaped(strings)
+          new strings.collect {|str| CGI.unescape str}
+        end     
+      end     
+    end
 
-      def initialize_keys
-        @keys = (components.collect {|c| c.key} + known.keys).compact
-        @keys.freeze
+    class RouteBuilder #:nodoc:
+      attr_accessor :separators, :optional_separators
+  
+      def initialize
+        self.separators = Routing::SEPARATORS
+        self.optional_separators = %w( / )
       end
   
-      def extra_keys(options)
-        options.keys - @keys
+      def separator_pattern(inverted = false)
+        &quot;[#{'^' if inverted}#{Regexp.escape(separators.join)}]&quot;
       end
+  
+      def interval_regexp
+        Regexp.new &quot;(.*?)(#{separators.source}|$)&quot;
+      end
+  
+      # Accepts a &quot;route path&quot; (a string defining a route), and returns the array
+      # of segments that corresponds to it. Note that the segment array is only
+      # partially initialized--the defaults and requirements, for instance, need
+      # to be set separately, via the #assign_route_options method, and the
+      # #optional? method for each segment will not be reliable until after
+      # #assign_route_options is called, as well.
+      def segments_for_route_path(path)
+        rest, segments = path, []
     
-      def matches_controller?(controller)
-        if known[:controller] then known[:controller] == controller
-        else
-          c = components.find {|c| c.key == :controller}
-          return false unless c
-          return c.condition.nil? || eval(Routing.test_condition('controller', c.condition))
+        until rest.empty?
+          segment, rest = segment_for rest
+          segments &lt;&lt; segment
+        end
+        segments
+      end
+
+      # A factory method that returns a new segment instance appropriate for the
+      # format of the given string.
+      def segment_for(string)
+        segment = case string
+          when /\A:(\w+)/
+            key = $1.to_sym
+            case key
+              when :controller then ControllerSegment.new(key)
+              else DynamicSegment.new key
+            end
+          when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional =&gt; true)
+          when /\A\?(.*?)\?/
+            returning segment = StaticSegment.new($1) do
+              segment.is_optional = true
+            end
+          when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1)
+          when Regexp.new(separator_pattern) then
+            returning segment = DividerSegment.new($&amp;) do
+              segment.is_optional = (optional_separators.include? $&amp;)
+            end
         end
+        [segment, $~.post_match]
       end
   
-      protected
-        def initialize_components(path)
-          path = path.split('/') if path.is_a? String
-          path.shift if path.first.blank?
-          self.components = path.collect {|str| Component.new str}
+      # Split the given hash of options into requirement and default hashes. The
+      # segments are passed alongside in order to distinguish between default values
+      # and requirements.
+      def divide_route_options(segments, options)
+        options = options.dup
+        requirements = (options.delete(:requirements) || {}).dup
+        defaults     = (options.delete(:defaults)     || {}).dup
+        conditions   = (options.delete(:conditions)   || {}).dup
+
+        path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact
+        options.each do |key, value|
+          hash = (path_keys.include?(key) &amp;&amp; ! value.is_a?(Regexp)) ? defaults : requirements
+          hash[key] = value
         end
     
-        def initialize_hashes(options)
-          path_keys = components.collect {|c| c.key }.compact 
-          self.known = {}
-          defaults = options.delete(:defaults) || {}
-          conditions = options.delete(:require) || {}
-          conditions.update(options.delete(:requirements) || {})
+        [defaults, requirements, conditions]
+      end
       
-          options.each do |k, v|
-            if path_keys.include?(k) then (v.is_a?(Regexp) ? conditions : defaults)[k] = v
-            else known[k] = v
-            end
-          end
-          [defaults, conditions]
+      # Takes a hash of defaults and a hash of requirements, and assigns them to
+      # the segments. Any unused requirements (which do not correspond to a segment)
+      # are returned as a hash.
+      def assign_route_options(segments, defaults, requirements)
+        route_requirements = {} # Requirements that do not belong to a segment
+        
+        segment_named = Proc.new do |key|
+          segments.detect { |segment| segment.key == key if segment.respond_to?(:key) }
         end
-    
-        def configure_components(defaults, conditions)
-          components.each do |component|
-            if defaults.key?(component.key) then component.default = defaults[component.key]
-            elsif component.key == :action  then component.default = 'index'
-            elsif component.key == :id      then component.default = nil
-            end
         
-            component.condition = conditions[component.key] if conditions.key?(component.key)
+        requirements.each do |key, requirement|
+          segment = segment_named[key]
+          if segment
+            raise TypeError, &quot;#{key}: requirements on a path segment must be regular expressions&quot; unless requirement.is_a?(Regexp)
+            if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
+              raise ArgumentError, &quot;Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}&quot;
+            end
+            segment.regexp = requirement
+          else
+            route_requirements[key] = requirement
           end
         end
         
-        def add_default_requirements
-          component_keys = components.collect {|c| c.key}
-          known[:action] ||= 'index' unless component_keys.include? :action
+        defaults.each do |key, default|
+          segment = segment_named[key]
+          raise ArgumentError, &quot;#{key}: No matching segment exists; cannot assign default&quot; unless segment
+          segment.is_optional = true
+          segment.default = default.to_param if default
         end
-    end
-
-    class RouteSet #:nodoc:
-      attr_reader :routes, :categories, :controller_to_selector
-      def initialize
-        @routes = []
-        @generation_methods = Hash.new(:generate_default_path)
-      end
-      
-      def generate(options, request_or_recall_hash = {})
-        recall = request_or_recall_hash.is_a?(Hash) ? request_or_recall_hash : request_or_recall_hash.symbolized_path_parameters
-        use_recall = true
-        
-        controller = options[:controller]
-        options[:action] ||= 'index' if controller
-        recall_controller = recall[:controller]
-        if (recall_controller &amp;&amp; recall_controller.include?(?/)) || (controller &amp;&amp; controller.include?(?/)) 
-          recall = {} if controller &amp;&amp; controller[0] == ?/
-          options[:controller] = Routing.controller_relative_to(controller, recall_controller)
-        end
-        options = recall.dup if options.empty? # XXX move to url_rewriter?
-        
-        keys_to_delete = []
-        Routing.treat_hash(options, keys_to_delete)
         
-        merged = recall.merge(options)
-        keys_to_delete.each {|key| merged.delete key}
-        expire_on = Routing.expiry_hash(options, recall)
-    
-        generate_path(merged, options, expire_on)
+        assign_default_route_options(segments)
+        ensure_required_segments(segments)
+        route_requirements
       end
       
-      def generate_path(merged, options, expire_on)
-        send @generation_methods[merged[:controller]], merged, options, expire_on
-      end
-      def generate_default_path(*args)
-        write_generation
-        generate_default_path(*args)
+      # Assign default options, such as 'index' as a default for :action. This
+      # method must be run *after* user supplied requirements and defaults have
+      # been applied to the segments.
+      def assign_default_route_options(segments)
+        segments.each do |segment|
+          next unless segment.is_a? DynamicSegment
+          case segment.key
+            when :action
+              if segment.regexp.nil? || segment.regexp.match('index').to_s == 'index'
+                segment.default ||= 'index'
+                segment.is_optional = true
+              end
+            when :id
+              if segment.default.nil? &amp;&amp; segment.regexp.nil? || segment.regexp =~ ''
+                segment.is_optional = true
+              end
+          end
+        end
       end
-  
-      def write_generation
-        method_sources = []
-        @generation_methods = Hash.new(:generate_default_path)
-        categorize_routes.each do |controller, routes|
-          next unless routes.length &lt; @routes.length
       
-          ivar = controller.gsub('/', '__')
-          method_name = &quot;generate_path_for_#{ivar}&quot;.to_sym
-          instance_variable_set &quot;@#{ivar}&quot;, routes
-          code = generation_code_for(ivar, method_name).to_s
-          method_sources &lt;&lt; code
-          
-          filename = &quot;generated_code/routing/generation_for_controller_#{controller}.rb&quot;
-          eval(code, nil, filename)
-      
-          @generation_methods[controller.to_s]   = method_name
-          @generation_methods[controller.to_sym] = method_name
+      # Makes sure that there are no optional segments that precede a required
+      # segment. If any are found that precede a required segment, they are
+      # made required.
+      def ensure_required_segments(segments)
+        allow_optional = true
+        segments.reverse_each do |segment|
+          allow_optional &amp;&amp;= segment.optional?
+          if !allow_optional &amp;&amp; segment.optional?
+            unless segment.optionality_implied?
+              warn &quot;Route segment \&quot;#{segment.to_s}\&quot; cannot be optional because it precedes a required segment. This segment will be required.&quot;
+            end
+            segment.is_optional = false
+          elsif allow_optional &amp; segment.respond_to?(:default) &amp;&amp; segment.default
+            # if a segment has a default, then it is optional
+            segment.is_optional = true
+          end
         end
-        
-        code = generation_code_for('routes', 'generate_default_path').to_s
-        eval(code, nil, 'generated_code/routing/generation.rb')
-        
-        return (method_sources &lt;&lt; code)
       end
+      
+      # Construct and return a route with the given path and options.
+      def build(path, options)
+        # Wrap the path with slashes
+        path = &quot;/#{path}&quot; unless path[0] == ?/
+        path = &quot;#{path}/&quot; unless path[-1] == ?/
+    
+        segments = segments_for_route_path(path)
+        defaults, requirements, conditions = divide_route_options(segments, options)
+        requirements = assign_route_options(segments, defaults, requirements)
 
-      def recognize(request)
-        string_path = request.path  
-        string_path.chomp! if string_path[0] == ?/  
-        path = string_path.split '/'  
-        path.shift  
-   
-        hash = recognize_path(path)  
-        return recognition_failed(request) unless hash &amp;&amp; hash['controller']  
-   
-        controller = hash['controller']  
-        hash['controller'] = controller.controller_path  
-        request.path_parameters = hash  
-        controller.new 
-      end
-      alias :recognize! :recognize
-  
-      def recognition_failed(request)
-        raise ActionController::RoutingError, &quot;Recognition failed for #{request.path.inspect}&quot;
+        route = Route.new
+        route.segments = segments
+        route.requirements = requirements
+        route.conditions = conditions
+
+        if !route.significant_keys.include?(:action) &amp;&amp; !route.requirements[:action]
+          route.requirements[:action] = &quot;index&quot;
+          route.significant_keys &lt;&lt; :action
+        end
+
+        if !route.significant_keys.include?(:controller)
+          raise ArgumentError, &quot;Illegal route: the :controller must be specified!&quot;
+        end
+
+        route
       end
+    end
 
-      def write_recognition
-        g = generator = CodeGeneration::RecognitionGenerator.new
-        g.finish_statement = Proc.new {|hash_expr| &quot;return #{hash_expr}&quot;}
+    class RouteSet #:nodoc:
+      # Mapper instances are used to build routes. The object passed to the draw
+      # block in config/routes.rb is a Mapper instance.
+      # 
+      # Mapper instances have relatively few instance methods, in order to avoid
+      # clashes with named routes.
+      class Mapper #:nodoc:
+        def initialize(set)
+          @set = set
+        end
     
-        g.def &quot;self.recognize_path(path)&quot; do
-          each do |route|
-            g &lt;&lt; 'index = 0'
-            route.write_recognition(g)
-          end
+        # Create an unnamed route with the provided +path+ and +options+. See 
+        # SomeHelpfulUrl for an introduction to routes.
+        def connect(path, options = {})
+          @set.add_route(path, options)
+        end
+
+        def named_route(name, path, options = {})
+          @set.add_named_route(name, path, options)
         end
         
-        eval g.to_s, nil, 'generated/routing/recognition.rb'
-        return g.to_s
+        def deprecated_named_route(name, deprecated_name, options = {})
+          @set.add_deprecated_named_route(name, deprecated_name)
+        end
+
+        # Added deprecation notice for anyone who already added a named route called &quot;root&quot;.
+        # It'll be used as a shortcut for map.connect '' in Rails 2.0.
+        def root(*args, &amp;proc)
+          super unless args.length &gt;= 1 &amp;&amp; proc.nil?
+          @set.add_named_route(&quot;root&quot;, *args)
+        end
+        deprecate :root =&gt; &quot;(as the the label for a named route) will become a shortcut for map.connect '', so find another name&quot;
+
+        def method_missing(route_name, *args, &amp;proc)
+          super unless args.length &gt;= 1 &amp;&amp; proc.nil?
+          @set.add_named_route(route_name, *args)
+        end
       end
-        
-      def generation_code_for(ivar = 'routes', method_name = nil)
-        routes = instance_variable_get('@' + ivar)
-        key_ivar = &quot;@keys_for_#{ivar}&quot;
-        instance_variable_set(key_ivar, routes.collect {|route| route.keys})
-    
-        g = generator = CodeGeneration::GenerationGenerator.new
-        g.def &quot;self.#{method_name}(merged, options, expire_on)&quot; do
-          g &lt;&lt; 'unused_count = options.length + 1'
-          g &lt;&lt; &quot;unused_keys = keys = options.keys&quot;
-          g &lt;&lt; 'path = nil'
-      
-          routes.each_with_index do |route, index|
-            g &lt;&lt; &quot;new_unused_keys = keys - #{key_ivar}[#{index}]&quot;
-            g &lt;&lt; 'new_path = ('
-            g.source.indent do
-              if index.zero?
-                g &lt;&lt; &quot;new_unused_count = new_unused_keys.length&quot;
-                g &lt;&lt; &quot;hash = merged; not_expired = true&quot;
-                route.write_generation(g.dup)
-              else
-                g.if &quot;(new_unused_count = new_unused_keys.length) &lt; unused_count&quot; do |gp|
-                  gp &lt;&lt; &quot;hash = merged; not_expired = true&quot;
-                  route.write_generation(gp)
-                end
-              end
-            end
-            g.source.lines.last &lt;&lt; ' )' # Add the closing brace to the end line
-            g.if 'new_path' do
-              g &lt;&lt; 'return new_path, [] if new_unused_count.zero?'
-              g &lt;&lt; 'path = new_path; unused_keys = new_unused_keys; unused_count = new_unused_count'
-            end
+
+      # A NamedRouteCollection instance is a collection of named routes, and also
+      # maintains an anonymous module that can be used to install helpers for the
+      # named routes.
+      class NamedRouteCollection #:nodoc:
+        include Enumerable
+
+        attr_reader :routes, :helpers
+
+        def initialize
+          clear!
+        end
+
+        def clear!
+          @routes = {}
+          @helpers = []
+
+          @module ||= Module.new
+          @module.instance_methods.each do |selector|
+            @module.send :remove_method, selector
           end
-        
-          g &lt;&lt; &quot;raise RoutingError, \&quot;No url can be generated for the hash \#{options.inspect}\&quot; unless path&quot;
-          g &lt;&lt; &quot;return path, unused_keys&quot;
+        end
+
+        def add(name, route)
+          routes[name.to_sym] = route
+          define_named_route_methods(name, route)
+        end
+
+        def get(name)
+          routes[name.to_sym]
+        end
+
+        alias []=   add
+        alias []    get
+        alias clear clear!
+
+        def each
+          routes.each { |name, route| yield name, route }
+          self
+        end
+
+        def names
+          routes.keys
+        end
+
+        def length
+          routes.length
+        end
+
+        def install(destinations = [ActionController::Base, ActionView::Base])
+          Array(destinations).each { |dest| dest.send :include, @module }
         end
         
-        return g
-      end
-      
-      def categorize_routes
-        @categorized_routes = by_controller = Hash.new(self)
-      
-        known_controllers.each do |name|
-          set = by_controller[name] = []
-          each do |route|
-            set &lt;&lt; route if route.matches_controller? name
+        def define_deprecated_named_route_methods(name, deprecated_name)
+
+          [:url, :path].each do |kind|
+            @module.send :module_eval, &lt;&lt;-end_eval # We use module_eval to avoid leaks
+
+              def #{url_helper_name(deprecated_name, kind)}(*args)
+
+                ActiveSupport::Deprecation.warn(
+                  'The named route &quot;#{url_helper_name(deprecated_name, kind)}&quot; uses a format that has been deprecated. ' +
+                  'You should use &quot;#{url_helper_name(name, kind)}&quot; instead.', caller
+                )
+
+                send :#{url_helper_name(name, kind)}, *args
+
+              end
+
+              def #{hash_access_name(deprecated_name, kind)}(*args)
+
+                ActiveSupport::Deprecation.warn(
+                  'The named route &quot;#{hash_access_name(deprecated_name, kind)}&quot; uses a format that has been deprecated. ' +
+                  'You should use &quot;#{hash_access_name(name, kind)}&quot; instead.', caller
+                )
+
+                send :#{hash_access_name(name, kind)}, *args
+
+              end
+
+            end_eval
           end
+
         end
-    
-        @categorized_routes
-      end
-      
-      def known_controllers
-        @routes.inject([]) do |known, route|
-          if (controller = route.known[:controller])
-            if controller.is_a?(Regexp)
-              known &lt;&lt; controller.source.scan(%r{[\w\d/]+}).select {|word| controller =~ word} 
-            else known &lt;&lt; controller
+
+        private
+          def url_helper_name(name, kind = :url)
+            :&quot;#{name}_#{kind}&quot;
+          end
+
+          def hash_access_name(name, kind = :url)
+            :&quot;hash_for_#{name}_#{kind}&quot;
+          end
+
+          def define_named_route_methods(name, route)
+            {:url =&gt; {:only_path =&gt; false}, :path =&gt; {:only_path =&gt; true}}.each do |kind, opts|
+              hash = route.defaults.merge(:use_route =&gt; name).merge(opts)
+              define_hash_access route, name, kind, hash
+              define_url_helper route, name, kind, hash
             end
           end
-          known
-        end.uniq
+          
+          def define_hash_access(route, name, kind, options)
+            selector = hash_access_name(name, kind)
+            @module.send :module_eval, &lt;&lt;-end_eval # We use module_eval to avoid leaks
+              def #{selector}(options = nil)
+                options ? #{options.inspect}.merge(options) : #{options.inspect}
+              end
+            end_eval
+            @module.send(:protected, selector)
+            helpers &lt;&lt; selector
+          end
+          
+          def define_url_helper(route, name, kind, options)
+            selector = url_helper_name(name, kind)
+            
+            # The segment keys used for positional paramters
+            segment_keys = route.segments.collect do |segment|
+              segment.key if segment.respond_to? :key
+            end.compact
+            hash_access_method = hash_access_name(name, kind)
+            
+            @module.send :module_eval, &lt;&lt;-end_eval # We use module_eval to avoid leaks
+              def #{selector}(*args)
+                opts = if args.empty? || Hash === args.first
+                  args.first || {}
+                else
+                  # allow ordered parameters to be associated with corresponding
+                  # dynamic segments, so you can do
+                  #
+                  #   foo_url(bar, baz, bang)
+                  #
+                  # instead of
+                  #
+                  #   foo_url(:bar =&gt; bar, :baz =&gt; baz, :bang =&gt; bang)
+                  args.zip(#{segment_keys.inspect}).inject({}) do |h, (v, k)|
+                    h[k] = v
+                    h
+                  end
+                end
+                
+                url_for(#{hash_access_method}(opts))
+              end
+            end_eval
+            @module.send(:protected, selector)
+            helpers &lt;&lt; selector
+          end
+          
+      end
+  
+      attr_accessor :routes, :named_routes
+  
+      def initialize
+        self.routes = []
+        self.named_routes = NamedRouteCollection.new
       end
 
-      def reload
-        NamedRoutes.clear
-        
-        if defined?(RAILS_ROOT) then load(File.join(RAILS_ROOT, 'config', 'routes.rb'))
-        else connect(':controller/:action/:id', :action =&gt; 'index', :id =&gt; nil)
-        end
+      # Subclasses and plugins may override this method to specify a different
+      # RouteBuilder instance, so that other route DSL's can be created.
+      def builder
+        @builder ||= RouteBuilder.new
+      end
 
-        NamedRoutes.install
+      def draw
+        clear!
+        yield Mapper.new(self)
+        named_routes.install
+      end
+  
+      def clear!
+        routes.clear
+        named_routes.clear
+        @combined_regexp = nil
+        @routes_by_controller = nil
       end
 
-      def connect(*args)
-        new_route = Route.new(*args)
-        @routes &lt;&lt; new_route
-        return new_route
+      def empty?
+        routes.empty?
+      end
+  
+      def load!
+        Routing.use_controllers! nil # Clear the controller cache so we may discover new ones
+        clear!
+        load_routes!
+        named_routes.install
       end
 
-      def draw
-        old_routes = @routes
-        @routes = []
-        
-        begin yield self
-        rescue
-          @routes = old_routes
-          raise
+      alias reload load!
+
+      def load_routes!
+        if defined?(RAILS_ROOT) &amp;&amp; defined?(::ActionController::Routing::Routes) &amp;&amp; self == ::ActionController::Routing::Routes
+          load File.join(&quot;#{RAILS_ROOT}/config/routes.rb&quot;)
+        else
+          add_route &quot;:controller/:action/:id&quot;
         end
-        write_generation
-        write_recognition
       end
-      
-      def empty?() @routes.empty? end
   
-      def each(&amp;block) @routes.each(&amp;block) end
-      
-      # Defines a new named route with the provided name and arguments.
-      # This method need only be used when you wish to use a name that a RouteSet instance
-      # method exists for, such as categories.
-      #
-      # For example, map.categories '/categories', :controller =&gt; 'categories' will not work
-      # due to RouteSet#categories.
-      def named_route(name, path, hash = {})
-        route = connect(path, hash)
-        NamedRoutes.name_route(route, name)
+      def add_route(path, options = {})
+        route = builder.build(path, options)
+        routes &lt;&lt; route
         route
       end
+  
+      def add_named_route(name, path, options = {})
+        named_routes[name] = add_route(path, options)
+      end
       
-      def method_missing(name, *args)
-        (1..2).include?(args.length) ? named_route(name, *args) : super(name, *args)
+      def add_deprecated_named_route(name, deprecated_name)
+        named_routes.define_deprecated_named_route_methods(name, deprecated_name)
       end
+  
+      def options_as_params(options)
+        # If an explicit :controller was given, always make :action explicit
+        # too, so that action expiry works as expected for things like
+        #
+        #   generate({:controller =&gt; 'content'}, {:controller =&gt; 'content', :action =&gt; 'show'})
+        #
+        # (the above is from the unit tests). In the above case, because the
+        # controller was explicitly given, but no action, the action is implied to
+        # be &quot;index&quot;, not the recalled action of &quot;show&quot;.
+        #
+        # great fun, eh?
 
-      def extra_keys(options, recall = {})
-        generate(options.dup, recall).last
+        options_as_params = options.clone
+        options_as_params[:action] ||= 'index' if options[:controller]
+        options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
+        options_as_params
       end
-    end
-
-    module NamedRoutes #:nodoc:
-      Helpers = []
-      class &lt;&lt; self
-        def clear() Helpers.clear end
   
-        def hash_access_name(name)
-          &quot;hash_for_#{name}_url&quot;
+      def build_expiry(options, recall)
+        recall.inject({}) do |expiry, (key, recalled_value)|
+          expiry[key] = (options.key?(key) &amp;&amp; options[key] != recalled_value)
+          expiry
         end
+      end
 
-        def url_helper_name(name)
-          &quot;#{name}_url&quot;
+      # Generate the path indicated by the arguments, and return an array of
+      # the keys that were not used to generate it.
+      def extra_keys(options, recall={})
+        generate_extras(options, recall).last
+      end
+
+      def generate_extras(options, recall={})
+        generate(options, recall, :generate_extras)
+      end
+
+      def generate(options, recall = {}, method=:generate)
+        named_route_name = options.delete(:use_route)
+        if named_route_name
+          named_route = named_routes[named_route_name]
+          options = named_route.parameter_shell.merge(options)
         end
-        
-        def known_hash_for_route(route)
-          hash = route.known.symbolize_keys
-          route.defaults.each do |key, value|
-            hash[key.to_sym] ||= value if value
-          end
-          hash[:controller] = &quot;/#{hash[:controller]}&quot;
-          
-          hash
+
+        options = options_as_params(options)
+        expire_on = build_expiry(options, recall)
+
+        if options[:controller]
+          options[:controller] = options[:controller].to_s
         end
-        
-        def define_hash_access_method(route, name)
-          hash = known_hash_for_route(route)
-          define_method(hash_access_name(name)) do |*args|
-            args.first ? hash.merge(args.first) : hash
+        # if the controller has changed, make sure it changes relative to the
+        # current controller module, if any. In other words, if we're currently
+        # on admin/get, and the new controller is 'set', the new controller
+        # should really be admin/set.
+        if !named_route &amp;&amp; expire_on[:controller] &amp;&amp; options[:controller] &amp;&amp; options[:controller][0] != ?/
+          old_parts = recall[:controller].split('/')
+          new_parts = options[:controller].split('/')
+          parts = old_parts[0..-(new_parts.length + 1)] + new_parts
+          options[:controller] = parts.join('/')
+        end
+
+        # drop the leading '/' on the controller name
+        options[:controller] = options[:controller][1..-1] if options[:controller] &amp;&amp; options[:controller][0] == ?/
+        merged = recall.merge(options)
+
+        if named_route
+          path = named_route.generate(options, merged, expire_on)
+          if path.nil? 
+            raise_named_route_error(options, named_route, named_route_name)
+          else
+            return path
+          end
+        else
+          merged[:action] ||= 'index'
+          options[:action] ||= 'index'
+  
+          controller = merged[:controller]
+          action = merged[:action]
+
+          raise RoutingError, &quot;Need controller and action!&quot; unless controller &amp;&amp; action
+          # don't use the recalled keys when determining which routes to check
+          routes = routes_by_controller[controller][action][options.keys.sort_by { |x| x.object_id }]
+
+          routes.each do |route|
+            results = route.send(method, options, merged, expire_on)
+            return results if results &amp;&amp; (!results.is_a?(Array) || results.first)
           end
         end
-        
-        def name_route(route, name)
-          define_hash_access_method(route, name)
-          
-          module_eval(%{def #{url_helper_name name}(options = {})
-            url_for(#{hash_access_name(name)}.merge(options))
-          end}, &quot;generated/routing/named_routes/#{name}.rb&quot;)
-      
-          protected url_helper_name(name), hash_access_name(name)
+    
+        raise RoutingError, &quot;No route matches #{options.inspect}&quot;
+      end
       
-          Helpers &lt;&lt; url_helper_name(name).to_sym
-          Helpers &lt;&lt; hash_access_name(name).to_sym
-          Helpers.uniq!
+      # try to give a helpful error message when named route generation fails
+      def raise_named_route_error(options, named_route, named_route_name)
+        diff = named_route.requirements.diff(options)
+        unless diff.empty?
+          raise RoutingError, &quot;#{named_route_name}_url failed to generate from #{options.inspect}, expected: #{named_route.requirements.inspect}, diff: #{named_route.requirements.diff(options).inspect}&quot;
+        else
+          required_segments = named_route.segments.select {|seg| (!seg.optional?) &amp;&amp; (!seg.is_a?(DividerSegment)) }
+          required_keys_or_values = required_segments.map { |seg| seg.key rescue seg.value } # we want either the key or the value from the segment
+          raise RoutingError, &quot;#{named_route_name}_url failed to generate from #{options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisifed?&quot;
         end
-    
-        def install(cls = ActionController::Base)
-          cls.send :include, self
-          if cls.respond_to? :helper_method
-            Helpers.each do |helper_name|
-              cls.send :helper_method, helper_name
+      end
+  
+      def recognize(request)
+        params = recognize_path(request.path, extract_request_environment(request))
+        request.path_parameters = params.with_indifferent_access
+        &quot;#{params[:controller].camelize}Controller&quot;.constantize
+      end
+  
+      def recognize_path(path, environment={})
+        path = CGI.unescape(path)
+        routes.each do |route|
+          result = route.recognize(path, environment) and return result
+        end
+        raise RoutingError, &quot;no route found to match #{path.inspect} with #{environment.inspect}&quot;
+      end
+  
+      def routes_by_controller
+        @routes_by_controller ||= Hash.new do |controller_hash, controller|
+          controller_hash[controller] = Hash.new do |action_hash, action|
+            action_hash[action] = Hash.new do |key_hash, keys|
+              key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys)
             end
           end
         end
       end
+  
+      def routes_for(options, merged, expire_on)
+        raise &quot;Need controller and action!&quot; unless controller &amp;&amp; action
+        controller = merged[:controller]
+        merged = options if expire_on[:controller]
+        action = merged[:action] || 'index'
+    
+        routes_by_controller[controller][action][merged.keys]
+      end
+  
+      def routes_for_controller_and_action(controller, action)
+        selected = routes.select do |route|
+          route.matches_controller_and_action? controller, action
+        end
+        (selected.length == routes.length) ? routes : selected
+      end
+  
+      def routes_for_controller_and_action_and_keys(controller, action, keys)
+        selected = routes.select do |route|
+          route.matches_controller_and_action? controller, action
+        end
+        selected.sort_by do |route|
+          (keys - route.significant_keys).length
+        end
+      end
+
+      # Subclasses and plugins may override this method to extract further attributes
+      # from the request, for use by route conditions and such.
+      def extract_request_environment(request)
+        { :method =&gt; request.method }
+      end
     end
 
     Routes = RouteSet.new
   end
 end
+</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/routing.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,6 @@
 module ActionController
   module Scaffolding # :nodoc:
-    def self.append_features(base)
-      super
+    def self.included(base)
       base.extend(ClassMethods)
     end
 
@@ -25,25 +24,25 @@ module ActionController
     #    end
     #
     #    def list
-    #      @entries = Entry.find_all
+    #      @entries = Entry.find(:all)
     #      render_scaffold &quot;list&quot;
     #    end
-    #  
+    #
     #    def show
     #      @entry = Entry.find(params[:id])
     #      render_scaffold
     #    end
-    #    
+    #
     #    def destroy
     #      Entry.find(params[:id]).destroy
     #      redirect_to :action =&gt; &quot;list&quot;
     #    end
-    #    
+    #
     #    def new
     #      @entry = Entry.new
     #      render_scaffold
     #    end
-    #    
+    #
     #    def create
     #      @entry = Entry.new(params[:entry])
     #      if @entry.save
@@ -53,16 +52,16 @@ module ActionController
     #        render_scaffold('new')
     #      end
     #    end
-    #    
+    #
     #    def edit
     #      @entry = Entry.find(params[:id])
     #      render_scaffold
     #    end
-    #    
+    #
     #    def update
     #      @entry = Entry.find(params[:id])
     #      @entry.attributes = params[:entry]
-    #  
+    #
     #      if @entry.save
     #        flash[:notice] = &quot;Entry was successfully updated&quot;
     #        redirect_to :action =&gt; &quot;show&quot;, :id =&gt; @entry
@@ -72,17 +71,17 @@ module ActionController
     #    end
     #  end
     #
-    # The &lt;tt&gt;render_scaffold&lt;/tt&gt; method will first check to see if you've made your own template (like &quot;weblog/show.rhtml&quot; for 
-    # the show action) and if not, then render the generic template for that action. This gives you the possibility of using the 
-    # scaffold while you're building your specific application. Start out with a totally generic setup, then replace one template 
+    # The &lt;tt&gt;render_scaffold&lt;/tt&gt; method will first check to see if you've made your own template (like &quot;weblog/show.rhtml&quot; for
+    # the show action) and if not, then render the generic template for that action. This gives you the possibility of using the
+    # scaffold while you're building your specific application. Start out with a totally generic setup, then replace one template
     # and one action at a time while relying on the rest of the scaffolded templates and actions.
     module ClassMethods
       # Adds a swath of generic CRUD actions to the controller. The +model_id+ is automatically converted into a class name unless
       # one is specifically provide through &lt;tt&gt;options[:class_name]&lt;/tt&gt;. So &lt;tt&gt;scaffold :post&lt;/tt&gt; would use Post as the class
       # and @post/@posts for the instance variables.
-      # 
+      #
       # It's possible to use more than one scaffold in a single controller by specifying &lt;tt&gt;options[:suffix] = true&lt;/tt&gt;. This will
-      # make &lt;tt&gt;scaffold :post, :suffix =&gt; true&lt;/tt&gt; use method names like list_post, show_post, and create_post 
+      # make &lt;tt&gt;scaffold :post, :suffix =&gt; true&lt;/tt&gt; use method names like list_post, show_post, and create_post
       # instead of just list, show, and post. If suffix is used, then no index method is added.
       def scaffold(model_id, options = {})
         options.assert_valid_keys(:class_name, :suffix)
@@ -99,13 +98,13 @@ module ActionController
             end
           end_eval
         end
-        
+
         module_eval &lt;&lt;-&quot;end_eval&quot;, __FILE__, __LINE__
-          
+
           verify :method =&gt; :post, :only =&gt; [ :destroy#{suffix}, :create#{suffix}, :update#{suffix} ],
                  :redirect_to =&gt; { :action =&gt; :list#{suffix} }
-          
-        
+
+
           def list#{suffix}
             @#{singular_name}_pages, @#{plural_name} = paginate :#{plural_name}, :per_page =&gt; 10
             render#{suffix}_scaffold &quot;list#{suffix}&quot;
@@ -115,17 +114,17 @@ module ActionController
             @#{singular_name} = #{class_name}.find(params[:id])
             render#{suffix}_scaffold
           end
-          
+
           def destroy#{suffix}
             #{class_name}.find(params[:id]).destroy
             redirect_to :action =&gt; &quot;list#{suffix}&quot;
           end
-          
+
           def new#{suffix}
             @#{singular_name} = #{class_name}.new
             render#{suffix}_scaffold
           end
-          
+
           def create#{suffix}
             @#{singular_name} = #{class_name}.new(params[:#{singular_name}])
             if @#{singular_name}.save
@@ -135,12 +134,12 @@ module ActionController
               render#{suffix}_scaffold('new')
             end
           end
-          
+
           def edit#{suffix}
             @#{singular_name} = #{class_name}.find(params[:id])
             render#{suffix}_scaffold
           end
-          
+
           def update#{suffix}
             @#{singular_name} = #{class_name}.find(params[:id])
             @#{singular_name}.attributes = params[:#{singular_name}]
@@ -152,14 +151,14 @@ module ActionController
               render#{suffix}_scaffold('edit')
             end
           end
-          
+
           private
             def render#{suffix}_scaffold(action=nil)
               action ||= caller_method_name(caller)
               # logger.info (&quot;testing template:&quot; + &quot;\#{self.class.controller_path}/\#{action}&quot;) if logger
-              
+
               if template_exists?(&quot;\#{self.class.controller_path}/\#{action}&quot;)
-                render_action(action)
+                render :action =&gt; action
               else
                 @scaffold_class = #{class_name}
                 @scaffold_singular_name, @scaffold_plural_name = &quot;#{singular_name}&quot;, &quot;#{plural_name}&quot;
@@ -169,9 +168,9 @@ module ActionController
                 @template.instance_variable_set(&quot;@content_for_layout&quot;, @template.render_file(scaffold_path(action.sub(/#{suffix}$/, &quot;&quot;)), false))
 
                 if !active_layout.nil?
-                  render_file(active_layout, nil, true)
+                  render :file =&gt; active_layout, :use_full_path =&gt; true
                 else
-                  render_file(scaffold_path(&quot;layout&quot;))
+                  render :file =&gt; scaffold_path('layout')
                 end
               end
             end
@@ -179,12 +178,12 @@ module ActionController
             def scaffold_path(template_name)
               File.dirname(__FILE__) + &quot;/templates/scaffolds/&quot; + template_name + &quot;.rhtml&quot;
             end
-            
+
             def caller_method_name(caller)
               caller.first.scan(/`(.*)'/).first.first # ' ruby-mode
             end
         end_eval
-      end      
+      end
     end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/scaffolding.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,6 +5,8 @@ require 'base64'
 
 class CGI
   class Session
+    attr_reader :data
+
     # Return this session's underlying Session instance. Useful for the DB-backed session stores.
     def model
       @dbman.model if @dbman</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/session/active_record_store.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,10 @@ class CGI #:nodoc:all
       def delete
         @@session_data.delete(@session_id)
       end
+      
+      def data
+        @@session_data[@session_id]
+      end
     end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/session/drb_store.rb</filename>
    </modified>
    <modified>
      <diff>@@ -93,6 +93,10 @@ begin
           end
           @session_data = {}
         end
+        
+        def data
+          @session_data
+        end
       end
     end
   end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,12 +8,9 @@ module ActionController #:nodoc:
   module SessionManagement #:nodoc:
     def self.included(base)
       base.extend(ClassMethods)
-
-      base.send :alias_method, :process_without_session_management_support, :process
-      base.send :alias_method, :process, :process_with_session_management_support
-
-      base.send :alias_method, :process_cleanup_without_session_management_support, :process_cleanup
-      base.send :alias_method, :process_cleanup, :process_cleanup_with_session_management_support
+      
+      base.send :alias_method_chain, :process, :session_management_support
+      base.send :alias_method_chain, :process_cleanup, :session_management_support
     end
 
     module ClassMethods
@@ -123,16 +120,16 @@ module ActionController #:nodoc:
       end
       
       def process_cleanup_with_session_management_support
-        process_cleanup_without_session_management_support
         clear_persistent_model_associations
+        process_cleanup_without_session_management_support
       end
 
       # Clear cached associations in session data so they don't overflow
       # the database field.  Only applies to ActiveRecordStore since there
       # is not a standard way to iterate over session data.
       def clear_persistent_model_associations #:doc:
-        if defined?(@session) &amp;&amp; @session.instance_variables.include?('@data')
-          session_data = @session.instance_variable_get('@data')
+        if defined?(@_session) &amp;&amp; @_session.respond_to?(:data)
+          session_data = @_session.data
 
           if session_data &amp;&amp; session_data.respond_to?(:each_value)
             session_data.each_value do |obj|</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/session_management.rb</filename>
    </modified>
    <modified>
      <diff>@@ -69,17 +69,8 @@ module ActionController #:nodoc:
             logger.info &quot;Streaming file #{path}&quot; unless logger.nil?
             len = options[:buffer_size] || 4096
             File.open(path, 'rb') do |file|
-              if output.respond_to?(:syswrite)
-                begin
-                  while true
-                    output.syswrite(file.sysread(len))
-                  end
-                rescue EOFError
-                end
-              else
-                while buf = file.read(len)
-                  output.write(buf)
-                end
+              while buf = file.read(len)
+                output.write(buf)
               end
             end
           }
@@ -97,8 +88,8 @@ module ActionController #:nodoc:
       # * &lt;tt&gt;:type&lt;/tt&gt; - specifies an HTTP content type.
       #   Defaults to 'application/octet-stream'.
       # * &lt;tt&gt;:disposition&lt;/tt&gt; - specifies whether the file will be shown inline or downloaded.  
-      # * &lt;tt&gt;:status&lt;/tt&gt; - specifies the status code to send with the response. Defaults to '200 OK'.
       #   Valid values are 'inline' and 'attachment' (default).
+      # * &lt;tt&gt;:status&lt;/tt&gt; - specifies the status code to send with the response. Defaults to '200 OK'.
       #
       # Generic data download:
       #   send_data buffer
@@ -125,10 +116,10 @@ module ActionController #:nodoc:
         end
 
         disposition = options[:disposition].dup || 'attachment'
-        
+
         disposition &lt;&lt;= %(; filename=&quot;#{options[:filename]}&quot;) if options[:filename]
 
-        @headers.update(
+        headers.update(
           'Content-Length'            =&gt; options[:length],
           'Content-Type'              =&gt; options[:type].strip,  # fixes a problem with extra '\r' with some browsers
           'Content-Disposition'       =&gt; disposition,
@@ -141,7 +132,7 @@ module ActionController #:nodoc:
         # after it displays the &quot;open/save&quot; dialog, which means that if you 
         # hit &quot;open&quot; the file isn't there anymore when the application that 
         # is called for handling the download is run, so let's workaround that
-        @headers['Cache-Control'] = 'private' if @headers['Cache-Control'] == 'no-cache'
+        headers['Cache-Control'] = 'private' if headers['Cache-Control'] == 'no-cache'
       end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/streaming.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,10 +8,10 @@
 &lt;% if false %&gt;
   &lt;br /&gt;&lt;br /&gt;
   &lt;% begin %&gt;
-    &lt;%= form_tag(@request.request_uri, &quot;method&quot; =&gt; @request.method) %&gt;
+    &lt;%= form_tag(request.request_uri, &quot;method&quot; =&gt; request.method) %&gt;
       &lt;input type=&quot;hidden&quot; name=&quot;BP-RETRY&quot; value=&quot;1&quot; /&gt;
   
-      &lt;% for key, values in @params %&gt;
+      &lt;% for key, values in params %&gt;
         &lt;% next if key == &quot;BP-RETRY&quot; %&gt;
         &lt;% for value in Array(values) %&gt;
           &lt;input type=&quot;hidden&quot; name=&quot;&lt;%= key %&gt;&quot; value=&quot;&lt;%= value %&gt;&quot; /&gt;
@@ -26,7 +26,7 @@
 &lt;% end %&gt;
 
 &lt;%
-  request_parameters_without_action = @request.parameters.clone
+  request_parameters_without_action = request.parameters.clone
   request_parameters_without_action.delete(&quot;action&quot;)
   request_parameters_without_action.delete(&quot;controller&quot;)
   
@@ -37,8 +37,8 @@
 &lt;p&gt;&lt;b&gt;Parameters&lt;/b&gt;: &lt;%=h request_dump == &quot;{}&quot; ? &quot;None&quot; : request_dump %&gt;&lt;/p&gt;
 
 &lt;p&gt;&lt;a href=&quot;#&quot; onclick=&quot;document.getElementById('session_dump').style.display='block'; return false;&quot;&gt;Show session dump&lt;/a&gt;&lt;/p&gt;
-&lt;div id=&quot;session_dump&quot; style=&quot;display:none&quot;&gt;&lt;%= debug(@request.session.instance_variable_get(&quot;@data&quot;)) %&gt;&lt;/div&gt;
+&lt;div id=&quot;session_dump&quot; style=&quot;display:none&quot;&gt;&lt;%= debug(request.session.instance_variable_get(&quot;@data&quot;)) %&gt;&lt;/div&gt;
 
 
 &lt;h2 style=&quot;margin-top: 30px&quot;&gt;Response&lt;/h2&gt;
-&lt;b&gt;Headers&lt;/b&gt;: &lt;%=h @response.headers.inspect.gsub(/,/, &quot;,\n&quot;) %&gt;&lt;br/&gt;
+&lt;b&gt;Headers&lt;/b&gt;: &lt;%=h response ? response.headers.inspect.gsub(/,/, &quot;,\n&quot;) : &quot;None&quot; %&gt;&lt;br/&gt;</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/templates/rescues/_request_and_response.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 &lt;h1&gt;
   &lt;%=h @exception.class.to_s %&gt;
-  &lt;% if @request.parameters['controller'] %&gt;
-    in &lt;%=h @request.parameters['controller'].humanize %&gt;Controller&lt;% if @request.parameters['action'] %&gt;#&lt;%=h @request.parameters['action'] %&gt;&lt;% end %&gt;
+  &lt;% if request.parameters['controller'] %&gt;
+    in &lt;%=h request.parameters['controller'].humanize %&gt;Controller&lt;% if request.parameters['action'] %&gt;#&lt;%=h request.parameters['action'] %&gt;&lt;% end %&gt;
   &lt;% end %&gt;
 &lt;/h1&gt;
 &lt;pre&gt;&lt;%=h @exception.clean_message %&gt;&lt;/pre&gt;</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/templates/rescues/diagnostics.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,10 @@
 &lt;h1&gt;Routing Error&lt;/h1&gt;
 &lt;p&gt;&lt;pre&gt;&lt;%=h @exception.message %&gt;&lt;/pre&gt;&lt;/p&gt;
 &lt;% unless @exception.failures.empty? %&gt;&lt;p&gt;
-	&lt;h2&gt;Failure reasons:&lt;/h2&gt;
-	&lt;ol&gt;
-	&lt;% @exception.failures.each do |route, reason| %&gt;
-		&lt;li&gt;&lt;code&gt;&lt;%=h route.inspect.gsub('\\', '') %&gt;&lt;/code&gt; failed because &lt;%=h reason.downcase %&gt;&lt;/li&gt;
+  &lt;h2&gt;Failure reasons:&lt;/h2&gt;
+  &lt;ol&gt;
+  &lt;% @exception.failures.each do |route, reason| %&gt;
+    &lt;li&gt;&lt;code&gt;&lt;%=h route.inspect.gsub('\\', '') %&gt;&lt;/code&gt; failed because &lt;%=h reason.downcase %&gt;&lt;/li&gt;
   &lt;% end %&gt;
   &lt;/ol&gt;
 &lt;/p&gt;&lt;% end %&gt;</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/templates/rescues/routing_error.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 &lt;h1&gt;
   &lt;%=h @exception.original_exception.class.to_s %&gt; in
-  &lt;%=h @request.parameters[&quot;controller&quot;].capitalize if @request.parameters[&quot;controller&quot;]%&gt;#&lt;%=h @request.parameters[&quot;action&quot;] %&gt;
+  &lt;%=h request.parameters[&quot;controller&quot;].capitalize if request.parameters[&quot;controller&quot;]%&gt;#&lt;%=h request.parameters[&quot;action&quot;] %&gt;
 &lt;/h1&gt;
 
 &lt;p&gt;</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/templates/rescues/template_error.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@
   &lt;% end %&gt;
     &lt;td&gt;&lt;%= link_to &quot;Show&quot;, :action =&gt; &quot;show#{@scaffold_suffix}&quot;, :id =&gt; entry %&gt;&lt;/td&gt;
     &lt;td&gt;&lt;%= link_to &quot;Edit&quot;, :action =&gt; &quot;edit#{@scaffold_suffix}&quot;, :id =&gt; entry %&gt;&lt;/td&gt;
-    &lt;td&gt;&lt;%= link_to &quot;Destroy&quot;, {:action =&gt; &quot;destroy#{@scaffold_suffix}&quot;, :id =&gt; entry}, { :confirm =&gt; &quot;Are you sure?&quot;, :post =&gt; true} %&gt;&lt;/td&gt;
+    &lt;td&gt;&lt;%= link_to &quot;Destroy&quot;, {:action =&gt; &quot;destroy#{@scaffold_suffix}&quot;, :id =&gt; entry}, { :confirm =&gt; &quot;Are you sure?&quot;, :method =&gt; :post } %&gt;&lt;/td&gt;
   &lt;/tr&gt;
 &lt;% end %&gt;
 &lt;/table&gt;</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/templates/scaffolds/list.rhtml</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,4 @@
 require File.dirname(__FILE__) + '/assertions'
-require File.dirname(__FILE__) + '/deprecated_assertions'
 
 module ActionController #:nodoc:
   class Base
@@ -18,14 +17,14 @@ module ActionController #:nodoc:
       end
     end
 
-    alias_method :process_without_test, :process
-    alias_method :process, :process_with_test
+    alias_method_chain :process, :test
   end
 
   class TestRequest &lt; AbstractRequest #:nodoc:
     attr_accessor :cookies, :session_options
     attr_accessor :query_parameters, :request_parameters, :path, :session, :env
     attr_accessor :host
+    attr_reader   :request_uri_overridden
 
     def initialize(query_parameters = nil, request_parameters = nil, session = nil)
       @query_parameters   = query_parameters || {}
@@ -39,8 +38,8 @@ module ActionController #:nodoc:
     end
 
     def reset_session
-      @session = {}
-    end              
+      @session = TestSession.new
+    end
 
     def raw_post
       if raw_post = env['RAW_POST_DATA']
@@ -69,16 +68,22 @@ module ActionController #:nodoc:
     # Used to check AbstractRequest's request_uri functionality.
     # Disables the use of @path and @request_uri so superclass can handle those.
     def set_REQUEST_URI(value)
+      @request_uri_overridden = true
       @env[&quot;REQUEST_URI&quot;] = value
       @request_uri = nil
       @path = nil
     end
 
     def request_uri=(uri)
+      @env[&quot;REQUEST_URI&quot;] = uri
       @request_uri = uri
       @path = uri.split(&quot;?&quot;).first
     end
 
+    def accept=(mime_types)
+      @env[&quot;HTTP_ACCEPT&quot;] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(&quot;,&quot;)
+    end
+
     def remote_addr=(addr)
       @env['REMOTE_ADDR'] = addr
     end
@@ -103,7 +108,7 @@ module ActionController #:nodoc:
         if value.is_a? Fixnum
           value = value.to_s
         elsif value.is_a? Array
-          value = ActionController::Routing::PathComponent::Result.new(value)
+          value = ActionController::Routing::PathSegment::Result.new(value)
         end
 
         if extra_keys.include?(key.to_sym)
@@ -112,6 +117,7 @@ module ActionController #:nodoc:
           path_parameters[key.to_s] = value
         end
       end
+      @parameters = nil # reset TestRequest#parameters to use the new path_parameters
     end                        
     
     def recycle!
@@ -176,7 +182,7 @@ module ActionController #:nodoc:
 
     # returns the redirection location or nil
     def redirect_url
-      redirect? ? headers['location'] : nil
+      headers['Location']
     end
 
     # does the redirect location match this regexp pattern?
@@ -272,27 +278,40 @@ module ActionController #:nodoc:
   end
 
   class TestSession #:nodoc:
-    def initialize(attributes = {})
+    attr_accessor :session_id
+
+    def initialize(attributes = nil)
+      @session_id = ''
       @attributes = attributes
+      @saved_attributes = nil
+    end
+
+    def data
+      @attributes ||= @saved_attributes || {}
     end
 
     def [](key)
-      @attributes[key]
+      data[key]
     end
 
     def []=(key, value)
-      @attributes[key] = value
+      data[key] = value
     end
 
-    def session_id
-      &quot;&quot;
+    def update
+      @saved_attributes = @attributes
     end
 
-    def update() end
-    def close() end
-    def delete() @attributes = {} end
+    def delete
+      @attributes = nil
+    end
+
+    def close
+      update
+      delete
+    end
   end
-  
+
   # Essentially generates a modified Tempfile object similar to the object
   # you'd get from the standard library CGI module in a multipart
   # request. This means you can use an ActionController::TestUploadedFile
@@ -301,6 +320,7 @@ module ActionController #:nodoc:
   #
   # Usage example, within a functional test:
   #   post :change_avatar, :avatar =&gt; ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png')
+  require 'tempfile'
   class TestUploadedFile
     # The filename, *not* including the path, of the &quot;uploaded&quot; file
     attr_reader :original_filename
@@ -309,7 +329,7 @@ module ActionController #:nodoc:
     attr_reader :content_type
     
     def initialize(path, content_type = 'text/plain')
-      raise &quot;file does not exist&quot; unless File.exist?(path)
+      raise &quot;#{path} file does not exist&quot; unless File.exist?(path)
       @content_type = content_type
       @original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
       @tempfile = Tempfile.new(@original_filename)
@@ -333,7 +353,7 @@ module ActionController #:nodoc:
       %w( get post put delete head ).each do |method|
         base.class_eval &lt;&lt;-EOV, __FILE__, __LINE__
           def #{method}(action, parameters = nil, session = nil, flash = nil)
-            @request.env['REQUEST_METHOD'] = &quot;#{method.upcase}&quot; if @request
+            @request.env['REQUEST_METHOD'] = &quot;#{method.upcase}&quot; if defined?(@request)
             process(action, parameters, session, flash)
           end
         EOV
@@ -344,8 +364,10 @@ module ActionController #:nodoc:
     def process(action, parameters = nil, session = nil, flash = nil)
       # Sanity check for required instance variables so we can give an
       # understandable error message.
-      %w(controller request response).each do |iv_name|
-        raise &quot;@#{iv_name} is nil: make sure you set it in your test's setup method.&quot; if instance_variable_get(&quot;@#{iv_name}&quot;).nil?
+      %w(@controller @request @response).each do |iv_name|
+        if !instance_variables.include?(iv_name) || instance_variable_get(iv_name).nil?
+          raise &quot;#{iv_name} is nil: make sure you set it in your test's setup method.&quot;
+        end
       end
 
       @request.recycle!
@@ -374,8 +396,9 @@ module ActionController #:nodoc:
     alias xhr :xml_http_request
 
     def follow_redirect
-      if @response.redirected_to[:controller]
-        raise &quot;Can't follow redirects outside of current controller (#{@response.redirected_to[:controller]})&quot;
+      redirected_controller = @response.redirected_to[:controller]
+      if redirected_controller &amp;&amp; redirected_controller != @controller.controller_name
+        raise &quot;Can't follow redirects outside of current controller (from #{@controller.controller_name} to #{redirected_controller})&quot;
       end
 
       get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys)
@@ -406,12 +429,12 @@ module ActionController #:nodoc:
     end
 
     def build_request_uri(action, parameters)
-      unless @request.env['REQUEST_URI']
+      unless @request.request_uri_overridden
         options = @controller.send(:rewrite_options, parameters)
         options.update(:only_path =&gt; true, :action =&gt; action)
 
         url = ActionController::UrlRewriter.new(@request, parameters)
-        @request.set_REQUEST_URI(url.rewrite(options))
+        @request.request_uri = url.rewrite(options)
       end
     end
 
@@ -428,7 +451,7 @@ module ActionController #:nodoc:
     end
 
     def method_missing(selector, *args)
-      return @controller.send(selector, *args) if ActionController::Routing::NamedRoutes::Helpers.include?(selector)
+      return @controller.send(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
       return super
     end
     
@@ -448,13 +471,15 @@ module ActionController #:nodoc:
     # The new instance is yielded to the passed block. Typically the block
     # will create some routes using map.draw { map.connect ... }:
     #
-    #   with_routing do |set|
-    #     set.draw { set.connect ':controller/:id/:action' }
-    #     assert_equal(
-    #        ['/content/10/show', {}],
-    #        set.generate(:controller =&gt; 'content', :id =&gt; 10, :action =&gt; 'show')
-    #     )
-    #   end
+    #  with_routing do |set|
+    #    set.draw do |map|
+    #      map.connect ':controller/:action/:id'
+    #        assert_equal(
+    #          ['/content/10/show', {}],
+    #          map.generate(:controller =&gt; 'content', :id =&gt; 10, :action =&gt; 'show')
+    #      end
+    #    end
+    #  end
     #
     def with_routing
       real_routes = ActionController::Routing::Routes</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/test_process.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,13 +1,72 @@
 module ActionController
+  
+  # Write URLs from arbitrary places in your codebase, such as your mailers.
+  # 
+  # Example:
+  # 
+  #   class MyMailer
+  #     include ActionController::UrlWriter
+  #     default_url_options[:host] = 'www.basecamphq.com'
+  #     
+  #     def signup_url(token)
+  #       url_for(:controller =&gt; 'signup', action =&gt; 'index', :token =&gt; token)
+  #     end
+  #  end
+  # 
+  # In addition to providing +url_for+, named routes are also accessible after
+  # including UrlWriter.
+  # 
+  module UrlWriter
+    
+    # The default options for urls written by this writer. Typically a :host pair
+    # is provided.
+    mattr_accessor :default_url_options
+    self.default_url_options = {}
+    
+    def self.included(base) #:nodoc:
+      ActionController::Routing::Routes.named_routes.install base
+      base.mattr_accessor :default_url_options
+      base.default_url_options ||= default_url_options
+    end
+    
+    # Generate a url with the provided options. The following special options may
+    # effect the constructed url:
+    # 
+    #   * :host Specifies the host the link should be targetted at. This option
+    #     must be provided either explicitly, or via default_url_options.
+    #   * :protocol The protocol to connect to. Defaults to 'http'
+    #   * :port Optionally specify the port to connect to.
+    # 
+    def url_for(options)
+      options = self.class.default_url_options.merge(options)
+      
+      url = ''
+      unless options.delete :only_path
+        url &lt;&lt; (options.delete(:protocol) || 'http')
+        url &lt;&lt; '://'
+        
+        raise &quot;Missing host to link to! Please provide :host parameter or set default_url_options[:host]&quot; unless options[:host]
+        url &lt;&lt; options.delete(:host)
+        url &lt;&lt; &quot;:#{options.delete(:port)}&quot; if options.key?(:port)
+      else
+        # Delete the unused options to prevent their appearance in the query string
+        [:protocol, :host, :port].each { |k| options.delete k }
+      end
+      anchor = &quot;##{options.delete(:anchor)}&quot; if options.key?(:anchor)
+      url &lt;&lt; Routing::Routes.generate(options, {})
+      return &quot;#{url}#{anchor}&quot;
+    end
+    
+  end
+  
   # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
-
   class UrlRewriter #:nodoc:
     RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :trailing_slash, :skip_relative_url_root]
     def initialize(request, parameters)
       @request, @parameters = request, parameters
     end
     
-    def rewrite(options = {})      
+    def rewrite(options = {})
       rewrite_url(rewrite_path(options), options)
     end
 
@@ -18,6 +77,7 @@ module ActionController
     alias_method :to_s, :to_str
 
     private
+      # Given a path and options, returns a rewritten URL string
       def rewrite_url(path, options)
         rewritten_url = &quot;&quot;
         unless options[:only_path]
@@ -33,6 +93,7 @@ module ActionController
         rewritten_url
       end
 
+      # Given a Hash of options, generates a route
       def rewrite_path(options)
         options = options.symbolize_keys
         options.update(options[:params].symbolize_keys) if options[:params]
@@ -41,34 +102,10 @@ module ActionController
           options.update(overwrite)
         end
         RESERVED_OPTIONS.each {|k| options.delete k}
-        path, extra_keys = Routing::Routes.generate(options.dup, @request) # Warning: Routes will mutate and violate the options hash
-
-        path &lt;&lt; build_query_string(options, extra_keys) unless extra_keys.empty?
-        
-        path
-      end
 
-      # Returns a query string with escaped keys and values from the passed hash. If the passed hash contains an &quot;id&quot; it'll
-      # be added as a path element instead of a regular parameter pair.
-      def build_query_string(hash, only_keys = nil)
-        elements = []
-        query_string = &quot;&quot;
-
-        only_keys ||= hash.keys
-        
-        only_keys.each do |key|
-          value = hash[key] 
-          key = CGI.escape key.to_s
-          if value.class == Array
-            key &lt;&lt;  '[]'
-          else
-            value = [ value ]
-          end
-          value.each { |val| elements &lt;&lt; &quot;#{key}=#{Routing.extract_parameter_value(val)}&quot; }
-        end
-        
-        query_string &lt;&lt; (&quot;?&quot; + elements.join(&quot;&amp;&quot;)) unless elements.empty?
-        query_string
+        # Generates the query string, too
+        Routing::Routes.generate(options, @request.symbolized_path_parameters)
       end
   end
+  
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/url_rewriter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,6 @@
 require File.dirname(__FILE__) + '/tokenizer'
 require File.dirname(__FILE__) + '/node'
+require File.dirname(__FILE__) + '/selector'
 
 module HTML #:nodoc:
   </diff>
      <filename>vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb</filename>
    </modified>
    <modified>
      <diff>@@ -92,7 +92,6 @@ module HTML #:nodoc:
     # returns non +nil+. Returns the result of the #find call that succeeded.
     def find(conditions)
       conditions = validate_conditions(conditions)
-
       @children.each do |child|        
         node = child.find(conditions)
         return node if node
@@ -152,11 +151,11 @@ module HTML #:nodoc:
 
           if scanner.skip(/!\[CDATA\[/)
             scanner.scan_until(/\]\]&gt;/)
-            return CDATA.new(parent, line, pos, scanner.pre_match)
+            return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/&lt;!\[CDATA\[/, ''))
           end
           
           closing = ( scanner.scan(/\//) ? :close : nil )
-          return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:]+/)
+          return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
           name.downcase!
   
           unless closing
@@ -239,7 +238,7 @@ module HTML #:nodoc:
     def match(conditions)
       case conditions
         when String
-          @content.index(conditions)
+          @content == conditions
         when Regexp
           @content =~ conditions
         when Hash
@@ -316,7 +315,7 @@ module HTML #:nodoc:
         s = &quot;&lt;#{@name}&quot;
         @attributes.each do |k,v|
           s &lt;&lt; &quot; #{k}&quot;
-          s &lt;&lt; &quot;='#{v.gsub(/'/,&quot;\\\\'&quot;)}'&quot; if String === v
+          s &lt;&lt; &quot;=\&quot;#{v}\&quot;&quot; if String === v
         end
         s &lt;&lt; &quot; /&quot; if @closing == :self
         s &lt;&lt; &quot;&gt;&quot;
@@ -410,7 +409,6 @@ module HTML #:nodoc:
     #                               :child =&gt; /hello world/ }
     def match(conditions)
       conditions = validate_conditions(conditions)
-
       # check content of child nodes
       if conditions[:content]
         if children.empty?
@@ -455,7 +453,6 @@ module HTML #:nodoc:
       # count children
       if opts = conditions[:children]
         matches = children.select do |c|
-          c.match(/./) or
           (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
         end
         </diff>
      <filename>vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,6 @@
 module ActionController #:nodoc:
   module Verification #:nodoc:
-    def self.append_features(base) #:nodoc:
-      super
+    def self.included(base) #:nodoc:
       base.extend(ClassMethods)
     end
 
@@ -18,19 +17,26 @@ module ActionController #:nodoc:
     # Usage:
     #
     #   class GlobalController &lt; ActionController::Base
-    #     # prevent the #update_settings action from being invoked unless
-    #     # the 'admin_privileges' request parameter exists.
+    #     # Prevent the #update_settings action from being invoked unless
+    #     # the 'admin_privileges' request parameter exists. The
+    #     # settings action will be redirected to in current controller
+    #     # if verification fails.
     #     verify :params =&gt; &quot;admin_privileges&quot;, :only =&gt; :update_post,
     #            :redirect_to =&gt; { :action =&gt; &quot;settings&quot; }
     #
-    #     # disallow a post from being updated if there was no information
+    #     # Disallow a post from being updated if there was no information
     #     # submitted with the post, and if there is no active post in the
-    #     # session, and if there is no &quot;note&quot; key in the flash.
+    #     # session, and if there is no &quot;note&quot; key in the flash. The route
+    #     # named category_url will be redirected to if verification fails.
+    #
     #     verify :params =&gt; &quot;post&quot;, :session =&gt; &quot;post&quot;, &quot;flash&quot; =&gt; &quot;note&quot;,
     #            :only =&gt; :update_post,
     #            :add_flash =&gt; { &quot;alert&quot; =&gt; &quot;Failed to create your message&quot; },
     #            :redirect_to =&gt; :category_url
     #
+    # Note that these prerequisites are not business rules. They do not examine 
+    # the content of the session or the parameters. That level of validation should
+    # be encapsulated by your domain model or helper methods in the controller.
     module ClassMethods
       # Verify the given actions so that if certain prerequisites are not met,
       # the user is redirected to a different action. The +options+ parameter
@@ -40,7 +46,7 @@ module ActionController #:nodoc:
       #   be in the &lt;tt&gt;params&lt;/tt&gt; hash in order for the action(s) to be safely
       #   called.
       # * &lt;tt&gt;:session&lt;/tt&gt;: a single key or an array of keys that must
-      #   be in the @session in order for the action(s) to be safely called.
+      #   be in the &lt;tt&gt;session&lt;/tt&gt; in order for the action(s) to be safely called.
       # * &lt;tt&gt;:flash&lt;/tt&gt;: a single key or an array of keys that must
       #   be in the flash in order for the action(s) to be safely called.
       # * &lt;tt&gt;:method&lt;/tt&gt;: a single key or an array of keys--any one of which
@@ -51,8 +57,12 @@ module ActionController #:nodoc:
       #   from an Ajax call or not. 
       # * &lt;tt&gt;:add_flash&lt;/tt&gt;: a hash of name/value pairs that should be merged
       #   into the session's flash if the prerequisites cannot be satisfied.
+      # * &lt;tt&gt;:add_headers&lt;/tt&gt;: a hash of name/value pairs that should be
+      #   merged into the response's headers hash if the prerequisites cannot
+      #   be satisfied.
       # * &lt;tt&gt;:redirect_to&lt;/tt&gt;: the redirection parameters to be used when
-      #   redirecting if the prerequisites cannot be satisfied.
+      #   redirecting if the prerequisites cannot be satisfied. You can 
+      #   redirect either to named route or to the action in some controller.
       # * &lt;tt&gt;:render&lt;/tt&gt;: the render parameters to be used when
       #   the prerequisites cannot be satisfied.
       # * &lt;tt&gt;:only&lt;/tt&gt;: only apply this verification to the actions specified
@@ -69,21 +79,23 @@ module ActionController #:nodoc:
 
     def verify_action(options) #:nodoc:
       prereqs_invalid =
-        [*options[:params] ].find { |v| @params[v].nil?  } ||
-        [*options[:session]].find { |v| @session[v].nil? } ||
+        [*options[:params] ].find { |v| params[v].nil?  } ||
+        [*options[:session]].find { |v| session[v].nil? } ||
         [*options[:flash]  ].find { |v| flash[v].nil?    }
       
       if !prereqs_invalid &amp;&amp; options[:method]
         prereqs_invalid ||= 
-          [*options[:method]].all? { |v| @request.method != v.to_sym }
+          [*options[:method]].all? { |v| request.method != v.to_sym }
       end
       
       prereqs_invalid ||= (request.xhr? != options[:xhr]) unless options[:xhr].nil?
       
       if prereqs_invalid
         flash.update(options[:add_flash]) if options[:add_flash]
+        response.headers.update(options[:add_headers]) if options[:add_headers]
         unless performed?
           render(options[:render]) if options[:render]
+          options[:redirect_to] = self.send(options[:redirect_to]) if options[:redirect_to].is_a? Symbol
           redirect_to(options[:redirect_to]) if options[:redirect_to]
         end
         return false</diff>
      <filename>vendor/rails/actionpack/lib/action_controller/verification.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 #--
-# Copyright (c) 2004 David Heinemeier Hansson
+# Copyright (c) 2004-2006 David Heinemeier Hansson
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the</diff>
      <filename>vendor/rails/actionpack/lib/action_pack.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,8 @@
 module ActionPack #:nodoc:
   module VERSION #:nodoc:
     MAJOR = 1
-    MINOR = 12
-    TINY  = 5
+    MINOR = 13
+    TINY  = 6
     
     STRING = [MAJOR, MINOR, TINY].join('.')
   end</diff>
      <filename>vendor/rails/actionpack/lib/action_pack/version.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 #--
-# Copyright (c) 2004 David Heinemeier Hansson
+# Copyright (c) 2004-2006 David Heinemeier Hansson
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the</diff>
      <filename>vendor/rails/actionpack/lib/action_view.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,6 @@
 require 'erb'
 
 module ActionView #:nodoc:
-
   class ActionViewError &lt; StandardError #:nodoc:
   end
 
@@ -54,13 +53,22 @@ module ActionView #:nodoc:
   # 
   # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
   #
-  #   &lt;%= render &quot;shared/header&quot;, { &quot;headline&quot; =&gt; &quot;Welcome&quot;, &quot;person&quot; =&gt; person } %&gt;
+  #   &lt;%= render &quot;shared/header&quot;, { :headline =&gt; &quot;Welcome&quot;, :person =&gt; person } %&gt;
   #
   # These can now be accessed in shared/header with:
   #
   #   Headline: &lt;%= headline %&gt;
   #   First name: &lt;%= person.first_name %&gt;
   #
+  # If you need to find out whether a certain local variable has been assigned a value in a particular render call,
+  # you need to use the following pattern:
+  #
+  #   &lt;% if local_assigns.has_key? :headline %&gt;
+  #     Headline: &lt;%= headline %&gt;
+  #   &lt;% end %&gt;
+  #
+  # Testing using &lt;tt&gt;defined? headline&lt;/tt&gt; will not work. This is an implementation restriction.
+  #
   # == Template caching
   #
   # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
@@ -140,7 +148,7 @@ module ActionView #:nodoc:
   #
   # This refreshes the sidebar, removes a person element and highlights the user list.
   # 
-  # See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator documentation for more details.
+  # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
   class Base
     include ERB::Util
 
@@ -148,10 +156,11 @@ module ActionView #:nodoc:
     attr_accessor :base_path, :assigns, :template_extension
     attr_accessor :controller
 
-    attr_reader :logger, :params, :request, :response, :session, :headers, :flash
+    attr_reader :logger, :response, :headers
+    attr_internal *ActionController::Base::DEPRECATED_INSTANCE_VARIABLES
 
     # Specify trim mode for the ERB compiler. Defaults to '-'.
-    # See ERB documentation for suitable values.
+    # See ERb documentation for suitable values.
     @@erb_trim_mode = '-'
     cattr_accessor :erb_trim_mode
 
@@ -182,24 +191,24 @@ module ActionView #:nodoc:
     end
     include CompiledTemplates
 
-    # maps inline templates to their method names 
+    # Maps inline templates to their method names 
     @@method_names = {}
-    # map method names to their compile time
+    # Map method names to their compile time
     @@compile_time = {}
-    # map method names to the names passed in local assigns so far
+    # Map method names to the names passed in local assigns so far
     @@template_args = {}
-    # count the number of inline templates
+    # Count the number of inline templates
     @@inline_template_count = 0
-    # maps template paths without extension to their file extension returned by pick_template_extension.
-    # if for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
-    # used by pick_template_extension determines whether ext1 or ext2 will be stored
+    # Maps template paths without extension to their file extension returned by pick_template_extension.
+    # If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
+    # used by pick_template_extension determines whether ext1 or ext2 will be stored.
     @@cached_template_extension = {}
 
     class ObjectWrapper &lt; Struct.new(:value) #:nodoc:
     end
 
     def self.load_helpers(helper_dir)#:nodoc:
-      Dir.foreach(helper_dir) do |helper_file| 
+      Dir.entries(helper_dir).sort.each do |helper_file|
         next unless helper_file =~ /^([a-z][a-z_]*_helper).rb$/
         require File.join(helper_dir, $1)
         helper_module_name = $1.camelize
@@ -296,11 +305,13 @@ module ActionView #:nodoc:
     # Render the provided template with the given local assigns. If the template has not been rendered with the provided
     # local assigns yet, or if the template has been updated on disk, then the template will be compiled to a method.
     #
-
     # Either, but not both, of template and file_path may be nil. If file_path is given, the template
     # will only be read if it has to be compiled.
     #
     def compile_and_render_template(extension, template = nil, file_path = nil, local_assigns = {}) #:nodoc:
+      # convert string keys to symbols if requested
+      local_assigns = local_assigns.symbolize_keys if @@local_assigns_support_string_keys
+
       # compile the given template, if necessary
       if compile_template?(template, file_path, local_assigns)
         template ||= read_template_file(file_path, extension)
@@ -309,9 +320,7 @@ module ActionView #:nodoc:
 
       # Get the method name for this template and run it
       method_name = @@method_names[file_path || template]
-      evaluate_assigns                                    
-
-      local_assigns = local_assigns.symbolize_keys if @@local_assigns_support_string_keys
+      evaluate_assigns
 
       send(method_name, local_assigns) do |*name|
         instance_variable_get &quot;@content_for_#{name.first || 'layout'}&quot;
@@ -337,14 +346,14 @@ module ActionView #:nodoc:
     def builder_template_exists?(template_path)#:nodoc:
       template_exists?(template_path, :rxml)
     end
-    
+
     def javascript_template_exists?(template_path)#:nodoc:
       template_exists?(template_path, :rjs)
     end
 
     def file_exists?(template_path)#:nodoc:
       template_file_name, template_file_extension = path_and_extension(template_path)
-      
+
       if template_file_extension
         template_exists?(template_file_name, template_file_extension)
       else
@@ -361,10 +370,12 @@ module ActionView #:nodoc:
     end
 
     private
+      # Builds a string holding the full path of the template including extension
       def full_template_path(template_path, extension)
         &quot;#{@base_path}/#{template_path}.#{extension}&quot;
       end
 
+      # Asserts the existence of a template.
       def template_exists?(template_path, extension)
         file_path = full_template_path(template_path, extension)
         @@method_names.has_key?(file_path) || FileTest.exists?(file_path)
@@ -374,11 +385,12 @@ module ActionView #:nodoc:
         template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
         [ template_path_without_extension, $1 ]
       end
-      
+
       def cached_template_extension(template_path)
         @@cache_template_extensions &amp;&amp; @@cached_template_extension[template_path]
-      end      
-          
+      end
+
+      # Determines the template's file extension, such as rhtml, rxml, or rjs.
       def find_template_extension_for(template_path)
         if match = delegate_template_exists?(template_path)
           match.first.to_sym
@@ -386,7 +398,7 @@ module ActionView #:nodoc:
         elsif builder_template_exists?(template_path):    :rxml
         elsif javascript_template_exists?(template_path): :rjs
         else
-          raise ActionViewError, &quot;No rhtml, rxml, rjs or delegate template found for #{template_path}&quot;
+          raise ActionViewError, &quot;No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}&quot;
         end
       end
 
@@ -395,6 +407,7 @@ module ActionView #:nodoc:
         File.read(template_path)
       end
 
+      # Evaluate the local assigns and pushes them to the view.
       def evaluate_assigns
         unless @assigns_added
           assign_variables_from_controller
@@ -406,6 +419,7 @@ module ActionView #:nodoc:
         handler.new(self).render(template, local_assigns)
       end
 
+      # Assigns instance variables from the controller to the view.
       def assign_variables_from_controller
         @assigns.each { |key, value| instance_variable_set(&quot;@#{key}&quot;, value) }
       end
@@ -416,35 +430,36 @@ module ActionView #:nodoc:
         local_assigns.empty? ||
           ((args = @@template_args[render_symbol]) &amp;&amp; local_assigns.all? { |k,_| args.has_key?(k) })
       end
-      
-      # Check whether compilation is necessary.
-      # Compile if the inline template or file has not been compiled yet.
-      # Or if local_assigns has a new key, which isn't supported by the compiled code yet.
-      # Or if the file has changed on disk and checking file mods hasn't been disabled. 
+
+      # Method to check whether template compilation is necessary.
+      # The template will be compiled if the inline template or file has not been compiled yet,
+      # if local_assigns has a new key, which isn't supported by the compiled code yet,
+      # or if the file has changed on disk and checking file mods hasn't been disabled.
       def compile_template?(template, file_name, local_assigns)
         method_key    = file_name || template
         render_symbol = @@method_names[method_key]
 
         if @@compile_time[render_symbol] &amp;&amp; supports_local_assigns?(render_symbol, local_assigns)
           if file_name &amp;&amp; !@@cache_template_loading 
-            @@compile_time[render_symbol] &lt; File.mtime(file_name) || (File.symlink?(file_name) ? 
-              @@compile_time[render_symbol] &lt; File.lstat(file_name).mtime : false)
+            @@compile_time[render_symbol] &lt; File.mtime(file_name) ||
+              (File.symlink?(file_name) &amp;&amp; (@@compile_time[render_symbol] &lt; File.lstat(file_name).mtime))
           end
         else
           true
         end
       end
 
-      # Create source code for given template
+      # Method to create the source code for a given template.
       def create_template_source(extension, template, render_symbol, locals)
         if template_requires_setup?(extension)
           body = case extension.to_sym
             when :rxml
-              &quot;xml = Builder::XmlMarkup.new(:indent =&gt; 2)\n&quot; +
-              &quot;@controller.headers['Content-Type'] ||= 'application/xml'\n&quot; +
-              template
+              &quot;controller.response.content_type ||= 'application/xml'\n&quot; +
+              &quot;xml ||= Builder::XmlMarkup.new(:indent =&gt; 2)\n&quot; +
+              template +
+              &quot;\nxml.target!\n&quot;
             when :rjs
-              &quot;@controller.headers['Content-Type'] ||= 'text/javascript'\n&quot; +
+              &quot;controller.response.content_type ||= 'text/javascript'\n&quot; +
               &quot;update_page do |page|\n#{template}\nend&quot;
           end
         else
@@ -457,49 +472,43 @@ module ActionView #:nodoc:
 
         locals_code = &quot;&quot;
         locals_keys.each do |key|
-          locals_code &lt;&lt; &quot;#{key} = local_assigns[:#{key}] if local_assigns.has_key?(:#{key})\n&quot;
+          locals_code &lt;&lt; &quot;#{key} = local_assigns[:#{key}]\n&quot;
         end
 
         &quot;def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend&quot;
       end
 
-      def template_requires_setup?(extension)
+      def template_requires_setup?(extension) #:nodoc:
         templates_requiring_setup.include? extension.to_s
       end
 
-      def templates_requiring_setup
+      def templates_requiring_setup #:nodoc:
         %w(rxml rjs)
       end
 
       def assign_method_name(extension, template, file_name)
-        method_name = '_run_'
-        method_name &lt;&lt; &quot;#{extension}_&quot; if extension
-
-        if file_name
-          file_path = File.expand_path(file_name)
-          base_path = File.expand_path(@base_path)
-
-          i = file_path.index(base_path)
-          l = base_path.length
+        method_key = file_name || template
+        @@method_names[method_key] ||= compiled_method_name(extension, template, file_name)
+      end
 
-          method_name_file_part = i ? file_path[i+l+1,file_path.length-l-1] : file_path.clone
-          method_name_file_part.sub!(/\.r(html|xml|js)$/,'')
-          method_name_file_part.tr!('/:-', '_')
-          method_name_file_part.gsub!(/[^a-zA-Z0-9_]/){|s| s[0].to_s}
+      def compiled_method_name(extension, template, file_name)
+        ['_run', extension, compiled_method_name_file_path_segment(file_name)].compact.join('_').to_sym
+      end
 
-          method_name += method_name_file_part
+      def compiled_method_name_file_path_segment(file_name)
+        if file_name
+          s = File.expand_path(file_name)
+          s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
+          s.gsub!(/([^a-zA-Z0-9_])/) { $1[0].to_s }
+          s
         else
-          @@inline_template_count += 1
-          method_name &lt;&lt; @@inline_template_count.to_s
+          (@@inline_template_count += 1).to_s
         end
-
-        @@method_names[file_name || template] = method_name.intern
       end
 
+      # Compile and evaluate the template's code
       def compile_template(extension, template, file_name, local_assigns)
-        method_key = file_name || template
-
-        render_symbol = @@method_names[method_key] || assign_method_name(extension, template, file_name)
+        render_symbol = assign_method_name(extension, template, file_name)
         render_source = create_template_source(extension, template, render_symbol, local_assigns.keys)
 
         line_offset = @@template_args[render_symbol].size
@@ -516,18 +525,18 @@ module ActionView #:nodoc:
           else
             CompiledTemplates.module_eval(render_source, 'compiled-template', -line_offset)
           end
-        rescue Object =&gt; e
+        rescue Exception =&gt; e  # errors from template code
           if logger
             logger.debug &quot;ERROR: compiling #{render_symbol} RAISED #{e}&quot;
             logger.debug &quot;Function body: #{render_source}&quot;
             logger.debug &quot;Backtrace: #{e.backtrace.join(&quot;\n&quot;)}&quot;
           end
 
-          raise TemplateError.new(@base_path, method_key, @assigns, template, e)
+          raise TemplateError.new(@base_path, file_name || template, @assigns, template, e)
         end
 
         @@compile_time[render_symbol] = Time.now
-        # logger.debug &quot;Compiled template #{method_key}\n  ==&gt; #{render_symbol}&quot; if logger
+        # logger.debug &quot;Compiled template #{file_name || template}\n  ==&gt; #{render_symbol}&quot; if logger
       end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,14 +3,14 @@ module ActionView
 
   # CompiledTemplates modules hold methods that have been compiled.
   # Templates are compiled into these methods so that they do not need to be
-  # re-read and re-parsed each request.
+  # read and parsed for each request.
   #
   # Each template may be compiled into one or more methods. Each method accepts a given
   # set of parameters which is used to implement local assigns passing.
   #
   # To use a compiled template module, create a new instance and include it into the class
   # in which you want the template to be rendered.
-  class CompiledTemplates &lt; Module #:nodoc:
+  class CompiledTemplates &lt; Module
     attr_reader :method_names
 
     def initialize
@@ -55,7 +55,7 @@ module ActionView
       begin
         module_eval(method_def, fake_file_name, initial_line_number)
         @mtimes[full_key(identifier, arg_names)] = Time.now
-      rescue Object =&gt; e
+      rescue Exception =&gt; e  # errors from compiled source
         e.blame_file! identifier
         raise
       end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/compiled_templates.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,17 +13,18 @@ module ActionView
     # is a great of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
     # In that case, it's better to use the input method and the specialized form methods in link:classes/ActionView/Helpers/FormHelper.html
     module ActiveRecordHelper
-      # Returns a default input tag for the type of object returned by the method. Example
-      # (title is a VARCHAR column and holds &quot;Hello World&quot;):
+      # Returns a default input tag for the type of object returned by the method. For example, let's say you have a model
+      # that has an attribute +title+ of type VARCHAR column, and this instance holds &quot;Hello World&quot;:
       #   input(&quot;post&quot;, &quot;title&quot;) =&gt;
       #     &lt;input id=&quot;post_title&quot; name=&quot;post[title]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;Hello World&quot; /&gt;
       def input(record_name, method, options = {})
         InstanceTag.new(record_name, method, self).to_tag(options)
       end
 
-      # Returns an entire form with input tags and everything for a specified Active Record object. Example
-      # (post is a new record that has a title using VARCHAR and a body using TEXT):
-      #   form(&quot;post&quot;) =&gt;
+      # Returns an entire form with all needed input tags for a specified Active Record object. For example, let's say you 
+      # have a table model &lt;tt&gt;Post&lt;/tt&gt; with attributes named &lt;tt&gt;title&lt;/tt&gt; of type &lt;tt&gt;VARCHAR&lt;/tt&gt; and &lt;tt&gt;body&lt;/tt&gt; of type &lt;tt&gt;TEXT&lt;/tt&gt;:
+      #   form(&quot;post&quot;) 
+      # That line would yield a form like the following:
       #     &lt;form action='/post/create' method='post'&gt;
       #       &lt;p&gt;
       #         &lt;label for=&quot;post_title&quot;&gt;Title&lt;/label&gt;&lt;br /&gt;
@@ -32,14 +33,13 @@ module ActionView
       #       &lt;p&gt;
       #         &lt;label for=&quot;post_body&quot;&gt;Body&lt;/label&gt;&lt;br /&gt;
       #         &lt;textarea cols=&quot;40&quot; id=&quot;post_body&quot; name=&quot;post[body]&quot; rows=&quot;20&quot;&gt;
-      #           Back to the hill and over it again!
       #         &lt;/textarea&gt;
       #       &lt;/p&gt;
       #       &lt;input type='submit' value='Create' /&gt;
       #     &lt;/form&gt;
       #
       # It's possible to specialize the form builder by using a different action name and by supplying another
-      # block renderer. Example (entry is a new record that has a message attribute using VARCHAR):
+      # block renderer. For example, let's say you have a model &lt;tt&gt;Entry&lt;/tt&gt; with an attribute &lt;tt&gt;message&lt;/tt&gt; of type &lt;tt&gt;VARCHAR&lt;/tt&gt;:
       #
       #   form(&quot;entry&quot;, :action =&gt; &quot;sign&quot;, :input_block =&gt;
       #        Proc.new { |record, column| &quot;#{column.human_name}: #{input(record, column.name)}&lt;br /&gt;&quot; }) =&gt;
@@ -74,47 +74,73 @@ module ActionView
         content_tag('form', contents, :action =&gt; action, :method =&gt; 'post', :enctype =&gt; options[:multipart] ? 'multipart/form-data': nil)
       end
 
-      # Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
-      # This error message is wrapped in a DIV tag, which can be specialized to include both a +prepend_text+ and +append_text+
-      # to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
-      # &quot;can't be empty&quot; on the title attribute):
+      # Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
+      # This error message is wrapped in a &lt;tt&gt;DIV&lt;/tt&gt; tag, which can be extended to include a +prepend_text+ and/or +append_text+
+      # (to properly explain the error), and a +css_class+ to style it accordingly. As an example, let's say you have a model
+      # +post+ that has an error message on the +title+ attribute:
       #
       #   &lt;%= error_message_on &quot;post&quot;, &quot;title&quot; %&gt; =&gt;
       #     &lt;div class=&quot;formError&quot;&gt;can't be empty&lt;/div&gt;
       #
-      #   &lt;%= error_message_on &quot;post&quot;, &quot;title&quot;, &quot;Title simply &quot;, &quot; (or it won't work)&quot;, &quot;inputError&quot; %&gt; =&gt;
-      #     &lt;div class=&quot;inputError&quot;&gt;Title simply can't be empty (or it won't work)&lt;/div&gt;
+      #   &lt;%= error_message_on &quot;post&quot;, &quot;title&quot;, &quot;Title simply &quot;, &quot; (or it won't work).&quot;, &quot;inputError&quot; %&gt; =&gt;
+      #     &lt;div class=&quot;inputError&quot;&gt;Title simply can't be empty (or it won't work).&lt;/div&gt;
       def error_message_on(object, method, prepend_text = &quot;&quot;, append_text = &quot;&quot;, css_class = &quot;formError&quot;)
-        if errors = instance_variable_get(&quot;@#{object}&quot;).errors.on(method)
+        if (obj = instance_variable_get(&quot;@#{object}&quot;)) &amp;&amp; (errors = obj.errors.on(method))
           content_tag(&quot;div&quot;, &quot;#{prepend_text}#{errors.is_a?(Array) ? errors.first : errors}#{append_text}&quot;, :class =&gt; css_class)
+        else 
+          ''
         end
       end
 
-      # Returns a string with a div containing all the error messages for the object located as an instance variable by the name
-      # of &lt;tt&gt;object_name&lt;/tt&gt;. This div can be tailored by the following options:
+      # Returns a string with a &lt;tt&gt;DIV&lt;/tt&gt; containing all of the error messages for the objects located as instance variables by the names
+      # given.  If more than one object is specified, the errors for the objects are displayed in the order that the object names are
+      # provided.
+      #
+      # This &lt;tt&gt;DIV&lt;/tt&gt; can be tailored by the following options:
       #
       # * &lt;tt&gt;header_tag&lt;/tt&gt; - Used for the header of the error div (default: h2)
       # * &lt;tt&gt;id&lt;/tt&gt; - The id of the error div (default: errorExplanation)
       # * &lt;tt&gt;class&lt;/tt&gt; - The class of the error div (default: errorExplanation)
+      # * &lt;tt&gt;object_name&lt;/tt&gt; - The object name to use in the header, or
+      # any text that you prefer. If &lt;tt&gt;object_name&lt;/tt&gt; is not set, the name of
+      # the first object will be used.
+      #
+      # To specify the display for one object, you simply provide its name as a parameter.  For example, for the +User+ model:
+      # 
+      #   error_messages_for 'user'
+      #
+      # To specify more than one object, you simply list them; optionally, you can add an extra +object_name+ parameter, which
+      # be the name in the header.
+      #
+      #   error_messages_for 'user_common', 'user', :object_name =&gt; 'user'
       #
       # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
       # you need is significantly different from the default presentation, it makes plenty of sense to access the object.errors
       # instance yourself and set it up. View the source of this method to see how easy it is.
-      def error_messages_for(object_name, options = {})
-        options = options.symbolize_keys
-        object = instance_variable_get(&quot;@#{object_name}&quot;)
-        if object &amp;&amp; !object.errors.empty?
-          content_tag(&quot;div&quot;,
-            content_tag(
-              options[:header_tag] || &quot;h2&quot;,
-              &quot;#{pluralize(object.errors.count, &quot;error&quot;)} prohibited this #{object_name.to_s.gsub(&quot;_&quot;, &quot; &quot;)} from being saved&quot;
-            ) +
-            content_tag(&quot;p&quot;, &quot;There were problems with the following fields:&quot;) +
-            content_tag(&quot;ul&quot;, object.errors.full_messages.collect { |msg| content_tag(&quot;li&quot;, msg) }),
-            &quot;id&quot; =&gt; options[:id] || &quot;errorExplanation&quot;, &quot;class&quot; =&gt; options[:class] || &quot;errorExplanation&quot;
+      def error_messages_for(*params)
+        options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {}
+        objects = params.collect {|object_name| instance_variable_get(&quot;@#{object_name}&quot;) }.compact
+        count   = objects.inject(0) {|sum, object| sum + object.errors.count }
+        unless count.zero?
+          html = {}
+          [:id, :class].each do |key|
+            if options.include?(key)
+              value = options[key]
+              html[key] = value unless value.blank?
+            else
+              html[key] = 'errorExplanation'
+            end
+          end
+          header_message = &quot;#{pluralize(count, 'error')} prohibited this #{(options[:object_name] || params.first).to_s.gsub('_', ' ')} from being saved&quot;
+          error_messages = objects.map {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }
+          content_tag(:div,
+            content_tag(options[:header_tag] || :h2, header_message) &lt;&lt;
+              content_tag(:p, 'There were problems with the following fields:') &lt;&lt;
+              content_tag(:ul, error_messages),
+            html
           )
         else
-          &quot;&quot;
+          ''
         end
       end
 
@@ -137,12 +163,14 @@ module ActionView
             to_input_field_tag(field_type, options)
           when :text
             to_text_area_tag(options)
-          when :integer, :float
+          when :integer, :float, :decimal
             to_input_field_tag(&quot;text&quot;, options)
           when :date
             to_date_select_tag(options)
           when :datetime, :timestamp
             to_datetime_select_tag(options)
+          when :time
+            to_time_select_tag(options)
           when :boolean
             to_boolean_select_tag(options)
         end
@@ -184,6 +212,15 @@ module ActionView
         end
       end
 
+      alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag
+      def to_time_select_tag(options = {})
+        if object.respond_to?(&quot;errors&quot;) &amp;&amp; object.errors.respond_to?(&quot;on&quot;)
+          error_wrapping(to_time_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
+        else
+          to_time_select_tag_without_error_wrapping(options)
+        end
+      end
+
       def error_wrapping(html_tag, has_error)
         has_error ? Base.field_error_proc.call(html_tag, self) : html_tag
       end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/active_record_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,20 +3,36 @@ require File.dirname(__FILE__) + '/url_helper'
 require File.dirname(__FILE__) + '/tag_helper'
 
 module ActionView
-  module Helpers
-    # Provides methods for linking a HTML page together with other assets, such as javascripts, stylesheets, and feeds.
+  module Helpers #:nodoc:
+    # Provides methods for linking an HTML page together with other assets such
+    # as images, javascripts, stylesheets, and feeds. You can direct Rails to
+    # link to assets from a dedicated assets server by setting ActionController::Base.asset_host
+    # in your environment.rb. These methods do not verify the assets exist before
+    # linking to them.
+    #
+    #   ActionController::Base.asset_host = &quot;http://assets.example.com&quot;
+    #   image_tag(&quot;rails.png&quot;)  
+    #     =&gt; &lt;img src=&quot;http://assets.example.com/images/rails.png&quot; alt=&quot;Rails&quot; /&gt;
+    #   stylesheet_include_tag(&quot;application&quot;)
+    #     =&gt; &lt;link href=&quot;http://assets.example.com/stylesheets/application.css&quot; media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&gt;
     module AssetTagHelper
-      # Returns a link tag that browsers and news readers can use to auto-detect a RSS or ATOM feed for this page. The +type+ can
-      # either be &lt;tt&gt;:rss&lt;/tt&gt; (default) or &lt;tt&gt;:atom&lt;/tt&gt; and the +options+ follow the url_for style of declaring a link target.
+      # Returns a link tag that browsers and news readers can use to auto-detect
+      # an RSS or ATOM feed. The +type+ can either be &lt;tt&gt;:rss&lt;/tt&gt; (default) or 
+      # &lt;tt&gt;:atom&lt;/tt&gt;. Control the link options in url_for format using the
+      # +url_options+. You can modify the LINK tag itself in +tag_options+.
       #
-      # Examples:
-      #   auto_discovery_link_tag # =&gt;
+      # Tag Options:
+      # * &lt;tt&gt;:rel&lt;/tt&gt;  - Specify the relation of this link, defaults to &quot;alternate&quot;
+      # * &lt;tt&gt;:type&lt;/tt&gt;  - Override the auto-generated mime type
+      # * &lt;tt&gt;:title&lt;/tt&gt;  - Specify the title of the link, defaults to the +type+
+      #
+      #  auto_discovery_link_tag # =&gt;
       #     &lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot; title=&quot;RSS&quot; href=&quot;http://www.curenthost.com/controller/action&quot; /&gt;
-      #   auto_discovery_link_tag(:atom) # =&gt;
+      #  auto_discovery_link_tag(:atom) # =&gt;
       #     &lt;link rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;ATOM&quot; href=&quot;http://www.curenthost.com/controller/action&quot; /&gt;
-      #   auto_discovery_link_tag(:rss, {:action =&gt; &quot;feed&quot;}) # =&gt;
+      #  auto_discovery_link_tag(:rss, {:action =&gt; &quot;feed&quot;}) # =&gt;
       #     &lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot; title=&quot;RSS&quot; href=&quot;http://www.curenthost.com/controller/feed&quot; /&gt;
-      #   auto_discovery_link_tag(:rss, {:action =&gt; &quot;feed&quot;}, {:title =&gt; &quot;My RSS&quot;}) # =&gt;
+      #  auto_discovery_link_tag(:rss, {:action =&gt; &quot;feed&quot;}, {:title =&gt; &quot;My RSS&quot;}) # =&gt;
       #     &lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot; title=&quot;My RSS&quot; href=&quot;http://www.curenthost.com/controller/feed&quot; /&gt;
       def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
         tag(
@@ -28,9 +44,14 @@ module ActionView
         )
       end
 
-      # Returns path to a javascript asset. Example:
+      # Computes the path to a javascript asset in the public javascripts directory.
+      # If the +source+ filename has no extension, .js will be appended.
+      # Full paths from the document root will be passed through.
+      # Used internally by javascript_include_tag to build the script path.
       #
       #   javascript_path &quot;xmlhr&quot; # =&gt; /javascripts/xmlhr.js
+      #   javascript_path &quot;dir/xmlhr.js&quot; # =&gt; /javascripts/dir/xmlhr.js
+      #   javascript_path &quot;/dir/xmlhr&quot; # =&gt; /dir/xmlhr.js
       def javascript_path(source)
         compute_public_path(source, 'javascripts', 'js')        
       end
@@ -38,7 +59,15 @@ module ActionView
       JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
       @@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
 
-      # Returns a script include tag per source given as argument. Examples:
+      # Returns an html script tag for each of the +sources+ provided. You
+      # can pass in the filename (.js extension is optional) of javascript files
+      # that exist in your public/javascripts directory for inclusion into the
+      # current page or you can pass the full path relative to your document
+      # root. To include the Prototype and Scriptaculous javascript libraries in
+      # your application, pass &lt;tt&gt;:defaults&lt;/tt&gt; as the source. When using 
+      # :defaults, if an &lt;tt&gt;application.js&lt;/tt&gt; file exists in your public 
+      # javascripts directory, it will be included as well. You can modify the 
+      # html attributes of the script tag by passing a hash as the last argument. 
       #
       #   javascript_include_tag &quot;xmlhr&quot; # =&gt;
       #     &lt;script type=&quot;text/javascript&quot; src=&quot;/javascripts/xmlhr.js&quot;&gt;&lt;/script&gt;
@@ -52,11 +81,6 @@ module ActionView
       #     &lt;script type=&quot;text/javascript&quot; src=&quot;/javascripts/effects.js&quot;&gt;&lt;/script&gt;
       #     ...
       #     &lt;script type=&quot;text/javascript&quot; src=&quot;/javascripts/application.js&quot;&gt;&lt;/script&gt; *see below
-      #   
-      # If there's an &lt;tt&gt;application.js&lt;/tt&gt; file in your &lt;tt&gt;public/javascripts&lt;/tt&gt; directory,
-      # &lt;tt&gt;javascript_include_tag :defaults&lt;/tt&gt; will automatically include it. This file
-      # facilitates the inclusion of small snippets of JavaScript code, along the lines of
-      # &lt;tt&gt;controllers/application.rb&lt;/tt&gt; and &lt;tt&gt;helpers/application_helper.rb&lt;/tt&gt;.
       def javascript_include_tag(*sources)
         options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
 
@@ -69,18 +93,16 @@ module ActionView
           sources &lt;&lt; &quot;application&quot; if defined?(RAILS_ROOT) &amp;&amp; File.exists?(&quot;#{RAILS_ROOT}/public/javascripts/application.js&quot;) 
         end
 
-        sources.collect { |source|
+        sources.collect do |source|
           source = javascript_path(source)        
           content_tag(&quot;script&quot;, &quot;&quot;, { &quot;type&quot; =&gt; &quot;text/javascript&quot;, &quot;src&quot; =&gt; source }.merge(options))
-        }.join(&quot;\n&quot;)
+        end.join(&quot;\n&quot;)
       end
       
       # Register one or more additional JavaScript files to be included when
-      #   
-      #   javascript_include_tag :defaults
-      #
-      # is called. This method is intended to be called only from plugin initialization
-      # to register extra .js files the plugin installed in &lt;tt&gt;public/javascripts&lt;/tt&gt;.
+      # &lt;tt&gt;javascript_include_tag :defaults&lt;/tt&gt; is called. This method is
+      # only intended to be called from plugin initialization to register additional 
+      # .js files that the plugin installed in &lt;tt&gt;public/javascripts&lt;/tt&gt;.
       def self.register_javascript_include_default(*sources)
         @@javascript_default_sources.concat(sources)
       end
@@ -89,14 +111,21 @@ module ActionView
         @@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
       end
 
-      # Returns path to a stylesheet asset. Example:
+      # Computes the path to a stylesheet asset in the public stylesheets directory.
+      # If the +source+ filename has no extension, .css will be appended.
+      # Full paths from the document root will be passed through.
+      # Used internally by stylesheet_link_tag to build the stylesheet path.
       #
       #   stylesheet_path &quot;style&quot; # =&gt; /stylesheets/style.css
+      #   stylesheet_path &quot;dir/style.css&quot; # =&gt; /stylesheets/dir/style.css
+      #   stylesheet_path &quot;/dir/style.css&quot; # =&gt; /dir/style.css
       def stylesheet_path(source)
         compute_public_path(source, 'stylesheets', 'css')
       end
 
-      # Returns a css link tag per source given as argument. Examples:
+      # Returns a stylesheet link tag for the sources specified as arguments. If
+      # you don't specify an extension, .css will be appended automatically.
+      # You can modify the link attributes by passing a hash as the last argument.
       #
       #   stylesheet_link_tag &quot;style&quot; # =&gt;
       #     &lt;link href=&quot;/stylesheets/style.css&quot; media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&gt;
@@ -109,31 +138,50 @@ module ActionView
       #     &lt;link href=&quot;/css/stylish.css&quot; media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&gt;
       def stylesheet_link_tag(*sources)
         options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
-        sources.collect { |source|
+        sources.collect do |source|
           source = stylesheet_path(source)
           tag(&quot;link&quot;, { &quot;rel&quot; =&gt; &quot;Stylesheet&quot;, &quot;type&quot; =&gt; &quot;text/css&quot;, &quot;media&quot; =&gt; &quot;screen&quot;, &quot;href&quot; =&gt; source }.merge(options))
-        }.join(&quot;\n&quot;)
+        end.join(&quot;\n&quot;)
       end
 
-      # Returns path to an image asset. Example:
+      # Computes the path to an image asset in the public images directory.
+      # Full paths from the document root will be passed through.
+      # Used internally by image_tag to build the image path. Passing
+      # a filename without an extension is deprecated.
       #
-      # The +src+ can be supplied as a...
-      # * full path, like &quot;/my_images/image.gif&quot;
-      # * file name, like &quot;rss.gif&quot;, that gets expanded to &quot;/images/rss.gif&quot;
-      # * file name without extension, like &quot;logo&quot;, that gets expanded to &quot;/images/logo.png&quot;
+      #   image_path(&quot;edit.png&quot;)  # =&gt; /images/edit.png
+      #   image_path(&quot;icons/edit.png&quot;)  # =&gt; /images/icons/edit.png
+      #   image_path(&quot;/icons/edit.png&quot;)  # =&gt; /icons/edit.png
       def image_path(source)
+        unless (source.split(&quot;/&quot;).last || source).include?(&quot;.&quot;) || source.blank?
+          ActiveSupport::Deprecation.warn(
+            &quot;You've called image_path with a source that doesn't include an extension. &quot; +
+            &quot;In Rails 2.0, that will not result in .png automatically being appended. &quot; +
+            &quot;So you should call image_path('#{source}.png') instead&quot;, caller
+          )
+        end
+
         compute_public_path(source, 'images', 'png')
       end
 
-      # Returns an image tag converting the +options+ into html options on the tag, but with these special cases:
+      # Returns an html image tag for the +source+. The +source+ can be a full
+      # path or a file that exists in your public images directory. Note that 
+      # specifying a filename without the extension is now deprecated in Rails.
+      # You can add html attributes using the +options+. The +options+ supports
+      # two additional keys for convienence and conformance:
       #
-      # * &lt;tt&gt;:alt&lt;/tt&gt;  - If no alt text is given, the file name part of the +src+ is used (capitalized and without the extension)
-      # * &lt;tt&gt;:size&lt;/tt&gt; - Supplied as &quot;XxY&quot;, so &quot;30x45&quot; becomes width=&quot;30&quot; and height=&quot;45&quot;
+      # * &lt;tt&gt;:alt&lt;/tt&gt;  - If no alt text is given, the file name part of the 
+      #   +source+ is used (capitalized and without the extension)
+      # * &lt;tt&gt;:size&lt;/tt&gt; - Supplied as &quot;{Width}x{Height}&quot;, so &quot;30x45&quot; becomes 
+      #   width=&quot;30&quot; and height=&quot;45&quot;. &lt;tt&gt;:size&lt;/tt&gt; will be ignored if the
+      #   value is not in the correct format.
       #
-      # The +src+ can be supplied as a...
-      # * full path, like &quot;/my_images/image.gif&quot;
-      # * file name, like &quot;rss.gif&quot;, that gets expanded to &quot;/images/rss.gif&quot;
-      # * file name without extension, like &quot;logo&quot;, that gets expanded to &quot;/images/logo.png&quot;
+      #  image_tag(&quot;icon.png&quot;)  # =&gt;
+      #    &lt;img src=&quot;/images/icon.png&quot; alt=&quot;Icon&quot; /&gt;
+      #  image_tag(&quot;icon.png&quot;, :size =&gt; &quot;16x10&quot;, :alt =&gt; &quot;Edit Entry&quot;)  # =&gt;
+      #    &lt;img src=&quot;/images/icon.png&quot; width=&quot;16&quot; height=&quot;10&quot; alt=&quot;Edit Entry&quot; /&gt;
+      #  image_tag(&quot;/icons/icon.gif&quot;, :size =&gt; &quot;16x16&quot;)  # =&gt;
+      #    &lt;img src=&quot;/icons/icon.gif&quot; width=&quot;16&quot; height=&quot;16&quot; alt=&quot;Icon&quot; /&gt;
       def image_tag(source, options = {})
         options.symbolize_keys!
                 
@@ -141,8 +189,8 @@ module ActionView
         options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
         
         if options[:size]
-          options[:width], options[:height] = options[:size].split(&quot;x&quot;)
-          options.delete :size
+          options[:width], options[:height] = options[:size].split(&quot;x&quot;) if options[:size] =~ %r{^\d+x\d+$}
+          options.delete(:size)
         end
 
         tag(&quot;img&quot;, options)
@@ -150,11 +198,14 @@ module ActionView
       
       private
         def compute_public_path(source, dir, ext)
-          source  = &quot;/#{dir}/#{source}&quot; unless source.first == &quot;/&quot; || source.include?(&quot;:&quot;)
-          source &lt;&lt; &quot;.#{ext}&quot; unless source.split(&quot;/&quot;).last.include?(&quot;.&quot;)
-          source &lt;&lt; '?' + rails_asset_id(source) if defined?(RAILS_ROOT) &amp;&amp; %r{^[-a-z]+://} !~ source
-          source  = &quot;#{@controller.request.relative_url_root}#{source}&quot; unless %r{^[-a-z]+://} =~ source
-          source = ActionController::Base.asset_host + source unless source.include?(&quot;:&quot;)
+          source = source.dup
+          source &lt;&lt; &quot;.#{ext}&quot; if File.extname(source).blank?
+          unless source =~ %r{^[-a-z]+://}
+            source = &quot;/#{dir}/#{source}&quot; unless source[0] == ?/
+            asset_id = rails_asset_id(source)
+            source &lt;&lt; '?' + asset_id if defined?(RAILS_ROOT) &amp;&amp; !asset_id.blank?
+            source = &quot;#{ActionController::Base.asset_host}#{@controller.request.relative_url_root}#{source}&quot;
+          end
           source
         end
         </diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/asset_tag_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -89,7 +89,7 @@ module ActionView
       # named @@content_for_#{name_of_the_content_block}@. So &lt;tt&gt;&lt;%= content_for('footer') %&gt;&lt;/tt&gt;
       # would be avaiable as &lt;tt&gt;&lt;%= @content_for_footer %&gt;&lt;/tt&gt;. The preferred notation now is
       # &lt;tt&gt;&lt;%= yield :footer %&gt;&lt;/tt&gt;.
-      def content_for(name, &amp;block)
+      def content_for(name, content = nil, &amp;block)
         eval &quot;@content_for_#{name} = (@content_for_#{name} || '') + capture(&amp;block)&quot;
       end
 </diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/capture_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,14 +13,38 @@ module ActionView
     module DateHelper
       DEFAULT_PREFIX = 'date' unless const_defined?('DEFAULT_PREFIX')
 
-      # Reports the approximate distance in time between two Time objects or integers. 
-      # For example, if the distance is 47 minutes, it'll return
-      # &quot;about 1 hour&quot;. See the source for the complete wording list.
+      # Reports the approximate distance in time between two Time or Date objects or integers as seconds.
+      # Set &lt;tt&gt;include_seconds&lt;/tt&gt; to true if you want more detailed approximations when distance &lt; 1 min, 29 secs
+      # Distances are reported base on the following table:
       #
-      # Integers are interpreted as seconds. So,
-      # &lt;tt&gt;distance_of_time_in_words(50)&lt;/tt&gt; returns &quot;less than a minute&quot;.
+      # 0 &lt;-&gt; 29 secs                                                             # =&gt; less than a minute
+      # 30 secs &lt;-&gt; 1 min, 29 secs                                                # =&gt; 1 minute
+      # 1 min, 30 secs &lt;-&gt; 44 mins, 29 secs                                       # =&gt; [2..44] minutes
+      # 44 mins, 30 secs &lt;-&gt; 89 mins, 29 secs                                     # =&gt; about 1 hour
+      # 89 mins, 29 secs &lt;-&gt; 23 hrs, 59 mins, 29 secs                             # =&gt; about [2..24] hours
+      # 23 hrs, 59 mins, 29 secs &lt;-&gt; 47 hrs, 59 mins, 29 secs                     # =&gt; 1 day
+      # 47 hrs, 59 mins, 29 secs &lt;-&gt; 29 days, 23 hrs, 59 mins, 29 secs            # =&gt; [2..29] days
+      # 29 days, 23 hrs, 59 mins, 30 secs &lt;-&gt; 59 days, 23 hrs, 59 mins, 29 secs   # =&gt; about 1 month
+      # 59 days, 23 hrs, 59 mins, 30 secs &lt;-&gt; 1 yr minus 31 secs                  # =&gt; [2..12] months
+      # 1 yr minus 30 secs &lt;-&gt; 2 yrs minus 31 secs                                # =&gt; about 1 year
+      # 2 yrs minus 30 secs &lt;-&gt; max time or date                                  # =&gt; over [2..X] years
       #
-      # Set &lt;tt&gt;include_seconds&lt;/tt&gt; to true if you want more detailed approximations if distance &lt; 1 minute
+      # With include_seconds = true and the difference &lt; 1 minute 29 seconds
+      # 0-4   secs      # =&gt; less than 5 seconds
+      # 5-9   secs      # =&gt; less than 10 seconds
+      # 10-19 secs      # =&gt; less than 20 seconds
+      # 20-39 secs      # =&gt; half a minute
+      # 40-59 secs      # =&gt; less than a minute
+      # 60-89 secs      # =&gt; 1 minute
+      #
+      # Examples:
+      #
+      #   from_time = Time.now
+      #   distance_of_time_in_words(from_time, from_time + 50.minutes) # =&gt; about 1 hour
+      #   distance_of_time_in_words(from_time, from_time + 15.seconds) # =&gt; less than a minute
+      #   distance_of_time_in_words(from_time, from_time + 15.seconds, true) # =&gt; less than 20 seconds
+      #
+      # Note: Rails calculates one year as 365.25 days.
       def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
         from_time = from_time.to_time if from_time.respond_to?(:to_time)
         to_time = to_time.to_time if to_time.respond_to?(:to_time)
@@ -29,29 +53,33 @@ module ActionView
 
         case distance_in_minutes
           when 0..1
-            return (distance_in_minutes==0) ? 'less than a minute' : '1 minute' unless include_seconds
+            return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds
             case distance_in_seconds
-              when 0..5   then 'less than 5 seconds'
-              when 6..10  then 'less than 10 seconds'
-              when 11..20 then 'less than 20 seconds'
-              when 21..40 then 'half a minute'
-              when 41..59 then 'less than a minute'
+              when 0..4   then 'less than 5 seconds'
+              when 5..9   then 'less than 10 seconds'
+              when 10..19 then 'less than 20 seconds'
+              when 20..39 then 'half a minute'
+              when 40..59 then 'less than a minute'
               else             '1 minute'
             end
-                                
-          when 2..45      then &quot;#{distance_in_minutes} minutes&quot;
-          when 46..90     then 'about 1 hour'
-          when 90..1440   then &quot;about #{(distance_in_minutes.to_f / 60.0).round} hours&quot;
-          when 1441..2880 then '1 day'
-          else                 &quot;#{(distance_in_minutes / 1440).round} days&quot;
+
+          when 2..44           then &quot;#{distance_in_minutes} minutes&quot;
+          when 45..89          then 'about 1 hour'
+          when 90..1439        then &quot;about #{(distance_in_minutes.to_f / 60.0).round} hours&quot;
+          when 1440..2879      then '1 day'
+          when 2880..43199     then &quot;#{(distance_in_minutes / 1440).round} days&quot;
+          when 43200..86399    then 'about 1 month'
+          when 86400..525959   then &quot;#{(distance_in_minutes / 43200).round} months&quot;
+          when 525960..1051919 then 'about 1 year'
+          else                      &quot;over #{(distance_in_minutes / 525960).round} years&quot;
         end
       end
-      
+
       # Like distance_of_time_in_words, but where &lt;tt&gt;to_time&lt;/tt&gt; is fixed to &lt;tt&gt;Time.now&lt;/tt&gt;.
       def time_ago_in_words(from_time, include_seconds = false)
         distance_of_time_in_words(from_time, Time.now, include_seconds)
       end
-      
+
       alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
 
       # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute (identified by
@@ -80,6 +108,19 @@ module ActionView
         InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_date_select_tag(options)
       end
 
+      # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified
+      # time-based attribute (identified by +method+) on an object assigned to the template (identified by +object+).
+      # You can include the seconds with &lt;tt&gt;:include_seconds&lt;/tt&gt;.
+      # Examples:
+      #
+      #   time_select(&quot;post&quot;, &quot;sunrise&quot;)
+      #   time_select(&quot;post&quot;, &quot;start_time&quot;, :include_seconds =&gt; true)
+      #
+      # The selects are prepared for multi-parameter assignment to an Active Record object.
+      def time_select(object_name, method, options = {})
+        InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_time_select_tag(options)
+      end
+
       # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based
       # attribute (identified by +method+) on an object assigned to the template (identified by +object+). Examples:
       #
@@ -91,36 +132,55 @@ module ActionView
         InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_datetime_select_tag(options)
       end
 
+      # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+.
+      # It's also possible to explicitly set the order of the tags using the &lt;tt&gt;:order&lt;/tt&gt; option with an array of
+      # symbols &lt;tt&gt;:year&lt;/tt&gt;, &lt;tt&gt;:month&lt;/tt&gt; and &lt;tt&gt;:day&lt;/tt&gt; in the desired order. If you do not supply a Symbol, it
+      # will be appened onto the &lt;tt&gt;:order&lt;/tt&gt; passed in. You can also add &lt;tt&gt;:date_separator&lt;/tt&gt; and &lt;tt&gt;:time_separator&lt;/tt&gt;
+      # keys to the +options+ to control visual display of the elements.
+       def select_datetime(datetime = Time.now, options = {})
+        separator = options[:datetime_separator] || ''
+        select_date(datetime, options) + separator + select_time(datetime, options)
+       end
+
       # Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
+      # It's possible to explicitly set the order of the tags using the &lt;tt&gt;:order&lt;/tt&gt; option with an array of
+      # symbols &lt;tt&gt;:year&lt;/tt&gt;, &lt;tt&gt;:month&lt;/tt&gt; and &lt;tt&gt;:day&lt;/tt&gt; in the desired order. If you do not supply a Symbol, it
+      # will be appened onto the &lt;tt&gt;:order&lt;/tt&gt; passed in.
       def select_date(date = Date.today, options = {})
-        select_year(date, options) + select_month(date, options) + select_day(date, options)
-      end
+        options[:order] ||= []
+        [:year, :month, :day].each { |o| options[:order].push(o) unless options[:order].include?(o) }
 
-      # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+.
-      def select_datetime(datetime = Time.now, options = {})
-        select_year(datetime, options) + select_month(datetime, options) + select_day(datetime, options) +
-        select_hour(datetime, options) + select_minute(datetime, options)
+        select_date = ''
+        options[:order].each do |o|
+          select_date &lt;&lt; self.send(&quot;select_#{o}&quot;, date, options)
+        end
+        select_date
       end
 
       # Returns a set of html select-tags (one for hour and minute)
+      # You can set &lt;tt&gt;:add_separator&lt;/tt&gt; key to format the output.
       def select_time(datetime = Time.now, options = {})
-        h = select_hour(datetime, options) + select_minute(datetime, options) + (options[:include_seconds] ? select_second(datetime, options) : '')
+        separator = options[:time_separator] || ''
+        select_hour(datetime, options) + separator + select_minute(datetime, options) + (options[:include_seconds] ? separator + select_second(datetime, options) : '')
       end
 
       # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
       # The &lt;tt&gt;second&lt;/tt&gt; can also be substituted for a second number.
       # Override the field name using the &lt;tt&gt;:field_name&lt;/tt&gt; option, 'second' by default.
       def select_second(datetime, options = {})
-        second_options = []
-
-        0.upto(59) do |second|
-          second_options &lt;&lt; ((datetime &amp;&amp; (datetime.kind_of?(Fixnum) ? datetime : datetime.sec) == second) ?
-            %(&lt;option value=&quot;#{leading_zero_on_single_digits(second)}&quot; selected=&quot;selected&quot;&gt;#{leading_zero_on_single_digits(second)}&lt;/option&gt;\n) :
-            %(&lt;option value=&quot;#{leading_zero_on_single_digits(second)}&quot;&gt;#{leading_zero_on_single_digits(second)}&lt;/option&gt;\n)
-          )
+        val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.sec) : ''
+        if options[:use_hidden]
+          options[:include_seconds] ? hidden_html(options[:field_name] || 'second', val, options) : ''
+        else
+          second_options = []
+          0.upto(59) do |second|
+            second_options &lt;&lt; ((val == second) ?
+              %(&lt;option value=&quot;#{leading_zero_on_single_digits(second)}&quot; selected=&quot;selected&quot;&gt;#{leading_zero_on_single_digits(second)}&lt;/option&gt;\n) :
+              %(&lt;option value=&quot;#{leading_zero_on_single_digits(second)}&quot;&gt;#{leading_zero_on_single_digits(second)}&lt;/option&gt;\n)
+            )
+          end
+          select_html(options[:field_name] || 'second', second_options, options)
         end
-
-        select_html(options[:field_name] || 'second', second_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
       end
 
       # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
@@ -128,84 +188,100 @@ module ActionView
       # The &lt;tt&gt;minute&lt;/tt&gt; can also be substituted for a minute number.
       # Override the field name using the &lt;tt&gt;:field_name&lt;/tt&gt; option, 'minute' by default.
       def select_minute(datetime, options = {})
-        minute_options = []
-
-        0.step(59, options[:minute_step] || 1) do |minute|
-          minute_options &lt;&lt; ((datetime &amp;&amp; (datetime.kind_of?(Fixnum) ? datetime : datetime.min) == minute) ?
-            %(&lt;option value=&quot;#{leading_zero_on_single_digits(minute)}&quot; selected=&quot;selected&quot;&gt;#{leading_zero_on_single_digits(minute)}&lt;/option&gt;\n) :
-            %(&lt;option value=&quot;#{leading_zero_on_single_digits(minute)}&quot;&gt;#{leading_zero_on_single_digits(minute)}&lt;/option&gt;\n)
-          )
-        end
-
-        select_html(options[:field_name] || 'minute', minute_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
+        val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.min) : ''
+        if options[:use_hidden]
+          hidden_html(options[:field_name] || 'minute', val, options)
+        else
+          minute_options = []
+          0.step(59, options[:minute_step] || 1) do |minute|
+            minute_options &lt;&lt; ((val == minute) ?
+              %(&lt;option value=&quot;#{leading_zero_on_single_digits(minute)}&quot; selected=&quot;selected&quot;&gt;#{leading_zero_on_single_digits(minute)}&lt;/option&gt;\n) :
+              %(&lt;option value=&quot;#{leading_zero_on_single_digits(minute)}&quot;&gt;#{leading_zero_on_single_digits(minute)}&lt;/option&gt;\n)
+            )
+          end
+          select_html(options[:field_name] || 'minute', minute_options, options)
+         end
       end
 
       # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
       # The &lt;tt&gt;hour&lt;/tt&gt; can also be substituted for a hour number.
       # Override the field name using the &lt;tt&gt;:field_name&lt;/tt&gt; option, 'hour' by default.
       def select_hour(datetime, options = {})
-        hour_options = []
-
-        0.upto(23) do |hour|
-          hour_options &lt;&lt; ((datetime &amp;&amp; (datetime.kind_of?(Fixnum) ? datetime : datetime.hour) == hour) ?
-            %(&lt;option value=&quot;#{leading_zero_on_single_digits(hour)}&quot; selected=&quot;selected&quot;&gt;#{leading_zero_on_single_digits(hour)}&lt;/option&gt;\n) :
-            %(&lt;option value=&quot;#{leading_zero_on_single_digits(hour)}&quot;&gt;#{leading_zero_on_single_digits(hour)}&lt;/option&gt;\n)
-          )
+        val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.hour) : ''
+        if options[:use_hidden]
+          hidden_html(options[:field_name] || 'hour', val, options)
+        else
+          hour_options = []
+          0.upto(23) do |hour|
+            hour_options &lt;&lt; ((val == hour) ?
+              %(&lt;option value=&quot;#{leading_zero_on_single_digits(hour)}&quot; selected=&quot;selected&quot;&gt;#{leading_zero_on_single_digits(hour)}&lt;/option&gt;\n) :
+              %(&lt;option value=&quot;#{leading_zero_on_single_digits(hour)}&quot;&gt;#{leading_zero_on_single_digits(hour)}&lt;/option&gt;\n)
+            )
+          end
+          select_html(options[:field_name] || 'hour', hour_options, options)
         end
-
-        select_html(options[:field_name] || 'hour', hour_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
       end
 
       # Returns a select tag with options for each of the days 1 through 31 with the current day selected.
       # The &lt;tt&gt;date&lt;/tt&gt; can also be substituted for a hour number.
       # Override the field name using the &lt;tt&gt;:field_name&lt;/tt&gt; option, 'day' by default.
       def select_day(date, options = {})
-        day_options = []
-
-        1.upto(31) do |day|
-          day_options &lt;&lt; ((date &amp;&amp; (date.kind_of?(Fixnum) ? date : date.day) == day) ?
-            %(&lt;option value=&quot;#{day}&quot; selected=&quot;selected&quot;&gt;#{day}&lt;/option&gt;\n) :
-            %(&lt;option value=&quot;#{day}&quot;&gt;#{day}&lt;/option&gt;\n)
-          )
+        val = date ? (date.kind_of?(Fixnum) ? date : date.day) : ''
+        if options[:use_hidden]
+          hidden_html(options[:field_name] || 'day', val, options)
+        else
+          day_options = []
+          1.upto(31) do |day|
+            day_options &lt;&lt; ((val == day) ?
+              %(&lt;option value=&quot;#{day}&quot; selected=&quot;selected&quot;&gt;#{day}&lt;/option&gt;\n) :
+              %(&lt;option value=&quot;#{day}&quot;&gt;#{day}&lt;/option&gt;\n)
+            )
+          end
+          select_html(options[:field_name] || 'day', day_options, options)
         end
-
-        select_html(options[:field_name] || 'day', day_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
       end
 
       # Returns a select tag with options for each of the months January through December with the current month selected.
       # The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values
       # (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names --
       # set the &lt;tt&gt;:use_month_numbers&lt;/tt&gt; key in +options+ to true for this to happen. If you want both numbers and names,
-      # set the &lt;tt&gt;:add_month_numbers&lt;/tt&gt; key in +options+ to true. Examples:
+      # set the &lt;tt&gt;:add_month_numbers&lt;/tt&gt; key in +options+ to true. If you would prefer to show month names as abbreviations,
+      # set the &lt;tt&gt;:use_short_month&lt;/tt&gt; key in +options+ to true. If you want to use your own month names, set the
+      # &lt;tt&gt;:use_month_names&lt;/tt&gt; key in +options+ to an array of 12 month names.
+      #
+      # Examples:
       #
       #   select_month(Date.today)                             # Will use keys like &quot;January&quot;, &quot;March&quot;
       #   select_month(Date.today, :use_month_numbers =&gt; true) # Will use keys like &quot;1&quot;, &quot;3&quot;
       #   select_month(Date.today, :add_month_numbers =&gt; true) # Will use keys like &quot;1 - January&quot;, &quot;3 - March&quot;
+      #   select_month(Date.today, :use_short_month =&gt; true)   # Will use keys like &quot;Jan&quot;, &quot;Mar&quot;
+      #   select_month(Date.today, :use_month_names =&gt; %w(Januar Februar Marts ...))   # Will use keys like &quot;Januar&quot;, &quot;Marts&quot;
       #
       # Override the field name using the &lt;tt&gt;:field_name&lt;/tt&gt; option, 'month' by default.
-      #
-      # If you would prefer to show month names as abbreviations, set the
-      # &lt;tt&gt;:use_short_month&lt;/tt&gt; key in +options+ to true.
       def select_month(date, options = {})
-        month_options = []
-        month_names = options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES
-
-        1.upto(12) do |month_number|
-          month_name = if options[:use_month_numbers]
-            month_number
-          elsif options[:add_month_numbers]
-            month_number.to_s + ' - ' + month_names[month_number]
-          else
-            month_names[month_number]
-          end
+        val = date ? (date.kind_of?(Fixnum) ? date : date.month) : ''
+        if options[:use_hidden]
+          hidden_html(options[:field_name] || 'month', val, options)
+        else
+          month_options = []
+          month_names = options[:use_month_names] || (options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES)
+          month_names.unshift(nil) if month_names.size &lt; 13
+          1.upto(12) do |month_number|
+            month_name = if options[:use_month_numbers]
+              month_number
+            elsif options[:add_month_numbers]
+              month_number.to_s + ' - ' + month_names[month_number]
+            else
+              month_names[month_number]
+            end
 
-          month_options &lt;&lt; ((date &amp;&amp; (date.kind_of?(Fixnum) ? date : date.month) == month_number) ?
-            %(&lt;option value=&quot;#{month_number}&quot; selected=&quot;selected&quot;&gt;#{month_name}&lt;/option&gt;\n) :
-            %(&lt;option value=&quot;#{month_number}&quot;&gt;#{month_name}&lt;/option&gt;\n)
-          )
+            month_options &lt;&lt; ((val == month_number) ?
+              %(&lt;option value=&quot;#{month_number}&quot; selected=&quot;selected&quot;&gt;#{month_name}&lt;/option&gt;\n) :
+              %(&lt;option value=&quot;#{month_number}&quot;&gt;#{month_name}&lt;/option&gt;\n)
+            )
+          end
+          select_html(options[:field_name] || 'month', month_options, options)
         end
-
-        select_html(options[:field_name] || 'month', month_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
       end
 
       # Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius
@@ -215,37 +291,51 @@ module ActionView
       #
       #   select_year(Date.today, :start_year =&gt; 1992, :end_year =&gt; 2007)  # ascending year values
       #   select_year(Date.today, :start_year =&gt; 2005, :end_year =&gt; 1900)  # descending year values
+      #   select_year(2006, :start_year =&gt; 2000, :end_year =&gt; 2010)
       #
       # Override the field name using the &lt;tt&gt;:field_name&lt;/tt&gt; option, 'year' by default.
       def select_year(date, options = {})
-        year_options = []
-        y = date ? (date.kind_of?(Fixnum) ? (y = (date == 0) ? Date.today.year : date) : date.year) : Date.today.year
-
-        start_year, end_year = (options[:start_year] || y-5), (options[:end_year] || y+5)
-        step_val = start_year &lt; end_year ? 1 : -1
-
-        start_year.step(end_year, step_val) do |year|
-          year_options &lt;&lt; ((date &amp;&amp; (date.kind_of?(Fixnum) ? date : date.year) == year) ?
-            %(&lt;option value=&quot;#{year}&quot; selected=&quot;selected&quot;&gt;#{year}&lt;/option&gt;\n) :
-            %(&lt;option value=&quot;#{year}&quot;&gt;#{year}&lt;/option&gt;\n)
-          )
+        val = date ? (date.kind_of?(Fixnum) ? date : date.year) : ''
+        if options[:use_hidden]
+          hidden_html(options[:field_name] || 'year', val, options)
+        else
+          year_options = []
+          y = date ? (date.kind_of?(Fixnum) ? (y = (date == 0) ? Date.today.year : date) : date.year) : Date.today.year
+
+          start_year, end_year = (options[:start_year] || y-5), (options[:end_year] || y+5)
+          step_val = start_year &lt; end_year ? 1 : -1
+          start_year.step(end_year, step_val) do |year|
+            year_options &lt;&lt; ((val == year) ?
+              %(&lt;option value=&quot;#{year}&quot; selected=&quot;selected&quot;&gt;#{year}&lt;/option&gt;\n) :
+              %(&lt;option value=&quot;#{year}&quot;&gt;#{year}&lt;/option&gt;\n)
+            )
+          end
+          select_html(options[:field_name] || 'year', year_options, options)
         end
-
-        select_html(options[:field_name] || 'year', year_options, options[:prefix], options[:include_blank], options[:discard_type], options[:disabled])
       end
 
       private
-        def select_html(type, options, prefix = nil, include_blank = false, discard_type = false, disabled = false)
-          select_html  = %(&lt;select name=&quot;#{prefix || DEFAULT_PREFIX})
-          select_html &lt;&lt; &quot;[#{type}]&quot; unless discard_type
-          select_html &lt;&lt; %(&quot;)
-          select_html &lt;&lt; %( disabled=&quot;disabled&quot;) if disabled
+
+        def select_html(type, html_options, options)
+          name_and_id_from_options(options, type)
+          select_html  = %(&lt;select id=&quot;#{options[:id]}&quot; name=&quot;#{options[:name]}&quot;)
+          select_html &lt;&lt; %( disabled=&quot;disabled&quot;) if options[:disabled]
           select_html &lt;&lt; %(&gt;\n)
-          select_html &lt;&lt; %(&lt;option value=&quot;&quot;&gt;&lt;/option&gt;\n) if include_blank
-          select_html &lt;&lt; options.to_s
+          select_html &lt;&lt; %(&lt;option value=&quot;&quot;&gt;&lt;/option&gt;\n) if options[:include_blank]
+          select_html &lt;&lt; html_options.to_s
           select_html &lt;&lt; &quot;&lt;/select&gt;\n&quot;
         end
 
+        def hidden_html(type, value, options)
+          name_and_id_from_options(options, type)
+          hidden_html = %(&lt;input type=&quot;hidden&quot; id=&quot;#{options[:id]}&quot; name=&quot;#{options[:name]}&quot; value=&quot;#{value}&quot; /&gt;\n)
+        end
+
+        def name_and_id_from_options(options, type)
+          options[:name] = (options[:prefix] || DEFAULT_PREFIX) + (options[:discard_type] ? '' : &quot;[#{type}]&quot;)
+          options[:id] = options[:name].gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
+        end
+
         def leading_zero_on_single_digits(number)
           number &gt; 9 ? number : &quot;0#{number}&quot;
         end
@@ -255,43 +345,71 @@ module ActionView
       include DateHelper
 
       def to_date_select_tag(options = {})
-        defaults = { :discard_type =&gt; true }
-        options  = defaults.merge(options)
-        options_with_prefix = Proc.new { |position| options.merge(:prefix =&gt; &quot;#{@object_name}[#{@method_name}(#{position}i)]&quot;) }
-        date     = options[:include_blank] ? (value || 0) : (value || Date.today)
+        date_or_time_select options.merge(:discard_hour =&gt; true)
+      end
 
-        date_select = ''
-        options[:order]   = [:month, :year, :day] if options[:month_before_year] # For backwards compatibility
-        options[:order] ||= [:year, :month, :day]
+      def to_time_select_tag(options = {})
+        date_or_time_select options.merge(:discard_year =&gt; true, :discard_month =&gt; true)
+      end
 
-        position = {:year =&gt; 1, :month =&gt; 2, :day =&gt; 3}
+      def to_datetime_select_tag(options = {})
+        date_or_time_select options
+      end
 
-        discard = {}
-        discard[:year]  = true if options[:discard_year]
-        discard[:month] = true if options[:discard_month]
-        discard[:day]   = true if options[:discard_day] or options[:discard_month]
+      private
+        def date_or_time_select(options)
+          defaults = { :discard_type =&gt; true }
+          options  = defaults.merge(options)
+          datetime = value(object)
+          datetime ||= Time.now unless options[:include_blank]
+
+          position = { :year =&gt; 1, :month =&gt; 2, :day =&gt; 3, :hour =&gt; 4, :minute =&gt; 5, :second =&gt; 6 }
+
+          order = (options[:order] ||= [:year, :month, :day])
+
+          # Discard explicit and implicit by not being included in the :order
+          discard = {}
+          discard[:year]   = true if options[:discard_year] or !order.include?(:year)
+          discard[:month]  = true if options[:discard_month] or !order.include?(:month)
+          discard[:day]    = true if options[:discard_day] or discard[:month] or !order.include?(:day)
+          discard[:hour]   = true if options[:discard_hour]
+          discard[:minute] = true if options[:discard_minute] or discard[:hour]
+          discard[:second] = true unless options[:include_seconds] &amp;&amp; !discard[:minute]
+
+          # Maintain valid dates by including hidden fields for discarded elements
+          [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
+          # Ensure proper ordering of :hour, :minute and :second
+          [:hour, :minute, :second].each { |o| order.delete(o); order.push(o) }
+
+          date_or_time_select = ''
+          order.reverse.each do |param|
+            # Send hidden fields for discarded elements once output has started
+            # This ensures AR can reconstruct valid dates using ParseDate
+            next if discard[param] &amp;&amp; date_or_time_select.empty?
+
+            date_or_time_select.insert(0, self.send(&quot;select_#{param}&quot;, datetime, options_with_prefix(position[param], options.merge(:use_hidden =&gt; discard[param]))))
+            date_or_time_select.insert(0,
+              case param
+                when :hour then (discard[:year] &amp;&amp; discard[:day] ? &quot;&quot; : &quot; &amp;mdash; &quot;)
+                when :minute then &quot; : &quot;
+                when :second then options[:include_seconds] ? &quot; : &quot; : &quot;&quot;
+                else &quot;&quot;
+              end)
 
-        options[:order].each do |param|
-          date_select &lt;&lt; self.send(&quot;select_#{param}&quot;, date, options_with_prefix.call(position[param])) unless discard[param]
-        end
+          end
 
-        date_select
-      end
+          date_or_time_select
+        end
 
-      def to_datetime_select_tag(options = {})
-        defaults = { :discard_type =&gt; true }
-        options  = defaults.merge(options)
-        options_with_prefix = Proc.new { |position| options.merge(:prefix =&gt; &quot;#{@object_name}[#{@method_name}(#{position}i)]&quot;) }
-        datetime = options[:include_blank] ? (value || nil) : (value || Time.now)
-
-        datetime_select  = select_year(datetime, options_with_prefix.call(1))
-        datetime_select &lt;&lt; select_month(datetime, options_with_prefix.call(2)) unless options[:discard_month]
-        datetime_select &lt;&lt; select_day(datetime, options_with_prefix.call(3)) unless options[:discard_day] || options[:discard_month]
-        datetime_select &lt;&lt; ' &amp;mdash; ' + select_hour(datetime, options_with_prefix.call(4)) unless options[:discard_hour]
-        datetime_select &lt;&lt; ' : ' + select_minute(datetime, options_with_prefix.call(5)) unless options[:discard_minute] || options[:discard_hour]
-
-        datetime_select
-      end
+        def options_with_prefix(position, options)
+          prefix = &quot;#{@object_name}&quot;
+          if options[:index]
+            prefix &lt;&lt; &quot;[#{options[:index]}]&quot;
+          elsif @auto_index
+            prefix &lt;&lt; &quot;[#{@auto_index}]&quot;
+          end
+          options.merge(:prefix =&gt; &quot;#{prefix}[#{@method_name}(#{position}i)]&quot;)
+        end
     end
 
     class FormBuilder
@@ -299,6 +417,10 @@ module ActionView
         @template.date_select(@object_name, method, options.merge(:object =&gt; @object))
       end
 
+      def time_select(method, options = {})
+        @template.time_select(@object_name, method, options.merge(:object =&gt; @object))
+      end
+
       def datetime_select(method, options = {})
         @template.datetime_select(@object_name, method, options.merge(:object =&gt; @object))
       end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,11 +3,21 @@ module ActionView
     # Provides a set of methods for making it easier to locate problems.
     module DebugHelper
       # Returns a &lt;pre&gt;-tag set with the +object+ dumped by YAML. Very readable way to inspect an object.
+      #  my_hash = {'first' =&gt; 1, 'second' =&gt; 'two', 'third' =&gt; [1,2,3]}
+      #  debug(my_hash)
+      #  =&gt; &lt;pre class='debug_dump'&gt;--- 
+      #  first: 1
+      #  second: two
+      #  third: 
+      #  - 1
+      #  - 2
+      #  - 3
+      #  &lt;/pre&gt;
       def debug(object)
         begin
           Marshal::dump(object)
           &quot;&lt;pre class='debug_dump'&gt;#{h(object.to_yaml).gsub(&quot;  &quot;, &quot;&amp;nbsp; &quot;)}&lt;/pre&gt;&quot;
-        rescue Object =&gt; e
+        rescue Exception =&gt; e  # errors from Marshal or YAML
           # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
           &quot;&lt;code class='debug_dump'&gt;#{h(object.inspect)}&lt;/code&gt;&quot;
         end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/debug_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -142,11 +142,13 @@ module ActionView
       #
       # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base.
       # Like collection_select and datetime_select.
-      def fields_for(object_name, *args, &amp;proc)
+      def fields_for(object_name, *args, &amp;block)
         raise ArgumentError, &quot;Missing block&quot; unless block_given?
         options = args.last.is_a?(Hash) ? args.pop : {}
         object  = args.first
-        yield((options[:builder] || FormBuilder).new(object_name, object, self, options, proc))
+
+        builder = options[:builder] || ActionView::Base.default_form_builder
+        yield builder.new(object_name, object, self, options, block)
       end
 
       # Returns an input tag of the &quot;text&quot; type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -238,7 +240,11 @@ module ActionView
         @template_object, @local_binding = template_object, local_binding
         @object = object
         if @object_name.sub!(/\[\]$/,&quot;&quot;)
-          @auto_index = @template_object.instance_variable_get(&quot;@#{Regexp.last_match.pre_match}&quot;).id_before_type_cast
+          if object ||= @template_object.instance_variable_get(&quot;@#{Regexp.last_match.pre_match}&quot;) and object.respond_to?(:id_before_type_cast)
+            @auto_index = object.id_before_type_cast
+          else
+            raise ArgumentError, &quot;object[] naming but object param and @object var don't exist or don't respond to id_before_type_cast: #{object.inspect}&quot;
+          end
         end
       end
 
@@ -250,7 +256,7 @@ module ActionView
           options.delete(&quot;size&quot;)
         end
         options[&quot;type&quot;] = field_type
-        options[&quot;value&quot;] ||= value_before_type_cast unless field_type == &quot;file&quot;
+        options[&quot;value&quot;] ||= value_before_type_cast(object) unless field_type == &quot;file&quot;
         add_default_name_and_id(options)
         tag(&quot;input&quot;, options)
       end
@@ -259,9 +265,15 @@ module ActionView
         options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
         options[&quot;type&quot;]     = &quot;radio&quot;
         options[&quot;value&quot;]    = tag_value
-        options[&quot;checked&quot;]  = &quot;checked&quot; if value.to_s == tag_value.to_s
+        if options.has_key?(&quot;checked&quot;)
+          cv = options.delete &quot;checked&quot;
+          checked = cv == true || cv == &quot;checked&quot;
+        else
+          checked = self.class.radio_button_checked?(value(object), tag_value)
+        end
+        options[&quot;checked&quot;]  = &quot;checked&quot; if checked
         pretty_tag_value    = tag_value.to_s.gsub(/\s/, &quot;_&quot;).gsub(/\W/, &quot;&quot;).downcase
-        options[&quot;id&quot;]       = @auto_index ?             
+        options[&quot;id&quot;]     ||= defined?(@auto_index) ?             
           &quot;#{@object_name}_#{@auto_index}_#{@method_name}_#{pretty_tag_value}&quot; :
           &quot;#{@object_name}_#{@method_name}_#{pretty_tag_value}&quot;
         add_default_name_and_id(options)
@@ -271,37 +283,32 @@ module ActionView
       def to_text_area_tag(options = {})
         options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
         add_default_name_and_id(options)
-        content_tag(&quot;textarea&quot;, html_escape(options.delete('value') || value_before_type_cast), options)
+
+        if size = options.delete(&quot;size&quot;)
+          options[&quot;cols&quot;], options[&quot;rows&quot;] = size.split(&quot;x&quot;)
+        end
+
+        content_tag(&quot;textarea&quot;, html_escape(options.delete('value') || value_before_type_cast(object)), options)
       end
 
       def to_check_box_tag(options = {}, checked_value = &quot;1&quot;, unchecked_value = &quot;0&quot;)
         options = options.stringify_keys
         options[&quot;type&quot;]     = &quot;checkbox&quot;
         options[&quot;value&quot;]    = checked_value
-        checked = case value
-          when TrueClass, FalseClass
-            value
-          when NilClass
-            false
-          when Integer
-            value != 0
-          when String
-            value == checked_value
-          else
-            value.to_i != 0
-          end
-        if checked || options[&quot;checked&quot;] == &quot;checked&quot;
-          options[&quot;checked&quot;] = &quot;checked&quot;
+        if options.has_key?(&quot;checked&quot;)
+          cv = options.delete &quot;checked&quot;
+          checked = cv == true || cv == &quot;checked&quot;
         else
-          options.delete(&quot;checked&quot;)
+          checked = self.class.check_box_checked?(value(object), checked_value)
         end
+        options[&quot;checked&quot;] = &quot;checked&quot; if checked
         add_default_name_and_id(options)
         tag(&quot;input&quot;, options) &lt;&lt; tag(&quot;input&quot;, &quot;name&quot; =&gt; options[&quot;name&quot;], &quot;type&quot; =&gt; &quot;hidden&quot;, &quot;value&quot; =&gt; unchecked_value)
       end
 
       def to_date_tag()
         defaults = DEFAULT_DATE_OPTIONS.dup
-        date     = value || Date.today
+        date     = value(object) || Date.today
         options  = Proc.new { |position| defaults.merge(:prefix =&gt; &quot;#{@object_name}[#{@method_name}(#{position}i)]&quot;) }
         html_day_select(date, options.call(3)) +
         html_month_select(date, options.call(2)) +
@@ -311,6 +318,7 @@ module ActionView
       def to_boolean_select_tag(options = {})
         options = options.stringify_keys
         add_default_name_and_id(options)
+        value = value(object)
         tag_text = &quot;&lt;select&quot;
         tag_text &lt;&lt; tag_options(options)
         tag_text &lt;&lt; &quot;&gt;&lt;option value=\&quot;false\&quot;&quot;
@@ -321,24 +329,51 @@ module ActionView
       end
       
       def to_content_tag(tag_name, options = {})
-        content_tag(tag_name, value, options)
+        content_tag(tag_name, value(object), options)
       end
       
       def object
         @object || @template_object.instance_variable_get(&quot;@#{@object_name}&quot;)
       end
 
-      def value
-        unless object.nil?
-          object.send(@method_name)
-        end
+      def value(object)
+        self.class.value(object, @method_name)
       end
 
-      def value_before_type_cast
-        unless object.nil?
-          object.respond_to?(@method_name + &quot;_before_type_cast&quot;) ?
-            object.send(@method_name + &quot;_before_type_cast&quot;) :
-            object.send(@method_name)
+      def value_before_type_cast(object)
+        self.class.value_before_type_cast(object, @method_name)
+      end
+      
+      class &lt;&lt; self
+        def value(object, method_name)
+          object.send method_name unless object.nil?
+        end
+        
+        def value_before_type_cast(object, method_name)
+          unless object.nil?
+            object.respond_to?(method_name + &quot;_before_type_cast&quot;) ?
+            object.send(method_name + &quot;_before_type_cast&quot;) :
+            object.send(method_name)
+          end
+        end
+        
+        def check_box_checked?(value, checked_value)
+          case value
+          when TrueClass, FalseClass
+            value
+          when NilClass
+            false
+          when Integer
+            value != 0
+          when String
+            value == checked_value
+          else
+            value.to_i != 0
+          end
+        end
+        
+        def radio_button_checked?(value, checked_value)
+          value.to_s == checked_value.to_s
         end
       end
 
@@ -348,11 +383,11 @@ module ActionView
             options[&quot;name&quot;] ||= tag_name_with_index(options[&quot;index&quot;])
             options[&quot;id&quot;]   ||= tag_id_with_index(options[&quot;index&quot;])
             options.delete(&quot;index&quot;)
-          elsif @auto_index
+          elsif defined?(@auto_index)
             options[&quot;name&quot;] ||= tag_name_with_index(@auto_index)
             options[&quot;id&quot;]   ||= tag_id_with_index(@auto_index)
           else
-            options[&quot;name&quot;] ||= tag_name
+            options[&quot;name&quot;] ||= tag_name + (options.has_key?('multiple') ? '[]' : '')
             options[&quot;id&quot;]   ||= tag_id
           end
         end
@@ -379,7 +414,7 @@ module ActionView
       class_inheritable_accessor :field_helpers
       self.field_helpers = (FormHelper.instance_methods - ['form_for'])
 
-      attr_accessor :object_name, :object
+      attr_accessor :object_name, :object, :options
 
       def initialize(object_name, object, template, options, proc)
         @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc        
@@ -403,4 +438,9 @@ module ActionView
       end
     end
   end
+
+  class Base
+    cattr_accessor :default_form_builder
+    self.default_form_builder = ::ActionView::Helpers::FormBuilder
+  end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,7 +26,7 @@ module ActionView
     #
     # Another common case is a select tag for an &lt;tt&gt;belongs_to&lt;/tt&gt;-associated object. For example,
     #
-    #   select(&quot;post&quot;, &quot;person_id&quot;, Person.find_all.collect {|p| [ p.name, p.id ] })
+    #   select(&quot;post&quot;, &quot;person_id&quot;, Person.find(:all).collect {|p| [ p.name, p.id ] })
     #
     # could become:
     #
@@ -43,7 +43,7 @@ module ActionView
       # See options_for_select for the required format of the choices parameter.
       #
       # Example with @post.person_id =&gt; 1:
-      #   select(&quot;post&quot;, &quot;person_id&quot;, Person.find_all.collect {|p| [ p.name, p.id ] }, { :include_blank =&gt; true })
+      #   select(&quot;post&quot;, &quot;person_id&quot;, Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank =&gt; true })
       #
       # could become:
       #
@@ -113,7 +113,6 @@ module ActionView
 
         options_for_select = container.inject([]) do |options, element|
           if !element.is_a?(String) and element.respond_to?(:first) and element.respond_to?(:last)
-            is_selected = ( (selected.respond_to?(:include?) ? selected.include?(element.last) : element.last == selected) )
             is_selected = ( (selected.respond_to?(:include?) &amp;&amp; !selected.is_a?(String) ? selected.include?(element.last) : element.last == selected) )
             if is_selected
               options &lt;&lt; &quot;&lt;option value=\&quot;#{html_escape(element.last.to_s)}\&quot; selected=\&quot;selected\&quot;&gt;#{html_escape(element.first.to_s)}&lt;/option&gt;&quot;
@@ -121,7 +120,6 @@ module ActionView
               options &lt;&lt; &quot;&lt;option value=\&quot;#{html_escape(element.last.to_s)}\&quot;&gt;#{html_escape(element.first.to_s)}&lt;/option&gt;&quot;
             end
           else
-            is_selected = ( (selected.respond_to?(:include?) ? selected.include?(element) : element == selected) )
             is_selected = ( (selected.respond_to?(:include?) &amp;&amp; !selected.is_a?(String) ? selected.include?(element) : element == selected) )
             options &lt;&lt; ((is_selected) ? &quot;&lt;option value=\&quot;#{html_escape(element.to_s)}\&quot; selected=\&quot;selected\&quot;&gt;#{html_escape(element.to_s)}&lt;/option&gt;&quot; : &quot;&lt;option value=\&quot;#{html_escape(element.to_s)}\&quot;&gt;#{html_escape(element.to_s)}&lt;/option&gt;&quot;)
           end
@@ -299,13 +297,15 @@ module ActionView
       def to_select_tag(choices, options, html_options)
         html_options = html_options.stringify_keys
         add_default_name_and_id(html_options)
+        value = value(object)
         selected_value = options.has_key?(:selected) ? options[:selected] : value
-        content_tag(&quot;select&quot;, add_options(options_for_select(choices, selected_value), options, value), html_options)
+        content_tag(&quot;select&quot;, add_options(options_for_select(choices, selected_value), options, selected_value), html_options)
       end
 
       def to_collection_select_tag(collection, value_method, text_method, options, html_options)
         html_options = html_options.stringify_keys
         add_default_name_and_id(html_options)
+        value = value(object)
         content_tag(
           &quot;select&quot;, add_options(options_from_collection_for_select(collection, value_method, text_method, value), options, value), html_options
         )
@@ -314,12 +314,14 @@ module ActionView
       def to_country_select_tag(priority_countries, options, html_options)
         html_options = html_options.stringify_keys
         add_default_name_and_id(html_options)
+        value = value(object)
         content_tag(&quot;select&quot;, add_options(country_options_for_select(value, priority_countries), options, value), html_options)
       end
 
       def to_time_zone_select_tag(priority_zones, options, html_options)
         html_options = html_options.stringify_keys
         add_default_name_and_id(html_options)
+        value = value(object)
         content_tag(&quot;select&quot;,
           add_options(
             time_zone_options_for_select(value, priority_zones, options[:model] || TimeZone),</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/form_options_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,14 +12,49 @@ module ActionView
       # Starts a form tag that points the action to an url configured with &lt;tt&gt;url_for_options&lt;/tt&gt; just like
       # ActionController::Base#url_for. The method for the form defaults to POST.
       #
+      # Examples:
+      # * &lt;tt&gt;form_tag('/posts') =&gt; &lt;form action=&quot;/posts&quot; method=&quot;post&quot;&gt;&lt;/tt&gt;
+      # * &lt;tt&gt;form_tag('/posts/1', :method =&gt; :put) =&gt; &lt;form action=&quot;/posts/1&quot; method=&quot;put&quot;&gt;&lt;/tt&gt;
+      # * &lt;tt&gt;form_tag('/upload', :multipart =&gt; true) =&gt; &lt;form action=&quot;/upload&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;&lt;/tt&gt;
+      # 
+      # ERb example:
+      #   &lt;% form_tag '/posts' do -%&gt;
+      #     &lt;div&gt;&lt;%= submit_tag 'Save' %&gt;&lt;/div&gt;
+      #   &lt;% end -%&gt;
+      #
+      # Will output:
+      #   &lt;form action=&quot;/posts&quot; method=&quot;post&quot;&gt;&lt;div&gt;&lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Save&quot; /&gt;&lt;/div&gt;&lt;/form&gt;
+      #
       # Options:
       # * &lt;tt&gt;:multipart&lt;/tt&gt; - If set to true, the enctype is set to &quot;multipart/form-data&quot;.
-      # * &lt;tt&gt;:method&lt;/tt&gt; - The method to use when submitting the form, usually either &quot;get&quot; or &quot;post&quot;.
-      def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &amp;proc)
-        html_options = { &quot;method&quot; =&gt; &quot;post&quot; }.merge(options.stringify_keys)
+      # * &lt;tt&gt;:method&lt;/tt&gt;    - The method to use when submitting the form, usually either &quot;get&quot; or &quot;post&quot;.
+      #                         If &quot;put&quot;, &quot;delete&quot;, or another verb is used, a hidden input with name _method 
+      #                         is added to simulate the verb over post.
+      def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &amp;block)
+        html_options = options.stringify_keys
         html_options[&quot;enctype&quot;] = &quot;multipart/form-data&quot; if html_options.delete(&quot;multipart&quot;)
-        html_options[&quot;action&quot;] = url_for(url_for_options, *parameters_for_url)
-        tag :form, html_options, true
+        html_options[&quot;action&quot;]  = url_for(url_for_options, *parameters_for_url)
+
+        method_tag = &quot;&quot;
+        
+        case method = html_options.delete(&quot;method&quot;).to_s
+          when /^get$/i # must be case-insentive, but can't use downcase as might be nil
+            html_options[&quot;method&quot;] = &quot;get&quot;
+          when /^post$/i, &quot;&quot;, nil
+            html_options[&quot;method&quot;] = &quot;post&quot;
+          else
+            html_options[&quot;method&quot;] = &quot;post&quot;
+            method_tag = content_tag(:div, tag(:input, :type =&gt; &quot;hidden&quot;, :name =&gt; &quot;_method&quot;, :value =&gt; method), :style =&gt; 'margin:0;padding:0')
+        end
+        
+        if block_given?
+          content = capture(&amp;block)
+          concat(tag(:form, html_options, true) + method_tag, block.binding)
+          concat(content, block.binding)
+          concat(&quot;&lt;/form&gt;&quot;, block.binding)
+        else
+          tag(:form, html_options, true) + method_tag
+        end
       end
 
       alias_method :start_form_tag, :form_tag
@@ -28,6 +63,8 @@ module ActionView
       def end_form_tag
         &quot;&lt;/form&gt;&quot;
       end
+      
+      deprecate :end_form_tag, :start_form_tag =&gt; :form_tag
 
       # Creates a dropdown selection box, or if the &lt;tt&gt;:multiple&lt;/tt&gt; option is set to true, a multiple
       # choice selection box.
@@ -110,7 +147,8 @@ module ActionView
 
       # Creates a radio button.
       def radio_button_tag(name, value, checked = false, options = {})
-        html_options = { &quot;type&quot; =&gt; &quot;radio&quot;, &quot;name&quot; =&gt; name, &quot;id&quot; =&gt; name, &quot;value&quot; =&gt; value }.update(options.stringify_keys)
+        pretty_tag_value = value.to_s.gsub(/\s/, &quot;_&quot;).gsub(/(?!-)\W/, &quot;&quot;).downcase
+        html_options = { &quot;type&quot; =&gt; &quot;radio&quot;, &quot;name&quot; =&gt; name, &quot;id&quot; =&gt; &quot;#{name}_#{pretty_tag_value}&quot;, &quot;value&quot; =&gt; value }.update(options.stringify_keys)
         html_options[&quot;checked&quot;] = &quot;checked&quot; if checked
         tag :input, html_options
       end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/form_tag_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,6 +7,8 @@ module ActionView
     # editing relies on ActionController::Base.in_place_edit_for and the autocompletion relies on 
     # ActionController::Base.auto_complete_for.
     module JavaScriptMacrosHelper
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Makes an HTML element specified by the DOM ID +field_id+ become an in-place
       # editor of a property.
       #
@@ -27,20 +29,21 @@ module ActionView
       # &lt;tt&gt;:url&lt;/tt&gt;::       Specifies the url where the updated value should
       #                       be sent after the user presses &quot;ok&quot;.
       # 
-      #
       # Addtional +options+ are:
       # &lt;tt&gt;:rows&lt;/tt&gt;::              Number of rows (more than 1 will use a TEXTAREA)
       # &lt;tt&gt;:cols&lt;/tt&gt;::              Number of characters the text input should span (works for both INPUT and TEXTAREA)
       # &lt;tt&gt;:size&lt;/tt&gt;::              Synonym for :cols when using a single line text input.
       # &lt;tt&gt;:cancel_text&lt;/tt&gt;::       The text on the cancel link. (default: &quot;cancel&quot;)
       # &lt;tt&gt;:save_text&lt;/tt&gt;::         The text on the save link. (default: &quot;ok&quot;)
-      # &lt;tt&gt;:loading_text&lt;/tt&gt;::      The text to display when submitting to the server (default: &quot;Saving...&quot;)
+      # &lt;tt&gt;:loading_text&lt;/tt&gt;::      The text to display while the data is being loaded from the server (default: &quot;Loading...&quot;)
+      # &lt;tt&gt;:saving_text&lt;/tt&gt;::       The text to display when submitting to the server (default: &quot;Saving...&quot;)
       # &lt;tt&gt;:external_control&lt;/tt&gt;::  The id of an external control used to enter edit mode.
       # &lt;tt&gt;:load_text_url&lt;/tt&gt;::     URL where initial value of editor (content) is retrieved.
       # &lt;tt&gt;:options&lt;/tt&gt;::           Pass through options to the AJAX call (see prototype's Ajax.Updater)
       # &lt;tt&gt;:with&lt;/tt&gt;::              JavaScript snippet that should return what is to be sent
       #                               in the AJAX call, +form+ is an implicit parameter
       # &lt;tt&gt;:script&lt;/tt&gt;::            Instructs the in-place editor to evaluate the remote JavaScript response (default: false)
+      # &lt;tt&gt;:click_to_edit_text&lt;/tt&gt;::The text shown during mouseover the editable text (default: &quot;Click to edit&quot;)
       def in_place_editor(field_id, options = {})
         function =  &quot;new Ajax.InPlaceEditor(&quot;
         function &lt;&lt; &quot;'#{field_id}', &quot;
@@ -50,6 +53,7 @@ module ActionView
         js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
         js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
         js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
+        js_options['savingText'] = %('#{options[:saving_text]}') if options[:saving_text]
         js_options['rows'] = options[:rows] if options[:rows]
         js_options['cols'] = options[:cols] if options[:cols]
         js_options['size'] = options[:size] if options[:size]
@@ -58,6 +62,7 @@ module ActionView
         js_options['ajaxOptions'] = options[:options] if options[:options]
         js_options['evalScripts'] = options[:script] if options[:script]
         js_options['callback']   = &quot;function(form) { return #{options[:with]} }&quot; if options[:with]
+        js_options['clickToEditText'] = %('#{options[:click_to_edit_text]}') if options[:click_to_edit_text]
         function &lt;&lt; (', ' + options_for_javascript(js_options)) unless js_options.empty?
         
         function &lt;&lt; ')'
@@ -65,6 +70,8 @@ module ActionView
         javascript_tag(function)
       end
       
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Renders the value of the specified object and method with in-place editing capabilities.
       #
       # See the RDoc on ActionController::InPlaceEditing to learn more about this.
@@ -76,14 +83,16 @@ module ActionView
         in_place_editor(tag_options[:id], in_place_editor_options)
       end
       
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Adds AJAX autocomplete functionality to the text input field with the 
       # DOM ID specified by +field_id+.
       #
-      # This function expects that the called action returns a HTML &lt;ul&gt; list,
+      # This function expects that the called action returns an HTML &lt;ul&gt; list,
       # or nothing if no entries should be displayed for autocompletion.
       #
       # You'll probably want to turn the browser's built-in autocompletion off,
-      # so be sure to include a autocomplete=&quot;off&quot; attribute with your text
+      # so be sure to include an &lt;tt&gt;autocomplete=&quot;off&quot;&lt;/tt&gt; attribute with your text
       # input field.
       #
       # The autocompleter object is assigned to a Javascript variable named &lt;tt&gt;field_id&lt;/tt&gt;_auto_completer.
@@ -91,45 +100,45 @@ module ActionView
       # other means than user input (for that specific case, call the &lt;tt&gt;activate&lt;/tt&gt; method on that object). 
       # 
       # Required +options+ are:
-      # &lt;tt&gt;:url&lt;/tt&gt;::       URL to call for autocompletion results
-      #                       in url_for format.
+      # &lt;tt&gt;:url&lt;/tt&gt;::                  URL to call for autocompletion results
+      #                                  in url_for format.
       # 
       # Addtional +options+ are:
-      # &lt;tt&gt;:update&lt;/tt&gt;::    Specifies the DOM ID of the element whose 
-      #                       innerHTML should be updated with the autocomplete
-      #                       entries returned by the AJAX request. 
-      #                       Defaults to field_id + '_auto_complete'
-      # &lt;tt&gt;:with&lt;/tt&gt;::      A JavaScript expression specifying the
-      #                       parameters for the XMLHttpRequest. This defaults
-      #                       to 'fieldname=value'.
-      # &lt;tt&gt;:frequency&lt;/tt&gt;:: Determines the time to wait after the last keystroke
-      #                       for the AJAX request to be initiated.
-      # &lt;tt&gt;:indicator&lt;/tt&gt;:: Specifies the DOM ID of an element which will be
-      #                       displayed while autocomplete is running.
-      # &lt;tt&gt;:tokens&lt;/tt&gt;::    A string or an array of strings containing
-      #                       separator tokens for tokenized incremental 
-      #                       autocompletion. Example: &lt;tt&gt;:tokens =&gt; ','&lt;/tt&gt; would
-      #                       allow multiple autocompletion entries, separated
-      #                       by commas.
-      # &lt;tt&gt;:min_chars&lt;/tt&gt;:: The minimum number of characters that should be
-      #                       in the input field before an Ajax call is made
-      #                       to the server.
-      # &lt;tt&gt;:on_hide&lt;/tt&gt;::   A Javascript expression that is called when the
-      #                       autocompletion div is hidden. The expression
-      #                       should take two variables: element and update.
-      #                       Element is a DOM element for the field, update
-      #                       is a DOM element for the div from which the
-      #                       innerHTML is replaced.
-      # &lt;tt&gt;:on_show&lt;/tt&gt;::   Like on_hide, only now the expression is called
-      #                       then the div is shown.
-      # &lt;tt&gt;:after_update_element&lt;/tt&gt;::   A Javascript expression that is called when the
-      #                                    user has selected one of the proposed values. 
-      #                                    The expression should take two variables: element and value.
-      #                                    Element is a DOM element for the field, value
-      #                                    is the value selected by the user.
-      # &lt;tt&gt;:select&lt;/tt&gt;::    Pick the class of the element from which the value for 
-      #                       insertion should be extracted. If this is not specified,
-      #                       the entire element is used.
+      # &lt;tt&gt;:update&lt;/tt&gt;::               Specifies the DOM ID of the element whose 
+      #                                  innerHTML should be updated with the autocomplete
+      #                                  entries returned by the AJAX request. 
+      #                                  Defaults to &lt;tt&gt;field_id&lt;/tt&gt; + '_auto_complete'
+      # &lt;tt&gt;:with&lt;/tt&gt;::                 A JavaScript expression specifying the
+      #                                  parameters for the XMLHttpRequest. This defaults
+      #                                  to 'fieldname=value'.
+      # &lt;tt&gt;:frequency&lt;/tt&gt;::            Determines the time to wait after the last keystroke
+      #                                  for the AJAX request to be initiated.
+      # &lt;tt&gt;:indicator&lt;/tt&gt;::            Specifies the DOM ID of an element which will be
+      #                                  displayed while autocomplete is running.
+      # &lt;tt&gt;:tokens&lt;/tt&gt;::               A string or an array of strings containing
+      #                                  separator tokens for tokenized incremental 
+      #                                  autocompletion. Example: &lt;tt&gt;:tokens =&gt; ','&lt;/tt&gt; would
+      #                                  allow multiple autocompletion entries, separated
+      #                                  by commas.
+      # &lt;tt&gt;:min_chars&lt;/tt&gt;::            The minimum number of characters that should be
+      #                                  in the input field before an Ajax call is made
+      #                                  to the server.
+      # &lt;tt&gt;:on_hide&lt;/tt&gt;::              A Javascript expression that is called when the
+      #                                  autocompletion div is hidden. The expression
+      #                                  should take two variables: element and update.
+      #                                  Element is a DOM element for the field, update
+      #                                  is a DOM element for the div from which the
+      #                                  innerHTML is replaced.
+      # &lt;tt&gt;:on_show&lt;/tt&gt;::              Like on_hide, only now the expression is called
+      #                                  then the div is shown.
+      # &lt;tt&gt;:after_update_element&lt;/tt&gt;:: A Javascript expression that is called when the
+      #                                  user has selected one of the proposed values. 
+      #                                  The expression should take two variables: element and value.
+      #                                  Element is a DOM element for the field, value
+      #                                  is the value selected by the user.
+      # &lt;tt&gt;:select&lt;/tt&gt;::               Pick the class of the element from which the value for 
+      #                                  insertion should be extracted. If this is not specified,
+      #                                  the entire element is used.
       def auto_complete_field(field_id, options = {})
         function =  &quot;var #{field_id}_auto_completer = new Ajax.Autocompleter(&quot;
         function &lt;&lt; &quot;'#{field_id}', &quot;
@@ -141,6 +150,7 @@ module ActionView
         js_options[:callback]   = &quot;function(element, value) { return #{options[:with]} }&quot; if options[:with]
         js_options[:indicator]  = &quot;'#{options[:indicator]}'&quot; if options[:indicator]
         js_options[:select]     = &quot;'#{options[:select]}'&quot; if options[:select]
+        js_options[:paramName]  = &quot;'#{options[:param_name]}'&quot; if options[:param_name]
         js_options[:frequency]  = &quot;#{options[:frequency]}&quot; if options[:frequency]
 
         { :after_update_element =&gt; :afterUpdateElement, 
@@ -153,6 +163,8 @@ module ActionView
         javascript_tag(function)
       end
       
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Use this method in your view to generate a return for the AJAX autocomplete requests.
       #
       # Example action:
@@ -161,7 +173,7 @@ module ActionView
       #     @items = Item.find(:all, 
       #       :conditions =&gt; [ 'LOWER(description) LIKE ?', 
       #       '%' + request.raw_post.downcase + '%' ])
-      #     render :inline =&gt; '&lt;%= auto_complete_result(@items, 'description') %&gt;'
+      #     render :inline =&gt; &quot;&lt;%= auto_complete_result(@items, 'description') %&gt;&quot;
       #   end
       #
       # The auto_complete_result can of course also be called from a view belonging to the 
@@ -172,12 +184,14 @@ module ActionView
         content_tag(&quot;ul&quot;, items.uniq)
       end
       
+      # DEPRECATION WARNING: This method will become a separate plugin when Rails 2.0 ships.
+      #
       # Wrapper for text_field with added AJAX autocompletion functionality.
       #
       # In your controller, you'll need to define an action called
-      # auto_complete_for_object_method to respond the AJAX calls,
+      # auto_complete_for to respond the AJAX calls,
       # 
-      # See the RDoc on ActionController::AutoComplete to learn more about this.
+      # See the RDoc on ActionController::Macros::AutoComplete to learn more about this.
       def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
         (completion_options[:skip_style] ? &quot;&quot; : auto_complete_stylesheet) +
         text_field(object, method, tag_options) +
@@ -187,7 +201,7 @@ module ActionView
       
       private
         def auto_complete_stylesheet
-          content_tag(&quot;style&quot;, &lt;&lt;-EOT
+          content_tag('style', &lt;&lt;-EOT, :type =&gt; 'text/css')
             div.auto_complete {
               width: 350px;
               background: #fff;
@@ -212,7 +226,6 @@ module ActionView
               padding:0;
             }
           EOT
-          )
         end
       
     end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/java_script_macros_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,5 @@
 require File.dirname(__FILE__) + '/tag_helper'
+require File.dirname(__FILE__) + '/prototype_helper'
 
 module ActionView
   module Helpers
@@ -40,15 +41,50 @@ module ActionView
       unless const_defined? :JAVASCRIPT_PATH
         JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts')
       end
+
+      include PrototypeHelper
       
-      # Returns a link that'll trigger a JavaScript +function+ using the 
+      # Returns a link that will trigger a JavaScript +function+ using the 
       # onclick handler and return false after the fact.
       #
+      # The +function+ argument can be omitted in favor of an +update_page+
+      # block, which evaluates to a string when the template is rendered
+      # (instead of making an Ajax request first).      
+      #
       # Examples:
       #   link_to_function &quot;Greeting&quot;, &quot;alert('Hello world!')&quot;
-      #   link_to_function(image_tag(&quot;delete&quot;), &quot;if confirm('Really?'){ do_delete(); }&quot;)
-      def link_to_function(name, function, html_options = {})
+      #     Produces:
+      #       &lt;a onclick=&quot;alert('Hello world!'); return false;&quot; href=&quot;#&quot;&gt;Greeting&lt;/a&gt;
+      #
+      #   link_to_function(image_tag(&quot;delete&quot;), &quot;if (confirm('Really?')) do_delete()&quot;)
+      #     Produces:
+      #       &lt;a onclick=&quot;if (confirm('Really?')) do_delete(); return false;&quot; href=&quot;#&quot;&gt;
+      #         &lt;img src=&quot;/images/delete.png?&quot; alt=&quot;Delete&quot;/&gt;
+      #       &lt;/a&gt;
+      #
+      #   link_to_function(&quot;Show me more&quot;, nil, :id =&gt; &quot;more_link&quot;) do |page|
+      #     page[:details].visual_effect  :toggle_blind
+      #     page[:more_link].replace_html &quot;Show me less&quot;
+      #   end
+      #     Produces:
+      #       &lt;a href=&quot;#&quot; id=&quot;more_link&quot; onclick=&quot;try {
+      #         $(&amp;quot;details&amp;quot;).visualEffect(&amp;quot;toggle_blind&amp;quot;);
+      #         $(&amp;quot;more_link&amp;quot;).update(&amp;quot;Show me less&amp;quot;);
+      #       } 
+      #       catch (e) { 
+      #         alert('RJS error:\n\n' + e.toString()); 
+      #         alert('$(\&amp;quot;details\&amp;quot;).visualEffect(\&amp;quot;toggle_blind\&amp;quot;);
+      #         \n$(\&amp;quot;more_link\&amp;quot;).update(\&amp;quot;Show me less\&amp;quot;);');
+      #         throw e 
+      #       };
+      #       return false;&quot;&gt;Show me more&lt;/a&gt;
+      #
+      def link_to_function(name, *args, &amp;block)
+        html_options = args.last.is_a?(Hash) ? args.pop : {}
+        function = args[0] || ''
+
         html_options.symbolize_keys!
+        function = update_page(&amp;block) if block_given?
         content_tag(
           &quot;a&quot;, name, 
           html_options.merge({ 
@@ -58,14 +94,28 @@ module ActionView
         )
       end
       
-      # Returns a link that'll trigger a JavaScript +function+ using the 
+      # Returns a button that'll trigger a JavaScript +function+ using the 
       # onclick handler.
       #
+      # The +function+ argument can be omitted in favor of an +update_page+
+      # block, which evaluates to a string when the template is rendered
+      # (instead of making an Ajax request first).      
+      #
       # Examples:
       #   button_to_function &quot;Greeting&quot;, &quot;alert('Hello world!')&quot;
-      #   button_to_function &quot;Delete&quot;, &quot;if confirm('Really?'){ do_delete(); }&quot;)
-      def button_to_function(name, function, html_options = {})
+      #   button_to_function &quot;Delete&quot;, &quot;if (confirm('Really?')) do_delete()&quot;
+      #   button_to_function &quot;Details&quot; do |page|
+      #     page[:details].visual_effect :toggle_slide
+      #   end
+      #   button_to_function &quot;Details&quot;, :class =&gt; &quot;details_button&quot; do |page|
+      #     page[:details].visual_effect :toggle_slide
+      #   end
+      def button_to_function(name, *args, &amp;block)
+        html_options = args.last.is_a?(Hash) ? args.pop : {}
+        function = args[0] || ''
+
         html_options.symbolize_keys!
+        function = update_page(&amp;block) if block_given?
         tag(:input, html_options.merge({ 
           :type =&gt; &quot;button&quot;, :value =&gt; name, 
           :onclick =&gt; (html_options[:onclick] ? &quot;#{html_options[:onclick]}; &quot; : &quot;&quot;) + &quot;#{function};&quot; 
@@ -99,13 +149,24 @@ module ActionView
 
       # Escape carrier returns and single and double quotes for JavaScript segments.
       def escape_javascript(javascript)
-        (javascript || '').gsub(/\r\n|\n|\r/, &quot;\\n&quot;).gsub(/[&quot;']/) { |m| &quot;\\#{m}&quot; }
+        (javascript || '').gsub('\\','\0\0').gsub(/\r\n|\n|\r/, &quot;\\n&quot;).gsub(/[&quot;']/) { |m| &quot;\\#{m}&quot; }
       end
 
       # Returns a JavaScript tag with the +content+ inside. Example:
-      #   javascript_tag &quot;alert('All is good')&quot; # =&gt; &lt;script type=&quot;text/javascript&quot;&gt;alert('All is good')&lt;/script&gt;
-      def javascript_tag(content)
-        content_tag(&quot;script&quot;, javascript_cdata_section(content), :type =&gt; &quot;text/javascript&quot;)
+      #   javascript_tag &quot;alert('All is good')&quot;
+      #
+      # Returns:
+      #
+      #   &lt;script type=&quot;text/javascript&quot;&gt;
+      #   //&lt;![CDATA[
+      #   alert('All is good')
+      #   //]]&gt;
+      #   &lt;/script&gt;
+      #
+      # +html_options+ may be a hash of attributes for the &lt;script&gt; tag. Example:
+      #   javascript_tag &quot;alert('All is good')&quot;, :defer =&gt; 'true' # =&gt; &lt;script defer=&quot;true&quot; type=&quot;text/javascript&quot;&gt;alert('All is good')&lt;/script&gt;
+      def javascript_tag(content, html_options = {})
+        content_tag(&quot;script&quot;, javascript_cdata_section(content), html_options.merge(:type =&gt; &quot;text/javascript&quot;))
       end
 
       def javascript_cdata_section(content) #:nodoc:</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/javascript_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,13 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
-//           (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+//           (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com)
 // Contributors:
 //  Richard Livsey
 //  Rahul Bhargava
 //  Rob Wills
 // 
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
 
 // Autocompleter.Base handles all the autocompletion functionality 
 // that's independent of the data source for autocompletion. This
@@ -33,6 +34,9 @@
 // useful when one of the tokens is \n (a newline), as it 
 // allows smart autocompletion after linebreaks.
 
+if(typeof Effect == 'undefined')
+  throw(&quot;controls.js requires including script.aculo.us' effects.js library&quot;);
+
 var Autocompleter = {}
 Autocompleter.Base = function() {};
 Autocompleter.Base.prototype = {
@@ -45,7 +49,7 @@ Autocompleter.Base.prototype = {
     this.index       = 0;     
     this.entryCount  = 0;
 
-    if (this.setOptions)
+    if(this.setOptions)
       this.setOptions(options);
     else
       this.options = options || {};
@@ -55,17 +59,20 @@ Autocompleter.Base.prototype = {
     this.options.frequency    = this.options.frequency || 0.4;
     this.options.minChars     = this.options.minChars || 1;
     this.options.onShow       = this.options.onShow || 
-    function(element, update){ 
-      if(!update.style.position || update.style.position=='absolute') {
-        update.style.position = 'absolute';
-        Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
-      }
-      Effect.Appear(update,{duration:0.15});
-    };
+      function(element, update){ 
+        if(!update.style.position || update.style.position=='absolute') {
+          update.style.position = 'absolute';
+          Position.clone(element, update, {
+            setHeight: false, 
+            offsetTop: element.offsetHeight
+          });
+        }
+        Effect.Appear(update,{duration:0.15});
+      };
     this.options.onHide = this.options.onHide || 
-    function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
 
-    if (typeof(this.options.tokens) == 'string') 
+    if(typeof(this.options.tokens) == 'string') 
       this.options.tokens = new Array(this.options.tokens);
 
     this.observer = null;
@@ -94,7 +101,7 @@ Autocompleter.Base.prototype = {
   },
   
   fixIEOverlapping: function() {
-    Position.clone(this.update, this.iefix);
+    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
     this.iefix.style.zIndex = 1;
     this.update.style.zIndex = 2;
     Element.show(this.iefix);
@@ -202,11 +209,13 @@ Autocompleter.Base.prototype = {
   markPrevious: function() {
     if(this.index &gt; 0) this.index--
       else this.index = this.entryCount-1;
+    this.getEntry(this.index).scrollIntoView(true);
   },
   
   markNext: function() {
     if(this.index &lt; this.entryCount-1) this.index++
       else this.index = 0;
+    this.getEntry(this.index).scrollIntoView(false);
   },
   
   getEntry: function(index) {
@@ -254,11 +263,11 @@ Autocompleter.Base.prototype = {
     if(!this.changed &amp;&amp; this.hasFocus) {
       this.update.innerHTML = choices;
       Element.cleanWhitespace(this.update);
-      Element.cleanWhitespace(this.update.firstChild);
+      Element.cleanWhitespace(this.update.down());
 
-      if(this.update.firstChild &amp;&amp; this.update.firstChild.childNodes) {
+      if(this.update.firstChild &amp;&amp; this.update.down().childNodes) {
         this.entryCount = 
-          this.update.firstChild.childNodes.length;
+          this.update.down().childNodes.length;
         for (var i = 0; i &lt; this.entryCount; i++) {
           var entry = this.getEntry(i);
           entry.autocompleteIndex = i;
@@ -269,9 +278,14 @@ Autocompleter.Base.prototype = {
       }
 
       this.stopIndicator();
-
       this.index = 0;
-      this.render();
+      
+      if(this.entryCount==1 &amp;&amp; this.options.autoSelect) {
+        this.selectEntry();
+        this.hide();
+      } else {
+        this.render();
+      }
     }
   },
 
@@ -459,6 +473,7 @@ Ajax.InPlaceEditor.prototype = {
     this.element = $(element);
 
     this.options = Object.extend({
+      paramName: &quot;value&quot;,
       okButton: true,
       okText: &quot;ok&quot;,
       cancelLink: true,
@@ -531,7 +546,7 @@ Ajax.InPlaceEditor.prototype = {
     Element.hide(this.element);
     this.createForm();
     this.element.parentNode.insertBefore(this.form, this.element);
-    Field.scrollFreeActivate(this.editField);
+    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
     // stop the event to avoid a page refresh in Safari
     if (evt) {
       Event.stop(evt);
@@ -590,7 +605,7 @@ Ajax.InPlaceEditor.prototype = {
       var textField = document.createElement(&quot;input&quot;);
       textField.obj = this;
       textField.type = &quot;text&quot;;
-      textField.name = &quot;value&quot;;
+      textField.name = this.options.paramName;
       textField.value = text;
       textField.style.backgroundColor = this.options.highlightcolor;
       textField.className = 'editor_field';
@@ -603,7 +618,7 @@ Ajax.InPlaceEditor.prototype = {
       this.options.textarea = true;
       var textArea = document.createElement(&quot;textarea&quot;);
       textArea.obj = this;
-      textArea.name = &quot;value&quot;;
+      textArea.name = this.options.paramName;
       textArea.value = this.convertHTMLLineBreaks(text);
       textArea.rows = this.options.rows;
       textArea.cols = this.options.cols || 40;
@@ -636,6 +651,7 @@ Ajax.InPlaceEditor.prototype = {
     Element.removeClassName(this.form, this.options.loadingClassName);
     this.editField.disabled = false;
     this.editField.value = transport.responseText.stripTags();
+    Field.scrollFreeActivate(this.editField);
   },
   onclickCancel: function() {
     this.onComplete();
@@ -772,6 +788,8 @@ Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
       collection.each(function(e,i) {
         optionTag = document.createElement(&quot;option&quot;);
         optionTag.value = (e instanceof Array) ? e[0] : e;
+        if((typeof this.options.value == 'undefined') &amp;&amp; 
+          ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
         if(this.options.value==optionTag.value) optionTag.selected = true;
         optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
         selectTag.appendChild(optionTag);</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/javascripts/controls.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,11 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
-//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//           (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
 // 
-// See scriptaculous.js for full license.
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
 
-/*--------------------------------------------------------------------------*/
+if(typeof Effect == 'undefined')
+  throw(&quot;dragdrop.js requires including script.aculo.us' effects.js library&quot;);
 
 var Droppables = {
   drops: [],
@@ -145,8 +147,16 @@ var Draggables = {
   },
   
   activate: function(draggable) {
-    window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
-    this.activeDraggable = draggable;
+    if(draggable.options.delay) { 
+      this._timeout = setTimeout(function() { 
+        Draggables._timeout = null; 
+        window.focus(); 
+        Draggables.activeDraggable = draggable; 
+      }.bind(this), draggable.options.delay); 
+    } else {
+      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+      this.activeDraggable = draggable;
+    }
   },
   
   deactivate: function() {
@@ -160,10 +170,15 @@ var Draggables = {
     // the same coordinates, prevent needless redrawing (moz bug?)
     if(this._lastPointer &amp;&amp; (this._lastPointer.inspect() == pointer.inspect())) return;
     this._lastPointer = pointer;
+    
     this.activeDraggable.updateDrag(event, pointer);
   },
   
   endDrag: function(event) {
+    if(this._timeout) { 
+      clearTimeout(this._timeout); 
+      this._timeout = null; 
+    }
     if(!this.activeDraggable) return;
     this._lastPointer = null;
     this.activeDraggable.endDrag(event);
@@ -190,6 +205,7 @@ var Draggables = {
       this.observers.each( function(o) {
         if(o[eventName]) o[eventName](eventName, draggable, event);
       });
+    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
   },
   
   _cacheObserverCallbacks: function() {
@@ -204,39 +220,59 @@ var Draggables = {
 /*--------------------------------------------------------------------------*/
 
 var Draggable = Class.create();
+Draggable._dragging    = {};
+
 Draggable.prototype = {
   initialize: function(element) {
-    var options = Object.extend({
+    var defaults = {
       handle: false,
-      starteffect: function(element) { 
-        new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); 
-      },
       reverteffect: function(element, top_offset, left_offset) {
         var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
-        element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
+          queue: {scope:'_draggable', position:'end'}
+        });
       },
-      endeffect: function(element) { 
-        new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); 
+      endeffect: function(element) {
+        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
+        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
+          queue: {scope:'_draggable', position:'end'},
+          afterFinish: function(){ 
+            Draggable._dragging[element] = false 
+          }
+        }); 
       },
       zindex: 1000,
       revert: false,
       scroll: false,
       scrollSensitivity: 20,
       scrollSpeed: 15,
-      snap: false   // false, or xy or [x,y] or function(x,y){ return [x,y] }
-    }, arguments[1] || {});
+      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
+      delay: 0
+    };
+    
+    if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
+      Object.extend(defaults, {
+        starteffect: function(element) {
+          element._opacity = Element.getOpacity(element);
+          Draggable._dragging[element] = true;
+          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
+        }
+      });
+    
+    var options = Object.extend(defaults, arguments[1] || {});
 
     this.element = $(element);
     
-    if(options.handle &amp;&amp; (typeof options.handle == 'string')) {
-      var h = Element.childrenWithClassName(this.element, options.handle, true);
-      if(h.length&gt;0) this.handle = h[0];
-    }
+    if(options.handle &amp;&amp; (typeof options.handle == 'string'))
+      this.handle = this.element.down('.'+options.handle, 0);
+    
     if(!this.handle) this.handle = $(options.handle);
     if(!this.handle) this.handle = this.element;
     
-    if(options.scroll &amp;&amp; !options.scroll.scrollTo &amp;&amp; !options.scroll.outerHTML)
+    if(options.scroll &amp;&amp; !options.scroll.scrollTo &amp;&amp; !options.scroll.outerHTML) {
       options.scroll = $(options.scroll);
+      this._isScrollChild = Element.childOf(this.element, options.scroll);
+    }
 
     Element.makePositioned(this.element); // fix IE    
 
@@ -262,6 +298,8 @@ Draggable.prototype = {
   },
   
   initDrag: function(event) {
+    if(typeof Draggable._dragging[this.element] != 'undefined' &amp;&amp;
+      Draggable._dragging[this.element]) return;
     if(Event.isLeftClick(event)) {    
       // abort on form elements, fixes a Firefox issue
       var src = Event.element(event);
@@ -272,11 +310,6 @@ Draggable.prototype = {
         src.tagName=='BUTTON' ||
         src.tagName=='TEXTAREA')) return;
         
-      if(this.element._revert) {
-        this.element._revert.cancel();
-        this.element._revert = null;
-      }
-      
       var pointer = [Event.pointerX(event), Event.pointerY(event)];
       var pos     = Position.cumulativeOffset(this.element);
       this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
@@ -312,6 +345,7 @@ Draggable.prototype = {
     }
     
     Draggables.notify('onStart', this, event);
+        
     if(this.options.starteffect) this.options.starteffect(this.element);
   },
   
@@ -320,6 +354,7 @@ Draggable.prototype = {
     Position.prepare();
     Droppables.show(pointer, this.element);
     Draggables.notify('onDrag', this, event);
+    
     this.draw(pointer);
     if(this.options.change) this.options.change(this);
     
@@ -331,8 +366,8 @@ Draggable.prototype = {
         with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
       } else {
         p = Position.page(this.options.scroll);
-        p[0] += this.options.scroll.scrollLeft;
-        p[1] += this.options.scroll.scrollTop;
+        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
+        p[1] += this.options.scroll.scrollTop + Position.deltaY;
         p.push(p[0]+this.options.scroll.offsetWidth);
         p.push(p[1]+this.options.scroll.offsetHeight);
       }
@@ -378,7 +413,7 @@ Draggable.prototype = {
 
     if(this.options.endeffect) 
       this.options.endeffect(this.element);
-
+      
     Draggables.deactivate(this);
     Droppables.reset();
   },
@@ -398,10 +433,15 @@ Draggable.prototype = {
   
   draw: function(point) {
     var pos = Position.cumulativeOffset(this.element);
+    if(this.options.ghosting) {
+      var r   = Position.realOffset(this.element);
+      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
+    }
+    
     var d = this.currentDelta();
     pos[0] -= d[0]; pos[1] -= d[1];
     
-    if(this.options.scroll &amp;&amp; (this.options.scroll != window)) {
+    if(this.options.scroll &amp;&amp; (this.options.scroll != window &amp;&amp; this._isScrollChild)) {
       pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
       pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
     }
@@ -412,7 +452,7 @@ Draggable.prototype = {
     
     if(this.options.snap) {
       if(typeof this.options.snap == 'function') {
-        p = this.options.snap(p[0],p[1]);
+        p = this.options.snap(p[0],p[1],this);
       } else {
       if(this.options.snap instanceof Array) {
         p = p.map( function(v, i) {
@@ -428,6 +468,7 @@ Draggable.prototype = {
       style.left = p[0] + &quot;px&quot;;
     if((!this.options.constraint) || (this.options.constraint=='vertical'))
       style.top  = p[1] + &quot;px&quot;;
+    
     if(style.visibility==&quot;hidden&quot;) style.visibility = &quot;&quot;; // fix gecko rendering
   },
   
@@ -440,6 +481,7 @@ Draggable.prototype = {
   },
   
   startScrolling: function(speed) {
+    if(!(speed[0] || speed[1])) return;
     this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
     this.lastScrolled = new Date();
     this.scrollInterval = setInterval(this.scroll.bind(this), 10);
@@ -464,14 +506,16 @@ Draggable.prototype = {
     Position.prepare();
     Droppables.show(Draggables._lastPointer, this.element);
     Draggables.notify('onDrag', this);
-    Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
-    Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
-    Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
-    if (Draggables._lastScrollPointer[0] &lt; 0)
-      Draggables._lastScrollPointer[0] = 0;
-    if (Draggables._lastScrollPointer[1] &lt; 0)
-      Draggables._lastScrollPointer[1] = 0;
-    this.draw(Draggables._lastScrollPointer);
+    if (this._isScrollChild) {
+      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
+      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
+      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
+      if (Draggables._lastScrollPointer[0] &lt; 0)
+        Draggables._lastScrollPointer[0] = 0;
+      if (Draggables._lastScrollPointer[1] &lt; 0)
+        Draggables._lastScrollPointer[1] = 0;
+      this.draw(Draggables._lastScrollPointer);
+    }
     
     if(this.options.change) this.options.change(this);
   },
@@ -523,6 +567,8 @@ SortableObserver.prototype = {
 }
 
 var Sortable = {
+  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
+  
   sortables: {},
   
   _findRootElement: function(element) {
@@ -563,12 +609,13 @@ var Sortable = {
       containment: element,    // also takes array of elements (or id's); or false
       handle:      false,      // or a CSS class
       only:        false,
+      delay:       0,
       hoverclass:  null,
       ghosting:    false,
       scroll:      false,
       scrollSensitivity: 20,
       scrollSpeed: 15,
-      format:      /^[^_]*_(.*)$/,
+      format:      this.SERIALIZE_RULE,
       onChange:    Prototype.emptyFunction,
       onUpdate:    Prototype.emptyFunction
     }, arguments[1] || {});
@@ -582,6 +629,7 @@ var Sortable = {
       scroll:      options.scroll,
       scrollSpeed: options.scrollSpeed,
       scrollSensitivity: options.scrollSensitivity,
+      delay:       options.delay,
       ghosting:    options.ghosting,
       constraint:  options.constraint,
       handle:      options.handle };
@@ -610,7 +658,6 @@ var Sortable = {
       tree:        options.tree,
       hoverclass:  options.hoverclass,
       onHover:     Sortable.onHover
-      //greedy:      !options.dropOnEmpty
     }
     
     var options_for_tree = {
@@ -635,7 +682,7 @@ var Sortable = {
     (this.findElements(element, options) || []).each( function(e) {
       // handles are per-draggable
       var handle = options.handle ? 
-        Element.childrenWithClassName(e, options.handle)[0] : e;    
+        $(e).down('.'+options.handle,0) : e;    
       options.draggables.push(
         new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
       Droppables.add(e, options_for_droppable);
@@ -706,7 +753,7 @@ var Sortable = {
     if(!Element.isParent(dropon, element)) {
       var index;
       
-      var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
+      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
       var child = null;
             
       if(children) {
@@ -733,7 +780,7 @@ var Sortable = {
   },
 
   unmark: function() {
-    if(Sortable._marker) Element.hide(Sortable._marker);
+    if(Sortable._marker) Sortable._marker.hide();
   },
 
   mark: function(dropon, position) {
@@ -742,23 +789,21 @@ var Sortable = {
     if(sortable &amp;&amp; !sortable.ghosting) return; 
 
     if(!Sortable._marker) {
-      Sortable._marker = $('dropmarker') || document.createElement('DIV');
-      Element.hide(Sortable._marker);
-      Element.addClassName(Sortable._marker, 'dropmarker');
-      Sortable._marker.style.position = 'absolute';
+      Sortable._marker = 
+        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
+          hide().addClassName('dropmarker').setStyle({position:'absolute'});
       document.getElementsByTagName(&quot;body&quot;).item(0).appendChild(Sortable._marker);
     }    
     var offsets = Position.cumulativeOffset(dropon);
-    Sortable._marker.style.left = offsets[0] + 'px';
-    Sortable._marker.style.top = offsets[1] + 'px';
+    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
     
     if(position=='after')
       if(sortable.overlap == 'horizontal') 
-        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
       else
-        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
     
-    Element.show(Sortable._marker);
+    Sortable._marker.show();
   },
   
   _tree: function(element, options, parent) {
@@ -773,9 +818,9 @@ var Sortable = {
         id: encodeURIComponent(match ? match[1] : null),
         element: element,
         parent: parent,
-        children: new Array,
+        children: [],
         position: parent.children.length,
-        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
+        container: $(children[i]).down(options.treeTag)
       }
       
       /* Get the element containing the children and recurse over it */
@@ -788,17 +833,6 @@ var Sortable = {
     return parent; 
   },
 
-  /* Finds the first element of the given tag type within a parent element.
-    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
-  _findChildrenElement: function (element, containerTag) {
-    if (element &amp;&amp; element.hasChildNodes)
-      for (var i = 0; i &lt; element.childNodes.length; ++i)
-        if (element.childNodes[i].tagName == containerTag)
-          return element.childNodes[i];
-  
-    return null;
-  },
-
   tree: function(element) {
     element = $(element);
     var sortableOptions = this.options(element);
@@ -813,12 +847,12 @@ var Sortable = {
     var root = {
       id: null,
       parent: null,
-      children: new Array,
+      children: [],
       container: element,
       position: 0
     }
     
-    return Sortable._tree (element, options, root);
+    return Sortable._tree(element, options, root);
   },
 
   /* Construct a [i] index for a particular node */
@@ -867,7 +901,7 @@ var Sortable = {
     
     if (options.tree) {
       return Sortable.tree(element, arguments[1]).children.map( function (item) {
-        return [name + Sortable._constructIndex(item) + &quot;=&quot; + 
+        return [name + Sortable._constructIndex(item) + &quot;[id]=&quot; + 
                 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
       }).flatten().join('&amp;');
     } else {
@@ -878,12 +912,10 @@ var Sortable = {
   }
 }
 
-/* Returns true if child is contained within element */
+// Returns true if child is contained within element
 Element.isParent = function(child, element) {
   if (!child.parentNode || child == element) return false;
-
   if (child.parentNode == element) return true;
-
   return Element.isParent(child.parentNode, element);
 }
 
@@ -906,8 +938,5 @@ Element.findChildren = function(element, only, recursive, tagName) {
 }
 
 Element.offsetSize = function (element, type) {
-  if (type == 'vertical' || type == 'height')
-    return element.offsetHeight;
-  else
-    return element.offsetWidth;
-}
\ No newline at end of file
+  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
+}</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/javascripts/dragdrop.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,16 @@
-// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
 // Contributors:
 //  Justin Palmer (http://encytemedia.com/)
 //  Mark Pilgrim (http://diveintomark.org/)
 //  Martin Bialasinki
 // 
-// See scriptaculous.js for full license.  
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/ 
 
 // converts rgb() and #xxx to #xxxxxx format,  
 // returns self (or first argument) if not convertable  
 String.prototype.parseColor = function() {  
-  var color = '#';  
+  var color = '#';
   if(this.slice(0,4) == 'rgb(') {  
     var cols = this.slice(4,this.length-1).split(',');  
     var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i&lt;3);  
@@ -41,15 +42,17 @@ Element.collectTextNodesIgnoreClass = function(element, className) {
 
 Element.setContentZoom = function(element, percent) {
   element = $(element);  
-  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
+  element.setStyle({fontSize: (percent/100) + 'em'});   
   if(navigator.appVersion.indexOf('AppleWebKit')&gt;0) window.scrollBy(0,0);
+  return element;
 }
 
-Element.getOpacity = function(element){  
+Element.getOpacity = function(element){
+  element = $(element);
   var opacity;
-  if (opacity = Element.getStyle(element, 'opacity'))  
+  if (opacity = element.getStyle('opacity'))  
     return parseFloat(opacity);  
-  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
+  if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))  
     if(opacity[1]) return parseFloat(opacity[1]) / 100;  
   return 1.0;  
 }
@@ -57,34 +60,26 @@ Element.getOpacity = function(element){
 Element.setOpacity = function(element, value){  
   element= $(element);  
   if (value == 1){
-    Element.setStyle(element, { opacity: 
+    element.setStyle({ opacity: 
       (/Gecko/.test(navigator.userAgent) &amp;&amp; !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
-      0.999999 : null });
-    if(/MSIE/.test(navigator.userAgent))  
-      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
+      0.999999 : 1.0 });
+    if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera)  
+      element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
   } else {  
     if(value &lt; 0.00001) value = 0;  
-    Element.setStyle(element, {opacity: value});
-    if(/MSIE/.test(navigator.userAgent))  
-     Element.setStyle(element, 
-       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
-                 'alpha(opacity='+value*100+')' });  
+    element.setStyle({opacity: value});
+    if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera)  
+      element.setStyle(
+        { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
+            'alpha(opacity='+value*100+')' });  
   }
+  return element;
 }  
  
 Element.getInlineOpacity = function(element){  
   return $(element).style.opacity || '';
 }  
 
-Element.childrenWithClassName = function(element, className, findFirst) {
-  var classNameRegExp = new RegExp(&quot;(^|\\s)&quot; + className + &quot;(\\s|$)&quot;);
-  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
-    return (c.className &amp;&amp; c.className.match(classNameRegExp));
-  });
-  if(!results) results = [];
-  return results;
-}
-
 Element.forceRerendering = function(element) {
   try {
     element = $(element);
@@ -104,9 +99,17 @@ Array.prototype.call = function() {
 /*--------------------------------------------------------------------------*/
 
 var Effect = {
+  _elementDoesNotExistError: {
+    name: 'ElementDoesNotExistError',
+    message: 'The specified DOM element does not exist, but is required for this effect to operate'
+  },
   tagifyText: function(element) {
+    if(typeof Builder == 'undefined')
+      throw(&quot;Effect.tagifyText requires including script.aculo.us' builder.js library&quot;);
+      
     var tagifyStyle = 'position:relative';
-    if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+    if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera) tagifyStyle += ';zoom:1';
+    
     element = $(element);
     $A(element.childNodes).each( function(child) {
       if(child.nodeType==3) {
@@ -159,33 +162,35 @@ var Effect2 = Effect; // deprecated
 
 /* ------------- transitions ------------- */
 
-Effect.Transitions = {}
-
-Effect.Transitions.linear = function(pos) {
-  return pos;
-}
-Effect.Transitions.sinoidal = function(pos) {
-  return (-Math.cos(pos*Math.PI)/2) + 0.5;
-}
-Effect.Transitions.reverse  = function(pos) {
-  return 1-pos;
-}
-Effect.Transitions.flicker = function(pos) {
-  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
-}
-Effect.Transitions.wobble = function(pos) {
-  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
-}
-Effect.Transitions.pulse = function(pos) {
-  return (Math.floor(pos*10) % 2 == 0 ? 
-    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
-}
-Effect.Transitions.none = function(pos) {
-  return 0;
-}
-Effect.Transitions.full = function(pos) {
-  return 1;
-}
+Effect.Transitions = {
+  linear: Prototype.K,
+  sinoidal: function(pos) {
+    return (-Math.cos(pos*Math.PI)/2) + 0.5;
+  },
+  reverse: function(pos) {
+    return 1-pos;
+  },
+  flicker: function(pos) {
+    return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+  },
+  wobble: function(pos) {
+    return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+  },
+  pulse: function(pos, pulses) { 
+    pulses = pulses || 5; 
+    return (
+      Math.round((pos % (1/pulses)) * pulses) == 0 ? 
+            ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 
+        1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
+      );
+  },
+  none: function(pos) {
+    return 0;
+  },
+  full: function(pos) {
+    return 1;
+  }
+};
 
 /* ------------- core effects ------------- */
 
@@ -212,6 +217,9 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
             e.finishOn += effect.finishOn;
           });
         break;
+      case 'with-last':
+        timestamp = this.effects.pluck('startOn').max() || timestamp;
+        break;
       case 'end':
         // start effect after last queued effect has finished
         timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -348,12 +356,24 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
   }
 });
 
+Effect.Event = Class.create();
+Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
+  initialize: function() {
+    var options = Object.extend({
+      duration: 0
+    }, arguments[0] || {});
+    this.start(options);
+  },
+  update: Prototype.emptyFunction
+});
+
 Effect.Opacity = Class.create();
 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     // make this work on IE on elements without 'layout'
-    if(/MSIE/.test(navigator.userAgent) &amp;&amp; (!this.element.hasLayout))
+    if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera &amp;&amp; (!this.element.currentStyle.hasLayout))
       this.element.setStyle({zoom: 1});
     var options = Object.extend({
       from: this.element.getOpacity() || 0.0,
@@ -370,6 +390,7 @@ Effect.Move = Class.create();
 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({
       x:    0,
       y:    0,
@@ -393,8 +414,8 @@ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
   },
   update: function(position) {
     this.element.setStyle({
-      left: this.options.x  * position + this.originalLeft + 'px',
-      top:  this.options.y  * position + this.originalTop  + 'px'
+      left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
+      top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
     });
   }
 });
@@ -408,7 +429,8 @@ Effect.MoveBy = function(element, toTop, toLeft) {
 Effect.Scale = Class.create();
 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
   initialize: function(element, percent) {
-    this.element = $(element)
+    this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({
       scaleX: true,
       scaleY: true,
@@ -433,7 +455,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
     this.originalLeft = this.element.offsetLeft;
     
     var fontSize = this.element.getStyle('font-size') || '100%';
-    ['em','px','%'].each( function(fontSizeType) {
+    ['em','px','%','pt'].each( function(fontSizeType) {
       if(fontSize.indexOf(fontSizeType)&gt;0) {
         this.fontSize     = parseFloat(fontSize);
         this.fontSizeType = fontSizeType;
@@ -458,12 +480,12 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
     this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
   },
   finish: function(position) {
-    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+    if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
   },
   setDimensions: function(height, width) {
     var d = {};
-    if(this.options.scaleX) d.width = width + 'px';
-    if(this.options.scaleY) d.height = height + 'px';
+    if(this.options.scaleX) d.width = Math.round(width) + 'px';
+    if(this.options.scaleY) d.height = Math.round(height) + 'px';
     if(this.options.scaleFromCenter) {
       var topd  = (height - this.dims[0])/2;
       var leftd = (width  - this.dims[1])/2;
@@ -483,6 +505,7 @@ Effect.Highlight = Class.create();
 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
   initialize: function(element) {
     this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
     var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
     this.start(options);
   },
@@ -547,8 +570,7 @@ Effect.Fade = function(element) {
   to:   0.0,
   afterFinishInternal: function(effect) { 
     if(effect.options.to!=0) return;
-    effect.element.hide();
-    effect.element.setStyle({opacity: oldOpacity}); 
+    effect.element.hide().setStyle({opacity: oldOpacity}); 
   }}, arguments[1] || {});
   return new Effect.Opacity(element,options);
 }
@@ -563,25 +585,31 @@ Effect.Appear = function(element) {
     effect.element.forceRerendering();
   },
   beforeSetup: function(effect) {
-    effect.element.setOpacity(effect.options.from);
-    effect.element.show(); 
+    effect.element.setOpacity(effect.options.from).show(); 
   }}, arguments[1] || {});
   return new Effect.Opacity(element,options);
 }
 
 Effect.Puff = function(element) {
   element = $(element);
-  var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
+  var oldStyle = { 
+    opacity: element.getInlineOpacity(), 
+    position: element.getStyle('position'),
+    top:  element.style.top,
+    left: element.style.left,
+    width: element.style.width,
+    height: element.style.height
+  };
   return new Effect.Parallel(
    [ new Effect.Scale(element, 200, 
       { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
      Object.extend({ duration: 1.0, 
       beforeSetupInternal: function(effect) {
-        effect.effects[0].element.setStyle({position: 'absolute'}); },
+        Position.absolutize(effect.effects[0].element)
+      },
       afterFinishInternal: function(effect) {
-         effect.effects[0].element.hide();
-         effect.effects[0].element.setStyle(oldStyle); }
+         effect.effects[0].element.hide().setStyle(oldStyle); }
      }, arguments[1] || {})
    );
 }
@@ -589,13 +617,12 @@ Effect.Puff = function(element) {
 Effect.BlindUp = function(element) {
   element = $(element);
   element.makeClipping();
-  return new Effect.Scale(element, 0, 
+  return new Effect.Scale(element, 0,
     Object.extend({ scaleContent: false, 
       scaleX: false, 
       restoreAfterFinish: true,
       afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping();
+        effect.element.hide().undoClipping();
       } 
     }, arguments[1] || {})
   );
@@ -604,28 +631,25 @@ Effect.BlindUp = function(element) {
 Effect.BlindDown = function(element) {
   element = $(element);
   var elementDimensions = element.getDimensions();
-  return new Effect.Scale(element, 100, 
-    Object.extend({ scaleContent: false, 
-      scaleX: false,
-      scaleFrom: 0,
-      scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
-      restoreAfterFinish: true,
-      afterSetup: function(effect) {
-        effect.element.makeClipping();
-        effect.element.setStyle({height: '0px'});
-        effect.element.show(); 
-      },  
-      afterFinishInternal: function(effect) {
-        effect.element.undoClipping();
-      }
-    }, arguments[1] || {})
-  );
+  return new Effect.Scale(element, 100, Object.extend({ 
+    scaleContent: false, 
+    scaleX: false,
+    scaleFrom: 0,
+    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+    restoreAfterFinish: true,
+    afterSetup: function(effect) {
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.undoClipping();
+    }
+  }, arguments[1] || {}));
 }
 
 Effect.SwitchOff = function(element) {
   element = $(element);
   var oldOpacity = element.getInlineOpacity();
-  return new Effect.Appear(element, { 
+  return new Effect.Appear(element, Object.extend({
     duration: 0.4,
     from: 0,
     transition: Effect.Transitions.flicker,
@@ -634,18 +658,14 @@ Effect.SwitchOff = function(element) {
         duration: 0.3, scaleFromCenter: true,
         scaleX: false, scaleContent: false, restoreAfterFinish: true,
         beforeSetup: function(effect) { 
-          effect.element.makePositioned();
-          effect.element.makeClipping();
+          effect.element.makePositioned().makeClipping();
         },
         afterFinishInternal: function(effect) {
-          effect.element.hide();
-          effect.element.undoClipping();
-          effect.element.undoPositioned();
-          effect.element.setStyle({opacity: oldOpacity});
+          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
         }
       })
     }
-  });
+  }, arguments[1] || {}));
 }
 
 Effect.DropOut = function(element) {
@@ -663,9 +683,7 @@ Effect.DropOut = function(element) {
           effect.effects[0].element.makePositioned(); 
         },
         afterFinishInternal: function(effect) {
-          effect.effects[0].element.hide();
-          effect.effects[0].element.undoPositioned();
-          effect.effects[0].element.setStyle(oldStyle);
+          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
         } 
       }, arguments[1] || {}));
 }
@@ -687,54 +705,42 @@ Effect.Shake = function(element) {
       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     new Effect.Move(effect.element,
       { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
-        effect.element.undoPositioned();
-        effect.element.setStyle(oldStyle);
+        effect.element.undoPositioned().setStyle(oldStyle);
   }}) }}) }}) }}) }}) }});
 }
 
 Effect.SlideDown = function(element) {
-  element = $(element);
-  element.cleanWhitespace();
+  element = $(element).cleanWhitespace();
   // SlideDown need to have the content of the element wrapped in a container element with fixed height!
-  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
+  var oldInnerBottom = element.down().getStyle('bottom');
   var elementDimensions = element.getDimensions();
   return new Effect.Scale(element, 100, Object.extend({ 
     scaleContent: false, 
     scaleX: false, 
-    scaleFrom: 0,
+    scaleFrom: window.opera ? 0 : 1,
     scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
     restoreAfterFinish: true,
     afterSetup: function(effect) {
       effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
+      effect.element.down().makePositioned();
       if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.setStyle({height: '0px'});
-      effect.element.show(); },
+      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
+    },
     afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
+      effect.element.down().setStyle({bottom:
         (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
     },
     afterFinishInternal: function(effect) {
-      effect.element.undoClipping(); 
-      // IE will crash if child is undoPositioned first
-      if(/MSIE/.test(navigator.userAgent)){
-        effect.element.undoPositioned();
-        effect.element.firstChild.undoPositioned();
-      }else{
-        effect.element.firstChild.undoPositioned();
-        effect.element.undoPositioned();
-      }
-      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
+      effect.element.undoClipping().undoPositioned();
+      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
     }, arguments[1] || {})
   );
 }
-  
+
 Effect.SlideUp = function(element) {
-  element = $(element);
-  element.cleanWhitespace();
-  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
-  return new Effect.Scale(element, 0, 
+  element = $(element).cleanWhitespace();
+  var oldInnerBottom = element.down().getStyle('bottom');
+  return new Effect.Scale(element, window.opera ? 0 : 1,
    Object.extend({ scaleContent: false, 
     scaleX: false, 
     scaleMode: 'box',
@@ -742,32 +748,32 @@ Effect.SlideUp = function(element) {
     restoreAfterFinish: true,
     beforeStartInternal: function(effect) {
       effect.element.makePositioned();
-      effect.element.firstChild.makePositioned();
+      effect.element.down().makePositioned();
       if(window.opera) effect.element.setStyle({top: ''});
-      effect.element.makeClipping();
-      effect.element.show(); },  
+      effect.element.makeClipping().show();
+    },  
     afterUpdateInternal: function(effect) {
-      effect.element.firstChild.setStyle({bottom:
-        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
+      effect.element.down().setStyle({bottom:
+        (effect.dims[0] - effect.element.clientHeight) + 'px' });
+    },
     afterFinishInternal: function(effect) {
-      effect.element.hide();
-      effect.element.undoClipping();
-      effect.element.firstChild.undoPositioned();
-      effect.element.undoPositioned();
-      effect.element.setStyle({bottom: oldInnerBottom}); }
+      effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
+      effect.element.down().undoPositioned();
+    }
    }, arguments[1] || {})
   );
 }
 
 // Bug in opera makes the TD containing this element expand for a instance after finish 
 Effect.Squish = function(element) {
-  return new Effect.Scale(element, window.opera ? 1 : 0, 
-    { restoreAfterFinish: true,
-      beforeSetup: function(effect) {
-        effect.element.makeClipping(effect.element); },  
-      afterFinishInternal: function(effect) {
-        effect.element.hide(effect.element); 
-        effect.element.undoClipping(effect.element); }
+  return new Effect.Scale(element, window.opera ? 1 : 0, { 
+    restoreAfterFinish: true,
+    beforeSetup: function(effect) {
+      effect.element.makeClipping(); 
+    },  
+    afterFinishInternal: function(effect) {
+      effect.element.hide().undoClipping(); 
+    }
   });
 }
 
@@ -823,9 +829,7 @@ Effect.Grow = function(element) {
     y: initialMoveY,
     duration: 0.01, 
     beforeSetup: function(effect) {
-      effect.element.hide();
-      effect.element.makeClipping();
-      effect.element.makePositioned();
+      effect.element.hide().makeClipping().makePositioned();
     },
     afterFinishInternal: function(effect) {
       new Effect.Parallel(
@@ -836,13 +840,10 @@ Effect.Grow = function(element) {
             sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
         ], Object.extend({
              beforeSetup: function(effect) {
-               effect.effects[0].element.setStyle({height: '0px'});
-               effect.effects[0].element.show(); 
+               effect.effects[0].element.setStyle({height: '0px'}).show(); 
              },
              afterFinishInternal: function(effect) {
-               effect.effects[0].element.undoClipping();
-               effect.effects[0].element.undoPositioned();
-               effect.effects[0].element.setStyle(oldStyle); 
+               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
              }
            }, options)
       )
@@ -896,13 +897,10 @@ Effect.Shrink = function(element) {
       new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
     ], Object.extend({            
          beforeStartInternal: function(effect) {
-           effect.effects[0].element.makePositioned();
-           effect.effects[0].element.makeClipping(); },
+           effect.effects[0].element.makePositioned().makeClipping(); 
+         },
          afterFinishInternal: function(effect) {
-           effect.effects[0].element.hide();
-           effect.effects[0].element.undoClipping();
-           effect.effects[0].element.undoPositioned();
-           effect.effects[0].element.setStyle(oldStyle); }
+           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
        }, options)
   );
 }
@@ -912,10 +910,10 @@ Effect.Pulsate = function(element) {
   var options    = arguments[1] || {};
   var oldOpacity = element.getInlineOpacity();
   var transition = options.transition || Effect.Transitions.sinoidal;
-  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
   reverser.bind(transition);
   return new Effect.Opacity(element, 
-    Object.extend(Object.extend({  duration: 3.0, from: 0,
+    Object.extend(Object.extend({  duration: 2.0, from: 0,
       afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
     }, options), {transition: reverser}));
 }
@@ -927,7 +925,7 @@ Effect.Fold = function(element) {
     left: element.style.left,
     width: element.style.width,
     height: element.style.height };
-  Element.makeClipping(element);
+  element.makeClipping();
   return new Effect.Scale(element, 5, Object.extend({   
     scaleContent: false,
     scaleX: false,
@@ -936,15 +934,147 @@ Effect.Fold = function(element) {
       scaleContent: false, 
       scaleY: false,
       afterFinishInternal: function(effect) {
-        effect.element.hide();
-        effect.element.undoClipping(); 
-        effect.element.setStyle(oldStyle);
+        effect.element.hide().undoClipping().setStyle(oldStyle);
       } });
   }}, arguments[1] || {}));
 };
 
+Effect.Morph = Class.create();
+Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
+  initialize: function(element) {
+    this.element = $(element);
+    if(!this.element) throw(Effect._elementDoesNotExistError);
+    var options = Object.extend({
+      style: ''
+    }, arguments[1] || {});
+    this.start(options);
+  },
+  setup: function(){
+    function parseColor(color){
+      if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+      color = color.parseColor();
+      return $R(0,2).map(function(i){
+        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
+      });
+    }
+    this.transforms = this.options.style.parseStyle().map(function(property){
+      var originalValue = this.element.getStyle(property[0]);
+      return $H({ 
+        style: property[0], 
+        originalValue: property[1].unit=='color' ? 
+          parseColor(originalValue) : parseFloat(originalValue || 0), 
+        targetValue: property[1].unit=='color' ? 
+          parseColor(property[1].value) : property[1].value,
+        unit: property[1].unit
+      });
+    }.bind(this)).reject(function(transform){
+      return (
+        (transform.originalValue == transform.targetValue) ||
+        (
+          transform.unit != 'color' &amp;&amp;
+          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+        )
+      )
+    });
+  },
+  update: function(position) {
+    var style = $H(), value = null;
+    this.transforms.each(function(transform){
+      value = transform.unit=='color' ?
+        $R(0,2).inject('#',function(m,v,i){
+          return m+(Math.round(transform.originalValue[i]+
+            (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : 
+        transform.originalValue + Math.round(
+          ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
+      style[transform.style] = value;
+    });
+    this.element.setStyle(style);
+  }
+});
+
+Effect.Transform = Class.create();
+Object.extend(Effect.Transform.prototype, {
+  initialize: function(tracks){
+    this.tracks  = [];
+    this.options = arguments[1] || {};
+    this.addTracks(tracks);
+  },
+  addTracks: function(tracks){
+    tracks.each(function(track){
+      var data = $H(track).values().first();
+      this.tracks.push($H({
+        ids:     $H(track).keys().first(),
+        effect:  Effect.Morph,
+        options: { style: data }
+      }));
+    }.bind(this));
+    return this;
+  },
+  play: function(){
+    return new Effect.Parallel(
+      this.tracks.map(function(track){
+        var elements = [$(track.ids) || $$(track.ids)].flatten();
+        return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
+      }).flatten(),
+      this.options
+    );
+  }
+});
+
+Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage', 
+  'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', 
+  'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
+  'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
+  'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
+  'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
+  'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
+  'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
+  'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
+  'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
+  'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
+  'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
+  'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
+  'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
+  'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
+  'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
+  'width', 'wordSpacing', 'zIndex'];
+  
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.prototype.parseStyle = function(){
+  var element = Element.extend(document.createElement('div'));
+  element.innerHTML = '&lt;div style=&quot;' + this + '&quot;&gt;&lt;/div&gt;';
+  var style = element.down().style, styleRules = $H();
+  
+  Element.CSS_PROPERTIES.each(function(property){
+   if(style[property]) styleRules[property] = style[property]; 
+  });
+  
+  var result = $H();
+  
+  styleRules.each(function(pair){
+    var property = pair[0], value = pair[1], unit = null;
+    
+    if(value.parseColor('#zzzzzz') != '#zzzzzz') {
+      value = value.parseColor();
+      unit  = 'color';
+    } else if(Element.CSS_LENGTH.test(value)) 
+      var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
+          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
+    
+    result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
+  }.bind(this));
+  
+  return result;
+};
+
+Element.morph = function(element, style) {
+  new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
+  return element;
+};
+
 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( 
   function(f) { Element.Methods[f] = Element[f]; }
 );
 </diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/javascripts/effects.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
-/*  Prototype JavaScript framework, version 1.5.0_rc0
- *  (c) 2005 Sam Stephenson &lt;sam@conio.net&gt;
+/*  Prototype JavaScript framework, version 1.5.0
+ *  (c) 2005-2007 Sam Stephenson
  *
  *  Prototype is freely distributable under the terms of an MIT-style license.
  *  For details, see the Prototype web site: http://prototype.conio.net/
@@ -7,11 +7,14 @@
 /*--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.5.0_rc0',
-  ScriptFragment: '(?:&lt;script.*?&gt;)((\n|\r|.)*?)(?:&lt;\/script&gt;)',
+  Version: '1.5.0',
+  BrowserFeatures: {
+    XPath: !!document.evaluate
+  },
 
+  ScriptFragment: '(?:&lt;script.*?&gt;)((\n|\r|.)*?)(?:&lt;\/script&gt;)',
   emptyFunction: function() {},
-  K: function(x) {return x}
+  K: function(x) { return x }
 }
 
 var Class = {
@@ -31,16 +34,36 @@ Object.extend = function(destination, source) {
   return destination;
 }
 
-Object.inspect = function(object) {
-  try {
-    if (object == undefined) return 'undefined';
-    if (object == null) return 'null';
-    return object.inspect ? object.inspect() : object.toString();
-  } catch (e) {
-    if (e instanceof RangeError) return '...';
-    throw e;
+Object.extend(Object, {
+  inspect: function(object) {
+    try {
+      if (object === undefined) return 'undefined';
+      if (object === null) return 'null';
+      return object.inspect ? object.inspect() : object.toString();
+    } catch (e) {
+      if (e instanceof RangeError) return '...';
+      throw e;
+    }
+  },
+
+  keys: function(object) {
+    var keys = [];
+    for (var property in object)
+      keys.push(property);
+    return keys;
+  },
+
+  values: function(object) {
+    var values = [];
+    for (var property in object)
+      values.push(object[property]);
+    return values;
+  },
+
+  clone: function(object) {
+    return Object.extend({}, object);
   }
-}
+});
 
 Function.prototype.bind = function() {
   var __method = this, args = $A(arguments), object = args.shift();
@@ -50,9 +73,9 @@ Function.prototype.bind = function() {
 }
 
 Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
+  var __method = this, args = $A(arguments), object = args.shift();
   return function(event) {
-    return __method.call(object, event || window.event);
+    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
   }
 }
 
@@ -77,7 +100,7 @@ var Try = {
   these: function() {
     var returnValue;
 
-    for (var i = 0; i &lt; arguments.length; i++) {
+    for (var i = 0, length = arguments.length; i &lt; length; i++) {
       var lambda = arguments[i];
       try {
         returnValue = lambda();
@@ -102,20 +125,30 @@ PeriodicalExecuter.prototype = {
   },
 
   registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+
+  stop: function() {
+    if (!this.timer) return;
+    clearInterval(this.timer);
+    this.timer = null;
   },
 
   onTimerEvent: function() {
     if (!this.currentlyExecuting) {
       try {
         this.currentlyExecuting = true;
-        this.callback();
+        this.callback(this);
       } finally {
         this.currentlyExecuting = false;
       }
     }
   }
 }
+String.interpret = function(value){
+  return value == null ? '' : String(value);
+}
+
 Object.extend(String.prototype, {
   gsub: function(pattern, replacement) {
     var result = '', source = this, match;
@@ -124,7 +157,7 @@ Object.extend(String.prototype, {
     while (source.length &gt; 0) {
       if (match = source.match(pattern)) {
         result += source.slice(0, match.index);
-        result += (replacement(match) || '').toString();
+        result += String.interpret(replacement(match));
         source  = source.slice(match.index + match[0].length);
       } else {
         result += source, source = '';
@@ -189,15 +222,28 @@ Object.extend(String.prototype, {
   unescapeHTML: function() {
     var div = document.createElement('div');
     div.innerHTML = this.stripTags();
-    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+    return div.childNodes[0] ? (div.childNodes.length &gt; 1 ?
+      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
+      div.childNodes[0].nodeValue) : '';
   },
 
-  toQueryParams: function() {
-    var pairs = this.match(/^\??(.*)$/)[1].split('&amp;');
-    return pairs.inject({}, function(params, pairString) {
-      var pair = pairString.split('=');
-      params[pair[0]] = pair[1];
-      return params;
+  toQueryParams: function(separator) {
+    var match = this.strip().match(/([^?#]*)(#.*)?$/);
+    if (!match) return {};
+
+    return match[1].split(separator || '&amp;').inject({}, function(hash, pair) {
+      if ((pair = pair.split('='))[0]) {
+        var name = decodeURIComponent(pair[0]);
+        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
+
+        if (hash[name] !== undefined) {
+          if (hash[name].constructor != Array)
+            hash[name] = [hash[name]];
+          if (value) hash[name].push(value);
+        }
+        else hash[name] = value;
+      }
+      return hash;
     });
   },
 
@@ -205,24 +251,43 @@ Object.extend(String.prototype, {
     return this.split('');
   },
 
+  succ: function() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  },
+
   camelize: function() {
-    var oStringList = this.split('-');
-    if (oStringList.length == 1) return oStringList[0];
+    var parts = this.split('-'), len = parts.length;
+    if (len == 1) return parts[0];
 
-    var camelizedString = this.indexOf('-') == 0
-      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
-      : oStringList[0];
+    var camelized = this.charAt(0) == '-'
+      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+      : parts[0];
 
-    for (var i = 1, len = oStringList.length; i &lt; len; i++) {
-      var s = oStringList[i];
-      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
-    }
+    for (var i = 1; i &lt; len; i++)
+      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
 
-    return camelizedString;
+    return camelized;
   },
 
-  inspect: function() {
-    return &quot;'&quot; + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + &quot;'&quot;;
+  capitalize: function(){
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  },
+
+  underscore: function() {
+    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+  },
+
+  dasherize: function() {
+    return this.gsub(/_/,'-');
+  },
+
+  inspect: function(useDoubleQuotes) {
+    var escapedString = this.replace(/\\/g, '\\\\');
+    if (useDoubleQuotes)
+      return '&quot;' + escapedString.replace(/&quot;/g, '\\&quot;') + '&quot;';
+    else
+      return &quot;'&quot; + escapedString.replace(/'/g, '\\\'') + &quot;'&quot;;
   }
 });
 
@@ -246,7 +311,7 @@ Template.prototype = {
     return this.template.gsub(this.pattern, function(match) {
       var before = match[1];
       if (before == '\\') return match[2];
-      return before + (object[match[3]] || '').toString();
+      return before + String.interpret(object[match[3]]);
     });
   }
 }
@@ -268,6 +333,14 @@ var Enumerable = {
     } catch (e) {
       if (e != $break) throw e;
     }
+    return this;
+  },
+
+  eachSlice: function(number, iterator) {
+    var index = -number, slices = [], array = this.toArray();
+    while ((index += number) &lt; array.length)
+      slices.push(array.slice(index, index+number));
+    return slices.map(iterator);
   },
 
   all: function(iterator) {
@@ -280,7 +353,7 @@ var Enumerable = {
   },
 
   any: function(iterator) {
-    var result = true;
+    var result = false;
     this.each(function(value, index) {
       if (result = !!(iterator || Prototype.K)(value, index))
         throw $break;
@@ -291,12 +364,12 @@ var Enumerable = {
   collect: function(iterator) {
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
+      results.push((iterator || Prototype.K)(value, index));
     });
     return results;
   },
 
-  detect: function (iterator) {
+  detect: function(iterator) {
     var result;
     this.each(function(value, index) {
       if (iterator(value, index)) {
@@ -337,6 +410,14 @@ var Enumerable = {
     return found;
   },
 
+  inGroupsOf: function(number, fillWith) {
+    fillWith = fillWith === undefined ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length &lt; number) slice.push(fillWith);
+      return slice;
+    });
+  },
+
   inject: function(memo, iterator) {
     this.each(function(value, index) {
       memo = iterator(memo, value, index);
@@ -346,7 +427,7 @@ var Enumerable = {
 
   invoke: function(method) {
     var args = $A(arguments).slice(1);
-    return this.collect(function(value) {
+    return this.map(function(value) {
       return value[method].apply(value, args);
     });
   },
@@ -398,7 +479,7 @@ var Enumerable = {
   },
 
   sortBy: function(iterator) {
-    return this.collect(function(value, index) {
+    return this.map(function(value, index) {
       return {value: value, criteria: iterator(value, index)};
     }).sort(function(left, right) {
       var a = left.criteria, b = right.criteria;
@@ -407,7 +488,7 @@ var Enumerable = {
   },
 
   toArray: function() {
-    return this.collect(Prototype.K);
+    return this.map();
   },
 
   zip: function() {
@@ -421,6 +502,10 @@ var Enumerable = {
     });
   },
 
+  size: function() {
+    return this.toArray().length;
+  },
+
   inspect: function() {
     return '#&lt;Enumerable:' + this.toArray().inspect() + '&gt;';
   }
@@ -439,7 +524,7 @@ var $A = Array.from = function(iterable) {
     return iterable.toArray();
   } else {
     var results = [];
-    for (var i = 0; i &lt; iterable.length; i++)
+    for (var i = 0, length = iterable.length; i &lt; length; i++)
       results.push(iterable[i]);
     return results;
   }
@@ -452,7 +537,7 @@ if (!Array.prototype._reverse)
 
 Object.extend(Array.prototype, {
   _each: function(iterator) {
-    for (var i = 0; i &lt; this.length; i++)
+    for (var i = 0, length = this.length; i &lt; length; i++)
       iterator(this[i]);
   },
 
@@ -471,7 +556,7 @@ Object.extend(Array.prototype, {
 
   compact: function() {
     return this.select(function(value) {
-      return value != undefined || value != null;
+      return value != null;
     });
   },
 
@@ -490,7 +575,7 @@ Object.extend(Array.prototype, {
   },
 
   indexOf: function(object) {
-    for (var i = 0; i &lt; this.length; i++)
+    for (var i = 0, length = this.length; i &lt; length; i++)
       if (this[i] == object) return i;
     return -1;
   },
@@ -499,15 +584,88 @@ Object.extend(Array.prototype, {
     return (inline !== false ? this : this.toArray())._reverse();
   },
 
+  reduce: function() {
+    return this.length &gt; 1 ? this : this[0];
+  },
+
+  uniq: function() {
+    return this.inject([], function(array, value) {
+      return array.include(value) ? array : array.concat([value]);
+    });
+  },
+
+  clone: function() {
+    return [].concat(this);
+  },
+
+  size: function() {
+    return this.length;
+  },
+
   inspect: function() {
     return '[' + this.map(Object.inspect).join(', ') + ']';
   }
 });
-var Hash = {
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string){
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+if(window.opera){
+  Array.prototype.concat = function(){
+    var array = [];
+    for(var i = 0, length = this.length; i &lt; length; i++) array.push(this[i]);
+    for(var i = 0, length = arguments.length; i &lt; length; i++) {
+      if(arguments[i].constructor == Array) {
+        for(var j = 0, arrayLength = arguments[i].length; j &lt; arrayLength; j++)
+          array.push(arguments[i][j]);
+      } else {
+        array.push(arguments[i]);
+      }
+    }
+    return array;
+  }
+}
+var Hash = function(obj) {
+  Object.extend(this, obj || {});
+};
+
+Object.extend(Hash, {
+  toQueryString: function(obj) {
+    var parts = [];
+
+	  this.prototype._each.call(obj, function(pair) {
+      if (!pair.key) return;
+
+      if (pair.value &amp;&amp; pair.value.constructor == Array) {
+        var values = pair.value.compact();
+        if (values.length &lt; 2) pair.value = values.reduce();
+        else {
+        	key = encodeURIComponent(pair.key);
+          values.each(function(value) {
+            value = value != undefined ? encodeURIComponent(value) : '';
+            parts.push(key + '=' + encodeURIComponent(value));
+          });
+          return;
+        }
+      }
+      if (pair.value == undefined) pair[1] = '';
+      parts.push(pair.map(encodeURIComponent).join('='));
+	  });
+
+    return parts.join('&amp;');
+  }
+});
+
+Object.extend(Hash.prototype, Enumerable);
+Object.extend(Hash.prototype, {
   _each: function(iterator) {
     for (var key in this) {
       var value = this[key];
-      if (typeof value == 'function') continue;
+      if (value &amp;&amp; value == Hash.prototype[key]) continue;
 
       var pair = [key, value];
       pair.key = key;
@@ -525,16 +683,30 @@ var Hash = {
   },
 
   merge: function(hash) {
-    return $H(hash).inject($H(this), function(mergedHash, pair) {
+    return $H(hash).inject(this, function(mergedHash, pair) {
       mergedHash[pair.key] = pair.value;
       return mergedHash;
     });
   },
 
+  remove: function() {
+    var result;
+    for(var i = 0, length = arguments.length; i &lt; length; i++) {
+      var value = this[arguments[i]];
+      if (value !== undefined){
+        if (result === undefined) result = value;
+        else {
+          if (result.constructor != Array) result = [result];
+          result.push(value)
+        }
+      }
+      delete this[arguments[i]];
+    }
+    return result;
+  },
+
   toQueryString: function() {
-    return this.map(function(pair) {
-      return pair.map(encodeURIComponent).join('=');
-    }).join('&amp;');
+    return Hash.toQueryString(this);
   },
 
   inspect: function() {
@@ -542,14 +714,12 @@ var Hash = {
       return pair.map(Object.inspect).join(': ');
     }).join(', ') + '}&gt;';
   }
-}
+});
 
 function $H(object) {
-  var hash = Object.extend({}, object || {});
-  Object.extend(hash, Enumerable);
-  Object.extend(hash, Hash);
-  return hash;
-}
+  if (object &amp;&amp; object.constructor == Hash) return object;
+  return new Hash(object);
+};
 ObjectRange = Class.create();
 Object.extend(ObjectRange.prototype, Enumerable);
 Object.extend(ObjectRange.prototype, {
@@ -561,10 +731,10 @@ Object.extend(ObjectRange.prototype, {
 
   _each: function(iterator) {
     var value = this.start;
-    do {
+    while (this.include(value)) {
       iterator(value);
       value = value.succ();
-    } while (this.include(value));
+    }
   },
 
   include: function(value) {
@@ -599,18 +769,18 @@ Ajax.Responders = {
     this.responders._each(iterator);
   },
 
-  register: function(responderToAdd) {
-    if (!this.include(responderToAdd))
-      this.responders.push(responderToAdd);
+  register: function(responder) {
+    if (!this.include(responder))
+      this.responders.push(responder);
   },
 
-  unregister: function(responderToRemove) {
-    this.responders = this.responders.without(responderToRemove);
+  unregister: function(responder) {
+    this.responders = this.responders.without(responder);
   },
 
   dispatch: function(callback, request, transport, json) {
     this.each(function(responder) {
-      if (responder[callback] &amp;&amp; typeof responder[callback] == 'function') {
+      if (typeof responder[callback] == 'function') {
         try {
           responder[callback].apply(responder, [request, transport, json]);
         } catch (e) {}
@@ -625,7 +795,6 @@ Ajax.Responders.register({
   onCreate: function() {
     Ajax.activeRequestCount++;
   },
-
   onComplete: function() {
     Ajax.activeRequestCount--;
   }
@@ -638,19 +807,14 @@ Ajax.Base.prototype = {
       method:       'post',
       asynchronous: true,
       contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
       parameters:   ''
     }
     Object.extend(this.options, options || {});
-  },
-
-  responseIsSuccess: function() {
-    return this.transport.status == undefined
-        || this.transport.status == 0
-        || (this.transport.status &gt;= 200 &amp;&amp; this.transport.status &lt; 300);
-  },
 
-  responseIsFailure: function() {
-    return !this.responseIsSuccess();
+    this.options.method = this.options.method.toLowerCase();
+    if (typeof this.options.parameters == 'string')
+      this.options.parameters = this.options.parameters.toQueryParams();
   }
 }
 
@@ -659,6 +823,8 @@ Ajax.Request.Events =
   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 
 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+  _complete: false,
+
   initialize: function(url, options) {
     this.transport = Ajax.getTransport();
     this.setOptions(options);
@@ -666,111 +832,146 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
   },
 
   request: function(url) {
-    var parameters = this.options.parameters || '';
-    if (parameters.length &gt; 0) parameters += '&amp;_=';
+    this.url = url;
+    this.method = this.options.method;
+    var params = this.options.parameters;
 
-    try {
-      this.url = url;
-      if (this.options.method == 'get' &amp;&amp; parameters.length &gt; 0)
-        this.url += (this.url.match(/\?/) ? '&amp;' : '?') + parameters;
+    if (!['get', 'post'].include(this.method)) {
+      // simulate other verbs over post
+      params['_method'] = this.method;
+      this.method = 'post';
+    }
 
+    params = Hash.toQueryString(params);
+    if (params &amp;&amp; /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&amp;_='
+
+    // when GET, append parameters to URL
+    if (this.method == 'get' &amp;&amp; params)
+      this.url += (this.url.indexOf('?') &gt; -1 ? '&amp;' : '?') + params;
+
+    try {
       Ajax.Responders.dispatch('onCreate', this, this.transport);
 
-      this.transport.open(this.options.method, this.url,
+      this.transport.open(this.method.toUpperCase(), this.url,
         this.options.asynchronous);
 
-      if (this.options.asynchronous) {
-        this.transport.onreadystatechange = this.onStateChange.bind(this);
-        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
-      }
+      if (this.options.asynchronous)
+        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
 
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
       this.setRequestHeaders();
 
-      var body = this.options.postBody ? this.options.postBody : parameters;
-      this.transport.send(this.options.method == 'post' ? body : null);
+      var body = this.method == 'post' ? (this.options.postBody || params) : null;
 
-    } catch (e) {
-      this.dispatchException(e);
-    }
-  },
-
-  setRequestHeaders: function() {
-    var requestHeaders =
-      ['X-Requested-With', 'XMLHttpRequest',
-       'X-Prototype-Version', Prototype.Version,
-       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
+      this.transport.send(body);
 
-    if (this.options.method == 'post') {
-      requestHeaders.push('Content-type', this.options.contentType);
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous &amp;&amp; this.transport.overrideMimeType)
+        this.onStateChange();
 
-      /* Force &quot;Connection: close&quot; for Mozilla browsers to work around
-       * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651.
-       */
-      if (this.transport.overrideMimeType)
-        requestHeaders.push('Connection', 'close');
     }
-
-    if (this.options.requestHeaders)
-      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
-
-    for (var i = 0; i &lt; requestHeaders.length; i += 2)
-      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+    catch (e) {
+      this.dispatchException(e);
+    }
   },
 
   onStateChange: function() {
     var readyState = this.transport.readyState;
-    if (readyState != 1)
+    if (readyState &gt; 1 &amp;&amp; !((readyState == 4) &amp;&amp; this._complete))
       this.respondToReadyState(this.transport.readyState);
   },
 
-  header: function(name) {
-    try {
-      return this.transport.getResponseHeader(name);
-    } catch (e) {}
-  },
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
+
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+      /* Force &quot;Connection: close&quot; for older Mozilla browsers to work
+       * around a bug where XMLHttpRequest sends an incorrect
+       * Content-length header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType &amp;&amp;
+          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] &lt; 2005)
+            headers['Connection'] = 'close';
+    }
 
-  evalJSON: function() {
-    try {
-      return eval('(' + this.header('X-JSON') + ')');
-    } catch (e) {}
-  },
+    // user-defined headers
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
 
-  evalResponse: function() {
-    try {
-      return eval(this.transport.responseText);
-    } catch (e) {
-      this.dispatchException(e);
+      if (typeof extras.push == 'function')
+        for (var i = 0, length = extras.length; i &lt; length; i += 2)
+          headers[extras[i]] = extras[i+1];
+      else
+        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
     }
+
+    for (var name in headers)
+      this.transport.setRequestHeader(name, headers[name]);
+  },
+
+  success: function() {
+    return !this.transport.status
+        || (this.transport.status &gt;= 200 &amp;&amp; this.transport.status &lt; 300);
   },
 
   respondToReadyState: function(readyState) {
-    var event = Ajax.Request.Events[readyState];
+    var state = Ajax.Request.Events[readyState];
     var transport = this.transport, json = this.evalJSON();
 
-    if (event == 'Complete') {
+    if (state == 'Complete') {
       try {
+        this._complete = true;
         (this.options['on' + this.transport.status]
-         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
          || Prototype.emptyFunction)(transport, json);
       } catch (e) {
         this.dispatchException(e);
       }
 
-      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
-        this.evalResponse();
+      if ((this.getHeader('Content-type') || 'text/javascript').strip().
+        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
+          this.evalResponse();
     }
 
     try {
-      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
-      Ajax.Responders.dispatch('on' + event, this, transport, json);
+      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
+      Ajax.Responders.dispatch('on' + state, this, transport, json);
     } catch (e) {
       this.dispatchException(e);
     }
 
-    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
-    if (event == 'Complete')
+    if (state == 'Complete') {
+      // avoid memory leak in MSIE: clean up
       this.transport.onreadystatechange = Prototype.emptyFunction;
+    }
+  },
+
+  getHeader: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) { return null }
+  },
+
+  evalJSON: function() {
+    try {
+      var json = this.getHeader('X-JSON');
+      return json ? eval('(' + json + ')') : null;
+    } catch (e) { return null }
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
   },
 
   dispatchException: function(exception) {
@@ -783,41 +984,37 @@ Ajax.Updater = Class.create();
 
 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
   initialize: function(container, url, options) {
-    this.containers = {
-      success: container.success ? $(container.success) : $(container),
-      failure: container.failure ? $(container.failure) :
-        (container.success ? null : $(container))
+    this.container = {
+      success: (container.success || container),
+      failure: (container.failure || (container.success ? null : container))
     }
 
     this.transport = Ajax.getTransport();
     this.setOptions(options);
 
     var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function(transport, object) {
+    this.options.onComplete = (function(transport, param) {
       this.updateContent();
-      onComplete(transport, object);
+      onComplete(transport, param);
     }).bind(this);
 
     this.request(url);
   },
 
   updateContent: function() {
-    var receiver = this.responseIsSuccess() ?
-      this.containers.success : this.containers.failure;
+    var receiver = this.container[this.success() ? 'success' : 'failure'];
     var response = this.transport.responseText;
 
-    if (!this.options.evalScripts)
-      response = response.stripScripts();
+    if (!this.options.evalScripts) response = response.stripScripts();
 
-    if (receiver) {
-      if (this.options.insertion) {
+    if (receiver = $(receiver)) {
+      if (this.options.insertion)
         new this.options.insertion(receiver, response);
-      } else {
-        Element.update(receiver, response);
-      }
+      else
+        receiver.update(response);
     }
 
-    if (this.responseIsSuccess()) {
+    if (this.success()) {
       if (this.onComplete)
         setTimeout(this.onComplete.bind(this), 10);
     }
@@ -846,7 +1043,7 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
   },
 
   stop: function() {
-    this.updater.onComplete = undefined;
+    this.updater.options.onComplete = undefined;
     clearTimeout(this.timer);
     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
   },
@@ -866,25 +1063,43 @@ Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
     this.updater = new Ajax.Updater(this.container, this.url, this.options);
   }
 });
-function $() {
-  var results = [], element;
-  for (var i = 0; i &lt; arguments.length; i++) {
-    element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-    results.push(Element.extend(element));
+function $(element) {
+  if (arguments.length &gt; 1) {
+    for (var i = 0, elements = [], length = arguments.length; i &lt; length; i++)
+      elements.push($(arguments[i]));
+    return elements;
   }
-  return results.length &lt; 2 ? results[0] : results;
+  if (typeof element == 'string')
+    element = document.getElementById(element);
+  return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+  document._getElementsByXPath = function(expression, parentElement) {
+    var results = [];
+    var query = document.evaluate(expression, $(parentElement) || document,
+      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+    for (var i = 0, length = query.snapshotLength; i &lt; length; i++)
+      results.push(query.snapshotItem(i));
+    return results;
+  };
 }
 
 document.getElementsByClassName = function(className, parentElement) {
-  var children = ($(parentElement) || document.body).getElementsByTagName('*');
-  return $A(children).inject([], function(elements, child) {
-    if (child.className.match(new RegExp(&quot;(^|\\s)&quot; + className + &quot;(\\s|$)&quot;)))
-      elements.push(Element.extend(child));
+  if (Prototype.BrowserFeatures.XPath) {
+    var q = &quot;.//*[contains(concat(' ', @class, ' '), ' &quot; + className + &quot; ')]&quot;;
+    return document._getElementsByXPath(q, parentElement);
+  } else {
+    var children = ($(parentElement) || document.body).getElementsByTagName('*');
+    var elements = [], child;
+    for (var i = 0, length = children.length; i &lt; length; i++) {
+      child = children[i];
+      if (Element.hasClassName(child, className))
+        elements.push(Element.extend(child));
+    }
     return elements;
-  });
-}
+  }
+};
 
 /*--------------------------------------------------------------------------*/
 
@@ -892,21 +1107,28 @@ if (!window.Element)
   var Element = new Object();
 
 Element.extend = function(element) {
-  if (!element) return;
-  if (_nativeExtensions) return element;
+  if (!element || _nativeExtensions || element.nodeType == 3) return element;
 
   if (!element._extended &amp;&amp; element.tagName &amp;&amp; element != window) {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
+    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
+
+    if (element.tagName == 'FORM')
+      Object.extend(methods, Form.Methods);
+    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
+      Object.extend(methods, Form.Element.Methods);
+
+    Object.extend(methods, Element.Methods.Simulated);
+
+    for (var property in methods) {
       var value = methods[property];
-      if (typeof value == 'function')
+      if (typeof value == 'function' &amp;&amp; !(property in element))
         element[property] = cache.findOrStore(value);
     }
   }
 
   element._extended = true;
   return element;
-}
+};
 
 Element.extend.cache = {
   findOrStore: function(value) {
@@ -914,46 +1136,45 @@ Element.extend.cache = {
       return value.apply(null, [this].concat($A(arguments)));
     }
   }
-}
+};
 
 Element.Methods = {
   visible: function(element) {
     return $(element).style.display != 'none';
   },
 
-  toggle: function() {
-    for (var i = 0; i &lt; arguments.length; i++) {
-      var element = $(arguments[i]);
-      Element[Element.visible(element) ? 'hide' : 'show'](element);
-    }
+  toggle: function(element) {
+    element = $(element);
+    Element[Element.visible(element) ? 'hide' : 'show'](element);
+    return element;
   },
 
-  hide: function() {
-    for (var i = 0; i &lt; arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 'none';
-    }
+  hide: function(element) {
+    $(element).style.display = 'none';
+    return element;
   },
 
-  show: function() {
-    for (var i = 0; i &lt; arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = '';
-    }
+  show: function(element) {
+    $(element).style.display = '';
+    return element;
   },
 
   remove: function(element) {
     element = $(element);
     element.parentNode.removeChild(element);
+    return element;
   },
 
   update: function(element, html) {
+    html = typeof html == 'undefined' ? '' : html.toString();
     $(element).innerHTML = html.stripScripts();
     setTimeout(function() {html.evalScripts()}, 10);
+    return element;
   },
 
   replace: function(element, html) {
     element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
     if (element.outerHTML) {
       element.outerHTML = html.stripScripts();
     } else {
@@ -963,11 +1184,106 @@ Element.Methods = {
         range.createContextualFragment(html.stripScripts()), element);
     }
     setTimeout(function() {html.evalScripts()}, 10);
+    return element;
   },
 
-  getHeight: function(element) {
+  inspect: function(element) {
+    element = $(element);
+    var result = '&lt;' + element.tagName.toLowerCase();
+    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+      var property = pair.first(), attribute = pair.last();
+      var value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    });
+    return result + '&gt;';
+  },
+
+  recursivelyCollect: function(element, property) {
+    element = $(element);
+    var elements = [];
+    while (element = element[property])
+      if (element.nodeType == 1)
+        elements.push(Element.extend(element));
+    return elements;
+  },
+
+  ancestors: function(element) {
+    return $(element).recursivelyCollect('parentNode');
+  },
+
+  descendants: function(element) {
+    return $A($(element).getElementsByTagName('*'));
+  },
+
+  immediateDescendants: function(element) {
+    if (!(element = $(element).firstChild)) return [];
+    while (element &amp;&amp; element.nodeType != 1) element = element.nextSibling;
+    if (element) return [element].concat($(element).nextSiblings());
+    return [];
+  },
+
+  previousSiblings: function(element) {
+    return $(element).recursivelyCollect('previousSibling');
+  },
+
+  nextSiblings: function(element) {
+    return $(element).recursivelyCollect('nextSibling');
+  },
+
+  siblings: function(element) {
     element = $(element);
-    return element.offsetHeight;
+    return element.previousSiblings().reverse().concat(element.nextSiblings());
+  },
+
+  match: function(element, selector) {
+    if (typeof selector == 'string')
+      selector = new Selector(selector);
+    return selector.match($(element));
+  },
+
+  up: function(element, expression, index) {
+    return Selector.findElement($(element).ancestors(), expression, index);
+  },
+
+  down: function(element, expression, index) {
+    return Selector.findElement($(element).descendants(), expression, index);
+  },
+
+  previous: function(element, expression, index) {
+    return Selector.findElement($(element).previousSiblings(), expression, index);
+  },
+
+  next: function(element, expression, index) {
+    return Selector.findElement($(element).nextSiblings(), expression, index);
+  },
+
+  getElementsBySelector: function() {
+    var args = $A(arguments), element = $(args.shift());
+    return Selector.findChildElements(element, args);
+  },
+
+  getElementsByClassName: function(element, className) {
+    return document.getElementsByClassName(className, element);
+  },
+
+  readAttribute: function(element, name) {
+    element = $(element);
+    if (document.all &amp;&amp; !window.opera) {
+      var t = Element._attributeTranslations;
+      if (t.values[name]) return t.values[name](element, name);
+      if (t.names[name])  name = t.names[name];
+      var attribute = element.attributes[name];
+      if(attribute) return attribute.nodeValue;
+    }
+    return element.getAttribute(name);
+  },
+
+  getHeight: function(element) {
+    return $(element).getDimensions().height;
+  },
+
+  getWidth: function(element) {
+    return $(element).getDimensions().width;
   },
 
   classNames: function(element) {
@@ -976,34 +1292,60 @@ Element.Methods = {
 
   hasClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).include(className);
+    var elementClassName = element.className;
+    if (elementClassName.length == 0) return false;
+    if (elementClassName == className ||
+        elementClassName.match(new RegExp(&quot;(^|\\s)&quot; + className + &quot;(\\s|$)&quot;)))
+      return true;
+    return false;
   },
 
   addClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).add(className);
+    Element.classNames(element).add(className);
+    return element;
   },
 
   removeClassName: function(element, className) {
     if (!(element = $(element))) return;
-    return Element.classNames(element).remove(className);
+    Element.classNames(element).remove(className);
+    return element;
+  },
+
+  toggleClassName: function(element, className) {
+    if (!(element = $(element))) return;
+    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
+    return element;
+  },
+
+  observe: function() {
+    Event.observe.apply(Event, arguments);
+    return $A(arguments).first();
+  },
+
+  stopObserving: function() {
+    Event.stopObserving.apply(Event, arguments);
+    return $A(arguments).first();
   },
 
   // removes whitespace-only text node children
   cleanWhitespace: function(element) {
     element = $(element);
-    for (var i = 0; i &lt; element.childNodes.length; i++) {
-      var node = element.childNodes[i];
+    var node = element.firstChild;
+    while (node) {
+      var nextNode = node.nextSibling;
       if (node.nodeType == 3 &amp;&amp; !/\S/.test(node.nodeValue))
-        Element.remove(node);
+        element.removeChild(node);
+      node = nextNode;
     }
+    return element;
   },
 
   empty: function(element) {
     return $(element).innerHTML.match(/^\s*$/);
   },
 
-  childOf: function(element, ancestor) {
+  descendantOf: function(element, ancestor) {
     element = $(element), ancestor = $(ancestor);
     while (element = element.parentNode)
       if (element == ancestor) return true;
@@ -1012,38 +1354,69 @@ Element.Methods = {
 
   scrollTo: function(element) {
     element = $(element);
-    var x = element.x ? element.x : element.offsetLeft,
-        y = element.y ? element.y : element.offsetTop;
-    window.scrollTo(x, y);
+    var pos = Position.cumulativeOffset(element);
+    window.scrollTo(pos[0], pos[1]);
+    return element;
   },
 
   getStyle: function(element, style) {
     element = $(element);
-    var value = element.style[style.camelize()];
+    if (['float','cssFloat'].include(style))
+      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
+    style = style.camelize();
+    var value = element.style[style];
     if (!value) {
       if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) {
         var css = document.defaultView.getComputedStyle(element, null);
-        value = css ? css.getPropertyValue(style) : null;
+        value = css ? css[style] : null;
       } else if (element.currentStyle) {
-        value = element.currentStyle[style.camelize()];
+        value = element.currentStyle[style];
       }
     }
 
+    if((value == 'auto') &amp;&amp; ['width','height'].include(style) &amp;&amp; (element.getStyle('display') != 'none'))
+      value = element['offset'+style.capitalize()] + 'px';
+
     if (window.opera &amp;&amp; ['left', 'top', 'right', 'bottom'].include(style))
       if (Element.getStyle(element, 'position') == 'static') value = 'auto';
-
+    if(style == 'opacity') {
+      if(value) return parseFloat(value);
+      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+        if(value[1]) return parseFloat(value[1]) / 100;
+      return 1.0;
+    }
     return value == 'auto' ? null : value;
   },
 
   setStyle: function(element, style) {
     element = $(element);
-    for (var name in style)
-      element.style[name.camelize()] = style[name];
+    for (var name in style) {
+      var value = style[name];
+      if(name == 'opacity') {
+        if (value == 1) {
+          value = (/Gecko/.test(navigator.userAgent) &amp;&amp;
+            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
+          if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera)
+            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
+        } else if(value == '') {
+          if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera)
+            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
+        } else {
+          if(value &lt; 0.00001) value = 0;
+          if(/MSIE/.test(navigator.userAgent) &amp;&amp; !window.opera)
+            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
+              'alpha(opacity='+value*100+')';
+        }
+      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
+      element.style[name.camelize()] = value;
+    }
+    return element;
   },
 
   getDimensions: function(element) {
     element = $(element);
-    if (Element.getStyle(element, 'display') != 'none')
+    var display = $(element).getStyle('display');
+    if (display != 'none' &amp;&amp; display != null) // Safari bug
       return {width: element.offsetWidth, height: element.offsetHeight};
 
     // All *Width and *Height properties give 0 on elements with display none,
@@ -1051,12 +1424,13 @@ Element.Methods = {
     var els = element.style;
     var originalVisibility = els.visibility;
     var originalPosition = els.position;
+    var originalDisplay = els.display;
     els.visibility = 'hidden';
     els.position = 'absolute';
-    els.display = '';
+    els.display = 'block';
     var originalWidth = element.clientWidth;
     var originalHeight = element.clientHeight;
-    els.display = 'none';
+    els.display = originalDisplay;
     els.position = originalPosition;
     els.visibility = originalVisibility;
     return {width: originalWidth, height: originalHeight};
@@ -1075,6 +1449,7 @@ Element.Methods = {
         element.style.left = 0;
       }
     }
+    return element;
   },
 
   undoPositioned: function(element) {
@@ -1087,49 +1462,153 @@ Element.Methods = {
         element.style.bottom =
         element.style.right = '';
     }
+    return element;
   },
 
   makeClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element._overflow = element.style.overflow;
+    if (element._overflow) return element;
+    element._overflow = element.style.overflow || 'auto';
     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
       element.style.overflow = 'hidden';
+    return element;
   },
 
   undoClipping: function(element) {
     element = $(element);
-    if (element._overflow) return;
-    element.style.overflow = element._overflow;
-    element._overflow = undefined;
+    if (!element._overflow) return element;
+    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+    element._overflow = null;
+    return element;
   }
-}
+};
+
+Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
+
+Element._attributeTranslations = {};
+
+Element._attributeTranslations.names = {
+  colspan:   &quot;colSpan&quot;,
+  rowspan:   &quot;rowSpan&quot;,
+  valign:    &quot;vAlign&quot;,
+  datetime:  &quot;dateTime&quot;,
+  accesskey: &quot;accessKey&quot;,
+  tabindex:  &quot;tabIndex&quot;,
+  enctype:   &quot;encType&quot;,
+  maxlength: &quot;maxLength&quot;,
+  readonly:  &quot;readOnly&quot;,
+  longdesc:  &quot;longDesc&quot;
+};
+
+Element._attributeTranslations.values = {
+  _getAttr: function(element, attribute) {
+    return element.getAttribute(attribute, 2);
+  },
+
+  _flag: function(element, attribute) {
+    return $(element).hasAttribute(attribute) ? attribute : null;
+  },
+
+  style: function(element) {
+    return element.style.cssText.toLowerCase();
+  },
+
+  title: function(element) {
+    var node = element.getAttributeNode('title');
+    return node.specified ? node.nodeValue : null;
+  }
+};
+
+Object.extend(Element._attributeTranslations.values, {
+  href: Element._attributeTranslations.values._getAttr,
+  src:  Element._attributeTranslations.values._getAttr,
+  disabled: Element._attributeTranslations.values._flag,
+  checked:  Element._attributeTranslations.values._flag,
+  readonly: Element._attributeTranslations.values._flag,
+  multiple: Element._attributeTranslations.values._flag
+});
+
+Element.Methods.Simulated = {
+  hasAttribute: function(element, attribute) {
+    var t = Element._attributeTranslations;
+    attribute = t.names[attribute] || attribute;
+    return $(element).getAttributeNode(attribute).specified;
+  }
+};
+
+// IE is missing .innerHTML support for TABLE-related elements
+if (document.all &amp;&amp; !window.opera){
+  Element.Methods.update = function(element, html) {
+    element = $(element);
+    html = typeof html == 'undefined' ? '' : html.toString();
+    var tagName = element.tagName.toUpperCase();
+    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
+      var div = document.createElement('div');
+      switch (tagName) {
+        case 'THEAD':
+        case 'TBODY':
+          div.innerHTML = '&lt;table&gt;&lt;tbody&gt;' +  html.stripScripts() + '&lt;/tbody&gt;&lt;/table&gt;';
+          depth = 2;
+          break;
+        case 'TR':
+          div.innerHTML = '&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;' +  html.stripScripts() + '&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;';
+          depth = 3;
+          break;
+        case 'TD':
+          div.innerHTML = '&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;' +  html.stripScripts() + '&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;';
+          depth = 4;
+      }
+      $A(element.childNodes).each(function(node){
+        element.removeChild(node)
+      });
+      depth.times(function(){ div = div.firstChild });
+
+      $A(div.childNodes).each(
+        function(node){ element.appendChild(node) });
+    } else {
+      element.innerHTML = html.stripScripts();
+    }
+    setTimeout(function() {html.evalScripts()}, 10);
+    return element;
+  }
+};
 
 Object.extend(Element, Element.Methods);
 
 var _nativeExtensions = false;
 
-if(!HTMLElement &amp;&amp; /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
-  var HTMLElement = {}
-  HTMLElement.prototype = document.createElement('div').__proto__;
-}
+if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
+    var className = 'HTML' + tag + 'Element';
+    if(window[className]) return;
+    var klass = window[className] = {};
+    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
+  });
 
 Element.addMethods = function(methods) {
   Object.extend(Element.Methods, methods || {});
 
-  if(typeof HTMLElement != 'undefined') {
-    var methods = Element.Methods, cache = Element.extend.cache;
-    for (property in methods) {
+  function copy(methods, destination, onlyIfAbsent) {
+    onlyIfAbsent = onlyIfAbsent || false;
+    var cache = Element.extend.cache;
+    for (var property in methods) {
       var value = methods[property];
-      if (typeof value == 'function')
-        HTMLElement.prototype[property] = cache.findOrStore(value);
+      if (!onlyIfAbsent || !(property in destination))
+        destination[property] = cache.findOrStore(value);
     }
+  }
+
+  if (typeof HTMLElement != 'undefined') {
+    copy(Element.Methods, HTMLElement.prototype);
+    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+    copy(Form.Methods, HTMLFormElement.prototype);
+    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
+      copy(Form.Element.Methods, klass.prototype);
+    });
     _nativeExtensions = true;
   }
 }
 
-Element.addMethods();
-
 var Toggle = new Object();
 Toggle.display = Element.toggle;
 
@@ -1148,8 +1627,8 @@ Abstract.Insertion.prototype = {
       try {
         this.element.insertAdjacentHTML(this.adjacency, this.content);
       } catch (e) {
-        var tagName = this.element.tagName.toLowerCase();
-        if (tagName == 'tbody' || tagName == 'tr') {
+        var tagName = this.element.tagName.toUpperCase();
+        if (['TBODY', 'TR'].include(tagName)) {
           this.insertContent(this.contentFromAnonymousTable());
         } else {
           throw e;
@@ -1248,20 +1727,18 @@ Element.ClassNames.prototype = {
 
   add: function(classNameToAdd) {
     if (this.include(classNameToAdd)) return;
-    this.set(this.toArray().concat(classNameToAdd).join(' '));
+    this.set($A(this).concat(classNameToAdd).join(' '));
   },
 
   remove: function(classNameToRemove) {
     if (!this.include(classNameToRemove)) return;
-    this.set(this.select(function(className) {
-      return className != classNameToRemove;
-    }).join(' '));
+    this.set($A(this).without(classNameToRemove).join(' '));
   },
 
   toString: function() {
-    return this.toArray().join(' ');
+    return $A(this).join(' ');
   }
-}
+};
 
 Object.extend(Element.ClassNames.prototype, Enumerable);
 var Selector = Class.create();
@@ -1308,15 +1785,15 @@ Selector.prototype = {
     if (params.wildcard)
       conditions.push('true');
     if (clause = params.id)
-      conditions.push('element.id == ' + clause.inspect());
+      conditions.push('element.readAttribute(&quot;id&quot;) == ' + clause.inspect());
     if (clause = params.tagName)
       conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
     if ((clause = params.classNames).length &gt; 0)
-      for (var i = 0; i &lt; clause.length; i++)
-        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
+      for (var i = 0, length = clause.length; i &lt; length; i++)
+        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
     if (clause = params.attributes) {
       clause.each(function(attribute) {
-        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
+        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
         var splitValueBy = function(delimiter) {
           return value + ' &amp;&amp; ' + value + '.split(' + delimiter.inspect() + ')';
         }
@@ -1329,7 +1806,7 @@ Selector.prototype = {
                           ); break;
           case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
           case '':
-          case undefined: conditions.push(value + ' != null'); break;
+          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
           default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
         }
       });
@@ -1340,6 +1817,7 @@ Selector.prototype = {
 
   compileMatcher: function() {
     this.match = new Function('element', 'if (!element.tagName) return false; \
+      element = $(element); \
       return ' + this.buildMatchExpression());
   },
 
@@ -1354,7 +1832,7 @@ Selector.prototype = {
     scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
 
     var results = [];
-    for (var i = 0; i &lt; scope.length; i++)
+    for (var i = 0, length = scope.length; i &lt; length; i++)
       if (this.match(element = scope[i]))
         results.push(Element.extend(element));
 
@@ -1366,206 +1844,241 @@ Selector.prototype = {
   }
 }
 
-function $$() {
-  return $A(arguments).map(function(expression) {
-    return expression.strip().split(/\s+/).inject([null], function(results, expr) {
-      var selector = new Selector(expr);
-      return results.map(selector.findElements.bind(selector)).flatten();
-    });
-  }).flatten();
-}
-var Field = {
-  clear: function() {
-    for (var i = 0; i &lt; arguments.length; i++)
-      $(arguments[i]).value = '';
-  },
-
-  focus: function(element) {
-    $(element).focus();
-  },
-
-  present: function() {
-    for (var i = 0; i &lt; arguments.length; i++)
-      if ($(arguments[i]).value == '') return false;
-    return true;
+Object.extend(Selector, {
+  matchElements: function(elements, expression) {
+    var selector = new Selector(expression);
+    return elements.select(selector.match.bind(selector)).map(Element.extend);
   },
 
-  select: function(element) {
-    $(element).select();
+  findElement: function(elements, expression, index) {
+    if (typeof expression == 'number') index = expression, expression = false;
+    return Selector.matchElements(elements, expression || '*')[index || 0];
   },
 
-  activate: function(element) {
-    element = $(element);
-    element.focus();
-    if (element.select)
-      element.select();
+  findChildElements: function(element, expressions) {
+    return expressions.map(function(expression) {
+      return expression.match(/[^\s&quot;]+(?:&quot;[^&quot;]*&quot;[^\s&quot;]+)*/g).inject([null], function(results, expr) {
+        var selector = new Selector(expr);
+        return results.inject([], function(elements, result) {
+          return elements.concat(selector.findElements(result || element));
+        });
+      });
+    }).flatten();
   }
-}
-
-/*--------------------------------------------------------------------------*/
+});
 
+function $$() {
+  return Selector.findChildElements(document, $A(arguments));
+}
 var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($(form));
-    var queryComponents = new Array();
-
-    for (var i = 0; i &lt; elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
-    }
+  reset: function(form) {
+    $(form).reset();
+    return form;
+  },
+
+  serializeElements: function(elements, getHash) {
+    var data = elements.inject({}, function(result, element) {
+      if (!element.disabled &amp;&amp; element.name) {
+        var key = element.name, value = $(element).getValue();
+        if (value != undefined) {
+          if (result[key]) {
+            if (result[key].constructor != Array) result[key] = [result[key]];
+            result[key].push(value);
+          }
+          else result[key] = value;
+        }
+      }
+      return result;
+    });
+
+    return getHash ? data : Hash.toQueryString(data);
+  }
+};
 
-    return queryComponents.join('&amp;');
+Form.Methods = {
+  serialize: function(form, getHash) {
+    return Form.serializeElements(Form.getElements(form), getHash);
   },
 
   getElements: function(form) {
-    form = $(form);
-    var elements = new Array();
-
-    for (var tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j &lt; tagElements.length; j++)
-        elements.push(tagElements[j]);
-    }
-    return elements;
+    return $A($(form).getElementsByTagName('*')).inject([],
+      function(elements, child) {
+        if (Form.Element.Serializers[child.tagName.toLowerCase()])
+          elements.push(Element.extend(child));
+        return elements;
+      }
+    );
   },
 
   getInputs: function(form, typeName, name) {
     form = $(form);
     var inputs = form.getElementsByTagName('input');
 
-    if (!typeName &amp;&amp; !name)
-      return inputs;
+    if (!typeName &amp;&amp; !name) return $A(inputs).map(Element.extend);
 
-    var matchingInputs = new Array();
-    for (var i = 0; i &lt; inputs.length; i++) {
+    for (var i = 0, matchingInputs = [], length = inputs.length; i &lt; length; i++) {
       var input = inputs[i];
-      if ((typeName &amp;&amp; input.type != typeName) ||
-          (name &amp;&amp; input.name != name))
+      if ((typeName &amp;&amp; input.type != typeName) || (name &amp;&amp; input.name != name))
         continue;
-      matchingInputs.push(input);
+      matchingInputs.push(Element.extend(input));
     }
 
     return matchingInputs;
   },
 
   disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i &lt; elements.length; i++) {
-      var element = elements[i];
+    form = $(form);
+    form.getElements().each(function(element) {
       element.blur();
       element.disabled = 'true';
-    }
+    });
+    return form;
   },
 
   enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i &lt; elements.length; i++) {
-      var element = elements[i];
+    form = $(form);
+    form.getElements().each(function(element) {
       element.disabled = '';
-    }
+    });
+    return form;
   },
 
   findFirstElement: function(form) {
-    return Form.getElements(form).find(function(element) {
+    return $(form).getElements().find(function(element) {
       return element.type != 'hidden' &amp;&amp; !element.disabled &amp;&amp;
         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     });
   },
 
   focusFirstElement: function(form) {
-    Field.activate(Form.findFirstElement(form));
+    form = $(form);
+    form.findFirstElement().activate();
+    return form;
+  }
+}
+
+Object.extend(Form, Form.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+  focus: function(element) {
+    $(element).focus();
+    return element;
   },
 
-  reset: function(form) {
-    $(form).reset();
+  select: function(element) {
+    $(element).select();
+    return element;
   }
 }
 
-Form.Element = {
+Form.Element.Methods = {
   serialize: function(element) {
     element = $(element);
+    if (!element.disabled &amp;&amp; element.name) {
+      var value = element.getValue();
+      if (value != undefined) {
+        var pair = {};
+        pair[element.name] = value;
+        return Hash.toQueryString(pair);
+      }
+    }
+    return '';
+  },
+
+  getValue: function(element) {
+    element = $(element);
     var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    return Form.Element.Serializers[method](element);
+  },
 
-    if (parameter) {
-      var key = encodeURIComponent(parameter[0]);
-      if (key.length == 0) return;
+  clear: function(element) {
+    $(element).value = '';
+    return element;
+  },
 
-      if (parameter[1].constructor != Array)
-        parameter[1] = [parameter[1]];
+  present: function(element) {
+    return $(element).value != '';
+  },
 
-      return parameter[1].map(function(value) {
-        return key + '=' + encodeURIComponent(value);
-      }).join('&amp;');
-    }
+  activate: function(element) {
+    element = $(element);
+    element.focus();
+    if (element.select &amp;&amp; ( element.tagName.toLowerCase() != 'input' ||
+      !['button', 'reset', 'submit'].include(element.type) ) )
+      element.select();
+    return element;
   },
 
-  getValue: function(element) {
+  disable: function(element) {
     element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
+    element.disabled = true;
+    return element;
+  },
 
-    if (parameter)
-      return parameter[1];
+  enable: function(element) {
+    element = $(element);
+    element.blur();
+    element.disabled = false;
+    return element;
   }
 }
 
+Object.extend(Form.Element, Form.Element.Methods);
+var Field = Form.Element;
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
 Form.Element.Serializers = {
   input: function(element) {
     switch (element.type.toLowerCase()) {
-      case 'submit':
-      case 'hidden':
-      case 'password':
-      case 'text':
-        return Form.Element.Serializers.textarea(element);
       case 'checkbox':
       case 'radio':
         return Form.Element.Serializers.inputSelector(element);
+      default:
+        return Form.Element.Serializers.textarea(element);
     }
-    return false;
   },
 
   inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
+    return element.checked ? element.value : null;
   },
 
   textarea: function(element) {
-    return [element.name, element.value];
+    return element.value;
   },
 
   select: function(element) {
-    return Form.Element.Serializers[element.type == 'select-one' ?
+    return this[element.type == 'select-one' ?
       'selectOne' : 'selectMany'](element);
   },
 
   selectOne: function(element) {
-    var value = '', opt, index = element.selectedIndex;
-    if (index &gt;= 0) {
-      opt = element.options[index];
-      value = opt.value || opt.text;
-    }
-    return [element.name, value];
+    var index = element.selectedIndex;
+    return index &gt;= 0 ? this.optionValue(element.options[index]) : null;
   },
 
   selectMany: function(element) {
-    var value = [];
-    for (var i = 0; i &lt; element.length; i++) {
+    var values, length = element.length;
+    if (!length) return null;
+
+    for (var i = 0, values = []; i &lt; length; i++) {
       var opt = element.options[i];
-      if (opt.selected)
-        value.push(opt.value || opt.text);
+      if (opt.selected) values.push(this.optionValue(opt));
     }
-    return [element.name, value];
+    return values;
+  },
+
+  optionValue: function(opt) {
+    // extend element because hasAttribute may not be native
+    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
   }
 }
 
 /*--------------------------------------------------------------------------*/
 
-var $F = Form.Element.getValue;
-
-/*--------------------------------------------------------------------------*/
-
 Abstract.TimedObserver = function() {}
 Abstract.TimedObserver.prototype = {
   initialize: function(element, frequency, callback) {
@@ -1583,7 +2096,9 @@ Abstract.TimedObserver.prototype = {
 
   onTimerEvent: function() {
     var value = this.getValue();
-    if (this.lastValue != value) {
+    var changed = ('string' == typeof this.lastValue &amp;&amp; 'string' == typeof value
+      ? this.lastValue != value : String(this.lastValue) != String(value));
+    if (changed) {
       this.callback(this.element, value);
       this.lastValue = value;
     }
@@ -1628,9 +2143,7 @@ Abstract.EventObserver.prototype = {
   },
 
   registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i &lt; elements.length; i++)
-      this.registerCallback(elements[i]);
+    Form.getElements(this.element).each(this.registerCallback.bind(this));
   },
 
   registerCallback: function(element) {
@@ -1640,11 +2153,7 @@ Abstract.EventObserver.prototype = {
         case 'radio':
           Event.observe(element, 'click', this.onElementEvent.bind(this));
           break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
+        default:
           Event.observe(element, 'change', this.onElementEvent.bind(this));
           break;
       }
@@ -1679,6 +2188,10 @@ Object.extend(Event, {
   KEY_RIGHT:    39,
   KEY_DOWN:     40,
   KEY_DELETE:   46,
+  KEY_HOME:     36,
+  KEY_END:      35,
+  KEY_PAGEUP:   33,
+  KEY_PAGEDOWN: 34,
 
   element: function(event) {
     return event.target || event.srcElement;
@@ -1734,7 +2247,7 @@ Object.extend(Event, {
 
   unloadCache: function() {
     if (!Event.observers) return;
-    for (var i = 0; i &lt; Event.observers.length; i++) {
+    for (var i = 0, length = Event.observers.length; i &lt; length; i++) {
       Event.stopObserving.apply(this, Event.observers[i]);
       Event.observers[i][0] = null;
     }
@@ -1742,7 +2255,7 @@ Object.extend(Event, {
   },
 
   observe: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
 
     if (name == 'keypress' &amp;&amp;
@@ -1750,11 +2263,11 @@ Object.extend(Event, {
         || element.attachEvent))
       name = 'keydown';
 
-    this._observeAndCache(element, name, observer, useCapture);
+    Event._observeAndCache(element, name, observer, useCapture);
   },
 
   stopObserving: function(element, name, observer, useCapture) {
-    var element = $(element);
+    element = $(element);
     useCapture = useCapture || false;
 
     if (name == 'keypress' &amp;&amp;
@@ -1765,7 +2278,9 @@ Object.extend(Event, {
     if (element.removeEventListener) {
       element.removeEventListener(name, observer, useCapture);
     } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
+      try {
+        element.detachEvent('on' + name, observer);
+      } catch (e) {}
     }
   }
 });
@@ -1819,7 +2334,8 @@ var Position = {
       valueL += element.offsetLeft || 0;
       element = element.offsetParent;
       if (element) {
-        p = Element.getStyle(element, 'position');
+        if(element.tagName=='BODY') break;
+        var p = Element.getStyle(element, 'position');
         if (p == 'relative' || p == 'absolute') break;
       }
     } while (element);
@@ -1875,17 +2391,6 @@ var Position = {
         element.offsetWidth;
   },
 
-  clone: function(source, target) {
-    source = $(source);
-    target = $(target);
-    target.style.position = 'absolute';
-    var offsets = this.cumulativeOffset(source);
-    target.style.top    = offsets[1] + 'px';
-    target.style.left   = offsets[0] + 'px';
-    target.style.width  = source.offsetWidth + 'px';
-    target.style.height = source.offsetHeight + 'px';
-  },
-
   page: function(forElement) {
     var valueT = 0, valueL = 0;
 
@@ -1902,8 +2407,10 @@ var Position = {
 
     element = forElement;
     do {
-      valueT -= element.scrollTop  || 0;
-      valueL -= element.scrollLeft || 0;
+      if (!window.opera || element.tagName=='BODY') {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
     } while (element = element.parentNode);
 
     return [valueL, valueT];
@@ -1964,10 +2471,10 @@ var Position = {
     element._originalHeight = element.style.height;
 
     element.style.position = 'absolute';
-    element.style.top    = top + 'px';;
-    element.style.left   = left + 'px';;
-    element.style.width  = width + 'px';;
-    element.style.height = height + 'px';;
+    element.style.top    = top + 'px';
+    element.style.left   = left + 'px';
+    element.style.width  = width + 'px';
+    element.style.height = height + 'px';
   },
 
   relativize: function(element) {
@@ -2003,4 +2510,6 @@ if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
 
     return [valueL, valueT];
   }
-}
\ No newline at end of file
+}
+
+Element.addMethods();
\ No newline at end of file</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/javascripts/prototype.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,43 +1,64 @@
 module ActionView
-  module Helpers
-    # Provides methods for converting a number into a formatted string that currently represents
-    # one of the following forms: phone number, percentage, money, or precision level.
+  module Helpers #:nodoc:
+    # Provides methods for converting a numbers into formatted strings.
+    # Methods are provided for phone numbers, currency, percentage,
+    # precision, positional notation, and file size.
     module NumberHelper
-
-      # Formats a +number+ into a US phone number string. The +options+ can be a hash used to customize the format of the output.
-      # The area code can be surrounded by parentheses by setting +:area_code+ to true; default is false
-      # The delimiter can be set using +:delimiter+; default is &quot;-&quot;
-      # Examples:
-      #   number_to_phone(1235551234)   =&gt; 123-555-1234
-      #   number_to_phone(1235551234, {:area_code =&gt; true})   =&gt; (123) 555-1234
-      #   number_to_phone(1235551234, {:delimiter =&gt; &quot; &quot;})    =&gt; 123 555 1234
-      #   number_to_phone(1235551234, {:area_code =&gt; true, :extension =&gt; 555})  =&gt; (123) 555-1234 x 555
+      # Formats a +number+ into a US phone number. You can customize the format
+      # in the +options+ hash.
+      # * &lt;tt&gt;:area_code&lt;/tt&gt;  - Adds parentheses around the area code.
+      # * &lt;tt&gt;:delimiter&lt;/tt&gt;  - Specifies the delimiter to use, defaults to &quot;-&quot;.
+      # * &lt;tt&gt;:extension&lt;/tt&gt;  - Specifies an extension to add to the end of the
+      #   generated number
+      # * &lt;tt&gt;:country_code&lt;/tt&gt;  - Sets the country code for the phone number.
+      #
+      #  number_to_phone(1235551234)   =&gt; 123-555-1234
+      #  number_to_phone(1235551234, :area_code =&gt; true)   =&gt; (123) 555-1234
+      #  number_to_phone(1235551234, :delimiter =&gt; &quot; &quot;)    =&gt; 123 555 1234
+      #  number_to_phone(1235551234, :area_code =&gt; true, :extension =&gt; 555)  =&gt; (123) 555-1234 x 555
+      #  number_to_phone(1235551234, :country_code =&gt; 1)
       def number_to_phone(number, options = {})
-        options   = options.stringify_keys
-        area_code = options.delete(&quot;area_code&quot;) { false }
-        delimiter = options.delete(&quot;delimiter&quot;) { &quot;-&quot; }
-        extension = options.delete(&quot;extension&quot;) { &quot;&quot; }
+        number       = number.to_s.strip unless number.nil?
+        options      = options.stringify_keys
+        area_code    = options[&quot;area_code&quot;] || nil
+        delimiter    = options[&quot;delimiter&quot;] || &quot;-&quot;
+        extension    = options[&quot;extension&quot;].to_s.strip || nil
+        country_code = options[&quot;country_code&quot;] || nil
+
         begin
-          str = area_code == true ? number.to_s.gsub(/([0-9]{3})([0-9]{3})([0-9]{4})/,&quot;(\\1) \\2#{delimiter}\\3&quot;) : number.to_s.gsub(/([0-9]{3})([0-9]{3})([0-9]{4})/,&quot;\\1#{delimiter}\\2#{delimiter}\\3&quot;)
-          extension.to_s.strip.empty? ? str : &quot;#{str} x #{extension.to_s.strip}&quot;
+          str = &quot;&quot;
+          str &lt;&lt; &quot;+#{country_code}#{delimiter}&quot; unless country_code.blank?
+          str &lt;&lt; if area_code
+            number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,&quot;(\\1) \\2#{delimiter}\\3&quot;)
+          else
+            number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,&quot;\\1#{delimiter}\\2#{delimiter}\\3&quot;)
+          end
+          str &lt;&lt; &quot; x #{extension}&quot; unless extension.blank?
+          str
         rescue
           number
         end
       end
 
-      # Formats a +number+ into a currency string. The +options+ hash can be used to customize the format of the output.
-      # The +number+ can contain a level of precision using the +precision+ key; default is 2
-      # The currency type can be set using the +unit+ key; default is &quot;$&quot;
-      # The unit separator can be set using the +separator+ key; default is &quot;.&quot;
-      # The delimiter can be set using the +delimiter+ key; default is &quot;,&quot;
-      # Examples:
-      #    number_to_currency(1234567890.50)     =&gt; $1,234,567,890.50
-      #    number_to_currency(1234567890.506)    =&gt; $1,234,567,890.51
-      #    number_to_currency(1234567890.50, {:unit =&gt; &quot;&amp;pound;&quot;, :separator =&gt; &quot;,&quot;, :delimiter =&gt; &quot;&quot;}) =&gt; &amp;pound;1234567890,50
+      # Formats a +number+ into a currency string. You can customize the format
+      # in the +options+ hash.
+      # * &lt;tt&gt;:precision&lt;/tt&gt;  -  Sets the level of precision, defaults to 2
+      # * &lt;tt&gt;:unit&lt;/tt&gt;  - Sets the denomination of the currency, defaults to &quot;$&quot;
+      # * &lt;tt&gt;:separator&lt;/tt&gt;  - Sets the separator between the units, defaults to &quot;.&quot;
+      # * &lt;tt&gt;:delimiter&lt;/tt&gt;  - Sets the thousands delimiter, defaults to &quot;,&quot;
+      #
+      #  number_to_currency(1234567890.50)     =&gt; $1,234,567,890.50
+      #  number_to_currency(1234567890.506)    =&gt; $1,234,567,890.51
+      #  number_to_currency(1234567890.506, :precision =&gt; 3)    =&gt; $1,234,567,890.506
+      #  number_to_currency(1234567890.50, :unit =&gt; &quot;&amp;pound;&quot;, :separator =&gt; &quot;,&quot;, :delimiter =&gt; &quot;&quot;) 
+      #     =&gt; &amp;pound;1234567890,50
       def number_to_currency(number, options = {})
-        options = options.stringify_keys
-        precision, unit, separator, delimiter = options.delete(&quot;precision&quot;) { 2 }, options.delete(&quot;unit&quot;) { &quot;$&quot; }, options.delete(&quot;separator&quot;) { &quot;.&quot; }, options.delete(&quot;delimiter&quot;) { &quot;,&quot; }
-        separator = &quot;&quot; unless precision &gt; 0
+        options   = options.stringify_keys
+        precision = options[&quot;precision&quot;] || 2
+        unit      = options[&quot;unit&quot;] || &quot;$&quot;
+        separator = precision &gt; 0 ? options[&quot;separator&quot;] || &quot;.&quot; : &quot;&quot;
+        delimiter = options[&quot;delimiter&quot;] || &quot;,&quot;
+        
         begin
           parts = number_with_precision(number, precision).split('.')
           unit + number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s
@@ -46,16 +67,19 @@ module ActionView
         end
       end
 
-      # Formats a +number+ as into a percentage string. The +options+ hash can be used to customize the format of the output.
-      # The +number+ can contain a level of precision using the +precision+ key; default is 3
-      # The unit separator can be set using the +separator+ key; default is &quot;.&quot;
-      # Examples:
-      #   number_to_percentage(100)    =&gt; 100.000%
-      #   number_to_percentage(100, {:precision =&gt; 0}) =&gt; 100%
-      #   number_to_percentage(302.0574, {:precision =&gt; 2})  =&gt; 302.06%
+      # Formats a +number+ as a percentage string. You can customize the
+      # format in the +options+ hash.
+      # * &lt;tt&gt;:precision&lt;/tt&gt;  - Sets the level of precision, defaults to 3
+      # * &lt;tt&gt;:separator&lt;/tt&gt;  - Sets the separator between the units, defaults to &quot;.&quot;
+      #
+      #  number_to_percentage(100)    =&gt; 100.000%
+      #  number_to_percentage(100, {:precision =&gt; 0})   =&gt; 100%
+      #  number_to_percentage(302.0574, {:precision =&gt; 2})   =&gt; 302.06%
       def number_to_percentage(number, options = {})
-        options = options.stringify_keys
-        precision, separator = options.delete(&quot;precision&quot;) { 3 }, options.delete(&quot;separator&quot;) { &quot;.&quot; }
+        options   = options.stringify_keys
+        precision = options[&quot;precision&quot;] || 3
+        separator = options[&quot;separator&quot;] || &quot;.&quot;
+        
         begin
           number = number_with_precision(number, precision)
           parts = number.split('.')
@@ -69,41 +93,63 @@ module ActionView
         end
       end
 
-      # Formats a +number+ with a +delimiter+.
-      # Example:
-      #    number_with_delimiter(12345678) =&gt; 12,345,678
-      def number_with_delimiter(number, delimiter=&quot;,&quot;)
-        number.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, &quot;\\1#{delimiter}&quot;)
+      # Formats a +number+ with grouped thousands using +delimiter+. You
+      # can customize the format using optional &lt;em&gt;delimiter&lt;/em&gt; and &lt;em&gt;separator&lt;/em&gt; parameters.
+      # * &lt;tt&gt;delimiter&lt;/tt&gt;  - Sets the thousands delimiter, defaults to &quot;,&quot;
+      # * &lt;tt&gt;separator&lt;/tt&gt;  - Sets the separator between the units, defaults to &quot;.&quot;
+      #
+      #  number_with_delimiter(12345678)      =&gt; 12,345,678
+      #  number_with_delimiter(12345678.05)   =&gt; 12,345,678.05
+      #  number_with_delimiter(12345678, &quot;.&quot;)   =&gt; 12.345.678
+      def number_with_delimiter(number, delimiter=&quot;,&quot;, separator=&quot;.&quot;)
+        begin
+          parts = number.to_s.split('.')
+          parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, &quot;\\1#{delimiter}&quot;)
+          parts.join separator
+        rescue
+          number
+        end
       end
-
-      # Returns a formatted-for-humans file size.
+      
+      # Formats a +number+ with the specified level of +precision+. The default
+      # level of precision is 3.
+      #
+      #  number_with_precision(111.2345)    =&gt; 111.235
+      #  number_with_precision(111.2345, 2) =&gt; 111.24
+      def number_with_precision(number, precision=3)
+        &quot;%01.#{precision}f&quot; % number
+      rescue
+        number
+      end
+      
+      # Formats the bytes in +size+ into a more understandable representation.
+      # Useful for reporting file sizes to users. This method returns nil if 
+      # +size+ cannot be converted into a number. You can change the default 
+      # precision of 1 in +precision+.
       # 
-      # Examples:
-      #   human_size(123)          =&gt; 123 Bytes
-      #   human_size(1234)         =&gt; 1.2 KB
-      #   human_size(12345)        =&gt; 12.1 KB
-      #   human_size(1234567)      =&gt; 1.2 MB
-      #   human_size(1234567890)   =&gt; 1.1 GB
-      def number_to_human_size(size)
+      #  number_to_human_size(123)           =&gt; 123 Bytes
+      #  number_to_human_size(1234)          =&gt; 1.2 KB
+      #  number_to_human_size(12345)         =&gt; 12.1 KB
+      #  number_to_human_size(1234567)       =&gt; 1.2 MB
+      #  number_to_human_size(1234567890)    =&gt; 1.1 GB
+      #  number_to_human_size(1234567890123) =&gt; 1.1 TB
+      #  number_to_human_size(1234567, 2)    =&gt; 1.18 MB
+      def number_to_human_size(size, precision=1)
+        size = Kernel.Float(size)
         case 
-          when size &lt; 1.kilobyte: '%d Bytes' % size
-          when size &lt; 1.megabyte: '%.1f KB'  % (size / 1.0.kilobyte)
-          when size &lt; 1.gigabyte: '%.1f MB'  % (size / 1.0.megabyte)
-          when size &lt; 1.terabyte: '%.1f GB'  % (size / 1.0.gigabyte)
-          else                    '%.1f TB'  % (size / 1.0.terabyte)
+          when size == 1        : &quot;1 Byte&quot;
+          when size &lt; 1.kilobyte: &quot;%d Bytes&quot; % size
+          when size &lt; 1.megabyte: &quot;%.#{precision}f KB&quot;  % (size / 1.0.kilobyte)
+          when size &lt; 1.gigabyte: &quot;%.#{precision}f MB&quot;  % (size / 1.0.megabyte)
+          when size &lt; 1.terabyte: &quot;%.#{precision}f GB&quot;  % (size / 1.0.gigabyte)
+          else                    &quot;%.#{precision}f TB&quot;  % (size / 1.0.terabyte)
         end.sub('.0', '')
       rescue
         nil
       end
       
       alias_method :human_size, :number_to_human_size # deprecated alias
-
-      # Formats a +number+ with a level of +precision+.
-      # Example:
-      #    number_with_precision(111.2345) =&gt; 111.235
-      def number_with_precision(number, precision=3)
-        sprintf(&quot;%01.#{precision}f&quot;, number)
-      end
+      deprecate :human_size =&gt; :number_to_human_size
     end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,3 @@
-require File.dirname(__FILE__) + '/javascript_helper'
 require 'set'
 
 module ActionView
@@ -39,7 +38,7 @@ module ActionView
       # XMLHttpRequest. The result of that request can then be inserted into a
       # DOM object whose id can be specified with &lt;tt&gt;options[:update]&lt;/tt&gt;. 
       # Usually, the result would be a partial prepared by the controller with
-      # either render_partial or render_partial_collection. 
+      # render :partial. 
       #
       # Examples:
       #   link_to_remote &quot;Delete this post&quot;, :update =&gt; &quot;posts&quot;, 
@@ -60,6 +59,12 @@ module ActionView
       # influence how the target DOM element is updated. It must be one of 
       # &lt;tt&gt;:before&lt;/tt&gt;, &lt;tt&gt;:top&lt;/tt&gt;, &lt;tt&gt;:bottom&lt;/tt&gt;, or &lt;tt&gt;:after&lt;/tt&gt;.
       #
+      # The method used is by default POST. You can also specify GET or you
+      # can simulate PUT or DELETE over POST. All specified with &lt;tt&gt;options[:method]&lt;/tt&gt;
+      #
+      # Example:
+      #   link_to_remote &quot;Destroy&quot;, :url =&gt; person_url(:id =&gt; person), :method =&gt; :delete
+      #
       # By default, these remote requests are processed asynchronous during 
       # which various JavaScript callbacks can be triggered (for progress 
       # indicators and the likes). All callbacks get access to the 
@@ -159,15 +164,20 @@ module ActionView
       #
       # By default the fall-through action is the same as the one specified in 
       # the :url (and the default method is :post).
-      def form_remote_tag(options = {})
+      #
+      # form_remote_tag also takes a block, like form_tag:
+      #   &lt;% form_remote_tag :url =&gt; '/posts' do -%&gt;
+      #     &lt;div&gt;&lt;%= submit_tag 'Save' %&gt;&lt;/div&gt;
+      #   &lt;% end -%&gt;
+      def form_remote_tag(options = {}, &amp;block)
         options[:form] = true
 
         options[:html] ||= {}
-        options[:html][:onsubmit] = &quot;#{remote_function(options)}; return false;&quot;
-        options[:html][:action] = options[:html][:action] || url_for(options[:url])
-        options[:html][:method] = options[:html][:method] || &quot;post&quot;
+        options[:html][:onsubmit] = 
+          (options[:html][:onsubmit] ? options[:html][:onsubmit] + &quot;; &quot; : &quot;&quot;) + 
+          &quot;#{remote_function(options)}; return false;&quot;
 
-        tag(&quot;form&quot;, options[:html], true)
+        form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &amp;block)
       end
 
       # Works like form_remote_tag, but uses form_for semantics.
@@ -194,81 +204,6 @@ module ActionView
         tag(&quot;input&quot;, options[:html], false)
       end
       
-      # Returns a JavaScript function (or expression) that'll update a DOM 
-      # element according to the options passed.
-      #
-      # * &lt;tt&gt;:content&lt;/tt&gt;: The content to use for updating. Can be left out
-      #   if using block, see example.
-      # * &lt;tt&gt;:action&lt;/tt&gt;: Valid options are :update (assumed by default), 
-      #   :empty, :remove
-      # * &lt;tt&gt;:position&lt;/tt&gt; If the :action is :update, you can optionally 
-      #   specify one of the following positions: :before, :top, :bottom, 
-      #   :after.
-      #
-      # Examples:
-      #   &lt;%= javascript_tag(update_element_function(&quot;products&quot;, 
-      #     :position =&gt; :bottom, :content =&gt; &quot;&lt;p&gt;New product!&lt;/p&gt;&quot;)) %&gt;
-      #
-      #   &lt;% replacement_function = update_element_function(&quot;products&quot;) do %&gt;
-      #     &lt;p&gt;Product 1&lt;/p&gt;
-      #     &lt;p&gt;Product 2&lt;/p&gt;
-      #   &lt;% end %&gt;
-      #   &lt;%= javascript_tag(replacement_function) %&gt;
-      #
-      # This method can also be used in combination with remote method call 
-      # where the result is evaluated afterwards to cause multiple updates on
-      # a page. Example:
-      #
-      #   # Calling view
-      #   &lt;%= form_remote_tag :url =&gt; { :action =&gt; &quot;buy&quot; }, 
-      #     :complete =&gt; evaluate_remote_response %&gt;
-      #   all the inputs here...
-      #
-      #   # Controller action
-      #   def buy
-      #     @product = Product.find(1)
-      #   end
-      #
-      #   # Returning view
-      #   &lt;%= update_element_function(
-      #         &quot;cart&quot;, :action =&gt; :update, :position =&gt; :bottom, 
-      #         :content =&gt; &quot;&lt;p&gt;New Product: #{@product.name}&lt;/p&gt;&quot;)) %&gt;
-      #   &lt;% update_element_function(&quot;status&quot;, :binding =&gt; binding) do %&gt;
-      #     You've bought a new product!
-      #   &lt;% end %&gt;
-      #
-      # Notice how the second call doesn't need to be in an ERb output block
-      # since it uses a block and passes in the binding to render directly. 
-      # This trick will however only work in ERb (not Builder or other 
-      # template forms).
-      #
-      # See also JavaScriptGenerator and update_page.
-      def update_element_function(element_id, options = {}, &amp;block)
-        content = escape_javascript(options[:content] || '')
-        content = escape_javascript(capture(&amp;block)) if block
-        
-        javascript_function = case (options[:action] || :update)
-          when :update
-            if options[:position]
-              &quot;new Insertion.#{options[:position].to_s.camelize}('#{element_id}','#{content}')&quot;
-            else
-              &quot;$('#{element_id}').innerHTML = '#{content}'&quot;
-            end
-          
-          when :empty
-            &quot;$('#{element_id}').innerHTML = ''&quot;
-          
-          when :remove
-            &quot;Element.remove('#{element_id}')&quot;
-          
-          else
-            raise ArgumentError, &quot;Invalid action, choose one of :update, :remove, :empty&quot;
-        end
-        
-        javascript_function &lt;&lt; &quot;;\n&quot;
-        options[:binding] ? concat(javascript_function, options[:binding]) : javascript_function
-      end
-      
       # Returns 'eval(request.responseText)' which is the JavaScript function
       # that form_remote_tag can call in :complete to evaluate a multiple
       # update return document using update_element_function calls.
@@ -289,7 +224,7 @@ module ActionView
         javascript_options = options_for_ajax(options)
 
         update = ''
-        if options[:update] and options[:update].is_a?Hash
+        if options[:update] &amp;&amp; options[:update].is_a?(Hash)
           update  = []
           update &lt;&lt; &quot;success:'#{options[:update][:success]}'&quot; if options[:update][:success]
           update &lt;&lt; &quot;failure:'#{options[:update][:failure]}'&quot; if options[:update][:failure]
@@ -303,7 +238,7 @@ module ActionView
           &quot;new Ajax.Updater(#{update}, &quot;
 
         url_options = options[:url]
-        url_options = url_options.merge(:escape =&gt; false) if url_options.is_a? Hash
+        url_options = url_options.merge(:escape =&gt; false) if url_options.is_a?(Hash)
         function &lt;&lt; &quot;'#{url_for(url_options)}'&quot;
         function &lt;&lt; &quot;, #{javascript_options})&quot;
 
@@ -315,8 +250,10 @@ module ActionView
         return function
       end
 
-      # Observes the field with the DOM ID specified by +field_id+ and makes
-      # an Ajax call when its contents have changed.
+      # Observes the field with the DOM ID specified by +field_id+ and calls a
+      # callback when its contents have changed. The default callback is an
+      # Ajax call. By default the value of the observed field is sent as a
+      # parameter with the Ajax call.
       # 
       # Required +options+ are either of:
       # &lt;tt&gt;:url&lt;/tt&gt;::       +url_for+-style options for the action to call
@@ -333,14 +270,24 @@ module ActionView
       # &lt;tt&gt;:update&lt;/tt&gt;::    Specifies the DOM ID of the element whose 
       #                       innerHTML should be updated with the
       #                       XMLHttpRequest response text.
-      # &lt;tt&gt;:with&lt;/tt&gt;::      A JavaScript expression specifying the
-      #                       parameters for the XMLHttpRequest. This defaults
-      #                       to 'value', which in the evaluated context 
-      #                       refers to the new field value. If you specify a
-      #                       string without a &quot;=&quot;, it'll be extended to mean
-      #                       the form key that the value should be assigned to.
-      #                       So :with =&gt; &quot;term&quot; gives &quot;'term'=value&quot;. If a &quot;=&quot; is
-      #                       present, no extension will happen.
+      # &lt;tt&gt;:with&lt;/tt&gt;::      A JavaScript expression specifying the parameters
+      #                       for the XMLHttpRequest. The default is to send the
+      #                       key and value of the observed field. Any custom
+      #                       expressions should return a valid URL query string.
+      #                       The value of the field is stored in the JavaScript
+      #                       variable +value+.
+      #
+      #                       Examples
+      #                       
+      #                         :with =&gt; &quot;'my_custom_key=' + value&quot;
+      #                         :with =&gt; &quot;'person[name]=' + prompt('New name')&quot;
+      #                         :with =&gt; &quot;Form.Element.serialize('other-field')&quot;
+      #
+      #                       Finally
+      #                         :with =&gt; 'name'
+      #                       is shorthand for
+      #                         :with =&gt; &quot;'name=' + value&quot;
+      #                       This essentially just changes the key of the parameter.
       # &lt;tt&gt;:on&lt;/tt&gt;::        Specifies which event handler to observe. By default,
       #                       it's set to &quot;changed&quot; for text fields and areas and
       #                       &quot;click&quot; for radio buttons and checkboxes. With this,
@@ -356,11 +303,15 @@ module ActionView
           build_observer('Form.Element.EventObserver', field_id, options)
         end
       end
-      
-      # Like +observe_field+, but operates on an entire form identified by the
-      # DOM ID +form_id+. +options+ are the same as +observe_field+, except 
-      # the default value of the &lt;tt&gt;:with&lt;/tt&gt; option evaluates to the
-      # serialized (request string) value of the form.
+     
+      # Observes the form with the DOM ID specified by +form_id+ and calls a
+      # callback when its contents have changed. The default callback is an
+      # Ajax call. By default all fields of the observed field are sent as
+      # parameters with the Ajax call.
+      #
+      # The +options+ for +observe_form+ are the same as the options for
+      # +observe_field+. The JavaScript variable +value+ available to the
+      # &lt;tt&gt;:with&lt;/tt&gt; option is set to the serialized form by default.
       def observe_form(form_id, options = {})
         if options[:frequency]
           build_observer('Form.Observer', form_id, options)
@@ -438,7 +389,7 @@ module ActionView
               if ActionView::Base.debug_rjs
                 source = javascript.dup
                 javascript.replace &quot;try {\n#{source}\n} catch (e) &quot;
-                javascript &lt;&lt; &quot;{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub(/\r\n|\n|\r/, &quot;\\n&quot;).gsub(/[&quot;']/) { |m| &quot;\\#{m}&quot; }}'); throw e }&quot;
+                javascript &lt;&lt; &quot;{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub('\\','\0\0').gsub(/\r\n|\n|\r/, &quot;\\n&quot;).gsub(/[&quot;']/) { |m| &quot;\\#{m}&quot; }}'); throw e }&quot;
               end
             end
           end
@@ -453,6 +404,12 @@ module ActionView
             JavaScriptElementProxy.new(self, id)
           end
           
+          # Returns an object whose &lt;tt&gt;#to_json&lt;/tt&gt; evaluates to +code+. Use this to pass a literal JavaScript 
+          # expression as an argument to another JavaScriptGenerator method.
+          def literal(code)
+            ActiveSupport::JSON::Variable.new(code.to_s)
+          end
+          
           # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
           # used for further method calls. Examples:
           #
@@ -526,7 +483,7 @@ module ActionView
           #
           #   # Replace the DOM element having ID 'person-45' with the
           #   # 'person' partial for the appropriate object.
-          #   replace_html 'person-45', :partial =&gt; 'person', :object =&gt; @person
+          #   replace 'person-45', :partial =&gt; 'person', :object =&gt; @person
           #
           # This allows the same partial that is used for the +insert_html+ to
           # be also used for the input to +replace+ without resorting to
@@ -550,22 +507,22 @@ module ActionView
           
           # Removes the DOM elements with the given +ids+ from the page.
           def remove(*ids)
-            record &quot;#{javascript_object_for(ids)}.each(Element.remove)&quot;
+            loop_on_multiple_args 'Element.remove', ids
           end
           
           # Shows hidden DOM elements with the given +ids+.
           def show(*ids)
-            call 'Element.show', *ids
+            loop_on_multiple_args 'Element.show', ids
           end
           
           # Hides the visible DOM elements with the given +ids+.
           def hide(*ids)
-            call 'Element.hide', *ids
+            loop_on_multiple_args 'Element.hide', ids           
           end
           
           # Toggles the visibility of the DOM elements with the given +ids+.
           def toggle(*ids)
-            call 'Element.toggle', *ids
+            loop_on_multiple_args 'Element.toggle', ids            
           end
           
           # Displays an alert dialog with the given +message+.
@@ -573,16 +530,18 @@ module ActionView
             call 'alert', message
           end
           
-          # Redirects the browser to the given +location+, in the same form as
-          # +url_for+.
+          # Redirects the browser to the given +location+, in the same form as +url_for+.
           def redirect_to(location)
             assign 'window.location.href', @context.url_for(location)
           end
           
-          # Calls the JavaScript +function+, optionally with the given 
-          # +arguments+.
-          def call(function, *arguments)
-            record &quot;#{function}(#{arguments_for_call(arguments)})&quot;
+          # Calls the JavaScript +function+, optionally with the given +arguments+.
+          #
+          # If a block is given, the block will be passed to a new JavaScriptGenerator;
+          # the resulting JavaScript code will then be wrapped inside &lt;tt&gt;function() { ... }&lt;/tt&gt; 
+          # and passed as the called function's final argument.
+          def call(function, *arguments, &amp;block)
+            record &quot;#{function}(#{arguments_for_call(arguments, block)})&quot;
           end
           
           # Assigns the JavaScript +variable+ the given +value+.
@@ -633,12 +592,18 @@ module ActionView
           end
           
           private
+            def loop_on_multiple_args(method, ids)
+              record(ids.size&gt;1 ? 
+                &quot;#{javascript_object_for(ids)}.each(#{method})&quot; : 
+                &quot;#{method}(#{ids.first.to_json})&quot;)
+            end
+              
             def page
               self
             end
           
             def record(line)
-              returning line = &quot;#{line.to_s.chomp.gsub /\;$/, ''};&quot; do
+              returning line = &quot;#{line.to_s.chomp.gsub(/\;\z/, '')};&quot; do
                 self &lt;&lt; line
               end
             end
@@ -653,10 +618,16 @@ module ActionView
               object.respond_to?(:to_json) ? object.to_json : object.inspect
             end
           
-            def arguments_for_call(arguments)
+            def arguments_for_call(arguments, block = nil)
+              arguments &lt;&lt; block_to_function(block) if block
               arguments.map { |argument| javascript_object_for(argument) }.join ', '
             end
             
+            def block_to_function(block)
+              generator = self.class.new(@context, &amp;block)
+              literal(&quot;function() { #{generator.to_s} }&quot;)
+            end  
+
             def method_missing(method, *arguments)
               JavaScriptProxy.new(self, method.to_s.camelize)
             end
@@ -673,8 +644,11 @@ module ActionView
       # Works like update_page but wraps the generated JavaScript in a &lt;script&gt;
       # tag. Use this to include generated JavaScript in an ERb template.
       # See JavaScriptGenerator for more information.
-      def update_page_tag(&amp;block)
-        javascript_tag update_page(&amp;block)
+      #
+      # +html_options+ may be a hash of &lt;script&gt; attributes to be passed
+      # to ActionView::Helpers::JavaScriptHelper#javascript_tag.  
+      def update_page_tag(html_options = {}, &amp;block)
+        javascript_tag update_page(&amp;block), html_options
       end
 
     protected
@@ -702,10 +676,10 @@ module ActionView
       end
     
       def build_observer(klass, name, options = {})
-        if options[:with] &amp;&amp; !options[:with].include?(&quot;=&quot;)
+        if options[:with] &amp;&amp; (options[:with] !~ /[=(.]/)
           options[:with] = &quot;'#{options[:with]}=' + value&quot;
         else
-          options[:with] ||= 'value' if options[:update]
+          options[:with] ||= 'value' unless options[:function]
         end
 
         callback = options[:function] || remote_function(options)
@@ -738,16 +712,16 @@ module ActionView
       end
 
       private
-        def method_missing(method, *arguments)
+        def method_missing(method, *arguments, &amp;block)
           if method.to_s =~ /(.*)=$/
             assign($1, arguments.first)
           else
-            call(&quot;#{method.to_s.camelize(:lower)}&quot;, *arguments)
+            call(&quot;#{method.to_s.camelize(:lower)}&quot;, *arguments, &amp;block)
           end
         end
       
-        def call(function, *arguments)
-          append_to_function_chain!(&quot;#{function}(#{@generator.send(:arguments_for_call, arguments)})&quot;)
+        def call(function, *arguments, &amp;block)
+          append_to_function_chain!(&quot;#{function}(#{@generator.send(:arguments_for_call, arguments, block)})&quot;)
           self
         end
 
@@ -756,7 +730,7 @@ module ActionView
         end
         
         def function_chain
-          @function_chain ||= @generator.instance_variable_get(&quot;@lines&quot;)
+          @function_chain ||= @generator.instance_variable_get(:@lines)
         end
         
         def append_to_function_chain!(call)
@@ -771,6 +745,21 @@ module ActionView
         super(generator, &quot;$(#{id.to_json})&quot;)
       end
       
+      # Allows access of element attributes through +attribute+. Examples:
+      #
+      #   page['foo']['style']                  # =&gt; $('foo').style;
+      #   page['foo']['style']['color']         # =&gt; $('blank_slate').style.color;
+      #   page['foo']['style']['color'] = 'red' # =&gt; $('blank_slate').style.color = 'red';
+      #   page['foo']['style'].color = 'red'    # =&gt; $('blank_slate').style.color = 'red';
+      def [](attribute)
+        append_to_function_chain!(attribute)
+        self
+      end
+      
+      def []=(variable, value)
+        assign(variable, value)
+      end
+      
       def replace_html(*options_for_render)
         call 'update', @generator.send(:render, *options_for_render)
       end
@@ -779,8 +768,8 @@ module ActionView
         call 'replace', @generator.send(:render, *options_for_render)
       end
       
-      def reload
-        replace :partial =&gt; @id.to_s
+      def reload(options_for_replace = {})
+        replace(options_for_replace.merge({ :partial =&gt; @id.to_s }))
       end
       
     end
@@ -811,8 +800,8 @@ module ActionView
     end
 
     class JavaScriptCollectionProxy &lt; JavaScriptProxy #:nodoc:
-      ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :find_all, :select, :max, :min, :partition, :reject, :sort_by]
-      ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each]
+      ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :find_all, :select, :max, :min, :partition, :reject, :sort_by] unless defined? ENUMERABLE_METHODS_WITH_RETURN
+      ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each] unless defined? ENUMERABLE_METHODS
       attr_reader :generator
       delegate :arguments_for_call, :to =&gt; :generator
 
@@ -899,3 +888,5 @@ module ActionView
     end
   end
 end
+
+require File.dirname(__FILE__) + '/javascript_helper'</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -69,6 +69,11 @@ module ActionView
       # containing the values of the ids of elements the sortable consists 
       # of, in the current order.
       #
+      # Important: For this to work, the sortable elements must have id
+      # attributes in the form &quot;string_identifier&quot;. For example, &quot;item_1&quot;. Only
+      # the identifier part of the id attribute will be serialized.
+      #
+      #
       # You can change the behaviour with various options, see
       # http://script.aculo.us for more documentation.
       def sortable_element(element_id, options = {})</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/scriptaculous_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,39 +2,87 @@ require 'cgi'
 require 'erb'
 
 module ActionView
-  module Helpers
-    # This is poor man's Builder for the rare cases where you need to programmatically make tags but can't use Builder.
+  module Helpers #:nodoc:
+    # Use these methods to generate HTML tags programmatically when you can't use
+    # a Builder. By default, they output XHTML compliant tags.
     module TagHelper
       include ERB::Util
 
-      # Examples:
-      # * &lt;tt&gt;tag(&quot;br&quot;) =&gt; &lt;br /&gt;&lt;/tt&gt;
-      # * &lt;tt&gt;tag(&quot;input&quot;, { &quot;type&quot; =&gt; &quot;text&quot;}) =&gt; &lt;input type=&quot;text&quot; /&gt;&lt;/tt&gt;
+      # Returns an empty HTML tag of type +name+ which by default is XHTML 
+      # compliant. Setting +open+ to true will create an open tag compatible 
+      # with HTML 4.0 and below. Add HTML attributes by passing an attributes 
+      # hash to +options+. For attributes with no value like (disabled and 
+      # readonly), give it a value of true in the +options+ hash. You can use
+      # symbols or strings for the attribute names.
+      #
+      #   tag(&quot;br&quot;)
+      #    # =&gt; &lt;br /&gt;
+      #   tag(&quot;br&quot;, nil, true)
+      #    # =&gt; &lt;br&gt;
+      #   tag(&quot;input&quot;, { :type =&gt; 'text', :disabled =&gt; true }) 
+      #    # =&gt; &lt;input type=&quot;text&quot; disabled=&quot;disabled&quot; /&gt;
       def tag(name, options = nil, open = false)
-        &quot;&lt;#{name}#{tag_options(options.stringify_keys) if options}&quot; + (open ? &quot;&gt;&quot; : &quot; /&gt;&quot;)
+        &quot;&lt;#{name}#{tag_options(options) if options}&quot; + (open ? &quot;&gt;&quot; : &quot; /&gt;&quot;)
       end
 
-      # Examples:
-      # * &lt;tt&gt;content_tag(&quot;p&quot;, &quot;Hello world!&quot;) =&gt; &lt;p&gt;Hello world!&lt;/p&gt;&lt;/tt&gt;
-      # * &lt;tt&gt;content_tag(&quot;div&quot;, content_tag(&quot;p&quot;, &quot;Hello world!&quot;), &quot;class&quot; =&gt; &quot;strong&quot;) =&gt; &lt;/tt&gt;
-      #   &lt;tt&gt;&lt;div class=&quot;strong&quot;&gt;&lt;p&gt;Hello world!&lt;/p&gt;&lt;/div&gt;&lt;/tt&gt;
-      def content_tag(name, content, options = nil)
-        &quot;&lt;#{name}#{tag_options(options.stringify_keys) if options}&gt;#{content}&lt;/#{name}&gt;&quot;
+      # Returns an HTML block tag of type +name+ surrounding the +content+. Add
+      # HTML attributes by passing an attributes hash to +options+. For attributes 
+      # with no value like (disabled and readonly), give it a value of true in 
+      # the +options+ hash. You can use symbols or strings for the attribute names.
+      #
+      #   content_tag(:p, &quot;Hello world!&quot;)
+      #    # =&gt; &lt;p&gt;Hello world!&lt;/p&gt;
+      #   content_tag(:div, content_tag(:p, &quot;Hello world!&quot;), :class =&gt; &quot;strong&quot;)
+      #    # =&gt; &lt;div class=&quot;strong&quot;&gt;&lt;p&gt;Hello world!&lt;/p&gt;&lt;/div&gt;
+      #   content_tag(&quot;select&quot;, options, :multiple =&gt; true)
+      #    # =&gt; &lt;select multiple=&quot;multiple&quot;&gt;...options...&lt;/select&gt;
+      #
+      # Instead of passing the content as an argument, you can also use a block
+      # in which case, you pass your +options+ as the second parameter.
+      #
+      #   &lt;% content_tag :div, :class =&gt; &quot;strong&quot; do -%&gt;
+      #     Hello world!
+      #   &lt;% end -%&gt;
+      #    # =&gt; &lt;div class=&quot;strong&quot;&gt;&lt;p&gt;Hello world!&lt;/p&gt;&lt;/div&gt;
+      def content_tag(name, content_or_options_with_block = nil, options = nil, &amp;block)
+        if block_given?
+          options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
+          content = capture(&amp;block)
+          concat(content_tag_string(name, content, options), block.binding)
+        else
+          content = content_or_options_with_block
+          content_tag_string(name, content, options)
+        end
       end
 
-      # Returns a CDATA section for the given +content+.  CDATA sections
+      # Returns a CDATA section with the given +content+.  CDATA sections
       # are used to escape blocks of text containing characters which would
       # otherwise be recognized as markup. CDATA sections begin with the string
-      # &lt;tt&gt;&amp;lt;![CDATA[&lt;/tt&gt; and end with (and may not contain) the string 
-      # &lt;tt&gt;]]&gt;&lt;/tt&gt;. 
+      # &lt;tt&gt;&lt;![CDATA[&lt;/tt&gt; and end with (and may not contain) the string &lt;tt&gt;]]&gt;&lt;/tt&gt;.
+      #
+      #   cdata_section(&quot;&lt;hello world&gt;&quot;)
+      #    # =&gt; &lt;![CDATA[&lt;hello world&gt;]]&gt;
       def cdata_section(content)
         &quot;&lt;![CDATA[#{content}]]&gt;&quot;
       end
 
+      # Returns the escaped +html+ without affecting existing escaped entities.
+      #
+      #   escape_once(&quot;1 &gt; 2 &amp;amp; 3&quot;)
+      #    # =&gt; &quot;1 &amp;lt; 2 &amp;amp; 3&quot;
+      def escape_once(html)
+        fix_double_escape(html_escape(html.to_s))
+      end
+
       private
+        def content_tag_string(name, content, options)
+          tag_options = options ? tag_options(options) : &quot;&quot;
+          &quot;&lt;#{name}#{tag_options}&gt;#{content}&lt;/#{name}&gt;&quot;
+        end
+      
         def tag_options(options)
           cleaned_options = convert_booleans(options.stringify_keys.reject {|key, value| value.nil?})
-          ' ' + cleaned_options.map {|key, value| %(#{key}=&quot;#{html_escape(value.to_s)}&quot;)}.sort * ' ' unless cleaned_options.empty?
+          ' ' + cleaned_options.map {|key, value| %(#{key}=&quot;#{escape_once(value)}&quot;)}.sort * ' ' unless cleaned_options.empty?
         end
 
         def convert_booleans(options)
@@ -45,6 +93,11 @@ module ActionView
         def boolean_attribute(options, attribute)
           options[attribute] ? options[attribute] = attribute : options.delete(attribute)
         end
+        
+        # Fix double-escaped entities, such as &amp;amp;amp;, &amp;amp;#123;, etc.
+        def fix_double_escape(escaped)
+          escaped.gsub(/&amp;amp;([a-z]+|(#\d+));/i) { &quot;&amp;#{$1};&quot; }
+        end
     end
   end
 end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/tag_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,65 +2,90 @@ require File.dirname(__FILE__) + '/tag_helper'
 
 module ActionView
   module Helpers #:nodoc:
-    # Provides a set of methods for working with text strings that can help unburden the level of inline Ruby code in the
-    # templates. In the example below we iterate over a collection of posts provided to the template and print each title
-    # after making sure it doesn't run longer than 20 characters:
-    #   &lt;% for post in @posts %&gt;
-    #     Title: &lt;%= truncate(post.title, 20) %&gt;
+    # The TextHelper Module provides a set of methods for filtering, formatting 
+    # and transforming strings that can reduce the amount of inline Ruby code in 
+    # your views. These helper methods extend ActionView making them callable 
+    # within your template files as shown in the following example which truncates
+    # the title of each post to 10 characters.
+    #
+    #   &lt;% @posts.each do |post| %&gt;
+    #     # post == 'This is my title'
+    #     Title: &lt;%= truncate(post.title, 10) %&gt;
     #   &lt;% end %&gt;
+    #    =&gt; Title: This is my...
     module TextHelper      
-      # The regular puts and print are outlawed in eRuby. It's recommended to use the &lt;%= &quot;hello&quot; %&gt; form instead of print &quot;hello&quot;.
-      # If you absolutely must use a method-based output, you can use concat. It's used like this: &lt;% concat &quot;hello&quot;, binding %&gt;. Notice that
-      # it doesn't have an equal sign in front. Using &lt;%= concat &quot;hello&quot; %&gt; would result in a double hello.
+      # The preferred method of outputting text in your views is to use the 
+      # &lt;%= &quot;text&quot; %&gt; eRuby syntax. The regular _puts_ and _print_ methods 
+      # do not operate as expected in an eRuby code block. If you absolutely must 
+      # output text within a code block, you can use the concat method.
+      #
+      #   &lt;% concat &quot;hello&quot;, binding %&gt;
+      # is equivalent to using:
+      #   &lt;%= &quot;hello&quot; %&gt;
       def concat(string, binding)
         eval(&quot;_erbout&quot;, binding).concat(string)
       end
 
-      # Truncates +text+ to the length of +length+ and replaces the last three characters with the +truncate_string+
-      # if the +text+ is longer than +length+.
+      # If +text+ is longer than +length+, +text+ will be truncated to the length of 
+      # +length+ and the last three characters will be replaced with the +truncate_string+.
+      #
+      #   truncate(&quot;Once upon a time in a world far far away&quot;, 14)  
+      #    =&gt; Once upon a...
       def truncate(text, length = 30, truncate_string = &quot;...&quot;)
         if text.nil? then return end
-        l = length - truncate_string.length
-        if $KCODE == &quot;NONE&quot;
-          text.length &gt; length ? text[0...l] + truncate_string : text
-        else
-          chars = text.split(//)
-          chars.length &gt; length ? chars[0...l].join + truncate_string : text
-        end
+        l = length - truncate_string.chars.length
+        text.chars.length &gt; length ? text.chars[0...l] + truncate_string : text
       end
 
-      # Highlights the +phrase+ where it is found in the +text+ by surrounding it like
-      # &lt;strong class=&quot;highlight&quot;&gt;I'm a highlight phrase&lt;/strong&gt;. The highlighter can be specialized by
-      # passing +highlighter+ as single-quoted string with \1 where the phrase is supposed to be inserted.
-      # N.B.: The +phrase+ is sanitized to include only letters, digits, and spaces before use.
+      # Highlights +phrase+ everywhere it is found in +text+ by inserting it into
+      # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+ 
+      # as a single-quoted string with \1 where the phrase is to be inserted.
+      #
+      #   highlight('You searched for: rails', 'rails')  
+      #    =&gt; You searched for: &lt;strong class=&quot;highlight&quot;&gt;rails&lt;/strong&gt;
       def highlight(text, phrase, highlighter = '&lt;strong class=&quot;highlight&quot;&gt;\1&lt;/strong&gt;')
         if phrase.blank? then return text end
         text.gsub(/(#{Regexp.escape(phrase)})/i, highlighter) unless text.nil?
       end
 
-      # Extracts an excerpt from the +text+ surrounding the +phrase+ with a number of characters on each side determined
-      # by +radius+. If the phrase isn't found, nil is returned. Ex:
-      #   excerpt(&quot;hello my world&quot;, &quot;my&quot;, 3) =&gt; &quot;...lo my wo...&quot;
+      # Extracts an excerpt from +text+ that matches the first instance of +phrase+. 
+      # The +radius+ expands the excerpt on each side of +phrase+ by the number of characters
+      # defined in +radius+. If the excerpt radius overflows the beginning or end of the +text+,
+      # then the +excerpt_string+ will be prepended/appended accordingly. If the +phrase+ 
+      # isn't found, nil is returned.
+      #
+      #   excerpt('This is an example', 'an', 5) 
+      #    =&gt; &quot;...s is an examp...&quot;
+      #
+      #   excerpt('This is an example', 'is', 5) 
+      #    =&gt; &quot;This is an...&quot;
       def excerpt(text, phrase, radius = 100, excerpt_string = &quot;...&quot;)
         if text.nil? || phrase.nil? then return end
         phrase = Regexp.escape(phrase)
 
-        if found_pos = text =~ /(#{phrase})/i
+        if found_pos = text.chars =~ /(#{phrase})/i
           start_pos = [ found_pos - radius, 0 ].max
-          end_pos   = [ found_pos + phrase.length + radius, text.length ].min
+          end_pos   = [ found_pos + phrase.chars.length + radius, text.chars.length ].min
 
           prefix  = start_pos &gt; 0 ? excerpt_string : &quot;&quot;
-          postfix = end_pos &lt; text.length ? excerpt_string : &quot;&quot;
+          postfix = end_pos &lt; text.chars.length ? excerpt_string : &quot;&quot;
 
-          prefix + text[start_pos..end_pos].strip + postfix
+          prefix + text.chars[start_pos..end_pos].strip + postfix
         else
           nil
         end
       end
 
-      # Attempts to pluralize the +singular+ word unless +count+ is 1. See source for pluralization rules.
+      # Attempts to pluralize the +singular+ word unless +count+ is 1. If +plural+
+      # is supplied, it will use that when count is &gt; 1, if the ActiveSupport Inflector 
+      # is loaded, it will use the Inflector to determine the plural form, otherwise 
+      # it will just add an 's' to the +singular+ word.
+      #
+      #   pluralize(1, 'person')  =&gt; 1 person
+      #   pluralize(2, 'person')  =&gt; 2 people
+      #   pluralize(3, 'person', 'users')  =&gt; 3 users
       def pluralize(count, singular, plural = nil)
-         &quot;#{count} &quot; + if count == 1
+         &quot;#{count} &quot; + if count == 1 || count == '1'
           singular
         elsif plural
           plural
@@ -71,7 +96,11 @@ module ActionView
         end
       end
 
-      # Word wrap long lines to line_width.
+      # Wraps the +text+ into lines no longer than +line_width+ width. This method
+      # breaks on the first whitespace character that does not exceed +line_width+.
+      #
+      #   word_wrap('Once upon a time', 4)
+      #    =&gt; Once\nupon\na\ntime
       def word_wrap(text, line_width = 80)
         text.gsub(/\n/, &quot;\n\n&quot;).gsub(/(.{1,#{line_width}})(\s+|$)/, &quot;\\1\n&quot;).strip
       end
@@ -79,8 +108,9 @@ module ActionView
       begin
         require_library_or_gem &quot;redcloth&quot; unless Object.const_defined?(:RedCloth)
 
-        # Returns the text with all the Textile codes turned into HTML-tags.
-        # &lt;i&gt;This method is only available if RedCloth can be required&lt;/i&gt;.
+        # Returns the text with all the Textile codes turned into HTML tags.
+        # &lt;i&gt;This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
+        # is available&lt;/i&gt;.
         def textilize(text)
           if text.blank?
             &quot;&quot;
@@ -91,8 +121,10 @@ module ActionView
           end
         end
 
-        # Returns the text with all the Textile codes turned into HTML-tags, but without the regular bounding &lt;p&gt; tag.
-        # &lt;i&gt;This method is only available if RedCloth can be required&lt;/i&gt;.
+        # Returns the text with all the Textile codes turned into HTML tags, 
+        # but without the bounding &lt;p&gt; tag that RedCloth adds.
+        # &lt;i&gt;This method is only available if RedCloth[http://whytheluckystiff.net/ruby/redcloth/]
+        # is available&lt;/i&gt;.
         def textilize_without_paragraph(text)
           textiled = textilize(text)
           if textiled[0..2] == &quot;&lt;p&gt;&quot; then textiled = textiled[3..-1] end
@@ -106,8 +138,9 @@ module ActionView
       begin
         require_library_or_gem &quot;bluecloth&quot; unless Object.const_defined?(:BlueCloth)
 
-        # Returns the text with all the Markdown codes turned into HTML-tags.
-        # &lt;i&gt;This method is only available if BlueCloth can be required&lt;/i&gt;.
+        # Returns the text with all the Markdown codes turned into HTML tags.
+        # &lt;i&gt;This method is only available if BlueCloth[http://www.deveiate.org/projects/BlueCloth]
+        # is available&lt;/i&gt;.
         def markdown(text)
           text.blank? ? &quot;&quot; : BlueCloth.new(text).to_html
         end
@@ -115,29 +148,30 @@ module ActionView
         # We can't really help what's not there
       end
       
-      # Returns +text+ transformed into HTML using very simple formatting rules
-      # Surrounds paragraphs with &lt;tt&gt;&lt;p&gt;&lt;/tt&gt; tags, and converts line breaks into &lt;tt&gt;&lt;br/&gt;&lt;/tt&gt;
-      # Two consecutive newlines(&lt;tt&gt;\n\n&lt;/tt&gt;) are considered as a paragraph, one newline (&lt;tt&gt;\n&lt;/tt&gt;) is
-      # considered a linebreak, three or more consecutive newlines are turned into two newlines 
+      # Returns +text+ transformed into HTML using simple formatting rules.
+      # Two or more consecutive newlines(&lt;tt&gt;\n\n&lt;/tt&gt;) are considered as a 
+      # paragraph and wrapped in &lt;tt&gt;&lt;p&gt;&lt;/tt&gt; tags. One newline (&lt;tt&gt;\n&lt;/tt&gt;) is
+      # considered as a linebreak and a &lt;tt&gt;&lt;br /&gt;&lt;/tt&gt; tag is appended. This
+      # method does not remove the newlines from the +text+. 
       def simple_format(text)
-        text.gsub!(/(\r\n|\n|\r)/, &quot;\n&quot;) # lets make them newlines crossplatform
-        text.gsub!(/\n\n+/, &quot;\n\n&quot;) # zap dupes
-        text.gsub!(/\n\n/, '&lt;/p&gt;\0&lt;p&gt;') # turn two newlines into paragraph
-        text.gsub!(/([^\n])(\n)([^\n])/, '\1\2&lt;br /&gt;\3') # turn single newline into &lt;br /&gt;
-        
-        content_tag(&quot;p&quot;, text)
+        content_tag 'p', text.to_s.
+          gsub(/\r\n?/, &quot;\n&quot;).                    # \r\n and \r -&gt; \n
+          gsub(/\n\n+/, &quot;&lt;/p&gt;\n\n&lt;p&gt;&quot;).           # 2+ newline  -&gt; paragraph
+          gsub(/([^\n]\n)(?=[^\n])/, '\1&lt;br /&gt;')  # 1 newline   -&gt; br
       end
 
-      # Turns all urls and email addresses into clickable links. The +link+ parameter can limit what should be linked.
-      # Options are &lt;tt&gt;:all&lt;/tt&gt; (default), &lt;tt&gt;:email_addresses&lt;/tt&gt;, and &lt;tt&gt;:urls&lt;/tt&gt;.
+      # Turns all urls and email addresses into clickable links. The +link+ parameter 
+      # will limit what should be linked. You can add html attributes to the links using
+      # +href_options+. Options for +link+ are &lt;tt&gt;:all&lt;/tt&gt; (default), 
+      # &lt;tt&gt;:email_addresses&lt;/tt&gt;, and &lt;tt&gt;:urls&lt;/tt&gt;.
       #
-      # Example:
-      #   auto_link(&quot;Go to http://www.rubyonrails.com and say hello to david@loudthinking.com&quot;) =&gt;
-      #     Go to &lt;a href=&quot;http://www.rubyonrails.com&quot;&gt;http://www.rubyonrails.com&lt;/a&gt; and
+      #   auto_link(&quot;Go to http://www.rubyonrails.org and say hello to david@loudthinking.com&quot;) =&gt;
+      #     Go to &lt;a href=&quot;http://www.rubyonrails.org&quot;&gt;http://www.rubyonrails.org&lt;/a&gt; and
       #     say hello to &lt;a href=&quot;mailto:david@loudthinking.com&quot;&gt;david@loudthinking.com&lt;/a&gt;
       #
       # If a block is given, each url and email address is yielded and the
-      # result is used as the link text.  Example:
+      # result is used as the link text.
+      #
       #   auto_link(post.body, :all, :target =&gt; '_blank') do |text|
       #     truncate(text, 15)
       #   end
@@ -150,9 +184,12 @@ module ActionView
         end
       end
 
-      # Turns all links into words, like &quot;&lt;a href=&quot;something&quot;&gt;else&lt;/a&gt;&quot; to &quot;else&quot;.
+      # Strips link tags from +text+ leaving just the link label.
+      #
+      #   strip_links('&lt;a href=&quot;http://www.rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;')
+      #    =&gt; Ruby on Rails
       def strip_links(text)
-        text.gsub(/&lt;a.*&gt;(.*)&lt;\/a&gt;/m, '\1')
+        text.gsub(/&lt;a\b.*?&gt;(.*?)&lt;\/a&gt;/mi, '\1')
       end
 
       # Try to require the html-scanner library
@@ -161,22 +198,26 @@ module ActionView
         require 'html/node'
       rescue LoadError
         # if there isn't a copy installed, use the vendor version in
-        # action controller
+        # ActionController
         $:.unshift File.join(File.dirname(__FILE__), &quot;..&quot;, &quot;..&quot;,
                       &quot;action_controller&quot;, &quot;vendor&quot;, &quot;html-scanner&quot;)
         require 'html/tokenizer'
         require 'html/node'
       end
 
-      VERBOTEN_TAGS = %w(form script) unless defined?(VERBOTEN_TAGS)
+      VERBOTEN_TAGS = %w(form script plaintext) unless defined?(VERBOTEN_TAGS)
       VERBOTEN_ATTRS = /^on/i unless defined?(VERBOTEN_ATTRS)
 
-      # Sanitizes the given HTML by making form and script tags into regular
+      # Sanitizes the +html+ by converting &lt;form&gt; and &lt;script&gt; tags into regular
       # text, and removing all &quot;onxxx&quot; attributes (so that arbitrary Javascript
-      # cannot be executed). Also removes href attributes that start with
-      # &quot;javascript:&quot;.
+      # cannot be executed). It also removes href= and src= attributes that start with
+      # &quot;javascript:&quot;. You can modify what gets sanitized by defining VERBOTEN_TAGS
+      # and VERBOTEN_ATTRS before this Module is loaded.
       #
-      # Returns the sanitized text.
+      #   sanitize('&lt;script&gt; do_nasty_stuff() &lt;/script&gt;')
+      #    =&gt; &amp;lt;script&gt; do_nasty_stuff() &amp;lt;/script&gt;
+      #   sanitize('&lt;a href=&quot;javascript: sucker();&quot;&gt;Click here for $100&lt;/a&gt;')
+      #    =&gt; &lt;a&gt;Click here for $100&lt;/a&gt;
       def sanitize(html)
         # only do this if absolutely necessary
         if html.index(&quot;&lt;&quot;)
@@ -192,8 +233,8 @@ module ActionView
                 else
                   if node.closing != :close
                     node.attributes.delete_if { |attr,v| attr =~ VERBOTEN_ATTRS }
-                    if node.attributes[&quot;href&quot;] =~ /^javascript:/i
-                      node.attributes.delete &quot;href&quot;
+                    %w(href src).each do |attr|
+                      node.attributes.delete attr if node.attributes[attr] =~ /^javascript:/i
                     end
                   end
                   node.to_s
@@ -209,11 +250,11 @@ module ActionView
         html
       end
       
-      # Strips all HTML tags from the input, including comments.  This uses the html-scanner
-      # tokenizer and so it's HTML parsing ability is limited by that of html-scanner.
-      #
-      # Returns the tag free text.
-      def strip_tags(html)
+      # Strips all HTML tags from the +html+, including comments.  This uses the 
+      # html-scanner tokenizer and so its HTML parsing ability is limited by 
+      # that of html-scanner.
+      def strip_tags(html)     
+        return html if html.blank?
         if html.index(&quot;&lt;&quot;)
           text = &quot;&quot;
           tokenizer = HTML::Tokenizer.new(html)
@@ -231,32 +272,33 @@ module ActionView
         end 
       end
       
-      # Returns a Cycle object whose to_s value cycles through items of an
-      # array every time it is called. This can be used to alternate classes
-      # for table rows:
+      # Creates a Cycle object whose _to_s_ method cycles through elements of an
+      # array every time it is called. This can be used for example, to alternate 
+      # classes for table rows:
       #
-      #   &lt;%- for item in @items do -%&gt;
-      #     &lt;tr class=&quot;&lt;%= cycle(&quot;even&quot;, &quot;odd&quot;) %&gt;&quot;&gt;
-      #       ... use item ...
+      #   &lt;% @items.each do |item| %&gt;
+      #     &lt;tr class=&quot;&lt;%= cycle(&quot;even&quot;, &quot;odd&quot;) -%&gt;&quot;&gt;
+      #       &lt;td&gt;item&lt;/td&gt;
       #     &lt;/tr&gt;
-      #   &lt;%- end -%&gt;
+      #   &lt;% end %&gt;
       #
-      # You can use named cycles to prevent clashes in nested loops.  You'll
-      # have to reset the inner cycle, manually:
+      # You can use named cycles to allow nesting in loops.  Passing a Hash as 
+      # the last parameter with a &lt;tt&gt;:name&lt;/tt&gt; key will create a named cycle.
+      # You can manually reset a cycle by calling reset_cycle and passing the 
+      # name of the cycle.
       #
-      #   &lt;%- for item in @items do -%&gt;
+      #   &lt;% @items.each do |item| %&gt;
       #     &lt;tr class=&quot;&lt;%= cycle(&quot;even&quot;, &quot;odd&quot;, :name =&gt; &quot;row_class&quot;)
       #       &lt;td&gt;
-      #         &lt;%- for value in item.values do -%&gt;
-      #           &lt;span style=&quot;color:'&lt;%= cycle(&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;
-      #                                         :name =&gt; &quot;colors&quot;) %&gt;'&quot;&gt;
-      #             item
+      #         &lt;% item.values.each do |value| %&gt;
+      #           &lt;span style=&quot;color:&lt;%= cycle(&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;, :name =&gt; &quot;colors&quot;) -%&gt;&quot;&gt;
+      #             value
       #           &lt;/span&gt;
-      #         &lt;%- end -%&gt;
-      #         &lt;%- reset_cycle(&quot;colors&quot;) -%&gt;
+      #         &lt;% end %&gt;
+      #         &lt;% reset_cycle(&quot;colors&quot;) %&gt;
       #       &lt;/td&gt;
       #    &lt;/tr&gt;
-      #  &lt;%- end -%&gt;
+      #  &lt;% end %&gt;
       def cycle(first_value, *values)
         if (values.last.instance_of? Hash)
           params = values.pop
@@ -273,12 +315,11 @@ module ActionView
         return cycle.to_s
       end
       
-      # Resets a cycle so that it starts from the first element in the array
-      # the next time it is used.
+      # Resets a cycle so that it starts from the first element the next time 
+      # it is called. Pass in +name+ to reset a named cycle.
       def reset_cycle(name = &quot;default&quot;)
         cycle = get_cycle(name)
-        return if cycle.nil?
-        cycle.reset
+        cycle.reset unless cycle.nil?
       end
 
       class Cycle #:nodoc:
@@ -305,42 +346,42 @@ module ActionView
         # guaranteed to be reset every time a page is rendered, so it
         # uses an instance variable of ActionView::Base.
         def get_cycle(name)
-          @_cycles = Hash.new if @_cycles.nil?
+          @_cycles = Hash.new unless defined?(@_cycles)
           return @_cycles[name]
         end
         
         def set_cycle(name, cycle_object)
-          @_cycles = Hash.new if @_cycles.nil?
+          @_cycles = Hash.new unless defined?(@_cycles)
           @_cycles[name] = cycle_object
         end
-      
-        AUTO_LINK_RE = /
-                        (                       # leading text
-                          &lt;\w+.*?&gt;|             #   leading HTML tag, or
-                          [^=!:'&quot;\/]|           #   leading punctuation, or 
-                          ^                     #   beginning of line
+
+        AUTO_LINK_RE = %r{
+                        (                          # leading text
+                          &lt;\w+.*?&gt;|                # leading HTML tag, or
+                          [^=!:'&quot;/]|               # leading punctuation, or 
+                          ^                        # beginning of line
                         )
                         (
-                          (?:http[s]?:\/\/)|    # protocol spec, or
-                          (?:www\.)             # www.*
+                          (?:https?://)|           # protocol spec, or
+                          (?:www\.)                # www.*
                         ) 
                         (
-                          ([\w]+:?[=?&amp;\/.-]?)*    # url segment
-                          \w+[\/]?              # url tail
-                          (?:\#\w*)?            # trailing anchor
+                          [-\w]+                   # subdomain or domain
+                          (?:\.[-\w]+)*            # remaining subdomains or domain
+                          (?::\d+)?                # port
+                          (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
+                          (?:\?[\w\+%&amp;=.;-]+)?     # query string
+                          (?:\#[\w\-]*)?           # trailing anchor
                         )
-                        ([[:punct:]]|\s|&lt;|$)    # trailing text
-                       /x unless const_defined?(:AUTO_LINK_RE)
+                        ([[:punct:]]|\s|&lt;|$)       # trailing text
+                       }x unless const_defined?(:AUTO_LINK_RE)
 
         # Turns all urls into clickable links.  If a block is given, each url
-        # is yielded and the result is used as the link text.  Example:
-        #   auto_link_urls(post.body, :all, :target =&gt; '_blank') do |text|
-        #     truncate(text, 15)
-        #   end
+        # is yielded and the result is used as the link text.
         def auto_link_urls(text, href_options = {})
           extra_options = tag_options(href_options.stringify_keys) || &quot;&quot;
           text.gsub(AUTO_LINK_RE) do
-            all, a, b, c, d = $&amp;, $1, $2, $3, $5
+            all, a, b, c, d = $&amp;, $1, $2, $3, $4
             if a =~ /&lt;a\s/i # don't replace URL's that are already linked
               all
             else
@@ -353,10 +394,6 @@ module ActionView
 
         # Turns all email addresses into clickable links.  If a block is given,
         # each email is yielded and the result is used as the link text.
-        # Example:
-        #   auto_link_email_addresses(post.body) do |text|
-        #     truncate(text, 15)
-        #   end
         def auto_link_email_addresses(text)
           text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
             text = $1</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/text_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,22 +1,21 @@
 require File.dirname(__FILE__) + '/javascript_helper'
 
 module ActionView
-  module Helpers
-    # Provides a set of methods for making easy links and getting urls that depend on the controller and action. This means that
-    # you can use the same format for links in the views that you do in the controller. The different methods are even named
-    # synchronously, so link_to uses that same url as is generated by url_for, which again is the same url used for
-    # redirection in redirect_to.
+  module Helpers #:nodoc:
+    # Provides a set of methods for making easy links and getting urls that 
+    # depend on the controller and action. This means that you can use the 
+    # same format for links in the views that you do in the controller.
     module UrlHelper
       include JavaScriptHelper
       
-      # Returns the URL for the set of +options+ provided. This takes the same options 
-      # as url_for. For a list, see the documentation for ActionController::Base#url_for.
-      # Note that it'll set :only_path =&gt; true so you'll get /controller/action instead of the 
-      # http://example.com/controller/action part (makes it harder to parse httpd log files)
-      # 
-      # When called from a view, url_for returns an HTML escaped url. If you need an unescaped
-      # url, pass :escape =&gt; false to url_for.
-      # 
+      # Returns the URL for the set of +options+ provided. This takes the 
+      # same options as url_for in action controller. For a list, see the 
+      # documentation for ActionController::Base#url_for. Note that it'll 
+      # set :only_path =&gt; true so you'll get the relative /controller/action 
+      # instead of the fully qualified http://example.com/controller/action.
+      #  
+      # When called from a view, url_for returns an HTML escaped url. If you 
+      # need an unescaped url, pass :escape =&gt; false in the +options+.
       def url_for(options = {}, *parameters_for_method_reference)
         if options.kind_of? Hash
           options = { :only_path =&gt; true }.update(options.symbolize_keys)
@@ -24,30 +23,46 @@ module ActionView
         else
           escape = true
         end
+
         url = @controller.send(:url_for, options, *parameters_for_method_reference)
         escape ? html_escape(url) : url
       end
 
-      # Creates a link tag of the given +name+ using an URL created by the set of +options+. See the valid options in
-      # the documentation for ActionController::Base#url_for. It's also possible to pass a string instead of an options hash to
-      # get a link tag that just points without consideration. If nil is passed as a name, the link itself will become the name.
+      # Creates a link tag of the given +name+ using a URL created by the set 
+      # of +options+. See the valid options in the documentation for 
+      # ActionController::Base#url_for. It's also possible to pass a string instead 
+      # of an options hash to get a link tag that uses the value of the string as the 
+      # href for the link. If nil is passed as a name, the link itself will become 
+      # the name.
       #
-      # The html_options has three special features. One for creating javascript confirm alerts where if you pass :confirm =&gt; 'Are you sure?',
-      # the link will be guarded with a JS popup asking that question. If the user accepts, the link is processed, otherwise not.
+      # The +html_options+ will accept a hash of html attributes for the link tag.
+      # It also accepts 3 modifiers that specialize the link behavior. 
       #
-      # Another for creating a popup window, which is done by either passing :popup with true or the options of the window in 
-      # Javascript form.
+      # * &lt;tt&gt;:confirm =&gt; 'question?'&lt;/tt&gt;: This will add a JavaScript confirm 
+      #   prompt with the question specified. If the user accepts, the link is 
+      #   processed normally, otherwise no action is taken.
+      # * &lt;tt&gt;:popup =&gt; true || array of window options&lt;/tt&gt;: This will force the 
+      #   link to open in a popup window. By passing true, a default browser window 
+      #   will be opened with the URL. You can also specify an array of options 
+      #   that are passed-thru to JavaScripts window.open method.
+      # * &lt;tt&gt;:method =&gt; symbol of HTTP verb&lt;/tt&gt;: This modifier will dynamically
+      #   create an HTML form and immediately submit the form for processing using 
+      #   the HTTP verb specified. Useful for having links perform a POST operation
+      #   in dangerous actions like deleting a record (which search bots can follow
+      #   while spidering your site). Supported verbs are :post, :delete and :put.
+      #   Note that if the user has JavaScript disabled, the request will fall back 
+      #   to using GET. If you are relying on the POST behavior, your should check
+      #   for it in your controllers action by using the request objects methods
+      #   for post?, delete? or put?.
       #
-      # And a third for making the link do a POST request (instead of the regular GET) through a dynamically added form element that
-      # is instantly submitted. Note that if the user has turned off Javascript, the request will fall back on the GET. So its
-      # your responsibility to determine what the action should be once it arrives at the controller. The POST form is turned on by
-      # passing :post as true. Note, it's not possible to use POST requests and popup targets at the same time (an exception will be thrown).
+      # You can mix and match the +html_options+ with the exception of
+      # :popup and :method which will raise an ActionView::ActionViewError
+      # exception.
       #
-      # Examples:
-      #   link_to &quot;Delete this page&quot;, { :action =&gt; &quot;destroy&quot;, :id =&gt; @page.id }, :confirm =&gt; &quot;Are you sure?&quot;
+      #   link_to &quot;Visit Other Site&quot;, &quot;http://www.rubyonrails.org/&quot;, :confirm =&gt; &quot;Are you sure?&quot;
       #   link_to &quot;Help&quot;, { :action =&gt; &quot;help&quot; }, :popup =&gt; true
-      #   link_to &quot;Busy loop&quot;, { :action =&gt; &quot;busy&quot; }, :popup =&gt; ['new_window', 'height=300,width=600']
-      #   link_to &quot;Destroy account&quot;, { :action =&gt; &quot;destroy&quot; }, :confirm =&gt; &quot;Are you sure?&quot;, :post =&gt; true
+      #   link_to &quot;View Image&quot;, { :action =&gt; &quot;view&quot; }, :popup =&gt; ['new_window_name', 'height=300,width=600']
+      #   link_to &quot;Delete Image&quot;, { :action =&gt; &quot;delete&quot;, :id =&gt; @image.id }, :confirm =&gt; &quot;Are you sure?&quot;, :method =&gt; :delete
       def link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)
         if html_options
           html_options = html_options.stringify_keys
@@ -56,76 +71,77 @@ module ActionView
         else
           tag_options = nil
         end
+
         url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
         &quot;&lt;a href=\&quot;#{url}\&quot;#{tag_options}&gt;#{name || url}&lt;/a&gt;&quot;
       end
 
-      # Generates a form containing a sole button that submits to the
-      # URL given by _options_.  Use this method instead of +link_to+
-      # for actions that do not have the safe HTTP GET semantics
-      # implied by using a hypertext link.
+      # Generates a form containing a single button that submits to the URL created
+      # by the set of +options+. This is the safest method to ensure links that
+      # cause changes to your data are not triggered by search bots or accelerators.
+      # If the HTML button does not work with your layout, you can also consider
+      # using the link_to method with the &lt;tt&gt;:method&lt;/tt&gt; modifier as described in
+      # the link_to documentation.
       #
-      # The parameters are the same as for +link_to+.  Any _html_options_
-      # that you pass will be applied to the inner +input+ element.
-      # In particular, pass
-      # 
-      #   :disabled =&gt; true/false
-      #
-      # as part of _html_options_ to control whether the button is
-      # disabled.  The generated form element is given the class
-      # 'button-to', to which you can attach CSS styles for display
-      # purposes.
-      #
-      # Example 1:
+      # The generated FORM element has a class name of &lt;tt&gt;button-to&lt;/tt&gt;
+      # to allow styling of the form itself and its children. You can control
+      # the form submission and input element behavior using +html_options+.
+      # This method accepts the &lt;tt&gt;:method&lt;/tt&gt; and &lt;tt&gt;:confirm&lt;/tt&gt; modifiers
+      # described in the link_to documentation. If no &lt;tt&gt;:method&lt;/tt&gt; modifier
+      # is given, it will default to performing a POST operation. You can also 
+      # disable the button by passing &lt;tt&gt;:disabled =&gt; true&lt;/tt&gt; in +html_options+.
       #
-      #   # inside of controller for &quot;feeds&quot;
-      #   button_to &quot;Edit&quot;, :action =&gt; 'edit', :id =&gt; 3
+      #   button_to &quot;New&quot;, :action =&gt; &quot;new&quot;
       #
-      # Generates the following HTML (sans formatting):
+      # Generates the following HTML:
       #
-      #   &lt;form method=&quot;post&quot; action=&quot;/feeds/edit/3&quot; class=&quot;button-to&quot;&gt;
-      #     &lt;div&gt;&lt;input value=&quot;Edit&quot; type=&quot;submit&quot; /&gt;&lt;/div&gt;
+      #   &lt;form method=&quot;post&quot; action=&quot;/controller/new&quot; class=&quot;button-to&quot;&gt;
+      #     &lt;div&gt;&lt;input value=&quot;New&quot; type=&quot;submit&quot; /&gt;&lt;/div&gt;
       #   &lt;/form&gt;
       #
-      # Example 2:
+      # If you are using RESTful routes, you can pass the &lt;tt&gt;:method&lt;/tt&gt;
+      # to change the HTTP verb used to submit the form.
       #
-      #   button_to &quot;Destroy&quot;, { :action =&gt; 'destroy', :id =&gt; 3 },
-      #             :confirm =&gt; &quot;Are you sure?&quot;
+      #   button_to &quot;Delete Image&quot;, { :action =&gt; &quot;delete&quot;, :id =&gt; @image.id },
+      #             :confirm =&gt; &quot;Are you sure?&quot;, :method =&gt; :delete
       #
-      # Generates the following HTML (sans formatting):
+      # Which generates the following HTML:
       #
-      #   &lt;form method=&quot;post&quot; action=&quot;/feeds/destroy/3&quot; class=&quot;button-to&quot;&gt;
-      #     &lt;div&gt;&lt;input onclick=&quot;return confirm('Are you sure?');&quot;
-      #                 value=&quot;Destroy&quot; type=&quot;submit&quot; /&gt;
+      #   &lt;form method=&quot;post&quot; action=&quot;/images/delete/1&quot; class=&quot;button-to&quot;&gt;
+      #     &lt;div&gt;
+      #       &lt;input type=&quot;hidden&quot; name=&quot;_method&quot; value=&quot;delete&quot; /&gt;
+      #       &lt;input onclick=&quot;return confirm('Are you sure?');&quot;
+      #                 value=&quot;Delete&quot; type=&quot;submit&quot; /&gt;
       #     &lt;/div&gt;
       #   &lt;/form&gt;
-      #
-      # *NOTE*: This method generates HTML code that represents a form.
-      # Forms are &quot;block&quot; content, which means that you should not try to
-      # insert them into your HTML where only inline content is expected.
-      # For example, you can legally insert a form inside of a +div+ or
-      # +td+ element or in between +p+ elements, but not in the middle of
-      # a run of text, nor can you place a form within another form.
-      # (Bottom line: Always validate your HTML before going public.)
-      def button_to(name, options = {}, html_options = nil)
-        html_options = (html_options || {}).stringify_keys
+      def button_to(name, options = {}, html_options = {})
+        html_options = html_options.stringify_keys
         convert_boolean_attributes!(html_options, %w( disabled ))
-        
+
+        method_tag = ''
+        if (method = html_options.delete('method')) &amp;&amp; %w{put delete}.include?(method.to_s)
+          method_tag = tag('input', :type =&gt; 'hidden', :name =&gt; '_method', :value =&gt; method.to_s)
+        end
+
+        form_method = method.to_s == 'get' ? 'get' : 'post'
+
         if confirm = html_options.delete(&quot;confirm&quot;)
           html_options[&quot;onclick&quot;] = &quot;return #{confirm_javascript_function(confirm)};&quot;
         end
-        
-        url = options.is_a?(String) ? options : url_for(options)
+
+        url = options.is_a?(String) ? options : self.url_for(options)
         name ||= url
-        
-        html_options.merge!(&quot;type&quot; =&gt; &quot;submit&quot;, &quot;value&quot; =&gt; name)
 
-        &quot;&lt;form method=\&quot;post\&quot; action=\&quot;#{h url}\&quot; class=\&quot;button-to\&quot;&gt;&lt;div&gt;&quot; +
-          tag(&quot;input&quot;, html_options) + &quot;&lt;/div&gt;&lt;/form&gt;&quot;
+        html_options.merge!(&quot;type&quot; =&gt; &quot;submit&quot;, &quot;value&quot; =&gt; name)
+        
+        &quot;&lt;form method=\&quot;#{form_method}\&quot; action=\&quot;#{escape_once url}\&quot; class=\&quot;button-to\&quot;&gt;&lt;div&gt;&quot; + 
+          method_tag + tag(&quot;input&quot;, html_options) + &quot;&lt;/div&gt;&lt;/form&gt;&quot;
       end
 
 
-      # This tag is deprecated. Combine the link_to and AssetTagHelper::image_tag yourself instead, like:
+      # DEPRECATED. It is reccommended to use the AssetTagHelper::image_tag within
+      # a link_to method to generate a linked image.
+      # 
       #   link_to(image_tag(&quot;rss&quot;, :size =&gt; &quot;30x45&quot;, :border =&gt; 0), &quot;http://www.example.com&quot;)
       def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_reference)
         image_options = { &quot;src&quot; =&gt; src.include?(&quot;/&quot;) ? src : &quot;/images/#{src}&quot; }
@@ -157,18 +173,42 @@ module ActionView
         link_to(tag(&quot;img&quot;, image_options), options, html_options, *parameters_for_method_reference)
       end
 
-      alias_method :link_to_image, :link_image_to # deprecated name
+      alias_method :link_to_image, :link_image_to
+      deprecate :link_to_image =&gt; &quot;use link_to(image_tag(...), url)&quot;,
+        :link_image_to =&gt; &quot;use link_to(image_tag(...), url)&quot;
 
-      # Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
-      # request uri is the same as the link's, in which case only the name is returned (or the
-      # given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
-      # to the page currently being viewed.
+      # Creates a link tag of the given +name+ using a URL created by the set of
+      # +options+ unless the current request uri is the same as the links, in 
+      # which case only the name is returned (or the given block is yielded, if
+      # one exists). Refer to the documentation for link_to_unless for block usage.
+      #
+      #   &lt;ul id=&quot;navbar&quot;&gt;
+      #     &lt;li&gt;&lt;%= link_to_unless_current(&quot;Home&quot;, { :action =&gt; &quot;index&quot; }) %&gt;&lt;/li&gt;
+      #     &lt;li&gt;&lt;%= link_to_unless_current(&quot;About Us&quot;, { :action =&gt; &quot;about&quot; }) %&gt;&lt;/li&gt;
+      #   &lt;/ul&gt;
+      #
+      # This will render the following HTML when on the about us page:
+      #
+      #   &lt;ul id=&quot;navbar&quot;&gt;
+      #     &lt;li&gt;&lt;a href=&quot;/controller/index&quot;&gt;Home&lt;/a&gt;&lt;/li&gt;
+      #     &lt;li&gt;About Us&lt;/li&gt;
+      #   &lt;/ul&gt;
       def link_to_unless_current(name, options = {}, html_options = {}, *parameters_for_method_reference, &amp;block)
         link_to_unless current_page?(options), name, options, html_options, *parameters_for_method_reference, &amp;block
       end
 
-      # Create a link tag of the given +name+ using an URL created by the set of +options+, unless +condition+
-      # is true, in which case only the name is returned (or the given block is yielded, if one exists). 
+      # Creates a link tag of the given +name+ using a URL created by the set of
+      # +options+ unless +condition+ is true, in which case only the name is 
+      # returned. To specialize the default behavior, you can pass a block that
+      # accepts the name or the full argument list for link_to_unless (see the example).
+      #
+      #   &lt;%= link_to_unless(@current_user.nil?, &quot;Reply&quot;, { :action =&gt; &quot;reply&quot; }) %&gt;
+      #
+      # This example uses a block to modify the link if the condition isn't met.
+      #
+      #   &lt;%= link_to_unless(@current_user.nil?, &quot;Reply&quot;, { :action =&gt; &quot;reply&quot; }) do |name|
+      #      link_to(name, { :controller =&gt; &quot;accounts&quot;, :action =&gt; &quot;signup&quot; })
+      #    end %&gt;     
       def link_to_unless(condition, name, options = {}, html_options = {}, *parameters_for_method_reference, &amp;block)
         if condition
           if block_given?
@@ -181,30 +221,56 @@ module ActionView
         end  
       end
       
-      # Create a link tag of the given +name+ using an URL created by the set of +options+, if +condition+
-      # is true, in which case only the name is returned (or the given block is yielded, if one exists). 
+      # Creates a link tag of the given +name+ using a URL created by the set of
+      # +options+ if +condition+ is true, in which case only the name is 
+      # returned. To specialize the default behavior, you can pass a block that
+      # accepts the name or the full argument list for link_to_unless (see the examples
+      # in link_to_unless).
       def link_to_if(condition, name, options = {}, html_options = {}, *parameters_for_method_reference, &amp;block)
         link_to_unless !condition, name, options, html_options, *parameters_for_method_reference, &amp;block
       end
 
-      # Creates a link tag for starting an email to the specified &lt;tt&gt;email_address&lt;/tt&gt;, which is also used as the name of the
-      # link unless +name+ is specified. Additional HTML options, such as class or id, can be passed in the &lt;tt&gt;html_options&lt;/tt&gt; hash.
+      # Creates a mailto link tag to the specified +email_address+, which is
+      # also used as the name of the link unless +name+ is specified. Additional
+      # html attributes for the link can be passed in +html_options+.
+      #
+      # mail_to has several methods for hindering email harvestors and customizing
+      # the email itself by passing special keys to +html_options+.
+      #
+      # Special HTML Options:
+      #
+      # * &lt;tt&gt;:encode&lt;/tt&gt;  - This key will accept the strings &quot;javascript&quot; or &quot;hex&quot;.
+      #   Passing &quot;javascript&quot; will dynamically create and encode the mailto: link then
+      #   eval it into the DOM of the page. This method will not show the link on
+      #   the page if the user has JavaScript disabled. Passing &quot;hex&quot; will hex
+      #   encode the +email_address+ before outputting the mailto: link.
+      # * &lt;tt&gt;:replace_at&lt;/tt&gt;  - When the link +name+ isn't provided, the
+      #   +email_address+ is used for the link label. You can use this option to
+      #   obfuscate the +email_address+ by substituting the @ sign with the string
+      #   given as the value.
+      # * &lt;tt&gt;:replace_dot&lt;/tt&gt;  - When the link +name+ isn't provided, the
+      #   +email_address+ is used for the link label. You can use this option to
+      #   obfuscate the +email_address+ by substituting the . in the email with the
+      #   string given as the value.
+      # * &lt;tt&gt;:subject&lt;/tt&gt;  - Preset the subject line of the email.
+      # * &lt;tt&gt;:body&lt;/tt&gt; - Preset the body of the email.
+      # * &lt;tt&gt;:cc&lt;/tt&gt;  - Carbon Copy addition recipients on the email.
+      # * &lt;tt&gt;:bcc&lt;/tt&gt;  - Blind Carbon Copy additional recipients on the email.
       #
-      # You can also make it difficult for spiders to harvest email address by obfuscating them.
       # Examples:
+      #   mail_to &quot;me@domain.com&quot;  # =&gt; &lt;a href=&quot;mailto:me@domain.com&quot;&gt;me@domain.com&lt;/a&gt;
       #   mail_to &quot;me@domain.com&quot;, &quot;My email&quot;, :encode =&gt; &quot;javascript&quot;  # =&gt;
-      #     &lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot;&gt;eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))&lt;/script&gt;
+      #     &lt;script type=&quot;text/javascript&quot;&gt;eval(unescape('%64%6f%63...%6d%65%6e'))&lt;/script&gt;
       #
       #   mail_to &quot;me@domain.com&quot;, &quot;My email&quot;, :encode =&gt; &quot;hex&quot;  # =&gt;
       #     &lt;a href=&quot;mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d&quot;&gt;My email&lt;/a&gt;
       #
-      # You can also specify the cc address, bcc address, subject, and body parts of the message header to create a complex e-mail using the
-      # corresponding +cc+, +bcc+, +subject+, and +body+ &lt;tt&gt;html_options&lt;/tt&gt; keys. Each of these options are URI escaped and then appended to
-      # the &lt;tt&gt;email_address&lt;/tt&gt; before being output. &lt;b&gt;Be aware that javascript keywords will not be escaped and may break this feature
-      # when encoding with javascript.&lt;/b&gt;
-      # Examples:
-      #   mail_to &quot;me@domain.com&quot;, &quot;My email&quot;, :cc =&gt; &quot;ccaddress@domain.com&quot;, :bcc =&gt; &quot;bccaddress@domain.com&quot;, :subject =&gt; &quot;This is an example email&quot;, :body =&gt; &quot;This is the body of the message.&quot;   # =&gt;
-      #     &lt;a href=&quot;mailto:me@domain.com?cc=&quot;ccaddress@domain.com&quot;&amp;bcc=&quot;bccaddress@domain.com&quot;&amp;body=&quot;This%20is%20the%20body%20of%20the%20message.&quot;&amp;subject=&quot;This%20is%20an%20example%20email&quot;&gt;My email&lt;/a&gt;
+      #   mail_to &quot;me@domain.com&quot;, nil, :replace_at =&gt; &quot;_at_&quot;, :replace_dot =&gt; &quot;_dot_&quot;, :class =&gt; &quot;email&quot;  # =&gt;
+      #     &lt;a href=&quot;mailto:me@domain.com&quot; class=&quot;email&quot;&gt;me_at_domain_dot_com&lt;/a&gt;
+      #
+      #   mail_to &quot;me@domain.com&quot;, &quot;My email&quot;, :cc =&gt; &quot;ccaddress@domain.com&quot;, 
+      #            :subject =&gt; &quot;This is an example email&quot;  # =&gt;
+      #     &lt;a href=&quot;mailto:me@domain.com?cc=ccaddress@domain.com&amp;subject=This%20is%20an%20example%20email&quot;&gt;My email&lt;/a&gt;
       def mail_to(email_address, name = nil, html_options = {})
         html_options = html_options.stringify_keys
         encode = html_options.delete(&quot;encode&quot;)
@@ -218,17 +284,19 @@ module ActionView
         extras &lt;&lt; &quot;subject=#{CGI.escape(subject).gsub(&quot;+&quot;, &quot;%20&quot;)}&amp;&quot; unless subject.nil?
         extras = &quot;?&quot; &lt;&lt; extras.gsub!(/&amp;?$/,&quot;&quot;) unless extras.empty?
 
+        email_address = email_address.to_s
+
         email_address_obfuscated = email_address.dup
         email_address_obfuscated.gsub!(/@/, html_options.delete(&quot;replace_at&quot;)) if html_options.has_key?(&quot;replace_at&quot;)
         email_address_obfuscated.gsub!(/\./, html_options.delete(&quot;replace_dot&quot;)) if html_options.has_key?(&quot;replace_dot&quot;)
 
-        if encode == 'javascript'
-          tmp = &quot;document.write('#{content_tag(&quot;a&quot;, name || email_address, html_options.merge({ &quot;href&quot; =&gt; &quot;mailto:&quot;+email_address.to_s+extras }))}');&quot;
+        if encode == &quot;javascript&quot;
+          tmp = &quot;document.write('#{content_tag(&quot;a&quot;, name || email_address, html_options.merge({ &quot;href&quot; =&gt; &quot;mailto:&quot;+email_address+extras }))}');&quot;
           for i in 0...tmp.length
             string &lt;&lt; sprintf(&quot;%%%x&quot;,tmp[i])
           end
           &quot;&lt;script type=\&quot;text/javascript\&quot;&gt;eval(unescape('#{string}'))&lt;/script&gt;&quot;
-        elsif encode == 'hex'
+        elsif encode == &quot;hex&quot;
           for i in 0...email_address.length
             if email_address[i,1] =~ /\w/
               string &lt;&lt; sprintf(&quot;%%%x&quot;,email_address[i])
@@ -242,26 +310,42 @@ module ActionView
         end
       end
 
-      # Returns true if the current page uri is generated by the options passed (in url_for format).
+      # True if the current request uri was generated by the given +options+.
       def current_page?(options)
-        CGI.escapeHTML(url_for(options)) == @controller.request.request_uri
+        url_string = CGI.escapeHTML(url_for(options))
+        request = @controller.request
+        if url_string =~ /^\w+:\/\//
+          url_string == &quot;#{request.protocol}#{request.host_with_port}#{request.request_uri}&quot;
+        else
+          url_string == request.request_uri
+        end
       end
 
       private
         def convert_options_to_javascript!(html_options)
-          confirm, popup, post = html_options.delete(&quot;confirm&quot;), html_options.delete(&quot;popup&quot;), html_options.delete(&quot;post&quot;)
+          confirm, popup = html_options.delete(&quot;confirm&quot;), html_options.delete(&quot;popup&quot;)
+
+          # post is deprecated, but if its specified and method is not, assume that method = :post
+          method, post   = html_options.delete(&quot;method&quot;), html_options.delete(&quot;post&quot;)
+          if !method &amp;&amp; post
+            ActiveSupport::Deprecation.warn(
+              &quot;Passing :post as a link modifier is deprecated. &quot; +
+              &quot;Use :method =&gt; \&quot;post\&quot; instead. :post will be removed in Rails 2.0.&quot;
+            )
+            method = :post
+          end
         
           html_options[&quot;onclick&quot;] = case
-            when popup &amp;&amp; post
+            when popup &amp;&amp; method
               raise ActionView::ActionViewError, &quot;You can't use :popup and :post in the same link&quot;
             when confirm &amp;&amp; popup
               &quot;if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;&quot;
-            when confirm &amp;&amp; post
-              &quot;if (#{confirm_javascript_function(confirm)}) { #{post_javascript_function} };return false;&quot;
+            when confirm &amp;&amp; method
+              &quot;if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;&quot;
             when confirm
               &quot;return #{confirm_javascript_function(confirm)};&quot;
-            when post
-              &quot;#{post_javascript_function}return false;&quot;
+            when method
+              &quot;#{method_javascript_function(method)}return false;&quot;
             when popup
               popup_javascript_function(popup) + 'return false;'
             else
@@ -277,8 +361,17 @@ module ActionView
           popup.is_a?(Array) ? &quot;window.open(this.href,'#{popup.first}','#{popup.last}');&quot; : &quot;window.open(this.href);&quot;
         end
         
-        def post_javascript_function
-          &quot;var f = document.createElement('form'); this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href; f.submit();&quot;
+        def method_javascript_function(method)
+          submit_function = 
+            &quot;var f = document.createElement('form'); f.style.display = 'none'; &quot; +
+            &quot;this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;&quot;
+          
+          unless method == :post
+            submit_function &lt;&lt; &quot;var m = document.createElement('input'); m.setAttribute('type', 'hidden'); &quot;
+            submit_function &lt;&lt; &quot;m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);&quot;
+          end
+          
+          submit_function &lt;&lt; &quot;f.submit();&quot;
         end
 
         # Processes the _html_options_ hash, converting the boolean</diff>
      <filename>vendor/rails/actionpack/lib/action_view/helpers/url_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,16 +6,22 @@ module ActionView
 
     attr_reader :original_exception
 
-    def initialize(base_path, file_name, assigns, source, original_exception)
-      @base_path, @assigns, @source, @original_exception = 
-        base_path, assigns, source, original_exception
-      @file_name = file_name
+    def initialize(base_path, file_path, assigns, source, original_exception)
+      @base_path, @assigns, @source, @original_exception =
+        base_path, assigns.dup, source, original_exception
+      @file_path = file_path
+
+      remove_deprecated_assigns!
     end
-  
+
     def message
-      original_exception.message
+      ActiveSupport::Deprecation.silence { original_exception.message }
+    end
+
+    def clean_backtrace
+      original_exception.clean_backtrace
     end
-  
+
     def sub_template_message
       if @sub_templates
         &quot;Trace of template inclusion: &quot; +
@@ -24,63 +30,81 @@ module ActionView
         &quot;&quot;
       end
     end
-  
-    def source_extract(indention = 0)
-      source_code = IO.readlines(@file_name)
-      
-      start_on_line = [ line_number - SOURCE_CODE_RADIUS - 1, 0 ].max
-      end_on_line   = [ line_number + SOURCE_CODE_RADIUS - 1, source_code.length].min
 
+    def source_extract(indentation = 0)
+      return unless num = line_number
+      num = num.to_i
+
+      source_code = IO.readlines(@file_path)
+
+      start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
+      end_on_line   = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
+
+      indent = ' ' * indentation
       line_counter = start_on_line
-      extract = source_code[start_on_line..end_on_line].collect do |line| 
+
+      source_code[start_on_line..end_on_line].sum do |line|
         line_counter += 1
-        &quot;#{' ' * indention}#{line_counter}: &quot; + line
+        &quot;#{indent}#{line_counter}: #{line}&quot;
       end
-
-      extract.join
     end
 
-    def sub_template_of(file_name)
+    def sub_template_of(template_path)
       @sub_templates ||= []
-      @sub_templates &lt;&lt; file_name
+      @sub_templates &lt;&lt; template_path
     end
-  
+
     def line_number
-      if file_name
-        regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
-        [@original_exception.message, @original_exception.clean_backtrace].flatten.each do |line|
-          return $1.to_i if regexp =~ line
+      @line_number ||=
+        if file_name
+          regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
+
+          $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
         end
-      end
-      0
     end
-  
+
     def file_name
-      stripped = strip_base_path(@file_name)
-      stripped[0] == ?/ ? stripped[1..-1] : stripped
+      stripped = strip_base_path(@file_path)
+      stripped.slice!(0,1) if stripped[0] == ?/
+      stripped
     end
-    
+
     def to_s
-      &quot;\n\n#{self.class} (#{message}) on line ##{line_number} of #{file_name}:\n&quot; + 
-      source_extract + &quot;\n    &quot; +
-      original_exception.clean_backtrace.join(&quot;\n    &quot;) +
-      &quot;\n\n&quot;
+      &quot;\n\n#{self.class} (#{message}) #{source_location}:\n&quot; +
+        &quot;#{source_extract}\n    #{clean_backtrace.join(&quot;\n    &quot;)}\n\n&quot;
     end
 
     def backtrace
-      [ 
-        &quot;On line ##{line_number} of #{file_name}\n\n#{source_extract(4)}\n    &quot; + 
-        original_exception.clean_backtrace.join(&quot;\n    &quot;)
+      [
+        &quot;#{source_location.capitalize}\n\n#{source_extract(4)}\n    &quot; +
+        clean_backtrace.join(&quot;\n    &quot;)
       ]
     end
 
     private
-      def strip_base_path(file_name)
-        file_name = File.expand_path(file_name).gsub(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '')
-        file_name.gsub(@base_path, &quot;&quot;)
+      def remove_deprecated_assigns!
+        ActionController::Base::DEPRECATED_INSTANCE_VARIABLES.each do |ivar|
+          @assigns.delete(ivar)
+        end
+      end
+
+      def strip_base_path(path)
+        File.expand_path(path).
+          gsub(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '').
+          gsub(@base_path, &quot;&quot;)
+      end
+
+      def source_location
+        if line_number
+          &quot;on line ##{line_number} of &quot;
+        else
+          'in '
+        end + file_name
       end
   end
 end
 
-Exception::TraceSubstitutions &lt;&lt; [/:in\s+`_run_(html|xml).*'\s*$/, ''] if defined?(Exception::TraceSubstitutions)
-Exception::TraceSubstitutions &lt;&lt; [%r{^\s*#{Regexp.escape RAILS_ROOT}}, '#{RAILS_ROOT}'] if defined?(RAILS_ROOT)
+if defined?(Exception::TraceSubstitutions)
+  Exception::TraceSubstitutions &lt;&lt; [/:in\s+`_run_(html|xml).*'\s*$/, '']
+  Exception::TraceSubstitutions &lt;&lt; [%r{^\s*#{Regexp.escape RAILS_ROOT}}, '#{RAILS_ROOT}'] if defined?(RAILS_ROOT)
+end</diff>
      <filename>vendor/rails/actionpack/lib/action_view/template_error.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,9 +6,11 @@ require 'yaml'
 require 'test/unit'
 require 'action_controller'
 require 'breakpoint'
-
 require 'action_controller/test_process'
 
+# Show backtraces for deprecated behavior for quicker cleanup.
+ActiveSupport::Deprecation.debug = true
+
 ActionController::Base.logger = nil
 ActionController::Base.ignore_missing_templates = false
-ActionController::Routing::Routes.reload rescue nil
\ No newline at end of file
+ActionController::Routing::Routes.reload rescue nil</diff>
      <filename>vendor/rails/actionpack/test/abstract_unit.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,85 +4,113 @@ require  File.dirname(__FILE__) + '/abstract_unit'
 class ActiveRecordTestConnector
   cattr_accessor :able_to_connect
   cattr_accessor :connected
-  
+
   # Set our defaults
   self.connected = false
   self.able_to_connect = true
 end
 
 # Try to grab AR
-begin
-  PATH_TO_AR = File.dirname(__FILE__) + '/../../activerecord'
-  require &quot;#{PATH_TO_AR}/lib/active_record&quot; unless Object.const_defined?(:ActiveRecord)
-  require &quot;#{PATH_TO_AR}/lib/active_record/fixtures&quot; unless Object.const_defined?(:Fixtures)
-rescue Object =&gt; e
-  $stderr.puts &quot;\nSkipping ActiveRecord assertion tests: #{e}&quot;
-  ActiveRecordTestConnector.able_to_connect = false
+if defined?(ActiveRecord) &amp;&amp; defined?(Fixtures)
+  $stderr.puts 'Active Record is already loaded, running tests'
+else
+  $stderr.print 'Attempting to load Active Record... '
+  begin
+    PATH_TO_AR = &quot;#{File.dirname(__FILE__)}/../../activerecord/lib&quot;
+    raise LoadError, &quot;#{PATH_TO_AR} doesn't exist&quot; unless File.directory?(PATH_TO_AR)
+    $LOAD_PATH.unshift PATH_TO_AR
+    require 'active_record'
+    require 'active_record/fixtures'
+    $stderr.puts 'success'
+  rescue LoadError =&gt; e
+    $stderr.print &quot;failed. Skipping Active Record assertion tests: #{e}&quot;
+    ActiveRecordTestConnector.able_to_connect = false
+  end
 end
+$stderr.flush
+
+
 
 # Define the rest of the connector
-class ActiveRecordTestConnector  
-  def self.setup
-    unless self.connected || !self.able_to_connect
-      setup_connection
-      load_schema
-      self.connected = true
+class ActiveRecordTestConnector
+  class &lt;&lt; self
+    def setup
+      unless self.connected || !self.able_to_connect
+        setup_connection
+        load_schema
+        require_fixture_models
+        self.connected = true
+      end
+    rescue Exception =&gt; e  # errors from ActiveRecord setup
+      $stderr.puts &quot;\nSkipping ActiveRecord assertion tests: #{e}&quot;
+      #$stderr.puts &quot;  #{e.backtrace.join(&quot;\n  &quot;)}\n&quot;
+      self.able_to_connect = false
     end
-  rescue Object =&gt; e
-    $stderr.puts &quot;\nSkipping ActiveRecord assertion tests: #{e}&quot;
-    #$stderr.puts &quot;  #{e.backtrace.join(&quot;\n  &quot;)}\n&quot;
-    self.able_to_connect = false
-  end
-  
-  private
-  
-  def self.setup_connection
-    if Object.const_defined?(:ActiveRecord)
-          
-      begin
-        ActiveRecord::Base.establish_connection(:adapter =&gt; 'sqlite3', :dbfile =&gt; ':memory:')
-        ActiveRecord::Base.connection
-      rescue Object
-        $stderr.puts 'SQLite 3 unavailable; falling to SQLite 2.'
-        ActiveRecord::Base.establish_connection(:adapter =&gt; 'sqlite', :dbfile =&gt; ':memory:')
-        ActiveRecord::Base.connection
+
+    private
+
+    def setup_connection
+      if Object.const_defined?(:ActiveRecord)
+        begin
+          connection_options = {:adapter =&gt; 'sqlite3', :dbfile =&gt; ':memory:'}
+          ActiveRecord::Base.establish_connection(connection_options)
+          ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' =&gt; connection_options } 
+          ActiveRecord::Base.connection
+        rescue Exception  # errors from establishing a connection
+          $stderr.puts 'SQLite 3 unavailable; falling to SQLite 2.'
+          connection_options = {:adapter =&gt; 'sqlite', :dbfile =&gt; ':memory:'}
+          ActiveRecord::Base.establish_connection(connection_options)
+          ActiveRecord::Base.configurations = { 'sqlite2_ar_integration' =&gt; connection_options } 
+          ActiveRecord::Base.connection
+        end
+
+        Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE)
+      else
+        raise &quot;Couldn't locate ActiveRecord.&quot;
       end
-    
-      Object.send(:const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')) unless Object.const_defined?(:QUOTED_TYPE)
-    else
-      raise &quot;Couldn't locate ActiveRecord.&quot;
     end
-  end
-  
-  # Load actionpack sqlite tables
-  def self.load_schema
-    File.read(File.dirname(__FILE__) + &quot;/fixtures/db_definitions/sqlite.sql&quot;).split(';').each do |sql|
-      ActiveRecord::Base.connection.execute(sql) unless sql.blank?
+
+    # Load actionpack sqlite tables
+    def load_schema
+      File.read(File.dirname(__FILE__) + &quot;/fixtures/db_definitions/sqlite.sql&quot;).split(';').each do |sql|
+        ActiveRecord::Base.connection.execute(sql) unless sql.blank?
+      end
+    end
+
+    def require_fixture_models
+      Dir.glob(File.dirname(__FILE__) + &quot;/fixtures/*.rb&quot;).each {|f| require f}
     end
   end
 end
-  
-# Test case for inheiritance  
+
+# Test case for inheiritance
 class ActiveRecordTestCase &lt; Test::Unit::TestCase
   # Set our fixture path
-  self.fixture_path = &quot;#{File.dirname(__FILE__)}/fixtures/&quot;
-  
+  if ActiveRecordTestConnector.able_to_connect
+    self.fixture_path = &quot;#{File.dirname(__FILE__)}/fixtures/&quot;
+    self.use_transactional_fixtures = false
+  end
+
+  def self.fixtures(*args)
+    super if ActiveRecordTestConnector.connected
+  end
+
   def setup
-    abort_tests unless ActiveRecordTestConnector.connected = true
+    abort_tests unless ActiveRecordTestConnector.connected
   end
-  
+
   # Default so Test::Unit::TestCase doesn't complain
   def test_truth
   end
-  
+
   private
-  
-  # If things go wrong, we don't want to run our test cases. We'll just define them to test nothing.
-  def abort_tests
-    self.class.public_instance_methods.grep(/^test./).each do |method|
-      self.class.class_eval { define_method(method.to_sym){} }
+    # If things go wrong, we don't want to run our test cases. We'll just define them to test nothing.
+    def abort_tests
+      $stderr.puts 'No Active Record connection, aborting tests.'
+      self.class.public_instance_methods.grep(/^test./).each do |method|
+        self.class.class_eval { define_method(method.to_sym){} }
+      end
     end
-  end
 end
 
-ActiveRecordTestConnector.setup
\ No newline at end of file
+ActiveRecordTestConnector.setup</diff>
      <filename>vendor/rails/actionpack/test/active_record_unit.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,4 @@
 require &quot;#{File.dirname(__FILE__)}/../active_record_unit&quot;
-require 'fixtures/company'
 
 class ActiveRecordAssertionsController &lt; ActionController::Base
   self.template_root = &quot;#{File.dirname(__FILE__)}/../fixtures/&quot;
@@ -37,10 +36,10 @@ class ActiveRecordAssertionsController &lt; ActionController::Base
   # the safety dance......
   def rescue_action(e) raise; end
 end
-                  
+
 class ActiveRecordAssertionsControllerTest &lt; ActiveRecordTestCase
   fixtures :companies
-  
+
   def setup
     @request = ActionController::TestRequest.new
     @response = ActionController::TestResponse.new
@@ -51,34 +50,43 @@ class ActiveRecordAssertionsControllerTest &lt; ActiveRecordTestCase
   # test for 1 bad apple column
   def test_some_invalid_columns
     process :nasty_columns_1
-    assert_success
-    assert_invalid_record 'company'
-    assert_invalid_column_on_record 'company', 'rating'
-    assert_valid_column_on_record 'company', 'name'
-    assert_valid_column_on_record 'company', %w(name id)
+    assert_response :success
+
+    assert_deprecated_assertion { assert_invalid_record 'company' }
+    assert_deprecated_assertion { assert_invalid_column_on_record 'company', 'rating' }
+    assert_deprecated_assertion { assert_valid_column_on_record 'company', 'name' }
+    assert_deprecated_assertion { assert_valid_column_on_record 'company', %w(name id) }
   end
 
   # test for 2 bad apples columns
   def test_all_invalid_columns
     process :nasty_columns_2
-    assert_success
-    assert_invalid_record 'company'
-    assert_invalid_column_on_record 'company', 'rating'
-    assert_invalid_column_on_record 'company', 'name'
-    assert_invalid_column_on_record 'company', %w(name rating)
+    assert_response :success
+
+    assert_deprecated_assertion { assert_invalid_record 'company' }
+    assert_deprecated_assertion { assert_invalid_column_on_record 'company', 'rating' }
+    assert_deprecated_assertion { assert_invalid_column_on_record 'company', 'name' }
+    assert_deprecated_assertion { assert_invalid_column_on_record 'company', %w(name rating) }
   end
 
   # ensure we have no problems with an ActiveRecord
   def test_valid_record
     process :good_company
-    assert_success
-    assert_valid_record 'company'
+    assert_response :success
+
+    assert_deprecated_assertion { assert_valid_record 'company' }
   end
 
   # ensure we have problems with an ActiveRecord
   def test_invalid_record
     process :bad_company
-    assert_success
-    assert_invalid_record 'company'
+    assert_response :success
+
+    assert_deprecated_assertion { assert_invalid_record 'company' }
   end
-end
\ No newline at end of file
+
+  protected
+    def assert_deprecated_assertion(message = nil, &amp;block)
+      assert_deprecated(/assert_.*from test_/, &amp;block)
+    end
+end</diff>
      <filename>vendor/rails/actionpack/test/activerecord/active_record_assertions_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,33 +1,8 @@
-# Unfurl the safety net.
-path_to_ar = File.dirname(__FILE__) + '/../../../activerecord'
-if Object.const_defined?(:ActiveRecord) or File.exist?(path_to_ar)
-  begin
-
 # These tests exercise CGI::Session::ActiveRecordStore, so you're going to
 # need AR in a sibling directory to AP and have SQLite installed.
-
-unless Object.const_defined?(:ActiveRecord)
-  require File.join(path_to_ar, 'lib', 'active_record')
-end
-
-require File.dirname(__FILE__) + '/../abstract_unit'
+require File.dirname(__FILE__) + '/../active_record_unit'
 require 'action_controller/session/active_record_store'
 
-#ActiveRecord::Base.logger = Logger.new($stdout)
-begin
-  CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter =&gt; 'sqlite3', :database =&gt; ':memory:')
-  CGI::Session::ActiveRecordStore::Session.connection
-rescue Object
-  $stderr.puts 'SQLite 3 unavailable; falling back to SQLite 2.'
-  begin
-    CGI::Session::ActiveRecordStore::Session.establish_connection(:adapter =&gt; 'sqlite', :database =&gt; ':memory:')
-    CGI::Session::ActiveRecordStore::Session.connection
-  rescue Object
-    $stderr.puts 'SQLite 2 unavailable; skipping ActiveRecordStore test suite.'
-    raise SystemExit
-  end
-end
-
 
 module CommonActiveRecordStoreTests
   def test_basics
@@ -55,7 +30,7 @@ module CommonActiveRecordStoreTests
   end
 end
 
-class ActiveRecordStoreTest &lt; Test::Unit::TestCase
+class ActiveRecordStoreTest &lt; ActiveRecordTestCase
   include CommonActiveRecordStoreTests
 
   def session_class
@@ -70,6 +45,7 @@ class ActiveRecordStoreTest &lt; Test::Unit::TestCase
     session_class.create_table!
 
     ENV['REQUEST_METHOD'] = 'GET'
+    ENV['REQUEST_URI'] = '/'
     CGI::Session::ActiveRecordStore.session_class = session_class
 
     @cgi = CGI.new
@@ -107,7 +83,7 @@ class ActiveRecordStoreTest &lt; Test::Unit::TestCase
   end
 end
 
-class ColumnLimitTest &lt; Test::Unit::TestCase
+class ColumnLimitTest &lt; ActiveRecordTestCase
   def setup
     @session_class = CGI::Session::ActiveRecordStore::Session
     @session_class.create_table!
@@ -164,11 +140,3 @@ class SqlBypassActiveRecordStoreTest &lt; ActiveRecordStoreTest
     assert_equal({ 'foo' =&gt; 'bar' }, @new_session.model.data)
   end
 end
-
-
-# End of safety net.
-  rescue Object =&gt; e
-    $stderr.puts &quot;Skipping CGI::Session::ActiveRecordStore tests: #{e}&quot;
-    #$stderr.puts &quot;  #{e.backtrace.join(&quot;\n  &quot;)}&quot;
-  end
-end</diff>
      <filename>vendor/rails/actionpack/test/activerecord/active_record_store_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,15 +1,12 @@
 require File.dirname(__FILE__) + '/../active_record_unit'
 
-require 'fixtures/topic'
-require 'fixtures/reply'
-require 'fixtures/developer'
-require 'fixtures/project'
-
 class PaginationTest &lt; ActiveRecordTestCase
   fixtures :topics, :replies, :developers, :projects, :developers_projects
-
+  
   class PaginationController &lt; ActionController::Base
     self.template_root = &quot;#{File.dirname(__FILE__)}/../fixtures/&quot;
+
+    around_filter :silence_deprecation_warnings
     
     def simple_paginate
       @topic_pages, @topics = paginate(:topics)
@@ -72,6 +69,13 @@ class PaginationTest &lt; ActiveRecordTestCase
                                              :count =&gt; &quot;d.id&quot;)        
       render :nothing =&gt; true
     end
+
+
+    def silence_deprecation_warnings
+      ActiveSupport::Deprecation.silence do
+        yield
+      end
+    end
     
     def rescue_errors(e) raise e end
 </diff>
      <filename>vendor/rails/actionpack/test/activerecord/pagination_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,14 +4,14 @@ require File.dirname(__FILE__) + '/../abstract_unit'
 class ActionPackAssertionsController &lt; ActionController::Base
 
   # this does absolutely nothing
-  def nothing() render_text &quot;&quot;; end
+  def nothing() head :ok end
 
   # a standard template
   def hello_world() render &quot;test/hello_world&quot;; end
 
   # a standard template
   def hello_xml_world() render &quot;test/hello_xml_world&quot;; end
- 
+
   # a redirect to an internal location
   def redirect_internal() redirect_to &quot;/nothing&quot;; end
 
@@ -19,21 +19,23 @@ class ActionPackAssertionsController &lt; ActionController::Base
 
   def redirect_to_controller() redirect_to :controller =&gt; &quot;elsewhere&quot;, :action =&gt; &quot;flash_me&quot;; end
 
+  def redirect_to_controller_with_symbol() redirect_to :controller =&gt; :elsewhere, :action =&gt; :flash_me; end
+
   def redirect_to_path() redirect_to '/some/path' end
 
   def redirect_to_named_route() redirect_to route_one_url end
-  
+
   # a redirect to an external location
   def redirect_external() redirect_to_url &quot;http://www.rubyonrails.org&quot;; end
-  
+
   # a 404
-  def response404() render_text &quot;&quot;, &quot;404 AWOL&quot;; end
+  def response404() head '404 AWOL' end
 
   # a 500
-  def response500() render_text &quot;&quot;, &quot;500 Sorry&quot;; end
+  def response500() head '500 Sorry' end
 
   # a fictional 599
-  def response599() render_text &quot;&quot;, &quot;599 Whoah!&quot;; end
+  def response599() head '599 Whoah!' end
 
   # putting stuff in the flash
   def flash_me
@@ -54,64 +56,68 @@ class ActionPackAssertionsController &lt; ActionController::Base
   end
 
   def render_based_on_parameters
-    render_text &quot;Mr. #{@params[&quot;name&quot;]}&quot;
+    render_text &quot;Mr. #{params[:name]}&quot;
   end
 
   def render_url
     render_text &quot;&lt;div&gt;#{url_for(:action =&gt; 'flash_me', :only_path =&gt; true)}&lt;/div&gt;&quot;
   end
 
+  def render_text_with_custom_content_type
+    render :text =&gt; &quot;Hello!&quot;, :content_type =&gt; Mime::RSS
+  end
+
   # puts something in the session
   def session_stuffing
     session['xmas'] = 'turkey'
     render_text &quot;ho ho ho&quot;
   end
-  
+
   # raises exception on get requests
   def raise_on_get
-    raise &quot;get&quot; if @request.get?
-    render_text &quot;request method: #{@request.env['REQUEST_METHOD']}&quot;
+    raise &quot;get&quot; if request.get?
+    render_text &quot;request method: #{request.env['REQUEST_METHOD']}&quot;
   end
 
   # raises exception on post requests
   def raise_on_post
-    raise &quot;post&quot; if @request.post?
-    render_text &quot;request method: #{@request.env['REQUEST_METHOD']}&quot;
-  end       
-  
+    raise &quot;post&quot; if request.post?
+    render_text &quot;request method: #{request.env['REQUEST_METHOD']}&quot;
+  end
+
   def get_valid_record
-    @record = Class.new do       
+    @record = Class.new do
       def valid?
         true
       end
 
       def errors
-        Class.new do 
-           def full_messages; []; end          
+        Class.new do
+           def full_messages; []; end
         end.new
-      end    
-    
+      end
+
     end.new
-        
-    render :nothing =&gt; true    
+
+    render :nothing =&gt; true
   end
 
 
   def get_invalid_record
-    @record = Class.new do 
-      
+    @record = Class.new do
+
       def valid?
         false
       end
-      
+
       def errors
-        Class.new do 
-           def full_messages; ['...stuff...']; end          
+        Class.new do
+           def full_messages; ['...stuff...']; end
         end.new
       end
-    end.new                
-    
-    render :nothing =&gt; true    
+    end.new
+
+    render :nothing =&gt; true
   end
 
   # 911
@@ -120,32 +126,51 @@ end
 
 module Admin
   class InnerModuleController &lt; ActionController::Base
+    def index
+      render :nothing =&gt; true
+    end
+
+    def redirect_to_index
+      redirect_to admin_inner_module_path
+    end
+
     def redirect_to_absolute_controller
       redirect_to :controller =&gt; '/content'
     end
+
     def redirect_to_fellow_controller
       redirect_to :controller =&gt; 'user'
     end
+
+    def redirect_to_top_level_named_route
+      redirect_to top_level_url(:id =&gt; &quot;foo&quot;)
+    end
   end
 end
 
 # ---------------------------------------------------------------------------
 
 
-# tell the controller where to find its templates but start from parent 
-# directory of test_request_response to simulate the behaviour of a 
+# tell the controller where to find its templates but start from parent
+# directory of test_request_response to simulate the behaviour of a
 # production environment
 ActionPackAssertionsController.template_root = File.dirname(__FILE__) + &quot;/../fixtures/&quot;
 
 
 # a test case to exercise the new capabilities TestRequest &amp; TestResponse
 class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
-  # let's get this party started  
+  # let's get this party started
   def setup
+    ActionController::Routing::Routes.reload
+    ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module content admin/user))
     @controller = ActionPackAssertionsController.new
     @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
   end
- 
+
+  def teardown
+    ActionController::Routing::Routes.reload
+  end
+
   # -- assertion-based testing ------------------------------------------------
 
   def test_assert_tag_and_url_for
@@ -156,10 +181,10 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
   # test the session assertion to make sure something is there.
   def test_assert_session_has
     process :session_stuffing
-    assert_session_has 'xmas'
-    assert_session_has_no 'halloween'
+    assert_deprecated_assertion { assert_session_has 'xmas' }
+    assert_deprecated_assertion { assert_session_has_no 'halloween' }
   end
-  
+
   # test the get method, make sure the request really was a get
   def test_get
     assert_raise(RuntimeError) { get :raise_on_get }
@@ -173,7 +198,7 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
     post :raise_on_get
     assert_equal @response.body, 'request method: POST'
   end
-  
+
 #   the following test fails because the request_method is now cached on the request instance
 #   test the get/post switch within one test action
 #   def test_get_post_switch
@@ -190,106 +215,168 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
   # test the assertion of goodies in the template
   def test_assert_template_has
     process :assign_this
-    assert_template_has 'howdy'
+    assert_deprecated_assertion { assert_template_has 'howdy' }
   end
 
   # test the assertion for goodies that shouldn't exist in the template
   def test_assert_template_has_no
     process :nothing
-    assert_template_has_no 'maple syrup'
-    assert_template_has_no 'howdy'
+    assert_deprecated_assertion { assert_template_has_no 'maple syrup' }
+    assert_deprecated_assertion { assert_template_has_no 'howdy' }
   end
-  
+
   # test the redirection assertions
   def test_assert_redirect
     process :redirect_internal
-    assert_redirect
+    assert_deprecated_assertion { assert_redirect }
   end
 
   # test the redirect url string
   def test_assert_redirect_url
     process :redirect_external
-    assert_redirect_url 'http://www.rubyonrails.org'
+    assert_deprecated_assertion do
+      assert_redirect_url 'http://www.rubyonrails.org'
+    end
   end
 
   # test the redirection pattern matching on a string
   def test_assert_redirect_url_match_string
     process :redirect_external
-    assert_redirect_url_match 'rails.org'
+    assert_deprecated_assertion do
+      assert_redirect_url_match 'rails.org'
+    end
   end
-  
+
   # test the redirection pattern matching on a pattern
   def test_assert_redirect_url_match_pattern
     process :redirect_external
-    assert_redirect_url_match /ruby/
+    assert_deprecated_assertion do
+      assert_redirect_url_match /ruby/
+    end
   end
 
   # test the redirection to a named route
   def test_assert_redirect_to_named_route
-    process :redirect_to_named_route
-    assert_raise(Test::Unit::AssertionFailedError) do
-      assert_redirected_to 'http://test.host/route_two'
+    with_routing do |set|
+      set.draw do |map|
+        map.route_one 'route_one', :controller =&gt; 'action_pack_assertions', :action =&gt; 'nothing'
+        map.connect   ':controller/:action/:id'
+      end
+      set.named_routes.install
+
+      process :redirect_to_named_route
+      assert_redirected_to 'http://test.host/route_one'
+      assert_redirected_to route_one_url
+      assert_redirected_to :route_one_url
+    end
+  end
+
+  def test_assert_redirect_to_named_route_failure
+    with_routing do |set|
+      set.draw do |map|
+        map.route_one 'route_one', :controller =&gt; 'action_pack_assertions', :action =&gt; 'nothing', :id =&gt; 'one'
+        map.route_two 'route_two', :controller =&gt; 'action_pack_assertions', :action =&gt; 'nothing', :id =&gt; 'two'
+        map.connect   ':controller/:action/:id'
+      end
+      process :redirect_to_named_route
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to 'http://test.host/route_two'
+      end
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to :controller =&gt; 'action_pack_assertions', :action =&gt; 'nothing', :id =&gt; 'two'
+      end
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to route_two_url
+      end
+      assert_raise(Test::Unit::AssertionFailedError) do
+        assert_redirected_to :route_two_url
+      end
+    end
+  end
+
+  def test_assert_redirect_to_nested_named_route
+    with_routing do |set|
+      set.draw do |map|
+        map.admin_inner_module 'admin/inner_module', :controller =&gt; 'admin/inner_module', :action =&gt; 'index'
+        map.connect            ':controller/:action/:id'
+      end
+      @controller = Admin::InnerModuleController.new
+      process :redirect_to_index
+      # redirection is &lt;{&quot;action&quot;=&gt;&quot;index&quot;, &quot;controller&quot;=&gt;&quot;admin/admin/inner_module&quot;}&gt;
+      assert_redirected_to admin_inner_module_path
+    end
+  end
+
+  def test_assert_redirected_to_top_level_named_route_from_nested_controller
+    with_routing do |set|
+      set.draw do |map|
+        map.top_level '/action_pack_assertions/:id', :controller =&gt; 'action_pack_assertions', :action =&gt; 'index'
+        map.connect   ':controller/:action/:id'
+      end
+      @controller = Admin::InnerModuleController.new
+      process :redirect_to_top_level_named_route
+      # passes -&gt; assert_redirected_to &quot;http://test.host/action_pack_assertions/foo&quot;
+      assert_redirected_to &quot;/action_pack_assertions/foo&quot;
     end
   end
-  
+
   # test the flash-based assertions with something is in the flash
   def test_flash_assertions_full
     process :flash_me
     assert @response.has_flash_with_contents?
-    assert_flash_exists
-    assert_flash_not_empty
-    assert_flash_has 'hello'
-    assert_flash_has_no 'stds'
+    assert_deprecated_assertion { assert_flash_exists }
+    assert_deprecated_assertion { assert_flash_not_empty }
+    assert_deprecated_assertion { assert_flash_has 'hello' }
+    assert_deprecated_assertion { assert_flash_has_no 'stds' }
   end
 
   # test the flash-based assertions with no flash at all
   def test_flash_assertions_negative
     process :nothing
-    assert_flash_empty
-    assert_flash_has_no 'hello'
-    assert_flash_has_no 'qwerty'
+    assert_deprecated_assertion { assert_flash_empty }
+    assert_deprecated_assertion { assert_flash_has_no 'hello' }
+    assert_deprecated_assertion { assert_flash_has_no 'qwerty' }
   end
-  
-  # test the assert_rendered_file 
+
+  # test the assert_rendered_file
   def test_assert_rendered_file
-    process :hello_world
-    assert_rendered_file 'test/hello_world'
-    assert_rendered_file 'hello_world'
+    assert_deprecated(/render/) { process :hello_world }
+    assert_deprecated_assertion { assert_rendered_file 'test/hello_world' }
+    assert_deprecated_assertion { assert_rendered_file 'hello_world' }
   end
-  
+
   # test the assert_success assertion
   def test_assert_success
     process :nothing
-    assert_success
-    assert_rendered_file
+    assert_deprecated_assertion { assert_success }
   end
-  
+
   # -- standard request/response object testing --------------------------------
- 
+
   # ensure our session is working properly
   def test_session_objects
     process :session_stuffing
     assert @response.has_session_object?('xmas')
-    assert_session_equal 'turkey', 'xmas'
+    assert_deprecated_assertion { assert_session_equal 'turkey', 'xmas' }
     assert !@response.has_session_object?('easter')
   end
-  
+
   # make sure that the template objects exist
   def test_template_objects_alive
     process :assign_this
     assert !@response.has_template_object?('hi')
     assert @response.has_template_object?('howdy')
   end
-  
+
   # make sure we don't have template objects when we shouldn't
   def test_template_object_missing
     process :nothing
     assert_nil @response.template_objects['howdy']
   end
-  
+
   def test_assigned_equal
     process :assign_this
-    assert_assigned_equal &quot;ho&quot;, :howdy
+    assert_deprecated_assertion { assert_assigned_equal &quot;ho&quot;, :howdy }
   end
 
   # check the empty flashing
@@ -314,24 +401,25 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
     assert !@response.has_flash_with_contents?
     assert_nil @response.flash['hello']
   end
-  
+
   # examine that the flash objects are what we expect
   def test_flash_equals
     process :flash_me
-    assert_flash_equal 'my name is inigo montoya...', 'hello'
+    assert_deprecated_assertion do
+      assert_flash_equal 'my name is inigo montoya...', 'hello'
+    end
   end
-  
-  
-  # check if we were rendered by a file-based template? 
+
+  # check if we were rendered by a file-based template?
   def test_rendered_action
     process :nothing
     assert !@response.rendered_with_file?
 
-    process :hello_world
+    assert_deprecated(/render/) { process :hello_world }
     assert @response.rendered_with_file?
     assert 'hello_world', @response.rendered_file
   end
-  
+
   # check the redirection location
   def test_redirection_location
     process :redirect_internal
@@ -339,24 +427,26 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
 
     process :redirect_external
     assert_equal 'http://www.rubyonrails.org', @response.redirect_url
+  end
 
+  def test_no_redirect_url
     process :nothing
     assert_nil @response.redirect_url
   end
-  
- 
-  # check server errors 
+
+
+  # check server errors
   def test_server_error_response_code
     process :response500
     assert @response.server_error?
-    
+
     process :response599
     assert @response.server_error?
-    
+
     process :response404
     assert !@response.server_error?
   end
-  
+
   # check a 404 response code
   def test_missing_response_code
     process :response404
@@ -372,7 +462,7 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
     assert !@response.redirect_url_match?(&quot;phpoffrails&quot;)
     assert !@response.redirect_url_match?(/perloffrails/)
   end
-  
+
   # check for a redirection
   def test_redirection
     process :redirect_internal
@@ -384,51 +474,57 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
     process :nothing
     assert !@response.redirect?
   end
-  
+
   # check a successful response code
   def test_successful_response_code
     process :nothing
     assert @response.success?
-  end 
-  
+  end
+
   # a basic check to make sure we have a TestResponse object
   def test_has_response
     process :nothing
     assert_kind_of ActionController::TestResponse, @response
   end
-  
+
   def test_render_based_on_parameters
     process :render_based_on_parameters, &quot;name&quot; =&gt; &quot;David&quot;
     assert_equal &quot;Mr. David&quot;, @response.body
   end
 
   def test_assert_template_xpath_match_no_matches
-    process :hello_xml_world
+    assert_deprecated(/render/) { process :hello_xml_world }
     assert_raises Test::Unit::AssertionFailedError do
-      assert_template_xpath_match('/no/such/node/in/document')
+      assert_deprecated_assertion do
+        assert_template_xpath_match('/no/such/node/in/document')
+      end
     end
   end
 
   def test_simple_one_element_xpath_match
-    process :hello_xml_world
-    assert_template_xpath_match('//title', &quot;Hello World&quot;)
+    assert_deprecated(/render/) { process :hello_xml_world }
+    assert_deprecated_assertion do
+      assert_template_xpath_match('//title', &quot;Hello World&quot;)
+    end
   end
 
   def test_array_of_elements_in_xpath_match
-    process :hello_xml_world
-    assert_template_xpath_match('//p', %w( abes monks wiseguys ))
+    assert_deprecated(/render/) { process :hello_xml_world }
+    assert_deprecated_assertion do
+      assert_template_xpath_match('//p', %w( abes monks wiseguys ))
+    end
   end
 
   def test_follow_redirect
     process :redirect_to_action
     assert_redirected_to :action =&gt; &quot;flash_me&quot;
-    
+
     follow_redirect
     assert_equal 1, @request.parameters[&quot;id&quot;].to_i
 
     assert &quot;Inconceivable!&quot;, @response.body
   end
-  
+
   def test_follow_redirect_outside_current_action
     process :redirect_to_controller
     assert_redirected_to :controller =&gt; &quot;elsewhere&quot;, :action =&gt; &quot;flash_me&quot;
@@ -436,6 +532,18 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
     assert_raises(RuntimeError, &quot;Can't follow redirects outside of current controller (elsewhere)&quot;) { follow_redirect }
   end
 
+  def test_assert_redirection_fails_with_incorrect_controller
+    process :redirect_to_controller
+    assert_raise(Test::Unit::AssertionFailedError) do
+      assert_redirected_to :controller =&gt; &quot;action_pack_assertions&quot;, :action =&gt; &quot;flash_me&quot;
+    end
+  end
+
+  def test_assert_redirection_with_extra_controller_option
+    get :redirect_to_action
+    assert_redirected_to :controller =&gt; 'action_pack_assertions', :action =&gt; &quot;flash_me&quot;, :id =&gt; 1, :params =&gt; { :panda =&gt; 'fun' }
+  end
+
   def test_redirected_to_url_leadling_slash
     process :redirect_to_path
     assert_redirected_to '/some/path'
@@ -449,45 +557,67 @@ class ActionPackAssertionsControllerTest &lt; Test::Unit::TestCase
     assert_redirected_to 'http://test.host/some/path'
   end
 
+  def test_assert_redirection_with_symbol
+    process :redirect_to_controller_with_symbol
+    assert_nothing_raised {
+      assert_redirected_to :controller =&gt; &quot;elsewhere&quot;, :action =&gt; &quot;flash_me&quot;
+    }
+    process :redirect_to_controller_with_symbol
+    assert_nothing_raised {
+      assert_redirected_to :controller =&gt; :elsewhere, :action =&gt; :flash_me
+    }
+  end
+
   def test_redirected_to_with_nested_controller
     @controller = Admin::InnerModuleController.new
     get :redirect_to_absolute_controller
     assert_redirected_to :controller =&gt; 'content'
-    
+
     get :redirect_to_fellow_controller
     assert_redirected_to :controller =&gt; 'admin/user'
-  end                 
-  
+  end
+
   def test_assert_valid
     get :get_valid_record
-    assert_valid assigns('record')    
-  end                
-  
+    assert_valid assigns('record')
+  end
+
   def test_assert_valid_failing
     get :get_invalid_record
-    
+
     begin
-      assert_valid assigns('record')    
+      assert_valid assigns('record')
       assert false
-    rescue Test::Unit::AssertionFailedError =&gt; e             
+    rescue Test::Unit::AssertionFailedError =&gt; e
     end
   end
+
+  protected
+    def assert_deprecated_assertion(&amp;block)
+      assert_deprecated(/assert/, &amp;block)
+    end
 end
 
-class ActionPackHeaderTest &lt; Test::Unit::TestCase  
+class ActionPackHeaderTest &lt; Test::Unit::TestCase
   def setup
     @controller = ActionPackAssertionsController.new
     @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
   end
 
   def test_rendering_xml_sets_content_type
-    process :hello_xml_world
-    assert_equal('application/xml', @controller.headers['Content-Type'])
+    assert_deprecated(/render/) { process :hello_xml_world }
+    assert_equal('application/xml; charset=utf-8', @controller.headers['Content-Type'])
   end
 
   def test_rendering_xml_respects_content_type
     @response.headers['Content-Type'] = 'application/pdf'
-    process :hello_xml_world
-    assert_equal('application/pdf', @controller.headers['Content-Type'])
+    assert_deprecated(/render/) { process :hello_xml_world }
+    assert_equal('application/pdf; charset=utf-8', @controller.headers['Content-Type'])
+  end
+
+
+  def test_render_text_with_custom_content_type
+    get :render_text_with_custom_content_type
+    assert_equal 'application/rss+xml; charset=utf-8', @response.headers['Content-Type']
   end
 end</diff>
      <filename>vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,23 +5,21 @@ class Address
   def Address.count(conditions = nil, join = nil)
     nil
   end
-  
+
   def Address.find_all(arg1, arg2, arg3, arg4)
     []
   end
-  
+
   def self.find(*args)
     []
   end
 end
 
 class AddressesTestController &lt; ActionController::Base
-
   scaffold :address
 
   def self.controller_name; &quot;addresses&quot;; end
   def self.controller_path; &quot;addresses&quot;; end
-
 end
 
 AddressesTestController.template_root = File.dirname(__FILE__) + &quot;/../fixtures/&quot;
@@ -41,9 +39,10 @@ class AddressesTest &lt; Test::Unit::TestCase
   end
 
   def test_list
-    get :list
+    # because pagination is deprecated
+    ActiveSupport::Deprecation.silence do
+      get :list
+    end
     assert_equal &quot;We only need to get this far!&quot;, @response.body.chomp
   end
-
-
 end</diff>
      <filename>vendor/rails/actionpack/test/controller/addresses_render_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,6 +12,7 @@ module Submodule
     
     hide_action :hidden_action
     def hidden_action
+      raise &quot;Noooo!&quot;
     end
     
     def another_hidden_action
@@ -23,7 +24,6 @@ module Submodule
   end
 end
 class EmptyController &lt; ActionController::Base
-  include ActionController::Caching
 end
 class NonEmptyController &lt; ActionController::Base
   def public_action
@@ -34,10 +34,27 @@ class NonEmptyController &lt; ActionController::Base
   end
 end
 
+class MethodMissingController &lt; ActionController::Base
+  
+  hide_action :shouldnt_be_called
+  def shouldnt_be_called
+    raise &quot;NO WAY!&quot;
+  end
+  
+protected
+  
+  def method_missing(selector)
+    render :text =&gt; selector.to_s
+  end
+  
+end
+
 class ControllerClassTests &lt; Test::Unit::TestCase
   def test_controller_path
     assert_equal 'empty', EmptyController.controller_path
+    assert_equal EmptyController.controller_path, EmptyController.new.controller_path
     assert_equal 'submodule/contained_empty', Submodule::ContainedEmptyController.controller_path
+    assert_equal Submodule::ContainedEmptyController.controller_path, Submodule::ContainedEmptyController.new.controller_path
   end
   def test_controller_name
     assert_equal 'empty', EmptyController.controller_name
@@ -57,10 +74,63 @@ class ControllerInstanceTests &lt; Test::Unit::TestCase
 
   def test_action_methods
     @empty_controllers.each do |c|
-      assert_equal Set.new, c.send(:action_methods), &quot;#{c.class.controller_path} should be empty!&quot;
+      hide_mocha_methods_from_controller(c)
+      assert_equal Set.new, c.send(:action_methods), &quot;#{c.controller_path} should be empty!&quot;
     end
     @non_empty_controllers.each do |c|
-      assert_equal Set.new('public_action'), c.send(:action_methods), &quot;#{c.class.controller_path} should not be empty!&quot;
+      hide_mocha_methods_from_controller(c)
+      assert_equal Set.new('public_action'), c.send(:action_methods), &quot;#{c.controller_path} should not be empty!&quot;
     end
   end
+  
+  protected
+  
+  # Mocha adds methods to Object which are then included in the public_instance_methods
+  # This method hides those from the controller so the above tests won't know the difference
+  def hide_mocha_methods_from_controller(controller)
+    mocha_methods = [:expects, :metaclass, :mocha, :mocha_inspect, :reset_mocha, :stubba_object, :stubba_method, :stubs, :verify, :__is_a__, :__metaclass__]
+    controller.class.send(:hide_action, *mocha_methods)
+  end
+  
 end
+
+
+class PerformActionTest &lt; Test::Unit::TestCase
+  def use_controller(controller_class)
+    @controller = controller_class.new
+
+    # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
+    # a more accurate simulation of what happens in &quot;real life&quot;.
+    @controller.logger = Logger.new(nil)
+
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+
+    @request.host = &quot;www.nextangle.com&quot;
+  end
+  
+  def test_get_on_priv_should_show_selector
+    use_controller MethodMissingController
+    get :shouldnt_be_called
+    assert_response :success
+    assert_equal 'shouldnt_be_called', @response.body
+  end
+  
+  def test_method_missing_is_not_an_action_name
+    use_controller MethodMissingController
+    assert ! @controller.send(:action_methods).include?('method_missing')
+    
+    get :method_missing
+    assert_response :success
+    assert_equal 'method_missing', @response.body
+  end
+  
+  def test_get_on_hidden_should_fail
+    use_controller NonEmptyController
+    get :hidden_action
+    assert_response 404
+    
+    get :another_hidden_action
+    assert_response 404
+  end
+end
\ No newline at end of file</diff>
      <filename>vendor/rails/actionpack/test/controller/base_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,15 +7,15 @@ class CaptureController &lt; ActionController::Base
   def content_for
     render :layout =&gt; &quot;talk_from_action&quot;
   end
-  
+
   def erb_content_for
     render :layout =&gt; &quot;talk_from_action&quot;
   end
-  
+
   def block_content_for
     render :layout =&gt; &quot;talk_from_action&quot;
   end
-  
+
   def non_erb_block_content_for
     render :layout =&gt; &quot;talk_from_action&quot;
   end
@@ -43,36 +43,38 @@ class CaptureTest &lt; Test::Unit::TestCase
     get :capturing
     assert_equal &quot;Dreamy days&quot;, @response.body.strip
   end
-  
+
   def test_content_for
     get :content_for
     assert_equal expected_content_for_output, @response.body
   end
-  
+
   def test_erb_content_for
     get :content_for
     assert_equal expected_content_for_output, @response.body
   end
-  
+
   def test_block_content_for
     get :block_content_for
     assert_equal expected_content_for_output, @response.body
   end
-  
+
   def test_non_erb_block_content_for
     get :non_erb_block_content_for
     assert_equal expected_content_for_output, @response.body
   end
 
   def test_update_element_with_capture
-    get :update_element_with_capture
+    assert_deprecated 'update_element_function' do
+      get :update_element_with_capture
+    end
     assert_equal(
       &quot;&lt;script type=\&quot;text/javascript\&quot;&gt;\n//&lt;![CDATA[\n$('products').innerHTML = '\\n  &lt;p&gt;Product 1&lt;/p&gt;\\n  &lt;p&gt;Product 2&lt;/p&gt;\\n';\n\n//]]&gt;\n&lt;/script&gt;&quot; +
-        &quot;\n\n$('status').innerHTML = '\\n  &lt;b&gt;You bought something!&lt;/b&gt;\\n';&quot;, 
+        &quot;\n\n$('status').innerHTML = '\\n  &lt;b&gt;You bought something!&lt;/b&gt;\\n';&quot;,
       @response.body.strip
     )
   end
-  
+
   private
   def expected_content_for_output
     &quot;&lt;title&gt;Putting stuff in the title!&lt;/title&gt;\n\nGreat stuff!&quot;</diff>
      <filename>vendor/rails/actionpack/test/controller/capture_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,6 +17,7 @@ class CGITest &lt; Test::Unit::TestCase
     @query_string_without_equal = &quot;action&quot;
     @query_string_with_many_ampersands =
       &quot;&amp;action=create_customer&amp;&amp;&amp;full_name=David%20Heinemeier%20Hansson&quot;
+    @query_string_with_empty_key = &quot;action=create_customer&amp;full_name=David%20Heinemeier%20Hansson&amp;=Save&quot;
   end
 
   def test_query_string
@@ -27,13 +28,43 @@ class CGITest &lt; Test::Unit::TestCase
   end
   
   def test_deep_query_string
-    assert_equal({'x' =&gt; {'y' =&gt; {'z' =&gt; '10'}}}, CGIMethods.parse_query_parameters('x[y][z]=10'))
+    expected = {'x' =&gt; {'y' =&gt; {'z' =&gt; '10'}}}
+    assert_equal(expected, CGIMethods.parse_query_parameters('x[y][z]=10'))
   end
   
   def test_deep_query_string_with_array
     assert_equal({'x' =&gt; {'y' =&gt; {'z' =&gt; ['10']}}}, CGIMethods.parse_query_parameters('x[y][z][]=10'))
     assert_equal({'x' =&gt; {'y' =&gt; {'z' =&gt; ['10', '5']}}}, CGIMethods.parse_query_parameters('x[y][z][]=10&amp;x[y][z][]=5'))
   end
+
+  def test_deep_query_string_with_array_of_hash
+    assert_equal({'x' =&gt; {'y' =&gt; [{'z' =&gt; '10'}]}}, CGIMethods.parse_query_parameters('x[y][][z]=10'))
+    assert_equal({'x' =&gt; {'y' =&gt; [{'z' =&gt; '10', 'w' =&gt; '10'}]}}, CGIMethods.parse_query_parameters('x[y][][z]=10&amp;x[y][][w]=10'))
+  end
+
+  def test_deep_query_string_with_array_of_hashes_with_one_pair
+    assert_equal({'x' =&gt; {'y' =&gt; [{'z' =&gt; '10'}, {'z' =&gt; '20'}]}}, CGIMethods.parse_query_parameters('x[y][][z]=10&amp;x[y][][z]=20'))
+    assert_equal(&quot;10&quot;, CGIMethods.parse_query_parameters('x[y][][z]=10&amp;x[y][][z]=20')[&quot;x&quot;][&quot;y&quot;].first[&quot;z&quot;])
+    assert_equal(&quot;10&quot;, CGIMethods.parse_query_parameters('x[y][][z]=10&amp;x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
+  end
+  
+  def test_request_hash_parsing
+    query = {
+      &quot;note[viewers][viewer][][type]&quot; =&gt; [&quot;User&quot;, &quot;Group&quot;],
+      &quot;note[viewers][viewer][][id]&quot;   =&gt; [&quot;1&quot;, &quot;2&quot;]
+    }
+
+    expected = { &quot;note&quot; =&gt; { &quot;viewers&quot;=&gt;{&quot;viewer&quot;=&gt;[{ &quot;id&quot;=&gt;&quot;1&quot;, &quot;type&quot;=&gt;&quot;User&quot;}, {&quot;type&quot;=&gt;&quot;Group&quot;, &quot;id&quot;=&gt;&quot;2&quot;} ]} } }
+
+    assert_equal(expected, CGIMethods.parse_request_parameters(query))
+  end
+  
+  def test_deep_query_string_with_array_of_hashes_with_multiple_pairs
+    assert_equal(
+      {'x' =&gt; {'y' =&gt; [{'z' =&gt; '10', 'w' =&gt; 'a'}, {'z' =&gt; '20', 'w' =&gt; 'b'}]}}, 
+      CGIMethods.parse_query_parameters('x[y][][z]=10&amp;x[y][][w]=a&amp;x[y][][z]=20&amp;x[y][][w]=b')
+    )
+  end
   
   def test_query_string_with_nil
     assert_equal(
@@ -69,6 +100,13 @@ class CGITest &lt; Test::Unit::TestCase
       CGIMethods.parse_query_parameters(@query_string_without_equal)
     )    
   end
+  
+  def test_query_string_with_empty_key
+    assert_equal(
+      { &quot;action&quot; =&gt; &quot;create_customer&quot;, &quot;full_name&quot; =&gt; &quot;David Heinemeier Hansson&quot; },
+      CGIMethods.parse_query_parameters(@query_string_with_empty_key)
+    )
+  end
 
   def test_query_string_with_many_ampersands
     assert_equal(
@@ -87,7 +125,8 @@ class CGITest &lt; Test::Unit::TestCase
       &quot;something_nil&quot; =&gt; [ nil ],
       &quot;something_empty&quot; =&gt; [ &quot;&quot; ],
       &quot;products[first]&quot; =&gt; [ &quot;Apple Computer&quot; ],
-      &quot;products[second]&quot; =&gt; [ &quot;Pc&quot; ]
+      &quot;products[second]&quot; =&gt; [ &quot;Pc&quot; ],
+      &quot;&quot; =&gt; [ 'Save' ]
     }
     
     expected_output =  {
@@ -116,9 +155,10 @@ class CGITest &lt; Test::Unit::TestCase
   end
   
   def test_parse_params_from_multipart_upload
-    mockup = Struct.new(:content_type, :original_filename)
+    mockup = Struct.new(:content_type, :original_filename, :read, :rewind)
     file = mockup.new('img/jpeg', 'foo.jpg')
     ie_file = mockup.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg')
+    non_file_text_part = mockup.new('text/plain', '', 'abc')
   
     input = {
       &quot;something&quot; =&gt; [ StringIO.new(&quot;&quot;) ],
@@ -129,9 +169,10 @@ class CGITest &lt; Test::Unit::TestCase
       &quot;products[string]&quot; =&gt; [ StringIO.new(&quot;Apple Computer&quot;) ],
       &quot;products[file]&quot; =&gt; [ file ],
       &quot;ie_products[string]&quot; =&gt; [ StringIO.new(&quot;Microsoft&quot;) ],
-      &quot;ie_products[file]&quot; =&gt; [ ie_file ]
+      &quot;ie_products[file]&quot; =&gt; [ ie_file ],
+      &quot;text_part&quot; =&gt; [non_file_text_part]
     }
-    
+
     expected_output =  {
       &quot;something&quot; =&gt; &quot;&quot;,
       &quot;array_of_stringios&quot; =&gt; [&quot;One&quot;, &quot;Two&quot;],
@@ -153,7 +194,8 @@ class CGITest &lt; Test::Unit::TestCase
       &quot;ie_products&quot; =&gt; {
         &quot;string&quot; =&gt; &quot;Microsoft&quot;,
         &quot;file&quot; =&gt; ie_file
-      }
+      },
+      &quot;text_part&quot; =&gt; &quot;abc&quot;
     }
 
     params = CGIMethods.parse_request_parameters(input)
@@ -206,29 +248,36 @@ class CGITest &lt; Test::Unit::TestCase
 
   def test_parse_params_with_single_brackets_in_middle
     input     = { &quot;a/b[c]d&quot; =&gt;  %w(e) }
-    expected  = { &quot;a/b[c]d&quot; =&gt; &quot;e&quot; }
+    expected  = { &quot;a/b&quot; =&gt; {} }
     assert_equal expected, CGIMethods.parse_request_parameters(input)
   end
 
   def test_parse_params_with_separated_brackets
     input     = { &quot;a/b@[c]d[e]&quot; =&gt;  %w(f) }
-    expected  = { &quot;a/b@&quot; =&gt; { &quot;c]d[e&quot; =&gt; &quot;f&quot; }}
+    expected  = { &quot;a/b@&quot; =&gt; { }}
     assert_equal expected, CGIMethods.parse_request_parameters(input)
   end
 
   def test_parse_params_with_separated_brackets_and_array
     input     = { &quot;a/b@[c]d[e][]&quot; =&gt;  %w(f) }
-    expected  = { &quot;a/b@&quot; =&gt; { &quot;c]d[e&quot; =&gt; [&quot;f&quot;] }}
+    expected  = { &quot;a/b@&quot; =&gt; { }}
     assert_equal expected , CGIMethods.parse_request_parameters(input)
   end
 
   def test_parse_params_with_unmatched_brackets_and_array
     input     = { &quot;a/b@[c][d[e][]&quot; =&gt;  %w(f) }
-    expected  = { &quot;a/b@&quot; =&gt; { &quot;c&quot; =&gt; { &quot;d[e&quot; =&gt; [&quot;f&quot;] }}}
+    expected  = { &quot;a/b@&quot; =&gt; { &quot;c&quot; =&gt; { }}}
+    assert_equal expected, CGIMethods.parse_request_parameters(input)
+  end
+  
+  def test_parse_params_with_nil_key
+    input = { nil =&gt; nil, &quot;test2&quot; =&gt; %w(value1) }
+    expected = { &quot;test2&quot; =&gt; &quot;value1&quot; }
     assert_equal expected, CGIMethods.parse_request_parameters(input)
   end
 end
 
+
 class MultipartCGITest &lt; Test::Unit::TestCase
   FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
 
@@ -292,23 +341,39 @@ class MultipartCGITest &lt; Test::Unit::TestCase
     assert_equal 'bar', params['foo']
 
     # Ruby CGI doesn't handle multipart/mixed for us.
-    assert_kind_of StringIO, params['files']
+    assert_kind_of String, params['files']
     assert_equal 19756, params['files'].size
   end
 
+  # Rewind readable cgi params so others may reread them (such as CGI::Session
+  # when passing the session id in a multipart form).
+  def test_multipart_param_rewound
+    params = process('text_file')
+    assert_equal 'bar', @cgi.params['foo'][0].read
+  end
+
   private
     def process(name)
       old_stdin = $stdin
       File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
         ENV['CONTENT_LENGTH'] = file.stat.size.to_s
         $stdin = file
-        CGIMethods.parse_request_parameters CGI.new.params
+        @cgi = CGI.new
+        CGIMethods.parse_request_parameters @cgi.params
       end
     ensure
       $stdin = old_stdin
     end
 end
 
+# Ensures that PUT works with multipart as well as POST.
+class PutMultipartCGITest &lt; MultipartCGITest
+  def setup
+    super
+    ENV['REQUEST_METHOD'] = 'PUT'
+  end
+end
+
 
 class CGIRequestTest &lt; Test::Unit::TestCase
   def setup
@@ -360,4 +425,16 @@ class CGIRequestTest &lt; Test::Unit::TestCase
     assert_equal [&quot;c84ace84796670c052c6ceb2451fb0f2&quot;], alt_cookies[&quot;_session_id&quot;]
     assert_equal [&quot;yes&quot;], alt_cookies[&quot;is_admin&quot;]
   end
+  
+  def test_unbalanced_query_string_with_array
+   assert_equal(
+     {'location' =&gt; [&quot;1&quot;, &quot;2&quot;], 'age_group' =&gt; [&quot;2&quot;]},
+  CGIMethods.parse_query_parameters(&quot;location[]=1&amp;location[]=2&amp;age_group[]=2&quot;)
+   )
+   assert_equal(
+     {'location' =&gt; [&quot;1&quot;, &quot;2&quot;], 'age_group' =&gt; [&quot;2&quot;]},
+     CGIMethods.parse_request_parameters({'location[]' =&gt; [&quot;1&quot;, &quot;2&quot;],
+  'age_group[]' =&gt; [&quot;2&quot;]})
+   )
+  end
 end</diff>
      <filename>vendor/rails/actionpack/test/controller/cgi_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -46,7 +46,7 @@ end
 
 class CalleeController &lt; ActionController::Base
   def being_called
-    render_text &quot;#{@params[&quot;name&quot;] || &quot;Lady&quot;} of the House, speaking&quot;
+    render_text &quot;#{params[:name] || &quot;Lady&quot;} of the House, speaking&quot;
   end
   
   def blowing_up
@@ -126,4 +126,26 @@ class ComponentsTest &lt; Test::Unit::TestCase
     
     assert_equal &quot;Lady of the House, speaking&quot;, @response.body
   end
-end
\ No newline at end of file
+end
+
+module A
+  module B
+    module C
+      class NestedController &lt; ActionController::Base
+        # Stub for uses_component_template_root
+        def self.caller
+          [ '/path/to/active_support/deprecation.rb:93:in `uses_component_template_root',
+            './test/fixtures/a/b/c/nested_controller.rb' ]
+        end
+      end
+    end
+  end
+end
+
+class UsesComponentTemplateRootTest &lt; Test::Unit::TestCase
+  def test_uses_component_template_root
+    assert_deprecated 'uses_component_template_root' do
+      assert_equal './test/fixtures/', A::B::C::NestedController.uses_component_template_root
+    end
+  end
+end</diff>
      <filename>vendor/rails/actionpack/test/controller/components_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,77 +4,104 @@ class CookieTest &lt; Test::Unit::TestCase
   class TestController &lt; ActionController::Base
     def authenticate_with_deprecated_writer
       cookie &quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;
-      render_text &quot;hello world&quot;
     end
 
     def authenticate
       cookies[&quot;user_name&quot;] = &quot;david&quot;
-      render_text &quot;hello world&quot;
     end
 
     def authenticate_for_fourten_days
       cookies[&quot;user_name&quot;] = { &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2005, 10, 10) }
-      render_text &quot;hello world&quot;
     end
 
     def authenticate_for_fourten_days_with_symbols
       cookies[:user_name] = { :value =&gt; &quot;david&quot;, :expires =&gt; Time.local(2005, 10, 10) }
-      render_text &quot;hello world&quot;
     end
 
     def set_multiple_cookies
       cookies[&quot;user_name&quot;] = { &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2005, 10, 10) }
       cookies[&quot;login&quot;]     = &quot;XJ-122&quot;
-      render_text &quot;hello world&quot;
     end
-
+    
     def access_frozen_cookies
-      @cookies[&quot;will&quot;] = &quot;work&quot;
+      cookies[&quot;will&quot;] = &quot;work&quot;
+    end
+
+    def logout
+      cookies.delete(&quot;user_name&quot;)
+    end
+
+    def delete_cookie_with_path
+      cookies.delete(&quot;user_name&quot;, :path =&gt; '/beaten')
       render_text &quot;hello world&quot;
     end
 
-    def rescue_action(e) raise end
+    def rescue_action(e) 
+      raise unless ActionController::MissingTemplate # No templates here, and we don't care about the output 
+    end
   end
 
   def setup
     @request  = ActionController::TestRequest.new
     @response = ActionController::TestResponse.new
 
+    @controller = TestController.new
     @request.host = &quot;www.nextangle.com&quot;
   end
 
   def test_setting_cookie_with_deprecated_writer
-    @request.action = &quot;authenticate_with_deprecated_writer&quot;
-    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;) ], process_request.headers[&quot;cookie&quot;]
+    get :authenticate_with_deprecated_writer
+    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;) ], @response.headers[&quot;cookie&quot;]
   end
 
   def test_setting_cookie
-    @request.action = &quot;authenticate&quot;
-    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;) ], process_request.headers[&quot;cookie&quot;]
+    get :authenticate
+    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;) ], @response.headers[&quot;cookie&quot;]
   end
 
   def test_setting_cookie_for_fourteen_days
-    @request.action = &quot;authenticate_for_fourten_days&quot;
-    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2005, 10, 10)) ], process_request.headers[&quot;cookie&quot;]
+    get :authenticate_for_fourten_days
+    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2005, 10, 10)) ], @response.headers[&quot;cookie&quot;]
   end
 
   def test_setting_cookie_for_fourteen_days_with_symbols
-    @request.action = &quot;authenticate_for_fourten_days&quot;
-    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2005, 10, 10)) ], process_request.headers[&quot;cookie&quot;]
+    get :authenticate_for_fourten_days
+    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2005, 10, 10)) ], @response.headers[&quot;cookie&quot;]
   end
 
   def test_multiple_cookies
-    @request.action = &quot;set_multiple_cookies&quot;
-    assert_equal 2, process_request.headers[&quot;cookie&quot;].size
+    get :set_multiple_cookies
+    assert_equal 2, @response.cookies.size
   end
 
   def test_setting_test_cookie
-    @request.action = &quot;access_frozen_cookies&quot;
-    assert_nothing_raised { process_request }
+    assert_nothing_raised { get :access_frozen_cookies }
+  end
+  
+  def test_expiring_cookie
+    get :logout
+    assert_equal [ CGI::Cookie::new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;&quot;, &quot;expires&quot; =&gt; Time.at(0)) ], @response.headers[&quot;cookie&quot;]
+  end  
+  
+  def test_cookiejar_accessor
+    @request.cookies[&quot;user_name&quot;] = CGI::Cookie.new(&quot;name&quot; =&gt; &quot;user_name&quot;, &quot;value&quot; =&gt; &quot;david&quot;, &quot;expires&quot; =&gt; Time.local(2025, 10, 10))
+    @controller.request = @request
+    jar = ActionController::CookieJar.new(@controller)
+    assert_equal &quot;david&quot;, jar[&quot;user_name&quot;]
+    assert_equal nil, jar[&quot;something_else&quot;]
   end
 
-  private
-    def process_request
-      TestController.process(@request, @response)
-    end
+  def test_cookiejar_accessor_with_array_value
+    a = %w{1 2 3}
+    @request.cookies[&quot;pages&quot;] = CGI::Cookie.new(&quot;name&quot; =&gt; &quot;pages&quot;, &quot;value&quot; =&gt; a, &quot;expires&quot; =&gt; Time.local(2025, 10, 10))
+    @controller.request = @request
+    jar = ActionController::CookieJar.new(@controller)
+    assert_equal a, jar[&quot;pages&quot;]
+  end
+  
+  def test_delete_cookie_with_path
+    get :delete_cookie_with_path
+    assert_equal &quot;/beaten&quot;, @response.headers[&quot;cookie&quot;].first.path
+    assert_not_equal &quot;/&quot;, @response.headers[&quot;cookie&quot;].first.path
+  end
 end</diff>
      <filename>vendor/rails/actionpack/test/controller/cookie_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,6 @@ class NotAController
 end
 module Admin
   class &lt;&lt; self; alias_method :const_available?, :const_defined?; end
-  SomeConstant = 10
   class UserController &lt; Class.new(ActionController::Base); end
   class NewsFeedController &lt; Class.new(ActionController::Base); end
 end</diff>
      <filename>vendor/rails/actionpack/test/controller/fake_controllers.rb</filename>
    </modified>
    <modified>
      <diff>@@ -16,6 +16,7 @@ class FilterParamTest &lt; Test::Unit::TestCase
     assert @controller.respond_to?(:filter_parameters)
     
     test_hashes = [[{},{},[]],
+    [{'foo'=&gt;nil},{'foo'=&gt;nil},[]],
     [{'foo'=&gt;'bar'},{'foo'=&gt;'bar'},[]],
     [{'foo'=&gt;'bar'},{'foo'=&gt;'bar'},%w'food'],
     [{'foo'=&gt;'bar'},{'foo'=&gt;'[FILTERED]'},%w'foo'],</diff>
      <filename>vendor/rails/actionpack/test/controller/filter_params_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,13 +14,41 @@ class FilterTest &lt; Test::Unit::TestCase
         @ran_filter ||= []
         @ran_filter &lt;&lt; &quot;ensure_login&quot;
       end
-      
+
       def clean_up
         @ran_after_filter ||= []
         @ran_after_filter &lt;&lt; &quot;clean_up&quot;
       end
   end
 
+  class ChangingTheRequirementsController &lt; TestController
+    before_filter :ensure_login, :except =&gt; [:go_wild]
+
+    def go_wild
+      render :text =&gt; &quot;gobble&quot;
+    end
+  end
+
+  class TestMultipleFiltersController &lt; ActionController::Base
+    before_filter :try_1
+    before_filter :try_2
+    before_filter :try_3
+
+    (1..3).each do |i|
+      define_method &quot;fail_#{i}&quot; do
+        render :text =&gt; i.to_s
+      end
+    end
+
+    protected
+    (1..3).each do |i|
+      define_method &quot;try_#{i}&quot; do
+        instance_variable_set :@try, i
+        action_name != &quot;fail_#{i}&quot;
+      end
+    end
+  end
+
   class RenderingController &lt; ActionController::Base
     before_filter :render_something_else
 
@@ -34,7 +62,7 @@ class FilterTest &lt; Test::Unit::TestCase
         render :inline =&gt; &quot;something else&quot;
       end
   end
-  
+
   class ConditionalFilterController &lt; ActionController::Base
     def show
       render :inline =&gt; &quot;ran action&quot;
@@ -58,7 +86,7 @@ class FilterTest &lt; Test::Unit::TestCase
         @ran_filter ||= []
         @ran_filter &lt;&lt; &quot;clean_up_tmp&quot;
       end
-      
+
       def rescue_action(e) raise(e) end
   end
 
@@ -66,7 +94,7 @@ class FilterTest &lt; Test::Unit::TestCase
     before_filter :ensure_login, :except =&gt; [ :show_without_filter, :another_action ]
   end
 
-  class OnlyConditionSymController &lt; ConditionalFilterController 
+  class OnlyConditionSymController &lt; ConditionalFilterController
     before_filter :ensure_login, :only =&gt; :show
   end
 
@@ -76,10 +104,10 @@ class FilterTest &lt; Test::Unit::TestCase
 
   class BeforeAndAfterConditionController &lt; ConditionalFilterController
     before_filter :ensure_login, :only =&gt; :show
-    after_filter  :clean_up_tmp, :only =&gt; :show 
+    after_filter  :clean_up_tmp, :only =&gt; :show
   end
-  
-  class OnlyConditionProcController &lt; ConditionalFilterController 
+
+  class OnlyConditionProcController &lt; ConditionalFilterController
     before_filter(:only =&gt; :show) {|c| c.assigns[&quot;ran_proc_filter&quot;] = true }
   end
 
@@ -103,6 +131,14 @@ class FilterTest &lt; Test::Unit::TestCase
     before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.assigns[&quot;ran_proc_filter1&quot;] = true }, :except =&gt; :show_without_filter) { |c| c.assigns[&quot;ran_proc_filter2&quot;] = true}
   end
 
+  class EmptyFilterChainController &lt; TestController
+    self.filter_chain.clear
+    def show
+      @action_executed = true
+      render :text =&gt; &quot;yawp!&quot;
+    end
+  end
+
   class PrependingController &lt; TestController
     prepend_before_filter :wonderful_life
     # skip_before_filter :fire_flash
@@ -117,7 +153,7 @@ class FilterTest &lt; Test::Unit::TestCase
   class ConditionalSkippingController &lt; TestController
     skip_before_filter :ensure_login, :only =&gt; [ :login ]
     skip_after_filter  :clean_up,     :only =&gt; [ :login ]
-    
+
     before_filter :find_user, :only =&gt; [ :change_password ]
 
     def login
@@ -127,31 +163,35 @@ class FilterTest &lt; Test::Unit::TestCase
     def change_password
       render :inline =&gt; &quot;ran action&quot;
     end
-    
+
     protected
       def find_user
         @ran_filter ||= []
         @ran_filter &lt;&lt; &quot;find_user&quot;
       end
   end
-  
+
   class ConditionalParentOfConditionalSkippingController &lt; ConditionalFilterController
     before_filter :conditional_in_parent, :only =&gt; [:show, :another_action]
     after_filter  :conditional_in_parent, :only =&gt; [:show, :another_action]
-    
+
     private
-      
+
       def conditional_in_parent
         @ran_filter ||= []
         @ran_filter &lt;&lt; 'conditional_in_parent'
       end
   end
-  
+
   class ChildOfConditionalParentController &lt; ConditionalParentOfConditionalSkippingController
     skip_before_filter :conditional_in_parent, :only =&gt; :another_action
     skip_after_filter  :conditional_in_parent, :only =&gt; :another_action
   end
 
+  class AnotherChildOfConditionalParentController &lt; ConditionalParentOfConditionalSkippingController
+    skip_before_filter :conditional_in_parent, :only =&gt; :show
+  end
+
   class ProcController &lt; PrependingController
     before_filter(proc { |c| c.assigns[&quot;ran_proc_filter&quot;] = true })
   end
@@ -165,7 +205,7 @@ class FilterTest &lt; Test::Unit::TestCase
       controller.assigns[&quot;was_audited&quot;] = true
     end
   end
-  
+
   class AroundFilter
     def before(controller)
       @execution_log = &quot;before&quot;
@@ -177,7 +217,7 @@ class FilterTest &lt; Test::Unit::TestCase
       controller.assigns[&quot;execution_log&quot;] = @execution_log + &quot; and after&quot;
       controller.assigns[&quot;after_ran&quot;] = true
       controller.class.execution_log &lt;&lt; &quot; after aroundfilter &quot; if controller.respond_to? :execution_log
-    end    
+    end
   end
 
   class AppendedAroundFilter
@@ -187,30 +227,29 @@ class FilterTest &lt; Test::Unit::TestCase
 
     def after(controller)
       controller.class.execution_log &lt;&lt; &quot; after appended aroundfilter &quot;
-    end    
-  end  
-  
+    end
+  end
+
   class AuditController &lt; ActionController::Base
     before_filter(AuditFilter)
-    
+
     def show
       render_text &quot;hello&quot;
     end
   end
 
-  class BadFilterController &lt; ActionController::Base
-    before_filter 2
-    
-    def show() &quot;show&quot; end
-    
-    protected
-      def rescue_action(e) raise(e) end
-  end
-
   class AroundFilterController &lt; PrependingController
     around_filter AroundFilter.new
   end
 
+  class BeforeAfterClassFilterController &lt; PrependingController
+    begin
+      filter = AroundFilter.new
+      before_filter filter
+      after_filter filter
+    end
+  end
+
   class MixedFilterController &lt; PrependingController
     cattr_accessor :execution_log
 
@@ -224,7 +263,7 @@ class FilterTest &lt; Test::Unit::TestCase
     after_filter  { |c| c.class.execution_log &lt;&lt; &quot; after procfilter &quot; }
     append_around_filter AppendedAroundFilter.new
   end
-  
+
   class MixedSpecializationController &lt; ActionController::Base
     class OutOfOrder &lt; StandardError; end
 
@@ -262,6 +301,101 @@ class FilterTest &lt; Test::Unit::TestCase
       end
   end
 
+  class PrependingBeforeAndAfterController &lt; ActionController::Base
+    prepend_before_filter :before_all
+    prepend_after_filter :after_all
+    before_filter :between_before_all_and_after_all
+
+    def before_all
+      @ran_filter ||= []
+      @ran_filter &lt;&lt; 'before_all'
+    end
+
+    def after_all
+      @ran_filter ||= []
+      @ran_filter &lt;&lt; 'after_all'
+    end
+
+    def between_before_all_and_after_all
+      @ran_filter ||= []
+      @ran_filter &lt;&lt; 'between_before_all_and_after_all'
+    end
+    def show
+      render :text =&gt; 'hello'
+    end
+  end
+
+  class NonYieldingAroundFilterController &lt; ActionController::Base
+
+    before_filter :filter_one
+    around_filter :non_yielding_filter
+    before_filter :filter_two
+    after_filter :filter_three
+
+    def index
+      render :inline =&gt; &quot;index&quot;
+    end
+
+    #make sure the controller complains
+    def rescue_action(e); raise e; end
+
+    private
+
+      def filter_one
+        @filters  ||= []
+        @filters  &lt;&lt; &quot;filter_one&quot;
+      end
+
+      def filter_two
+        @filters  &lt;&lt; &quot;filter_two&quot;
+      end
+
+      def non_yielding_filter
+        @filters  &lt;&lt; &quot;zomg it didn't yield&quot;
+        @filter_return_value
+      end
+
+      def filter_three
+        @filters  &lt;&lt; &quot;filter_three&quot;
+      end
+
+  end
+
+  def test_non_yielding_around_filters_not_returning_false_do_not_raise
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set &quot;@filter_return_value&quot;, true
+    assert_nothing_raised do
+      test_process(controller, &quot;index&quot;)
+    end
+  end
+
+  def test_non_yielding_around_filters_returning_false_do_not_raise
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set &quot;@filter_return_value&quot;, false
+    assert_nothing_raised do
+      test_process(controller, &quot;index&quot;)
+    end
+  end
+
+  def test_after_filters_are_not_run_if_around_filter_returns_false
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set &quot;@filter_return_value&quot;, false
+    test_process(controller, &quot;index&quot;)
+    assert_equal [&quot;filter_one&quot;, &quot;zomg it didn't yield&quot;], controller.assigns['filters']
+  end
+
+  def test_after_filters_are_not_run_if_around_filter_does_not_yield
+    controller = NonYieldingAroundFilterController.new
+    controller.instance_variable_set &quot;@filter_return_value&quot;, true
+    test_process(controller, &quot;index&quot;)
+    assert_equal [&quot;filter_one&quot;, &quot;zomg it didn't yield&quot;], controller.assigns['filters']
+  end
+
+  def test_empty_filter_chain
+    assert_equal 0, EmptyFilterChainController.filter_chain.size
+    assert test_process(EmptyFilterChainController).template.assigns['action_executed']
+  end
+
   def test_added_filter_to_inheritance_graph
     assert_equal [ :ensure_login ], TestController.before_filters
   end
@@ -269,11 +403,11 @@ class FilterTest &lt; Test::Unit::TestCase
   def test_base_class_in_isolation
     assert_equal [ ], ActionController::Base.before_filters
   end
-  
+
   def test_prepending_filter
     assert_equal [ :wonderful_life, :ensure_login ], PrependingController.before_filters
   end
-  
+
   def test_running_filters
     assert_equal %w( wonderful_life ensure_login ), test_process(PrependingController).template.assigns[&q