Archive for April, 2010

Writing an interactive form (Part 4)

Saturday, April 17th, 2010

I’m really considering shooting screencasts, blogging is tedious. I could talk stupid things and upload. How’d you like that?

app-with-form

Ok, last week we had a working app with a list widget showing the items. Today we’re gonna write a form widget to add new items. The code for this lesson is at tag 0.7.

1First thing to do is generating stubs.

$ script/generate widget item_form display create --haml
      create  app/cells/item_form
      create  app/cells/item_form.rb
      create  app/cells/item_form/display.html.haml
      create  app/cells/item_form/create.html.haml

The app/cells/item_form.rb file looks like this, after some editing.

1
2
3
4
5
6
class ItemForm < Apotomo::StatefulWidget
  def display
    @todo = Todo.new
    render
  end
end

I create a new Todo so I can use it in the view.

2The view needs to be implemented, too!

app/cells/item_form/display.html.haml

1
2
3
4
5
6
7
%h3 Create new item
 
= error_messages_for :todo
 
- form_to_event(:submit) do
  = text_area :todo, :text, :rows => 2
  = submit_tag "Create Todo!"

Easy. The only interesting here is the call to #form_to_event
which basically triggers a :submit event when the form is, nah, submitted (line 5).

3
Let’s append the form widget to our widget tree in app/controllers/dashboard_controller.rb.

1
2
3
4
5
6
7
8
9
10
class DashboardController < ApplicationController
  include Apotomo::ControllerMethods
 
  def index
    use_widgets do |root|
      root << widget(:item_list, 'dashboard_list')
      root << widget(:item_form, 'new_item')
    end
  end
end

I call the widget new_item (line 7).

4Pluggin’ the widget into the controller again happens in app/views/dashboard/index.html.erb (line 10 and 12).

<h2>Dashboard</h2>
 
<div class="slot">
  <%= render_widget 'dashboard_list' %>
</div>
 
<div class="slot">
  <%= render_widget 'new_item' %>
</div>

5In order to process the form, we need to observe that :submit event, so I will extend the widget class a bit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ItemForm < Apotomo::StatefulWidget
  def display
    respond_to_event :submit, :with => :create
 
    @todo = Todo.new
    render
  end
 
  def create
    @todo.update_attributes(param(:todo))
 
    render :view => :display
  end
end

The event handler (line 3) will execute the #create method when the form is submitted.

Nothing unusual happens there, I update the @todo and instruct the widget to render the former view display.html.haml.

6Entering some text into the form and pressing Submit yields the expected result! The new item gets saved.

app-with-form

However, the list doesn’t update so we can not see what we did. We should implement some notification so the list updates when there was a new item created.

That’s what we’re gonna do in the next lesson, the 12 minutes are over!

Using Cells View Inheritance to clean up your views

Monday, April 12th, 2010

To the right!

In every Rails app is some reoccurring element.

In my blog example, this is a small teaser box (to the right!) showing a snippet from the newest post, and a footer section that provides a link to the article.

The teaser on my RubyFlow portal page.

However. My imaginary blog also has a page about RubyFlow where I’d list the links I posted there, just to proof how cool I am.

On the RubyFlow page, I don’t want the teaser to show a link to the article, but a red button to RubyFlow. Of course, this doesn’t make sense at all.

My god, I hate that red in combination with the pink on this site.

The medieval if-cascade

Things like that are usually implemented in the partial with some if/else part.

if @current_page == 'rubyflow'
  Don't read it, better go to rubyflow
else
  = link_to "Read it!", "/blog/read/#{@post.id}"

Pretty ugly.

Things start getting really ugly when there are more conditions to respect and end up in a medieval if-cascade. It’s geting even worse when those deciders are copied and spread across multiple files.

How we do it

Cells brings back the power of OOP to your views. It offers a feature called View Inheritance that saves us here. I will briefly discuss that while showing you how I implemented the different teasers.

$ script/generate cell teaser box body footer --haml
      create  app/cells/teaser_cell.rb
      create  app/cells/teaser/box.html.haml   
      create  app/cells/teaser/body.html.haml
      create  app/cells/teaser/footer.html.haml

The TeaserCell has three states that don’t do much right now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TeaserCell < ::Cell::Base
  def box
    @post = Post.find :first
    render
  end
 
  def body
    render
  end
 
  def footer
    render
  end
end

Just grabbing the newest post in line 3.

Nesting states

I use the box state as a container. It renders the body and footer in its view app/cells/teaser/box.html.haml.

#teaser
  %h3
    Nick says
 
  .body
    = render :state => :body
  .footer
    = render :state => :footer

That’s right, I use render :state in a cell view. That’s somehow comparable to rendering a partial, although it first executes the #body method, which in turn renders its view.

