¶ ↑
ApotomoWeb Components for Rails.
¶ ↑
OverviewDo you need an interactive user interface for your Rails application? A cool Rich Client Application with dashboards, portlets and AJAX, Drag&Drop and jQuery?
Is your controller gettin’ fat? And your partial-helper-AJAX pile is getting out of control?
Do you want a framework to make the implementation easier? You want Apotomo.
¶ ↑
ApotomoApotomo is based on Cells, the popular View Components framework for Rails.
It gives you widgets and encapsulation, bubbling events, AJAX page updates, rock-solid testing and more. Check out apotomo.de for a bunch of tutorials and a nice web 2.0 logo.
¶ ↑
InstallationEasy as hell.
¶ ↑
Rails 3gem install apotomo
¶ ↑
Rails 2.3gem install apotomo -v 0.1.4
Don’t forget to load the gem in your app, either in your Gemfile
or environment.rb
.
¶ ↑
Example!A shitty example is worse a shitty framework, so let’s choose wisely…
Say you had a blog application. The page showing the post should have a comments block, with a list of comments and a form to post a new comment. Submitting should validate and send back the updated comments list, via AJAX.
Let’s wrap that comments block in a widget.
¶ ↑
GenerateGo and generate a widget stub.
$ rails generate apotomo:widget CommentsWidget display write -e haml create app/cells/ create app/cells/comments_widget create app/cells/comments_widget.rb create app/cells/comments_widget/display.html.haml create app/cells/comments_widget/write.html.haml create test/widgets/comments_widget_test.rb
Nothing special.
¶ ↑
Plug it inYou now tell your controller about the new widget.
class PostsController < ApplicationController include Apotomo::Rails::ControllerMethods has_widgets do |root| root << widget('comments_widget', 'post-comments', :post => @post) end
The widget is named post-comments
. We pass the current post into the widget - the block is executed in controller instance context, that’s were @post
comes from. Handy, isn’t it?
¶ ↑
Render the widgetRendering usually happens in your controller view, views/posts/show.haml
, for instance.
%h1 @post.title %p @post.body %p = render_widget 'post-comments'
¶ ↑
Write the widgetA widget is like a cell which is like a mini-controller.
class CommentsWidget < Apotomo::Widget responds_to_event :submit, :with => :write def display(args) @comments = args[:post].comments # the parameter from outside. render end
Having display
as the default state when rendering, this method collects comments to show and renders its view.
And look at line 2 - if encountering a :submit
event we invoke write
, which is simply another state. How cool is that?
def write(evt) @comment = Comment.new(:post_id => evt[:post_id]) @comment.update_attributes evt[:comment] # a bit like params[]. update :state => :display end end
The event is processes with three steps in our widget:
-
create the new comment
-
re-render the
display
state -
update itself on the page.
Apotomo helps you focusing on your app and takes away the pain of action dispatching and page updating.
¶ ↑
Triggering eventsSo how and where is the :submit
event triggered?
Take a look at the widget’s view display.html.haml
.
= widget_div do %ul - for c in @comments %li c.text - form_for :comment, @comment, :html => {"data-event-url" => url_for_event(:submit)} do |f| = f.error_messages = f.text_field :text = submit_tag "Don't be shy, comment!"
That’s a lot of familiar view code, almost looks like a partial.
The view also contains a bit of JavaScript.
:javascript var form = $("##{widget_id} form"); form.submit(function() { $.ajax({url: form.attr("data-event-url"), data: form.serialize()}) return false; });
Triggering happens by sending an AJAX request to an address that the Apotomo helper url_for_event
generated for us.
¶ ↑
Event processingNow what happens when the event request is sent? Apotomo - again - does three things for you, it
-
accepts the request on a special event route it adds to your app
-
triggers the event in your ruby widget tree, which will invoke the
#process
state in our comment widget -
sends back the page updates your widgets rendered
¶ ↑
JavaScript AgnosticismIn this example, we use jQuery for triggering. We could also use Prototype, RightJS, YUI, or a self-baked framework, that’s up to you.
Also, updating the page is in your hands. Where Apotomo provides handy helpers as #replace
, you could also emit your own JavaScript.
Look, replace
basically generates
$("post-comments").replaceWith(<the rendered view>);
If that’s not what you want, do
def write(evt) if evt[:comment][:text].explicit? render :text => 'alert("Hey, you wanted to submit a pervert comment!");' end end
Apotomo doesn’t depend on any JS framework - you choose!
¶ ↑
TestingApotomo comes with its own test case and assertions to build rock-solid web components.
class CommentsWidgetTest < Apotomo::TestCase has_widgets do |root| root << widget(:comments_widget, 'me', :post => @pervert_post) end def test_render render_widget 'me' assert_select "li#me" trigger :submit, :comment => {:text => "Sex on the beach"} assert_response 'alert("Hey, you wanted to submit a pervert comment!");' end end
You can render your widgets, spec the markup, trigger events and assert the event responses, so far. If you need more, let us know!
¶ ↑
More featuresThere’s even more, too much for a simple README.
- Statefulness
-
Deriving your widget from
StatefulWidget
gives you free statefulness. - Composability
-
Widgets can range from small standalone components to nested widget trees like complex dashboards.
- Bubbling events
-
Events bubble up from their triggering source to root and thus can be observed, providing a way to implement loosely coupled, distributable components.
- Team-friendly
-
Widgets encourage encapsulation and help having different developers working on different components without getting out of bounds.
Give it a try- you will love the power and simplicity of real web components!
¶ ↑
Bugs, CommunityPlease visit apotomo.de, the official project page with lots of examples.
If you have questions, visit us in the IRC channel #cells at irc.freenode.org.
If you wanna be cool, subscribe to our feed!
¶ ↑
LicenseCopyright © 2007-2011 Nick Sutterer <apotonick@gmail.com> under the MIT License