Thin-man

A Rails restful-ajax library that makes web apps lively while keeping all the logic on the server.

View the Project on GitHub edraut/thin-man

Restful AJAX apps

Let's take the example of Comments on a Post. Say we have a page that displays a Post with all its comments. We'll have a list of Comments and a button/link to create a new comment. If you're the moderator, you might have delete buttons/links for each comment as well.

To keep things manageable, you'll want a container for the list:

<div id="post_#{post.id}_comments">
  <% post.comments.each do |comment| %>
    <%= render partial: 'comments/show', locals: {comment: comment} %>
  <% end %>
</div>

Notice the DOM id of the div contains the unique id of the post. If you ever have multiple levels of resources nested on the page this will be important for avoiding duplicate DOM ids that would break ThinMan. Stay in the habit of using unique ids in your target selectors to avoid unpleasant, hard-to-debug problems down the line.

Now your new comment form can reference the DOM id of your comment list container:

  <%= form_for @post.comments.new, html: ajax_hash("#post_#{post.id}_comments",
      'append'), do |f| %>
    ...
  <% end %>

This link will put the AJAX response at the end of the container, as designated by the jQuery 'append' method.

Now let's look at the comment DOM. You'll need 2 rails view partials for displaying list elements.

The first renders a container, the second renders the contents.

app/views/comments/_show_container.html.erb

<div id="<%= dom_id(comment) %>">
  <%= render partial: 'comments/show', locals: {comment: comment} %>
</div>

app/views/comments/_show.html.erb

  <%= comment.body %>
  <%= ajax_link 'edit', edit_comment_url(comment), {} dom_target(comment) %>
  <%= ajax_delete 'delete', comment_url(comment), {}, dom_target(comment) %>

app/views/comments/_edit.html.erb

  <%= form_for comment, html: ajax_form_hash(dom_target(comment)), do |f| %>
    ...
  <% end %>

In your controllers:

app/controllers/comments_controller.rb

def show
  render partial: 'show', locals: {comment: @comment}
end

def edit
  render partial: 'edit', locals: {comment: @comment}
end

def create
  @comment = Comment.create(params[:comment])
  render partial: 'show_container', locals: {comment: @comment}
end

def update
  @comment.update_attributes(params[:comment])
  render partial: 'show', locals: {comment: @comment}
end

def destroy
  @comment.destroy
  render nothing: true
end