teaser-with-generic-body-and-footer

That’s how the teaser currently looks like as we didn’t customize the body and footer view, yet.

It is pretty obvious that our body view should print out the teaser text, whereas the footer displays the link.

app/cells/teaser/body.html.haml

%h2 #{@post.title}
%p  #{@post.body}

And the footer in app/cells/teaser/footer.html.haml.

= link_to "Read it!", "/blog/read/#{@post.id}"

Enriching the controller

In the blog view I then call #render_cell to integrate the teaser in my blog ERB page.

<div id="right_menu">
  <%= render_cell :teaser, :box %>
</div>

Ok, so now we got a beautiful teaser in the blog page. How do we get that teaser to show a red button on the RubyFlow page?

Inheritance strikes again!

The answer is - unsurprisingly: we derive a new RubyflowTeaserCell from the TeaserCell.

$ script/generate cell rubyflow_teaser footer --haml
      create  app/cells/rubyflow_teaser_cell.rb
      create  app/cells/rubyflow_teaser/footer.html.haml

The actual inheritance happens in the class.

class RubyflowTeaserCell < TeaserCell
end

Note that we don’t need any methods as they all come from TeaserCell. The same applies to the views, they’re inherited as well!

But wait, we planned on overwriting the footer so here’s how the app/cells/rubyflow_teaser/footer.html.haml looks like.

Don't read it, better go to
%span rubyflow

In the Rubyflow controller view I add the cells rendering.

<div id="right_menu">
  <%= render_cell :rubyflow_teaser, :box %>
</div>

That’s a call to RubyflowTeaserCell. Now, what happens here.

  • the derived RubyflowTeaserCell calls the #box method which is inherited
  • as the respective view is not found in app/cells/rubyflow_teaser it is looked up in the parent directory app/cells/teaser/ and found
  • the same happens with the :body state
  • when rendering the footer, the view is found in the cells own view directory and thus not inherited

Conclusion

View inheritance comes into play whenever complex deciders threaten the straightforwardness of your view code. When you encounter if-cascades with more than two ifs, think about using a cell with inheritance there.

Code?

The example code can be found at github.

ActiveHelper + Rails is no pain in the ass!

Saturday, April 10th, 2010

Recently (about 6 minutes ago) I added support for Rails in ActiveHelper. The library as-it is still completely independent from any MVC framework and simply provides a generic helper API.

However, we got some Rails bindings now.

If you’re not yet familiar with ActiveHelper, no problem, it’s brandnew. Consider skimming the introducing post first. Or directly jump to the github repository.

Writing the helper

Let’s assume you want to create a new helper to replace the #link_to method. Why not name it LinkingHelper?

class LinkingHelper < ActiveHelper::Base
  provides :link_for
 
  def link_for(title, url)
    '<a href="' + url + '">' + title + '</a>'
  end
end

You’d put this into app/active_helpers/linking_helper.rb.

Preparing the controller

Bla bla bla, it’s obvious how to use ActiveHelpers in controllers.

class BlogController < ActionController::Base
  active_helper LinkingHelper
 
  def show
    @post = Post.find params[:id]
  end
end

Helping the view

As soon as the controller did include the LinkingHelper its provided methods are around in the view.

1
2
3
4
5
6
Bookmarkable link: <%= link_for @post.title, @post.url %>
 
<h1><%= @post.title %></h1>
<p>
  <%= @post.body %>
</p>

You used your #link_for to generate a link in line 1.

So… where’s the benefit?

Being a good programmer you instantly decide to extract the markup logic from the LinkingHelper to some separate MarkupHelper.

The latter would provide a method #tag for generating markup.

Your former helper now looks like this.

1
2
3
4
5
6
7
class LinkingHelper < MarkupHelper
  provides :link_for
 
  def link_for(title, url)
    tag(:a, title, :href => url)
  end
end

Noticed the inheritance in line 1? Wow- that’s OOP features in helpers. Nice.

Reusing old-school helpers

Now what if you still want to use #url_for to setup the link address? It’s hidden somewhere in Rails gloomy mixin magic.

You know it’s floating around in your view. So go and use it.

1
2
3
4
5
6
7
8
9
class LinkingHelper < MarkupHelper
  provides :link_for
  needs :url_for
 
  def link_for(title, url)
    url = url_for(url)
    tag(:a, title, :href => url)
  end
end

Step-by-step, dude. You simply use #url_for in line 6. Your helper knows about it since you defined the dependency (line 3).

That’s gonna work- but… why?

Well, any ActiveHelper simply delegates dependencies back to where it was imported into. In our case, any call to #url_for is routed to the ActionView instance which has to care about that.

