Allowing custom CSS in your app

Courtenay : December 31st, 2008

There are a number of good reasons why you don't want your users providing their own CSS (for example, when theming their site). These are: taste (see: myspace) and security.

The former is pretty much your users' problem. The pages don't have to look terrible -- and in fact Myspace charges a LOT of money to do those custom movie or band pages (it's part of the service when you buy their primo ad space).

The latter, well, as it turns out there are a bunch of security vulnerabilities exposed in CSS. While these are mainly in IE, related to expressions (you can run javascript from your CSS). This means that users can steal others' sessions. So, while there are some excellent perl libraries out there for this, there hasn't been one for ruby -- until now! (at least that I could find).

So, here's my first attempt.

css_file_sanitize (github)

I stole most of the tests from LiveJournal's css sanitizing library, and rewrote the implementation in Ruby. I'd love to hear your collective feedback. It's a really lazy plugin; in fact, while it does have tests, you're best to just include the module in your model. This is a case of "it works on my machine" so send your patches!

Works on my machine

Ajax progress indicator with prototype

Courtenay : March 26th, 2008

Today I'm going to show you how I combine my old-school Javascript with the latest Prototype has to offer. I hope you learn a little about the basic use and misuse of "bind". Hopefully someone will chime into the comments and give a better way to do this code!

You may know that you can add a global ajax responder to your application:

Ajax.Responders.register({
  onCreate: function() {
    Ajax.activeRequestCount++;
  },
  onComplete: function() {
    Ajax.activeRequestCount--;
  }
});

But I'm not going to cover that here, because you can't get the originating object. For example, there's no way here for a global ajax responder to get the anchor object.

<a class="add" href="#" onclick="new Ajax.Request('/products/3/categorizations/18', {asynchronous:true, evalScripts:true, method:'put'}); return false;">add</a>

Here's the rails generator code:

<%= link_to_remote "add", { :url => product_categorization_path(@product, category), :method => :put } %>

So, let's add some code to prevent the user from clicking twice.
To keep the view clean, I'll implement it as a simple view helper in application_helper.rb

<%= link_to_remote "add", { :url => product_categorization_path(@product, category), :before => ajax_progress, :method => :put } %>

def ajax_progress
  "setTimeout(function(){ this.innerHTML = '...' }.bind(this), 100)"
end

SetTimeout takes either a function name or an anonymous function. I'm binding the anonymous function to "this", which in this context refers to the 'A' anchor tag. Because it's bound, I can refer to "this" inside the function and get the anchor tag.

The setTimeout is useful because it lets us modify the tag (even remove it from the DOM) without messing with the ajax request. You can use setTimeout on a form to change the action, so it can't be submitted twice (for important forms)

I'm going to step it up one more, because this code belongs in a library. Time to open up application.js and create a ghetto pseudoclass singleton thingy.

MyApp = {
  /* do this while we're waiting for ajax responses */
  ajaxing: function(){
    this.innerHTML = '...'; 
    this.onclick = FacetApp.nothing;
  },

  /* don't do anything. useful for descriptive declarations like $('a').onclick = FacetApp.nothing */
  nothing: function() { 
    return false 
  }
}

This will prevent the link from doing anything if the user clicks it twice.

Now, to modify the ajax_progress helper to simple beauty.

def ajax_progress
  "setTimeout(MyApp.ajaxing.bind(this), 100)"
end

Finally, let's show the user a GMail-style notice after 5 seconds, just to let them know that we're running slow or have just plain died.

MyApp = {

  ajaxing: function(){
    this.innerHTML = '...'; 
    this.onclick = FacetApp.nothing;
    setTimeout(MyApp.slooow.bind(this), 5000);
  },

  /* shows a ? symbol. useful for showing progress on an ajax action */
  slooow: function(){
    this.innerHTML = ".?."
    /* show a warning message in the ui somewhere */
  }
}

Got a better way of doing this?

safari 3.1 breaks your app

Courtenay : March 21st, 2008

If you're doing anything like

$('foo').getElementsByClassName("monkey").each(Element.hide)

Safari 3.1 out this week breaks this, because it now natively implements getElementsByClassName.

So, pending a fix from the Prototype guys, one of whom has a desk next to mine, be aware that your apps may be broken for your Safari users.

You can easily change that code to

$$("#foo .monkey").each(Element.hide)

implement sweet sparklines with plotr

Courtenay : May 6th, 2007

requires: plotr.

You have a bunch of data you want to plot in a sparkish fashion. Lets say they are sparse, one or two every 4 hours. So, lets pull some data clustered in 4-hour increments and spark it up.

Create the data like thusly:

distance = 4.hours # how much we are clustering the results
@sparks = @store.orders.find(:all, 
  :select => "count(id) as count, round(UNIX_TIMESTAMP(created_at)/#{distance}) as timestamp", 
  :group => "round(UNIX_TIMESTAMP(created_at) / #{distance})")

Install plotr javascripts, include them, then in your view dump the placeholder:

<div><canvas id="spark" width="510" height="50"></canvas></div>

And write some javascript

<% baseline = sparks[0]['timestamp'].to_i %>

<script type="text/javascript">
  var dataset = { 'spark': [ <% @sparks.each do |spark| %>
    [<%= spark['timestamp'].to_i - baseline %>, <%= spark.count %>],<% end %>
    []
  ]};
  var spark = new Plotr.BarChart('spark', { colorScheme:'blue', yNumberOfTicks:0,xNumberOfTicks:0,padding:{left:0,right:30,top:0,bottom:10}});
  spark.addDataset(dataset);
  spark.render();
</script>

Mine looked something like this:

Ajax File Upload

Courtenay : April 2nd, 2007

Here’s how to upload without leaving the page. It’s not ajax, but it feels like it. Sorry for the misleading heading :)

