Return of the Tag Cloud — ajaxing the tagged

On my continuing quest to become more Web-2.0-ish, I’ve tried my luck tilting at… well, tag clouds. To get an idea what I’m talking about, please have a look.

These are the rudimentary basics to create a tag cloud like that.

In the controller

def tags
  @popular_tags = Tag.find_most_popular(:limit => 30)
end

In the view

<ul id="tags" class="tagcloud">
<% @popular_tags.sort_by(&:name).each do |t| -%>
  <%= weighted_tag t, tag_options %>
<% end -%>
</ul>

or, to get a somewhat nicer look, and to demonstrate some of the options

<%
  tag_options = {
    :wrap => ['li', '<span class="dlm">[</span>', '<span class="dlm">]</span>'],
    :min_size => 100, :max_size => 500,
    :link_options => { :class => 'tag' })
  }
-%>
<h2>Tags</h2>
<ul id="tags" class="tagcloud">
<% @popular_tags.sort_by(&:name).each do |t| -%>
  <%= weighted_tag t, tag_options %>
<% end -%>
</ul>

Here you can download a do-it-yourself kit of all the pieces.

Popular Requests

It was late last night. And the night before, I admit it. I also admit that I don’t know much about what Web 2.0 is all about, but there are two things that I do know: rounded corners are involved and, of course tags and tag clouds.

Tagging is work, therefore ipso facto to be avoided, better still: automated.

Let’s say you want to show people which part of your site are popular. How about a tag cloud of the most requested locations? Well, the first thing to do would be to collect which requests are happening at all. Also, you’d need to attach a sensible, displayable title to the raw request. For the sake of this little demonstration, I’m going to assume that in your template — list.rhtml, edit.rhtml, and brethren — you have an instance variable @title. It could be either defined in the template itself or transplanted there from its controller, it doesn’t matter.

Given all this, you could hypothetically write code like this.

class ApplicationController < ActionController::Base
  collect_popular_requests :max_age => 7.days, :max_count => 10000,
    :title => proc { |controller| controller.response.template.instance_variable_get(:@title) }

Therein further assuming that at most the 10000 most recent requests are kept if they are no older than 7 days. Now that we have hypothetically collected the data to base our tag cloud on, we have to display it somehow. Again, let's assume it could be done with a route like this

map.popular '/', :controller => 'most_popular', :action => 'index'

a controller like that

class MostPopularController < ApplicationController
  helper BoilerPlate::PopularRequestsHelper
  collect_popular_requests :\off # Ignore this place

  def index
    @popular_requests = popular_requests_list.most_popular(30)
  end
end

and for good measure a view (index.rhtml)

<ul class="tagcloud">
<% alpha_reqs = @popular_requests.sort_by(&:title) -%>
<% alpha_reqs.each do |req| -%>
  <%= popular_request_tag(req, :wrap => 'li', :min_size => 30, :max_size => 400) %>
<% end -%>
</ul>

and some CSS for good style

.tagcloud {
  text-align: center;
  width: 70%;
}
.tagcloud li {
  display: inline-block;
  display: -moz-inline-box;
  white-space: nowrap;
  vertical-align: middle;
  line-height: 1.2em;
  padding: 0 0.2em;
}

Well, if you've made it this far, you might even consider doing all of this for real. For a good start, I suggest you download this Rails plugin, install it, create a database table with

$ script/generate popular_requests AddPopularRequests
$ rake db:migrate

and start playing.

Self-conscious Decoration

Variations on the Decorator Design Pattern in Ruby have already been discussed in several places. For my special purpose, none of these approaches works.

The common problem with a decorator based on delegation is the lost sense of self. Method calls of the decorated object to itself are not routed through the decorator, therefore remain woefully undecorated.

The Ruby idiom of aliasing and redefining methods, in effect adding around advice, is possible, when the decoration applies to all uses of the “core” class. It is not suitable when you need decorated as well as undecorated variants.