Delegation for a better world

The new keywords in the Helper World are delegation and interfaces.

Besides that, we got back OOP and inheritance to helpers.

Lemme end this boring post by quoting Manuel who’s just diving into ActiveHelpers.

It’s funny- ActiveHelpers is nothing more than 80 lines of code. Anyway, it’s forcing you to write clean code… in a gentle way!

Isn’t that great?

Making unobstrusive pagination (Part 3)

Friday, April 2nd, 2010

In the last lesson I demonstrated how we could quickly write our own pagination using #link_to_event.

A couple of guys (including mislav himself) told me to make the pagination unobstrusive, so here’s how i did this.

simple_paging

You’re gonna learn how to

  • pack unobstrusive JavaScript in Apotomo widgets
  • use Helpers in widgets
  • configure your widget view with #render

1I didn’t have any clue what unobstrusive JavaScript refers to. It means “separating JavaScript and markup”. After lots of tries, I figured out how to let will_paginate handle the markup and do the JS part myself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%h3 Things
%p
  = page_entries_info @items
 
%ul
  - @items.each do |item|
    %li
      = item.text
 
= will_paginate @items
 
- javascript_tag do
  $(document.body).observe('click', function(e) {
  var el = e.element()
 
  if (el.match('.pagination a')) {
  page = el.href.split("page=").last(); // I don't know how to do it better.
  = "new Ajax.Request('#{url_for_event(:paginate)}'+'&page='+page, { method: 'get' })"
 
  e.stop()
  }})

Instead of modifying will_paginate's output (line 10) I add an observer that triggers the :pagination event (line 13).

I have to extract the page from the element href, that’s a little bit fiddling. Maybe someone can tell me how to do it better (line 17)?

The interesting part here is the call to #url_for_event (line 18). It computes the url to trigger the paginate event, which happens in Ruby!

That works. After clicking a page, the widgets updates itself via AJAX.

2Anyway, I keeps adding observers as the widgets updates and re-injects the JS into the page over and over again. There are multiple solutions to the problem.

  • Fix the JS to add the observer only once
  • Use two different views in your widget, the initial one with, the following without the JS
  • Write the JS to a global variable using #capture that’s evaluated only once when the page is rendered completely