First, install the respondsto_parent plugin. Read more about respondsto_parent This plugin lets you fake rjs from an iframe response. Doesn’t make sense? Don’t worry, it will later.

script/plugin install http://sean.treadway.info/svn/plugins/responds_to_parent

Now, in your page, let’s say it’s an article editing form.

<form method="post" action="/articles">
  Title: <input type="text" name="article[name]" />
  &#8230;

Forget about this form.

Add a new form somewhere like the sidebar, looks like this:

<div id="asset_list">
  <%= render :partial => &#8216;asset&#8217;, :collection => @assets %>
</div>
<form method="post" action="/assets.js" target="background-uploader" enctype="multipart/form-data" id="background-upload-form">
  File: <input type="file" name="asset[uploaded_data]" />
  <input type="submit" value="Upload" />
</form>
<iframe width="1" height="1" name="background-uploader" src="about:blank"></iframe>

So, we have a multipart form that points to assets.js with a target, a 1x1 iframe, and a file field. These are all required. Optional, the div with the asset list, but it’s helpful for the user to see all the assets, or perhaps the most recent.

Open up your asset controller and modify the create action.

def create
  @asset = Image.create! params[:asset]
  respond_to do |wants|
    wants.html { redirect_to asset_path(@asset) }
    wants.js do
      responds_to_parent do
        render :update do |page|
          page.insert_html :top, &#8216;asset_list&#8217;, render(:partial => &#8216;articles/asset&#8217;, :object => @asset)
          page.visual_effect :highlight, &#8220;asset_#{@asset.to_param}&#8221;
          page[&#8216;background-upload-form&#8217;].reset
        end # render
      end # responds_to_parent
    end # wants
  end # respond_to
rescue ActiveRecord::RecordInvalid
  # eep! you&#8217;re screwed!
end

What does this do? Well.. first, it creates the asset. Remember how we pointed the form to “assets.js”? This tells rails that we wants.js and rails sets the content-type accordingly. This has the benefit of allowing us to create assets with the same action regardless of iframe or as before.

Unfortunately, since this isn’t ajax, it’s going to dumping its contents as text in the iframe and it won’t work. Enter the responds_to_parent plugin, which does some magic and makes our rjs response activate as expected.

Inside the responds_to_parent block we insert the new asset partial in the top of the list, flash it so the user is aware, and reset the upload form.

And that’s it. Error checking is left to the reader :)

Ajax File Upload

Courtenay : April 2nd, 2007

Here’s how to upload without leaving the page. It’s not ajax, but it feels like it. Sorry for the misleading heading :)

First, install the responds-to-parent plugin. Read more about responds_to_parent This plugin lets you fake rjs from an iframe response. Doesn’t make sense? Don’t worry, it will later.

script/plugin install http://sean.treadway.info/svn/plugins/responds_to_parent

Now, in your page, let’s say it’s an article editing form.

<form method="post" action="/articles">
  Title: <input type="text" name="article[name]" />
  &#8230;

Forget about this form.

Add a new form somewhere like the sidebar, looks like this:

<div id="asset_list">
  <%= render :partial => &#8216;asset&#8217;, :collection => @assets %>
</div>
<form method="post" action="/assets.js" target="background-uploader" enctype="multipart/form-data" id="background-upload-form">
  File: <input type="file" name="asset[uploaded_data]" />
  <input type="submit" value="Upload" />
</form>
<iframe width="1" height="1" name="background-uploader" src="about:blank"></iframe>

So, we have a multipart form that points to assets.js with a target, a 1x1 iframe, and a file field. These are all required. Optional, the div with the asset list, but it’s helpful for the user to see all the assets, or perhaps the most recent.