There’s an almost classical: Just derive a subclass from the “core” class and add the decoration in there. Doing this explicitly is tedious, ugly, and multiplies classes semper necessitatem. Luckily, for us and poor, overquoted Father Ockham, Ruby is a highly dynamic language where things are possible that users of other languages can only dream of (longingly or in nightmares, that’s quite a different issue).

So, without further ado, here’s a simple demonstration of the technique.

class Core
  def m1
    puts "Core#m1"
  end

  def m2
    puts "Core#m2"
    m3
  end

  def m3
    puts "Core#m3"
  end

  def m4
    puts "Core#m4"
  end
end

class Decorator
  class << self
    def new(*args)
      decorate(Core).new(*args)
    end

    private
    def decorate(klass)
      decorated_class = Class.new(klass)
      decorated_class.send(:include, Decoration)
      decorated_class
    end
  end

  module Decoration
    def m1
      puts "Decorator#m1"
      super
    end

    def m2
      puts "Decorator#m2"
      super
    end
    def m3
      puts "Decorator#m3"
      super
    end
  end
end

With that under under our collective belt, here’s the case that motivated my attempt at self-conscious delegation. In a Rails application, I have several layers of FormBuilders. At the base, derived from ActionView::Helpers::FormBuilder is a form builder that provides mechanisms. For example for adding labels to input elements and for automatically displaying appropriate widgets for various association types. On top of that, I have two form builders that add layout specifics; one for CSS-based layouts, one for table-based layouts. I still need a further layer for policy: only show users what they are allowed to see and touch. This last policy layer is, of course, independent of layout (and vice versa!), therefore, short of full-fledged AOP, decoration was the way to go.

class DecoFormBuilder

  class << self
    def new(object_name, object, template, options, proc)
      delegate_class = options.delete(:delegate_builder)
      raise ArgumentError, 'No :delegate_builder given to DecoFormBuilder' unless delegate_class
      decorate(delegate_class).new(object_name, object, template, options, proc)
    end

    private

    def decorate(klass)
      decorated = Class.new(klass)
      decorated.send(:include, Decoration)
      decorated
    end
  end

  module Decoration
    def initialize(object_name, object, template, option, proc)
      super
      # Do whatever initialization you need here
    end

    # Put the decorating methods here and don't forget to
    # call super.

  end
end

Rails valid markup testing

Coda Hale has given the intersection of the Rails and web standards communities a very nice plugin for keeping your markup clean. It is aptly, if slightly pretentiously, named Responsible Markup.

But let’s be honest, do you really want to spend much time on writing tests for your markup? After all, it’s taking long enough to get the markup right. Ruby to the rescue! Why write tests when we can get Ruby to write them?

Here’s what I’m using.

class ValidMarkupTest < Test::Unit::TestCase
  ResponsibleMarkup::validator_uri = 'http://localhost/w3c-markup-validator/check'

  def self.validate_markup_for_resources(*resources)
    default_actions = {
      :index => { :method => :get, :template => 'list' }, 
      :new   => { :method => :get, :template => 'new' },
      :edit  => { :method => :get, :id => 1, :template => 'edit' }
    }
    if resources.last.kind_of?(Hash)
      actions = resources.pop
      actions.each do |action, options|
        options.reverse_merge!(default_actions[action] || {})
      end
    else
      actions = default_actions
    end

    resources.each do |resource|
      fixtures resource

      controller_class = "#{resource.to_s.camelize}Controller".constantize

      actions.each do |action, options|
        options = options.dup

        define_method("test_action_#{action}_for_#{resource}_returns_valid_markup") do
          @controller = controller_class.new

          request_method    = options.delete(:method)
          expected_template = options.delete(:template)

          send(request_method, action, options)

          assert_response :success
          assert_template expected_template
          assert_valid_markup
        end
      end
    end
  end

  validate_markup_for_resources :thingamuhjigs, :humdingers, :gizmos

  def setup
    @request  = ActionController::TestRequest.new
    @response = ActionController::TestResponse.new
    login_test_user
  end

  def assert_valid_markup
    assert_doctype(:xhtml_10_strict)
    assert_content_type
