# Custom Tags and Code reuse

**URL:** <https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917>\
**Category:** Radiant CMS\
**Created:** [January 16, 2007, 8:09am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917 "2007-01-16T08:09:24Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 8:09am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/1 "2007-01-16T08:09:24Z")

</div>

In the standard tags module there is alot of useful code however it’s  
not encapsulated in classes so I can’t really build my custom tags  
upon the existing tags.

I would really like access to “children:each” and just pass it an  
options hash or something. Am I missing some code somewhere? Is there  
some magic way to reference these tags via your own custom tags or  
classes?

Also is there some good info on the way Tags work, after spending my  
life in methods this syntax seems so odd and I haven’t had much luck  
trying to hunt down particular methods in the api.

Thanks-  
Michael

BTW:  
[http://radiantcms.org/blog/2006/11/24/how-to-write-time-sensitive-or-expiring-content/](http://radiantcms.org/blog/2006/11/24/how-to-write-time-sensitive-or-expiring-content/)  
was very helpful for building custom tags via the new extensions  
method.

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 2:50pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/2 "2007-01-16T14:50:39Z")

</div>

Michael J. wrote:

> In the standard tags module there is alot of useful code however it’s  
> not encapsulated in classes so I can’t really build my custom tags  
> upon the existing tags.
> 
> I would really like access to “children:each” and just pass it an  
> options hash or something. Am I missing some code somewhere? Is there  
> some magic way to reference these tags via your own custom tags or  
> classes?

To call out to another tag you can call render on a tag binding:

tag ‘child\_urls’ do |tag|  
tag.render(‘children:each’, ‘limit’ =\> ‘5’) do  
tag.render(‘url’)  
end  
end

> Also is there some good info on the way Tags work, after spending my  
> life in methods this syntax seems so odd and I haven’t had much luck  
> trying to hunt down particular methods in the api.

The basic principles of tag construction are covered in the Radius  
quickstart guide:

[http://radius.rubyforge.org/files/QUICKSTART.html](http://radius.rubyforge.org/files/QUICKSTART.html)

–  
John L.  
[http://wiseheartdesign.com](http://wiseheartdesign.com)

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 8:06pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/3 "2007-01-16T20:06:17Z")

</div>

> To call out to another tag you can call render on a tag binding:
> 
> tag ‘child\_urls’ do |tag|  
> tag.render(‘children:each’, ‘limit’ =\> ‘5’) do  
> tag.render(‘url’)  
> end  
> end

Thanks that really helped, one other question:

How do I pass locals into a tag I’m rendering from another tag?

For example I want to create my own custom tag that dynamically  
generates the values for the \<r:navigation\> tag.

It would go something like:

tag ‘navigation\_dynamic’ do |tag|  
#some code to generate string of urls  
tag.render(‘navigation’, ‘urls’ =\> ‘url string here’)  
end

The ‘navigation\_dynamic’ would have the nested values that the  
‘navigation’ tag expects, but how do I pass those in?

Thanks-  
Michael

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 9:46pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/4 "2007-01-16T21:46:31Z")

</div>

> I believe you can just set tag.locals.whatever inside the do…end block.

I tried to set my locals to be the locals of the navigation tag like  
this:

tag ‘navigation\_dynamic’ do |tag|  
tag.render(‘navigation’, {‘urls’ =\> ‘Resources:  
/resources/;Search: /resources/resources-search/;’}) do  
tag.locals.navigation = tag.locals.navigation\_dynamic  
end  
end

I got the error about including a ‘normal tag’, I tried a few  
different approaches such as:  
tag.locals.navigation = {:normal=\>‘yo’}

The navigation tag in the standard\_tags.rb is kinda trick, could you  
explain the first line some?

hash = tag.locals.navigation = {}  
tag.expand  
raise TagError.new(“`navigation' tag must include a`normal’ tag”)  
unless hash.has\_key? :normal

Also, is there a good way to log debugging from the tags?

Thanks-  
Michael

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 10:03pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/5 "2007-01-16T22:03:34Z")

</div>

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.navigationinside 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).

I have a plan for the \<r:navigation\> tag that I haven’t completed yet,  
but  
it will allow you to have both external links and dynamically generated  
menus, so you could have something like:

\<r:navigation\>  
\<r:children:each\>  
\<r:item\>  
\<r:label\>\<r:title /\>\</r:label\>  
\<r:href\>\<r:url /\>\</r:href\>  
\</r:item\>  
\</r:children:each\>  
\<r:normal\>…\</r:normal\>  
…  
\</r:navigation\>

I also plan to have it support the old style for backwards  
compatibility’s  
sake, or even a combination.

Cheers,

Sean C.

On 1/16/07, Michael J. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > I believe you can just set tag.locals.whatever inside the do…end  
> > block.

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 10:25pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/6 "2007-01-16T22:25:48Z")

</div>

Thanks for you help on this, i’m still struggling…

> 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’.

If we assign a blank hash to navigation and assign that to a variable  
called ‘hash’ how can it contain any values if we assigned a blank  
one?

To establish a baseline I tried copying the existing ‘navigation’ code  
into my custom tag (with name edits) and I’m still getting the ‘normal  
tag not found error’.

snippet:  
\<r:navigation\_dynamic urls="[Resources: /resources/;Search:  
/resources/resources-search/;Health: /resources/food-and-health/;]"\>  
\<r:normal\>\<r:title /\>\</r:normal\>  
\<r:here\> **\<r:title /\>** \</r:here\>  
\<r:selected\>**\<r:title  
/\>**\</r:selected\>  
\<r:between\> | \</r:between\>  
\</r:navigation\_dynamic\>