Open up your asset controller and modify the create action.

def create
  @asset = Image.create! params[:asset]
  respond_to do |wants|
    wants.html { redirect_to asset_path(@asset) }
    wants.js do
      responds_to_parent do
        render :update do |page|
          page.insert_html :top, &#8216;asset_list&#8217;, render(:partial => &#8216;articles/asset&#8217;, :object => @asset)
          page.visual_effect :highlight, &#8220;asset_#{@asset.to_param}&#8221;
          page[&#8216;background-upload-form&#8217;].reset
        end # render
      end # responds_to_parent
    end # wants
  end # respond_to
rescue ActiveRecord::RecordInvalid
  # eep! you&#8217;re screwed!
end

What does this do? Well.. first, it creates the asset. Remember how we pointed the form to “assets.js”? This tells rails that we wants.js and rails sets the content-type accordingly. This has the benefit of allowing us to create assets with the same action regardless of iframe or as before.

Unfortunately, since this isn’t ajax, it’s going to dumping its contents as text in the iframe and it won’t work. Enter the responds_to_parent plugin, which does some magic and makes our rjs response activate as expected.

Inside the responds_to_parent block we insert the new asset partial in the top of the list, flash it so the user is aware, and reset the upload form.

And that’s it. Error checking is left to the reader :)

plugin watch: acts_as_dismissible, name nanny

Courtenay : February 21st, 2007

Mark Daggett, the very epitome of the artist-programmer, is the ultimate yak-shaver. You can set him onto a task and three days later, you’ll have four awesome plugins and half the task finished. He’s currently studying for his PHD in some arty computer thing, but in the meantime keeps putting out some quality code.

name nanny

Mark also likes coming up with silly names for his implementations, so you call name nanny in your model like

validates_wholesomeness_of :login

The plugin looks for any ‘bad’ words (which you define) and replaces them with the word, ‘bleep’. Or, with the word, ‘smurf’. Which is much more fun.

acts_as_dismissible

This useful plugin, which I originally spec’d for a shared client, gives users a box with a “dismiss” button, so they never need to see a particular message again, unless they clear their cookies.

Go check it out! http://locusfoc.us

optimizing prototype.js

Courtenay : June 8th, 2006

It seems like a lot of prototype and scriptaculous functions use Array#each, but this method uses a very slow way of iterating over the loop. Using an old-skool loop-unroll and other such optimziations from "faster duff device":http://www.websiteoptimization.com/speed/10/10-18.txt we get this faster method. If anyone has venkman and/or can benchmark this new method, please hit me up with some results! Preliminary tests indicate a 3x speedup.. (updated, bugfix)

