Understanding Navigation Tag Code
What is the Navigation Tag?
The *navigation tag* is a handy little tag that can render a a collection of menu items and apply certain styles to the 'selected' items. (also can apply styles to the 'here' items)
<r:navigation urls="Downloads: /en/downloads/ | Documentation: /en/documentation/ | Libraries: /en/libraries/"> <r:normal><a href="<r:url />"><r:title /></a></r:normal> <r:here><strong><r:title /></strong></r:here> <r:selected><strong><a href="<r:url />"><r:title /></a></strong></r:selected> <r:between><span class="separator"> | </span></r:between> </r:navigation>
This was exactly the functionality I was looking for however I did not want to hardcode the "Title: url;" items. Therefore I was going to hack the navigation tag to suit my needs. But before hacking on the code I needed to understand it.
This Wiki was created based on some of the great information I got from the mailing list from people like John and Sean.
Learning about Tags
Digging into standard_tags.rb was very disorienting for eyes that are use to looking at nice clean methods. However, reviewing the Radius Quickstart helps. There is also a good Tag Primer on the Radiant blog.
Tag Tricks
You can *call another tag* from an existing tag by calling *render* on a tag binding:
tag 'child_urls' do |tag|
tag.render('children:each', 'limit' => '5') do
tag.render('url')
end
end
You can pass nested tag values to the tag you are calling by setting tag.locals.whatever inside the do...end
Navigation Tag Explained
Looking at the first few lines of code for the *navigation* tag in the standard_tags.rb file you will find:
hash = tag.locals.navigation = {}
tag.expand
raise TagError.new("`navigation' tag must include a `normal' tag") unless hash.has_key? :normal
The first line does two things: Assigns a blank Hash to tag.locals.navigation and also assigns 'tag.locals.navigation' to the local variable 'hash'.
Since the next line is 'tag.expand', the block you pass would be executed at this point. The "trick" is that if you re-assign tag.locals.navigation inside that block, whatever you assigned it would fall out of scope after the block ends.
So, to get your values to pass through, you should either merge the contents of your navigation_dynamic local in to tag.locals.navigation like so:
tag.locals.navigation.merge! tag.locals.navigation_dynamic
or set each individual key:
tag.locals.navigation[:here] # = something tag.locals.navigation[:normal] #= something
The 'r:navigation' tag also expects that the elements in that hash will be tag-binding Procs (for lack of a better word).
Navigation Tag Explained in Detail
For reference, look at: standard_tags.rb.
The second line inside the tag definition, line 498, calls 'tag.expand'. This causes any tags nested inside (in the page, mind you) to be rendered/evaluated. The tags usually nested inside <r:navigation> are listed starting at line 524.
Here's their definitions:
[:normal, :here, :selected, :between].each do |symbol|
tag "navigation:#{symbol}" do |tag|
hash = tag.locals.navigation
hash[symbol] = tag.block
end
end
This generates tag definitions for everything in that array in the first line of the snippet. So, for each one, we grab the 'tag.locals.navigation' that was assigned by the *parent tag* (originally blank), and assign the hash key associated with this tag to the tag block. So essentially, you're passing a block/closure/proc back to the parent tag that can be evaluated contextually (multiple times).
So here's an overview, again:
1) <r:navigation> somewhere in your page invokes the tag definition.
2) We assign a blank Hash to tag.locals.navigation (and also, incidentally, to a local var called 'hash' -- all variables are just references/by-reference in Ruby, so they're really the same object)
3) We evaluate the contained block inside <r:navigation> by calling tag.expand
3.5) Each contained tag of 'normal', 'here', 'selected', 'between' assigns its contained block to a key in tag.locals.navigation for future evaluation.
4) <r:navigation> evaluation continues -- parsing the urls/titles, evaluating various of the passed blocks on each one, depending on URL, then joining them together for output.