#    assert_compatible_empty_elements
    assert_no_empty_attributes
    assert_no_long_style_attributes
    assert_unobtrusive_javascript :allowed => [ :inline_events, :blank_hrefs ]
    assert_valid_html
  end

end

The idea is to have separate the testing task into three parts.

  • A generic, lightly configurable, method for defining tests: validate_markup_for_resources
  • A setup method that is specific to your application. In the example above, I need to login a user.
  • A assert_valid_markup method that contains assertions for your validation goals.

Configuration

By default, validate_markup_for_resources generates tests for index, new, and edit actions. For the edit test, it assumes that an object with the id 1 exists. If you need to change the request options used by default, you can override them like this

validate_markup_for_resources :gizmos, :id => 2, :template => 'gizmo', :\other_param => 'xyz'

Running

Normally, by Rails convention, the above example test would belong in test/functional. However, even when using a validator installed on the local machine, it takes quite some time to run. So, in order not to slow down my functional tests too much, I’ve created a new directory, test/markup and run the tests in there with

$ rake test:markup

This rake task is provided by the following snippet in lib/tasks/markup.rake.

namespace :test do
  desc "Run the checks for valid markup defined in test/markup"
  Rake::TestTask.new(:markup => "environment") do |t|
    t.libs << "test"
    t.pattern = 'test/markup/**/*_test.rb'
    t.verbose = true
  end
end

Incidentally, yes, I know that the markup of this blog is broken. Blame WordPress.

ActiveRecord Enumerable

Ever wanted to iterate over a huge number of objects and found
that find(:all) doesn’t cut it? Then this plugin
might be for you. Install it with

$ script/plugin install svn://rubyforge.org/var/svn/ar-enumerable/ar_enumerable/trunk

First, a note of caution. This plugin has not seen much practical
use yet, in its current state it is only slightly above a
proof-of-concept. There are several tests included that cover most
of the functionality. If you intend to use this plugin, please
make sure to independently check that it does what you want.

Enumerating ActiveRecord objects

Let’s start with a boring example, to get the general idea

Person.where(['lastname = ?', 'Smith']).map(&:firstname)

Slightly more interesting

Person.where(['lastname = ?', 'Smith']).inject(0) do |income, person|
  income += person.income
end

siblings = Person.where(
    ['lastname = ?', 'Smith'], 
    :include => :siblings).inject([0, 0]) do |memo, person|
  memo[0] += person.siblings.count
  memo[1] += 1
  memo
end
siblings[0].to_f / siblings[1]

The point here is that your database may contain millions of Smiths,
for what it’s worth, the entire population of the earth could have
turned into Smiths, still, only a limited number of them would be
loaded into memory at the same time.

Internally, enumeration works in two completely different ways.
By default, a chunk of objects is retrieved from the database using
OFFSET and LIMIT clauses. This way is supported by all databases,
however it can be slow.
Therefore, the preferred alternative is to use a standard SQL cursor
for iterating over the result set of a query. It would simply look like
this

Person.where(['lastname = ?', 'Smith'], :use_cursor => true).inject(0) do |income, person|
  income += person.income
end

A further parameter is how many records (or rows, in the case of cursors)
are fetched from the database in one access. The default is 1000. You can
set it explicitly like this

Person.where(['lastname = ?', 'Smith'], :fetch_count => 100).inject(0) do |income, person|
  income += person.income
end

Performance Considerations

Some informal tests suggest that for large result sets without
included associations cursor-based enumeration can be faster than
OFFSET/LIMIT-based enumeration by a factor of 3 to 5.
When associations are included, things become more blurry. In this
case, cursor-based enumeration imposes an ordering on the result
set that can slow down things considerably.
The fetch count has considerable influence on performance, basically,
you can buy speed by spending memory.

Auto-completion for tag lists

See the end of this post for an important update.

Standard Rails auto-completion is geared toward completing a single value entered in a text field. For most cases this is exactly what’s needed, but there is one prominent case that is different: auto-completion of tags. If something can have a tag at all, it’s part and parcel of the deal that it can have multiple tags.

As far as I can tell, some configuration tweak is not enough to make standard auto-completion do our bidding. But not all is lost. It’s just a small matter of programming to get what we want.