Object.extend(Array.prototype, {
  _each: function(iterator) {
    var iter = 0;
    var len = this.length;
    var i = len % 8;
    if (i>0) do {
        iterator(this[iter++]);
      } while (--i);

    i = parseInt(len >> 3);
    if (i>0) do {
      iterator(this[iter++]);
      iterator(this[iter++]);
      iterator(this[iter++]);
      iterator(this[iter++]);
      iterator(this[iter++]);
      iterator(this[iter++]);
      iterator(this[iter++]);
      iterator(this[iter++]);
    } while (--i);

    //for (var i=0; i
You can test it with something like

[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18].each(function(i) { print(i) } );

integrate rails modules with javascript objects

Courtenay : February 23rd, 2006

This is a fancy one for you dry-configuration-heads out there. First up, lets say you're storing digital assets in a rails app, and you want the user to enter what sort of content-type it is: image, video, html, whatever, when (before) they upload the file. So, you want to fill in a text-box with a suitable default after they select the file on their system. And, to make matters more interesting, you want to use the same regex on the filename on the server-side as you do on the client-side. Read the rest of this entry

A Better Twisty

obie : February 23rd, 2006

A followup to cracked-out twistys by Court. Badri and I worked on a hierarchical view in Kipling last night and came up with this (IMHO) nice refinement of the original. First an example of the markup:
1
2
3
4
5
6
7
8
9
10
11
<div class="Story Node" id="story_105">
  <span class="Twisty">&#9658;</span>
  <div class="Name"><div>Node</div></div>
  <div class="Collapsible Details" style="display:none">
    <div class="LastUpdated">Updated 7 minutes ago</div>
    <div class="Body"><div><p>Description</p></div></div>
    
    <div class="Story Node" id="story_106">
    ... you get the picture I hope 
  </div>
</div>
Then use Behaviour.js to apply your JS client side code in a modular way:
1
2
3
4
5
6
7
8
9
10
11
12
var goal = {
  rules: {
    '.Collapsible' : function(details) {
      var twisty = details.parentNode.getElementsByTagName('span')[0];
      twisty.onclick = function() {
        toggleTwisty(twisty, details);
        return false;
      }
    }
  }
}
Behaviour.register(goal.rules);
And finally, the toggleTwisty function itself.
function toggleTwisty(twisty, target) {
  twisty.innerHTML = (target.style.display == 'none') ? '▼' : '►';
  new Effect[Element.visible(target) ? 'BlindUp' : 'BlindDown'](target,
 { duration:.2, fps:50, queue:'end' })
}
The "dynamic method invocation using square brackets and a conditional" idiom of JS just blows my mind. Cryptic, but cool.

Assert yourself, man! -- redirecting with RJS

Courtenay : February 19th, 2006

Got an ajax method that degrades but want to redirect? Ever tried sending a redirect with ajax? It either crashes, breaks, or just doesn't work. Here's the solution, and as a bonus, a new test method to test your redirects.


  def new
    if request.xhr?
      render :update { |page| page.redirect_to :action => 'create' }
    else
      redirect_to(:action => 'create') 
    end
  end
    
render :update is the infamous RJS call, wherein it returns a document with text/javascript content-type. If you look into prototype.js, it automagically evals such documents. The response body will be pure javascript, baby, and it'll look something like 'window.location.href = "/whatever/do/5"' Here's the test helper to test if your ajax response is actually called. I'd love for someone to rewrite this, write some tests, and submit it as a patch to rails.. Hi, lazyweb! In the mean time, you can just dump it in test/test_helper.rb

  def assert_js_redirected_to(options={}, message=nil)
    clean_backtrace do
      assert_response(:success, message)
      assert_equal 'text/javascript', @response.headers['Content-Type'], 'Response should be Javascript content-type';
      js_regexp = %r{(\w+://)?.*?(/|$|\\\?)(.*)}
      url_regexp = %r{^window\.location\.href [=] ['"]#{js_regexp}['"][;]$}
      redirected_to = @response.body.match(url_regexp)
      assert_not_nil(redirected_to, message)
      redirected_to = redirected_to[3]
      msg = build_message(message, "expected a JS redirect to , found one to ", options, redirected_to)

      if options.is_a?(String)
        assert_equal(options.gsub(/^\//, ''), redirected_to, message)
      else
        msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is )", redirected_to)
        assert_equal(@controller.url_for(options).match(js_regexp)[3], redirected_to, msg)
      end
    end
  end
Strip the ^ and $ from that regex if you're returning more than just a URL in your RJS update call. *update* fixed the method so it actually takes a hash. testing is not just for sissies!

more cracked-out scaffolding: Element.twisty

Courtenay : February 2nd, 2006

For anyone following this, I'm slowly building you up to a fully-featured sortable, filtered scaffold helper library. You may recall we already built filtering with SELECT dropdowns. I'm going to add an input box and some sorting to that. First, though, we need a little sumthin sumthin to simplify the UI: the twisty! The twisty is the proper name for that little triangly ► which turns into a ▼ when you click it, revealing some more information (maybe a tree node, or more options). Read the rest of this entry

disable all your form elements

Courtenay : January 31st, 2006

This code example is a perfect case of engineering overkill. I'm building a CMS that auto-saves drafts, but to keep it simple, the user can only access the most recent draft. However, in the case where they go and edit the document, I need to warn them that there's a more recent draft that they should be editing. Here's how I solved it (read on..) Read the rest of this entry

Selectors and Effects Queues : Squirrel Domination

Liquid error: undefined method `login' for nil:NilClass : January 25th, 2006

I recently came into a wealth of "squirrel photos":http://thesaq.com/pics I'd like to showcase. What I wanted was something like notebook tabs in a widget set. I didn't want *ALL* of them to be visible at once; I wanted little tab-like links to show specific squirrel photos. I figured I'd leverage some "scriptaculous effects":http://wiki.script.aculo.us/scriptaculous/search/?page%5Bname%5D=Effects to make the showing/hiding of my squirrel photos cooler looking. As a result I ended up using the new "selector functionality":http://dev.rubyonrails.org/changeset/3432 that "madrobby":http://mir.aculo.us/articles/2006/01/18/prototype-gets-selector-magic commented on recently. I also got a little bit of insight into "Scriptaculous Effects Queues":http://wiki.script.aculo.us/scriptaculous/show/EffectQueues. Read the rest of this entry
Do you have a massive list of items in a scaffold-type arrangement (e.g. in a typo-style admin)? Here's a way to quickly and easily filter and sort your big-assed list. Note: this requires the Hash#to_sql function posted here a few weeks ago .. told you it was useful! More after the jump.. Read the rest of this entry