head of custom tag;  
tag ‘navigation\_dynamic’ do |tag|  
hash = tag.locals.navigation\_dynamic = {}  
tag.expand  
raise TagError.new(“`navigation_d' tag must include a`normal’  
tag”) unless hash.has\_key? :normal

error:  
`navigation_d' tag must include a`normal’ tag

I’ve dumped my cache and restarted the server a bunch. How can I get  
logging to see what is contained in ‘tag.locals’? Can you see what  
I’m doing wrong here/

Thanks-  
Michael

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 10:31pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/7 "2007-01-16T22:31:50Z")

</div>

> If we assign a blank hash to navigation and assign that to a variable  
> called ‘hash’ how can it contain any values if we assigned a blank  
> one?

The tags that are rendered in the ‘tag.expand’ call set values in the  
hash.  
Look at their definitions and you’ll see it.

> To establish a baseline I tried copying the existing ‘navigation’ code  
> into my custom tag (with name edits) and I’m still getting the ‘normal  
> tag not found error’.

Make sure that you redefine each of the nested tags in under  
‘navigation’…  
i.e. ‘navigation:normal’, ‘navigation:here’, ‘navigation:selected’,  
‘navigation:between’; change the prefix (part before the colon) on each  
one  
to ‘navigation\_dynamic’.

Sean

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 11:50pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/8 "2007-01-16T23:50:10Z")

</div>

> The tags that are rendered in the ‘tag.expand’ call set values in the hash.  
> Look at their definitions and you’ll see it.

I must be missing it, I don’t see an expand method anywhere other than  
here:  
[http://radius.rubyforge.org/classes/Radius/TagBinding.html#M000011](http://radius.rubyforge.org/classes/Radius/TagBinding.html#M000011)

And with that I don’t see how the ‘hash’ is passed to that method  
inorder to set values in it?

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 16, 2007, 8:39pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/9 "2007-01-16T20:39:25Z")

</div>

Michael,

I believe you can just set tag.locals.whatever inside the do…end  
block.

Sean

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 12:57am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/10 "2007-01-17T00:57:19Z")

</div>

> I must be missing it, I don’t see an expand method anywhere other than here:  
> [http://radius.rubyforge.org/classes/Radius/TagBinding.html#M000011](http://radius.rubyforge.org/classes/Radius/TagBinding.html#M000011)

Ok, looks like I’ll have to explain in more detail. For reference, I’m  
looking at  
[http://dev.radiantcms.org/radiant/browser/branches/mental/radiant/app/models/standard\_tags.rb#L461](http://dev.radiantcms.org/radiant/browser/branches/mental/radiant/app/models/standard_tags.rb#L461)  
.

The second line inside the tag definition, line 463, 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 489. 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.

Let me know if I can clear anything else up.

Cheers,

Sean C.

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 2:15am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/11 "2007-01-17T02:15:28Z")

</div>

> Hmmm this smells like and looks like a great candidate for the  
> Wiki…or is it just me?

Yeah I agree, Sean has written up some pretty good stuff. I’ll be  
happy to wikify some notes on this once I figure it out.

Are wiki accounts open to anyone or do I need to get some account info  
in order to post? Any special area this should be placed?

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 1:40am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/12 "2007-01-17T01:40:26Z")

</div>

Hmmm this smells like and looks like a great candidate for the  
Wiki…or is it just me?

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 2:29am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/13 "2007-01-17T02:29:30Z")

</div>

Michael,

Anyone can open a Wiki account. If you don’t find a good home for the  
text, then you can create a page (it will be at the discretion of John  
wether to leave it or remove it).

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 2:32am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/14 "2007-01-17T02:32:30Z")

</div>

Michael J. wrote:

> Are wiki accounts open to anyone or do I need to get some account info  
> in order to post? Any special area this should be placed?

You presently have to register and login in order to use the wiki.

–  
John L.  
[http://wiseheartdesign.com](http://wiseheartdesign.com)

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 7:06pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/15 "2007-01-17T19:06:51Z")

</div>

I added a wiki HowTo that contains some of the great information I got  
on this thread about the ‘Navigation Tag’.

Thanks again for you great indepth explanations Sean. And for a great  
CMS John.

[http://dev.radiantcms.org/radiant/wiki/HowToUnderstandNavigationTagCode](http://dev.radiantcms.org/radiant/wiki/HowToUnderstandNavigationTagCode)

Cheers-  
Michael

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 7:56pm UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/16 "2007-01-17T19:56:57Z")

</div>

Nice Wiki entry, Michael. I’m sure it’ll come handy soon to many of  
us. If not for the navigation tag itself, but for the tag  
customization.

---

<div class="post-metadata">

**Author:** ![bluetrust](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/8e7dd6/32.png) [@bluetrust](https://www.ruby-forum.com/u/bluetrust)\
**Post date:** [January 17, 2007, 3:10am UTC](https://www.ruby-forum.com/t/custom-tags-and-code-reuse/81917/17 "2007-01-17T03:10:32Z")

</div>

> Let me know if I can clear anything else up.

Whew! I’m finally rolling along on Radiant. Got the dynamic generation  
of secondary nav menus worked out and now I’m easily adding my own  
custom tags.

Thank you _very much_ Sean for such detailed information. I’ll get  
some wiki info up on this.

The thing that I missed was:

[:normal, :here, :selected, :between].each do |symbol|  
tag “navigation:#{symbol}” do |tag|  
hash = tag.locals.navigation  
hash[symbol] = tag.block  
end  
end

I didn’t even recognize that this was multiple tag definitions, I  
needed to make changes in that code.

Thanks-  
Michael