I’m assuming that you’re using Acts As Taggable or Acts As Taggable On Steroids for the model-level tagging, or otherwise that you know what you’re doing.

First, we need client-side support for picking out only the tag we’re currently editing.

Ajax.TagAutocompleter = Class.create();
Object.extend(Object.extend(Ajax.TagAutocompleter.prototype, Ajax.Autocompleter.prototype), {
  getToken: function() {
    var range = this.rangeForCaret();
    var value = this.element.value.substring(range.start, range.end);
    return value;
  },
  updateElement: function(selectedElement) {
    var oldValue = this.element.value;
    var range = this.rangeForCaret();
    var prefix = oldValue.substring(0, range.start);
    var suffix = oldValue.substring(range.end);
    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    this.element.value = prefix + value + suffix;
    this.element.focus();
  },
  rangeForCaret: function() {
    var value = this.element.value;
    var start = 0, end = value.length;
    var pos;
    if (typeof this.element.selectionStart != 'undefined') {
      pos = this.element.selectionStart;
    } else if (document.selection) {
      var sel = document.selection.createRange();
      var selLength = sel.text.length;
      sel.moveStart('character', -(value.length));
      pos = sel.text.length - selLength;
    }
    if (pos != undefined) {
      end = pos;
      while (value.charAt(end) != ',' && end < value.length) {
        end++;
      }
      start = end - 1;
      while (value.charAt(start) != ',' && start > 0) {
        start--;
      }
      while (start < end && (value.charAt(start) == ' ' || value.charAt(start) == ',')) {
        start++;
      }
      while (start < end && value.charAt(end) == ' ') {
        end--;
      }
    }
    return {start: start, end: end};
  }
});

Yes, the code of rangeForCaret is a bit convoluted, let’s say traditional JavaScript-style. The general approach for getting the position of the text cursor (“caret”) is explained here. The purpose of all the stuff is to support auto-completion in the middle of the text field, not just at its end.

On the server-side, in the relevant controller, there’s still some work to do as the standard auto_complete_for can’t handle tags. Put the following method in your controller base class, app/controllers/application.rb.

def self.tag_auto_complete_for(object)
  define_method("auto_complete_for_#{object}_tag_list") do
    @items = Tag.find(:all,
      :conditions => [ 'LOWER(name) LIKE ?', '%' + params[object][:tag_list].downcase + '%' ], 
      :\order => 'name ASC',
      :limit => 10)
    render :inline => "<%= auto_complete_result @items, :name %>"
  end
end

Then, in an individual controller, create the tag auto-completion method like this

class PeopleController < ApplicationController
  tag_auto_complete_for :person
end

Almost done!

What’s still missing is a way to activate auto-completion for the tag input field. It would surely be possible to copy & change text_field_with_auto_complete or to monkey patch it appropriately. I prefer another way, in fact, I’m not using text_field_with_auto_complete at all, not even where it would work. Instead, I prefer more unobtrusive way.

The way starts with the class autocomplete to text fields that are amenable to auto-completion — including, of course, the tag list field.

  <%= f.text_field :tag_list, :class => 'autocomplete' %>

As this in itself is completely inert, we need to nudge it a bit in application.js. Here’s a function that looks for all text fields with class autocomplete and actually makes them do what they say.

function installAutocompletion() {
  $$('input.autocomplete[type=text]').each(function(element) {
    var fieldId = element.id;
    var completions = document.createElement('div');
    completions.id = fieldId + '_auto_complete';
    completions.className = 'auto_complete';
    completions.style.display = 'none';
    element.parentNode.insertBefore(completions, element.nextSibling);

    var url = 'auto_complete_for_' + fieldId;
    if (/_tag_list$/.test(fieldId)) {
      new Ajax.TagAutocompleter(element, completions, url);
    } else {
      new Ajax.Autocompleter(element, completions, url);
    }
  });
}

Be sure to call this method some time after the page is loaded. If you don’t have a function from where to call it, add this to application.js

  Event.observe(window, 'load', installAutocompletion);