Personally I hate using global variables (that’s what #capture does). I will use it anyway cause it shows a few interesting concepts.

3We need to render the JavaScript to the page footer.

Cells provide a view method #global_capture for sending local content to the global controller view. That’s not nice. Widgets shouldn’t know anything about the global page.

Apotomo widgets are based on cells, so they may use that technique as well.

In the display.html.erb view

...
- global_capture :ujs do
  - javascript_tag do
    $(document.body).observe('click', function(e) {
...

I embed the #javascript_tag into #global_capture that saves the whole block to the global view’s instance variable @ujs, which is a brutal interface violation.

In the app’s views/layouts/application.html.erb I have to render @ujs in order to embed the JavaScript into the page only once. I still hate myself for doing that.

  </body>
  <%= @ujs %>
</html>

That’s all for the markup.

4 The #global_capture method is not automatically available in the widget view, it’s provided by a helper, so let’s introduce it to our view.

class ItemList < Apotomo::StatefulWidget
  helper Cells::Helpers::CaptureHelper
 
  def display
  #...

That looks like in a controller. Note that you can use #helper to pull any helper in your widget view.

5Ok, the paging is working in some unobstrusive way. I’m still not sure if unobstrusive JavaScript is something I like.

What I like is CSS and how it can help us to beautify elements without adding obstrusive CSS.

To add a background to our item list I add a css class to it’s surrounding div.

1
2
3
4
5
6
7
8
9
10
11
class ItemList < Apotomo::StatefulWidget
  helper Cells::Helpers::CaptureHelper
 
  def display
    respond_to_event :paginate, :with => :display
 
    @items = Todo.paginate :page => param(:page), :per_page => 10, :order => 'created_at DESC'
 
    render :html_options => {:class => 'lined'}
  end
end

All I need is adding :html_options to the #render invocation (line 9), this will add the attributes to our list. Obviously I also added some class to the CSS document, but that’s not part of this course. My CSS-skills suck.

6Let’s view our page again.

lined_list

Looks cooooool, doesn’t it?

I’d love to see some JS fix to prevent the usage of #global_capture. Or do you like it? It’s a common technique in Rails, though. Doesn’t mean that this is a good thing.

In the next lesson we’ll add an interactive form to add Todo items which processes and updates like the list widget, via AJAX and superfast.

Go to: Writing forms

Making a Rails Helper predictable

Thursday, April 1st, 2010

Rails helpers are pretty messy. Everyone who’s ever tried to simply include, say, the UrlHelper into an object did instantly crash with some NoMethodError either complaining about

  • a missing method that should be included in the helper already, but isn’t
  • an undefined method for nil:NilClass
target = Object.new
target.extend ActionView::Helpers::UrlHelper
target.url_for('/fix/me')
 
#=> url_helper.rb:94:in `url_for': undefined method `escape_once' for #<Object:0xb7496300> (NoMethodError)

That’s due to dependencies between different helpers and the instances they’re mixed into.

Wrapping the UrlHelper

I’m gonna explain how ActiveHelper can help solving this problem by wrapping the UrlHelper step-by-step.

This will make the UrlHelper a predictable, well-behaving class. Helpers should be good kids, nothin’ more.

Here’s a first version for the new helper.

module Rails
  class UrlHelper < ActiveHelper::Base
    include ActionView::Helpers::UrlHelper
 
    provides :url_for, :link_to, :button_to # add more if you need.
  end
end

We derive the new helper from ActiveHelper::Base, include the original module and define our interface with provides.

A rails scenario setup

In Rails, this is what roughly happens when a view is rendered.

controller = ActionController::Base.new
view       = ActionView::Base.new([], {}, controller)
 
view.extend ActiveHelper
view.use Rails::UrlHelper

Usually the use call would happen in the controller’s action processing, or in #render.

view.url_for('fix/me')
# => url_helper.rb:85:in `send': undefined method `url_for' for nil:NilClass (NoMethodError)

Ok, send is accessing nil. Let’s inspect the original helper.

  def url_for(options = {})
    ...
    @controller.send(:url_for, options)

The rails helpers blindly accesses an instance var @controller, it simply assumes the instance it is mixed into does provide it.
Even worse, it uses send to invoke a method on the controller. That’s one of rails major drawbacks and must be fixed.

Providing the @controller

Unfortunately, we won’t touch the original UrlHelper and open pandora’s box.

Remember, we got a helper instance which mixed-in the original UrlHelper module. That means we’d just have to provide the @controller instance variable in our helper instance.

module Rails
  class UrlHelper < ActiveHelper::Base
    include ActionView::Helpers::UrlHelper
 
    provides :url_for, :link_to, :button_to # add more if you need.
 
    def initialize(*args)
      super(*args)
      @controller = parent.controller
    end

We setup an ivar @controller by accessing parent, that’s the object which is using us, so it’s the ActionView and that itself does provide a public accessor to its controller.

Fixing another helper dependency

Not nice, but anyway, cleaner than things were before.

view.url_for('fix/me')
url_helper.rb:94:in `url_for': undefined method `escape_once' for #<Rails::UrlHelper:0xb747b794> (NoMethodError)

Oh. #escape_once is needed in rails’ original helper, too. I will spare the details about the code now, the method is defined in TagHelper. However it is not mixed in UrlHelper. Shitty helpers.

That’s another broken dependency we’ll have to fix.

We now have three possibilities

  • we could wrap TagHelper in an ActiveHelper as well, and derive UrlHelper
  • wrapping and use‘ing it inside UrlHelper would be the second solution
  • or simply include the original module, hoping all will go well

For now I’ll choose the third, anyway, that’s far from good architecture.

module Rails
  class UrlHelper < ActiveHelper::Base
    include ActionView::Helpers::UrlHelper
    include ActionView::Helpers::TagHelper
 
    provides :url_for, :link_to, :button_to # add more if you need.
 
    def initialize(*args)
      super(*args)
      @controller = parent.controller
    end

Let’s see if it works.

view.url_for('fix/me') #=> "fix/me"
view.button_for('fix/me')
#=> url_helper.rb:298:in `button_to': undefined method `protect_against_forgery?' for #<Rails::UrlHelper:0xb74a7eac> (NoMethodError)

Now what’s that again? Looks as if #button_for needs protect_against_forgery?.

Delegating dependency to the ActionView

Let’s express that dependency.

module Rails
  class UrlHelper < ActiveHelper::Base
    include ActionView::Helpers::UrlHelper
    include ActionView::Helpers::TagHelper
 
    provides :url_for, :link_to, :button_to # add more if you need.
 
    needs :protect_against_forgery?
    ...

Having that defined, calls to protect_against_forgery? will be delegated back to the view. That should work.

view.button_for("Click me!", 'fix/me')
#=> <form method="post" action="fix/me" class="button-to"><div><input type="submit" value="Click me!" /></div></form>

Yeah!

Where to go from here?

Now that you know how easy it is to clean up the Rails helpers (or Merb, or …!!!), go and do it. I’d love to see ActiveHelper being used to replace nasty modules and bringing back the power of OOP to your views.