So this past week I've been working on a new data flow for our d-rails helpers in rails. I've implemented most of the dojo.fx functionality with this new flow, since it's a relatively small subset of functionality that would give me a pretty good idea if this was going to work well or not.
So let's start.
I ran across this page while researching dojo.fx, http://mzacher.de/sandbox/dojo.html , and while it's based on an older version of dojo, it gave me a good goal to work toward. Since the effects that he used on that page are unavailable in the current release of dojo, I did a bit of a stripped down version which I'll show you now.
First we'll look at a snippet of the view file:
Fade out
</div>
<div id="highlight" class="box" onclick='<%= highlight "highlight", { :color => "#ffe761" }%>'>
Highlight
</div>
<div id="slideby" class="box" onclick='<%= slide_by "slideby", { :top => 50, :left => 22 }%>'>
Slide by (click again and again)
</div>
Pretty simple stuff here: 3 divs, each with a server-side generated onclick property. Each helper an element ID as it's first argument, and an options hash as its second.
Now lets look at how these helpers actually generate the code. Here's what the highlight helper looks like:
def highlight(element_id, options = {})
dojo_require("dojox.fx")
options[:node] = element_id
dojo_fx_play("dojox.fx.highlight", options)
endAlso pretty simple so far, we're just adding element_id to the hash under the name node, and calling dojo_require (to ensure that we load the library when the page is rendered) and dojo_fx_play (which actually generates the code required).
Now it gets a little more complicated. Lets look at what dojo_require and dojo_fx_play are actually doing:
def dojo_require(module_name)
@_dojo_requires ||= Set.new
module_name.each { |m| @_dojo_requires.add(m) }
end
def dojo_fx_play(function_name, *args)
dojo_function_call(function_name, *args) + ".play();"
end
def dojo_function_call(function_name, *args)
out = function_name + "("
args.each do |arg|
out << arg.to_json
out << (args.last == arg ? ")" : ",")
end
out
endSo dojo_fx_play just calls dojo_function_call with the method name and args list. dojo_function_call just directly converts the args list to json and outputs a javascript statement. The advantage to this is that dojo developers don't need to learn a new vernacular of options to use the methods they've always had.
dojo_require on the other hand, just keeps a list of which modules have been require'd so far in an instance variable to be used later.
I'm guessing that right about now you're wondering why I'm bothering to do any of this at all. dojo HAS a dojo.require function, and all I'm really doing is converting dojo functions to ruby syntax. The magic here comes in final helper, which isn't actually referenced in the view I showed you earlier.
def dojo_script_tag
out = ""
unless @_dojo_requires.nil?
@_dojo_requires.each do |req|
out << "dojo.require('#{req}');"
end
end
javascript_tag(out)
enddojo_script_tag is (usually) called in the template instead of the views themselves. It makes sure you have all the dojo.require's that you need, and wraps them up in a script tag. The reason this is somewhat magical is because of the way rails handles templates.
If you have a template file, let's call it template.rhtml, it will look something like this:
<title>d-rails rocks!</title>
<%= javascript_include_tag :defaults %>
</head>
<body>
<%= render :partial => 'shared/header'%>
<%= yield %>
<%= render :partial => 'shared/footer'%>
<%= dojo_script_tag %>
</body>
The nice thing about templates is that they "compile" the files they include, before executing themselves. This means we can have lots of lots of helpers called from the header, footer, and main body, and not have to worry about keeping track of our dojo.require statements.
The code still felt a little incomplete though, so I wanted to try adding something else to make it a little more "dojo." Creating objects and running play() on them in the onclick handlers didn't seem like the best way to do this, so I came up with something a little bit different:
def dojo_fx_play(function_name, *args)
new_dojo_object(dojo_function_call(function_name, *args)) + ".play();"
end
def new_dojo_object(constructor)
@_dojo_objects ||= []
name = @_dojo_objects.last.nil? ? "drails_object_000" : @_dojo_objects.last[:name].next
@_dojo_objects << {:name => name, :constructor => constructor}
name
end
def dojo_script_tag
out = ""
unless @_dojo_requires.nil?
@_dojo_requires.each do |req|
out << "dojo.require('#{req}');"
end
end
unless @_dojo_objects.nil?
@_dojo_objects.each do |obj|
out << "var #{obj[:name]} = #{obj[:constructor]};"
end
end
javascript_tag(out)
endNow we can re-use the objects we've created. dojo_fx_play calls new_dojo_object, passing in what dojo_function_call returns, which adds it to the registry. new_dojo_object returns the name of the object after it's placed in the registry, which gets passed up the chain as "objectName.play()" for the onclick handlers. Finally, dojo_script_tag completes the circle by writing the object definitions in the same script tag as all the dojo.requires.
Of course there are several downfalls particular to this implementation.
- Can't name your own javascript objects (yet)
- I'm probably rewriting at least *some* functionality that rails already has. (dojo_function_call strikes me as very probable)
At the same time though, I think this is a good direction for things to be going. As always, I'd love to hear your thoughts, critiques, suggestions, etc.

new_dojo_object
Do you have any ideas about how you might add custom names for the javascript objects?