Update, 2007-02-05

I may not have seen the wood for the trees when looking at the original code of the Rails/Script.aculo.us autocompleter. Of course it is fully capable of auto-completing more than one token in the same text field — with a very little configuration tweak, no less.

What it can’t do is auto-completion in the middle of the line; it only works at the end. That’s an acceptable restriction, to my mind. So, forget about Ajax.TagAutocompleter from above and instead install your autocompleters like this

function installAutocompletion() {
  $$('input.autocomplete[type=text]').each(function(element) {
    var fieldId = element.id;
    var completions = document.createElement('div');
    completions.id = fieldId + '_auto_complete';
    completions.className = 'auto_complete';
    completions.style.display = 'none';
    element.parentNode.insertBefore(completions, element.nextSibling);

    var url = 'auto_complete_for_' + fieldId;
    var options = {};
    if (/_tag_list$/.test(fieldId)) {
      options.tokens = ',';
    }
    new Ajax.Autocompleter(element, completions, url, options);
  });
}

Eclipse: Printing Editor Templates using XSLT, CSS, and Firefox

I’m using Eclipse with RadRails to edit my Ruby and Rails code (and, yes, I’ve used it for Java, way back when).

Inside Eclipse the various text editors offer so-called templates. These are snippets of common code with placeholders for variable bits. Type in the abbreviation for a template, then type Ctrl-Space and the abbreviation is expanded to the template code. For editing Ruby/Rails templates are available from the RadRails Templates site.

I’ve only recently started to use templates and I’m still early on the learning curve, that is, I haven’t memorized many of the abbreviations. Unfortunately, Eclipse itself only has a list of templates with their expansions in its modal Preferences window. That’s not very helpful when coding. What Eclipse does have is a function to export templates to XML and that’s what I did.

The exported file looks like this

<?xml version="1.0" encoding="UTF-8"?>
<templates>
  <template autoinsert="true" context="ruby" deleted="false" description="tm - all? { |e| .. }" enabled="true" name="all">all? { |${e}| ${cursor} }
  </template>
  <template autoinsert="true" context="ruby" deleted="false" description="tm - alias_method .." enabled="true" name="am">alias_method :${new_name}, :${old_name}
  </template>
  ...
</templates>

The easiest approach I could think of to make this into something printable is this. First, transform it into HTML using an XSL stylesheet, then make the HTML pretty by styling it with an CSS stylesheet. Luckily, most of this can be done inside a web browser such as Firefox that understands XSLT.

Here’s the XSLT

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <html>
    <head>
      <xsl:apply-templates mode="head" />
      <link rel="stylesheet" type="text/css" href="templates.css" />
    </head>
    <body>
      <xsl:apply-templates mode="body" />
    </body>
  </html>
</xsl:template>

<xsl:template match="title" mode="head">
  <title>
    <xsl:value-of select="." />
  </title>
</xsl:template>

<xsl:template match="title" mode="body">
  <h1>
    <xsl:value-of select="." />
  </h1>
</xsl:template>

<xsl:template match="templates" mode="body">
  <dl>
    <xsl:apply-templates mode="body" />
  </dl>
</xsl:template>


<xsl:template match="template" mode="body">
  <dt>
    <xsl:value-of select="@name" />
  </dt>
  <dd>
    <xsl:value-of select="." />
  </dd>
</xsl:template>

</xsl:stylesheet>

And the CSS stylesheet

html {
  font-family: sans-serif;
}

dt {
  border-top: 1px solid #aaa;
  padding-top: 0.2em;
  font-weight: bold;
}

dd {
  margin-top: 0.3em;
  margin-bottom: 1em;
  white-space: pre;
  font-family: monospace;
}

Now, these parts need to be connected. For this, it is necessary to slightly edit the original XML template file. For good measure, I throw in a title.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="templates.xsl" ?>
<templates>
  <title>RadRails Ruby Templates</title>
  <template ...>
    ...
  </template>

You may find that the indentation for the template code looks wrong. The reason is that the code contains tabs for indentation. Ruby convention is to indent by 2 spaces for each level, by contrast, Firefox apparently expands tabs to 8 spaces. A small glitch that can be rectified easily

