Say you want to create a little box of functionality you can just drop into any page – say a sidebar or a search box. Here’s one approach to solving this problem in Rails.

The view: app/views/component/_comment.rhtml

<pre>
<code>
<form action="/component/comment/<%= category %>" method="post">
  Email: <input type="text" name="email" size="40" maxlength="80" /><br />
  Comment: <textarea rows="5" cols="65" name="text" /><br />
  <input type="hidden" name="return_url" value="<%= return_url %>">
  <input type="submit" value="Save">
</form>

<table>
  <tr>

    <th>Email</th>
    <th>Comment</th>
  </tr>
  <% for entry in entrylist do %>
  <tr>
    <td><%= entry.email %></td>
    <td><%= entry.text %></td>

  </tr>
  <% end %>
</table>
</code>
</pre>

The helper: app/helpers/component_helper.rb

<pre>
<code>
require 'action_view/partials'

require 'comment' # load the model

module ComponentHelper
  def self.append_features(controller) #:nodoc:
    controller.ancestors.include?(ActionController::Base) ? controller.add_template_helper(self) : super
  end

  def comment_component(comment_category)
    render_partial "component/comment", nil,
      {"category" => comment_category,
       "entrylist" => Comment.find(:all, :conditions => ["category = '%s'", comment_category]),
       "return_url" => @request.request_uri}
  end
end
</code>
</pre>

The controller: app/controllers/component_controller.rb

<pre>
<code>
require 'abstract_application'
require 'component_helper'

require 'comment' # load the model

class ComponentController < AbstractApplicationController
  include ComponentHelper

  def comment
    Comment.create(
      "category" => @params["id"],
      "email" => @params["email"],
      "text" => @params["text"])

    redirect_to_path @params["return_url"]
  end
end
</code>
</pre>

The view that wishes to insert this component simply needs to contain:

<pre>
<code>
<%
  require 'component_helper'
  extend ComponentHelper
%>
...
<%= comment_component("category") %>
</code>
</pre>

It is a simple matter to add more functionality to the component – for example, the controller could read:

<pre>
<code>
  def comment
    case @params["id"]
    when "create" 
      Comment.create(
        "category" => @params["category"],
        "email" => @params["email"],
        "text" => @params["text"])
    when "delete" 
      Comment.destroy(@params["target"].to_i)
    end

    redirect_to_path @params["return_url"]
  end
</code>
</pre>

Then you could have a form for deleting comments which directed the browser to /component/comment/delete?target=3 (for example).

If the component is complex enough to warrant it, you could even give it its own helper and controller, but use the same technique to drop it into other pages.

It’s also a simple matter to add more components – just add more methods to the helper to display the components, and more methods to the controller to handle their behaviour.

There is an example of how to implement this same thing in IOWA at http://enigo.com/demos/components.html
