“Apotomo”:http://github.com/apotonick/apotomo, the *web components framework for Rails*, got 1.1 today and we’re more than happy to announce nothing but *massive simplifications for users* and conventional changes.
h3. What happened?
The framework core got a slimming cure. *We threw out 50% of the code* and it still does the very same as before – this is nothing to worry about, just read on.
* *Convention over configuration* found its way into Apotomo, widgets now have a *conventional @Widget@ suffix* (and more).
* *State-args* are now heavily used in Apotomo (as known from “Cells”:http://nicksda.apotomo.de/2011/02/cells-3-5-release-party-summary/). For instance, triggered states receive the event object as argument.
* *Rendering children widgets* got nothing more than calling @#render_widget@ – as it’s done in controller views.
* *Start states* were removed. Nobody needs it.
* *Statefulness* got separated to its own gem. This reduces complexity in the core.
h3. Will it break my code?
In the *worst case you gotta move your widgets to their new residence*, rename a couple of directories and change calls to @#param@ back to @#params@.
However, we got a lot of *sweeeet improvements*.
h3. Convention over Configuration
This change was initiated by some famous Rails celebrity, recently. In a “public discussion”:http://groups.google.com/group/cells-and-apotomo/browse_thread/thread/9ca3cfac57825db7 the pseudo-democratic regime finally decided to introduce *the following changes to the widget file layout*.
$ rails g apotomo:widget Cart display -e haml create app/widgets/cart_widget.rb invoke haml create app/widgets/cart/display.html.haml invoke test_unit create test/widgets/cart_widget_test.rb
* *Widgets now reside in @app/widgets/@* to prevent mixing things with plain cells.
* Notice how the @_widget@ suffix is appended per convention. This means all your widgets _must_ have that suffix (otherwise, you need to change things a bit).
* Views used to live in something like @app/cells/cart_widget/@, they *are now at @app/widgets/cart@.* That’s how things already work with controllers, models, cells, and more.
* The *widget generator* now is a good rails citizen and provides hooks for template engine and test framework. BTW- we’re “soon” gonna release rspec-apotomo, if that is what you’re looking for.
These changes have a couple of nice side effects.
h3. Save the earth, plant a widget tree.
In earlier times the *widget tree setup always happened to look a bit clumsy*.
class DashboardController < ApplicationController include Apotomo::Rails::ControllerMethods has_widgets do |root| root << widget(:cart_widget, 'your_cart', :show)
You had to pass the class, an id and *a mysterious _start state_*. This now looks a bit simpler.
class DashboardController < ApplicationController has_widgets do |root| root << widget(:cart)
Simpler, isn’t it?
* You can skip the @include Apotomo::…@ part, *Apotomo will automatically its controller module* into the widget controller. Don’t worry, no other controller will be harmed.
* Apotomo *set the widget id to @:cart@*. Did you notice how the convention saved you from typing the suffix, also?
Now what if I want another id for my Rails widget? Maybe we need a second cart widget?
has_widgets do |root| root << widget(:cart) root << widget(:cart, 'your_private_cart')
Rendering widgets still requires you to pass the id.
= render_widget :cart = render_widget 'your_private_cart'
Simpler, isn’t it?
h3. Nobody needs start states.
You may wonder what happened to the start states. The answer is: Start states survived from the times when Apotomo was a *Modeldrivenarchitecture-generic-widgets-with-integrated-statemachines-and-space-simulator-beast*. Useless crap.
= render_widget :cart
will invoke the *conventional @#display@ state* on the widget. Pass the state explicitely if you want something _else_.
= render_widget :cart, :show
Simpler, isn’t it?
h3. Rendering the kids.
Until today *Apotomo did a horrible job trying to render children* automatically in a widget. There was this @rendered_children@ hash in the view and, oh come on, let’s just forget that and see how it works now.
My family: - children.each do |kid| = render_widget kid
Just *use @#render_widget@ in a state view* and you’re done.
Or maybe just a single child?
= render_widget 'billy-the-kid'
That’s all. Pass whatever state-args options you like.
Simpler, isn’t it?
h3. Using options for configuration.
Often you need to *stick configuration to widget instances*. You may do that in the @has_widgets@ block.
has_widgets do |root| root < "light")
This hash will be *available in _any_ state* using the new @#options@ method from Cells.
class CartWidget < Apotomo::Widget def display @theme = options[:theme] || "sunset"
Note that this also works in triggered states! What happens in the @has_widgets@ block is available anywhere.
h3. State-args FTW!
Since state-args were introduced in Cells *I _love_ them!* They make things so much easier and ruby-like. They basically allow *passing locals to a state*.
= render_widget :cart, :display, current_user.admin?
Note how you can set default values in the state, which is awesome.
def display(is_admin=false) raise "Admins do not shop!" if is_admin
Even cooler, *triggered states can receive the bubbling event* instance.
responds_to_event :submit def submit(evt)
Hey, that’s cool, @responds_to_event@ *doesn’t require a trigger state name* anymore if it’s identical to the event name.
So, how does that event argument help us? Actually, it helps twice!
h3. Where is #param?
Luckily, we removed @#param@. It sucked.
*If you need global request parameters*, use @#params@.
However, *in a good component-oriented design* you shouldn’t need to access global parameters _inside_ widgets.
= url_for_event :submit, :item_id => item.id
Now, why not query the event for request parameters.
def submit(evt) item = Item.find evt[:item_id]
You’re still reading request parameters, but from a local event object – definitely better.
h3. Using the event for message passing.
When triggering an event it sometimes *makes sense to add some payload data*. Consider that triggering state somewhere.
class Cart::ItemsWidget @items
Another widget interested in that event can use *_state-args_ to grab the event*.
class Cart::CheckoutWidget < Apotomo::Widget responds_to_event :items_changed def items_changed(evt) items = evt[:items]
Get the *event as argument and use @#@* to access its payload.
Simpler, isn’t it?
h3. Use it!
That was a not-so-brief overview. *We strongly encourage you to upgrade.* It’s actually _fun_ using the new features and will make your code even cleaner. Tell us if you like it.
8 thoughts on “Apotomo 1.1 released!”
Is there any greek on the development team ? Apotomo means abrupt in greek 🙂
Great release, i cannot wait to play with it.
I specially love the options, args and params changes.
Brilliant changes, Nick. The clarity is priceless.
The required changes were so simple to make.
I offer one addition to “Will it break my code ?”
You also have to change the widget test.
root << widget(:my_name_widget, ‘me’)
to this :
root << widget(:my_name, ‘me’)
Who is that cool guy in the picture? Oh wait, it’s me. 😀 As it just happens, I’m even wearing that t-shirt right now …
this place is like the Rails’ shortbus. but it’s good to have all the retards in one place.
@Spyros: Yeah, some other greek guys already told me 😀 It’s an abbreviation with greek meaning by accident, but I like it!
@Clemens: Dude I was trying to email you to ask about the rights for that pic but found out I DON’T HAVE YOUR EMAIL!!!
@anon: Clemens is not affiliated with Apotomo, so don’t put him in the shortbus! BTW, your breath stinks and your dick is too short haha
Wow, ‘been waiting for something like this. Wish I found out about it 3 months ago, hehe.
Awesome changes, Nick — we gotta update the README for Apotomo. But seriously, good changes!