$ sed -i 's/\t/  /g' template.xml

Rails: Almost Automatic Client-Side Validation

Ruby on Rails already does a good job of specifying almost declaratively what conditions objects must meet to be considered valid–that is, how they are validated.

Thus, in order to make sure that a Person’s last_name attribute does not exceed 100 characters, you would write something like this

class Person < ActiveRecord::Base
  validates_length_of :last_name, :maximum => 100
end

Somewhere in a view you’ll probably refer to the last_name attribute like this

<% form_for :person, @person, :url => { :action => "update" } do |f| -%>
  Last name : <%= f.text_field :last_name %>
  <%= submit_tag %>
<% end -%>

There you get a nice form where users can enter last names as long as they may wish–only to be shown an error message complaining about the length in cases where they have unwittingly overshot the limit. How should they have known?

Well, as a nice and caring person you could add an annotation to each input field saying how long its contents are allowed to be. That would be slightly better, but still leaves more work for the users than necessary.

How about this: Users are immediately notified in an unobtrusive way that some of the content they have (or have not yet) filled into a form does not constitute valid data. Furthermore, users are kept from submitting a form that contains obviously invalid data.

Should be easy, shouldn’t it? Yes, it should and actually it is just two plugins away.

Raising awareness of validations

Out of the box, Rails handles validations in such a way that it adds callbacks for checking them to model classes, but otherwise immediately loses awareness of them. When later on in the lifecycle of a model instance validations are checked, Rails does so on autopilot. A model class just does it’s thing, it won’t tell you what it’s doing, but you can be sure it’ll complain when something is wrong. Let’s add some reflection to make model classes a bit more loquacious. The details are irrelevant, just install the Validation Reflection plugin into your Rails application

$ script/plugin install svn://rubyforge.org//var/svn/valirefl/validation_reflection/trunk

Tell the browser

So far, the newfound awareness of validations idles away on the server-side. Somehow it has to be transported to the user’s browser. That is one of the things the Client-Side Validation plugin does. Install with

$ script/plugin install svn://rubyforge.org//var/svn/clientsidevali/client_side_validation/trunk

The first thing this plugin does is that it enhances tags generated by Rails’s helper methods in such a way that they contain encoded information about the validation constraints that apply to their values.

Engage the client

Still, even though validation information has been inserted into the HTML sent to the browser, where it lays dormant. On the client-side, JavaScript is where the action is. Therefore the task of checking validations there is handled by a validator written in JavaScript included with the Client-Side Validation plugin.

The generic script for the validator is included in your views or layout as part of the default scripts, therefore it is probably just there already.

<%= javascript_include_tag :defaults %>

Here we come to a point where you, the programmer, have to make some decisions and do some work. First, you need to decide (or have your code decide) what locale your users are in. Remember, dates don’t look the same all over the world.

 <%= javascript_include_tag 'validators-en' %>

Currently English (en) and German (de) are supported.

Then, you have to tell the validator that there’s work for it to do. For this, all forms that ought to be validated have to be marked with the class validated. Thus, the above form becomes

<% form_for :person, @person, :url => { :action => "update" }, :html => { :class => 'validated' } do |f| -%>
  Last name : <%= f.text_field :last_name %>
  <%= submit_tag %>
<% end -%>

Also, you need to nudge the validator to start looking at the forms by adding a line to public/javascripts/application.js

Form.Validator.installForAllValidatedForms();

You ain’t see nothing yet

If you’ve followed all these steps, the validator is probably doing its work, but hardly see anything of it. You may notice that the submit button is enabled when the form is valid and disabled when it is invalid, but that is all there is.

A further, but still invisible, thing that the validator does is that it adds (removes) the class “invalid” to input elements when they are invalid. Making this change in attribute visible is a small matter of CSS

 .invalid {
   border: 1px solid #f00;
 }

The result is not pretty, but you can push your stylesheeting abilities to the limit to make them look however you like. Also, the validator offers hooks for customizing most aspects. But that is left for a future installment.