You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Paul Cantrell <ca...@pobox.com> on 2006/05/07 23:37:42 UTC

A probably controversial attempt at improving template readability

I decided that I've had it up the wazoo with typing (and looking at)  
"ognl:" all the time. Probably my least favorite things about  
Tapestry is the eye-bending verbosity of its template files -- the  
previewability is fantastic, but it comes at a *high* price in  
keystrokes and readability, IMO.

I have a new approach I'm somewhat happier with, and thought I'd  
share it so that everyone else can either try it out or decry my  
foolishness, as they see fit.

I added this to my hivemodule.xml:

	<contribution configuration-id="tapestry.bindings.BindingFactories">
	  <binding prefix="" service- 
id="tapestry.bindings.OGNLBindingFactory"/>
	  <binding prefix="M" service- 
id="tapestry.bindings.MessageBindingFactory"/>
	  <binding prefix="-" service- 
id="tapestry.bindings.LiteralBindingFactory"/>
	  <binding prefix="A" service- 
id="tapestry.bindings.AssetBindingFactory"/>
	  <binding prefix="B" service- 
id="tapestry.bindings.BeanBindingFactory"/>
	  <binding prefix="L" service- 
id="tapestry.bindings.ListenerBindingFactory"/>
	  <binding prefix="C" service- 
id="tapestry.bindings.ComponentBindingFactory"/>
	  <binding prefix="S" service- 
id="tapestry.bindings.StateBindingFactory"/>
	  <binding prefix="H" service- 
id="tapestry.bindings.HiveMindBindingFactory"/>
	</contribution>

This makes allows me to write ":value" instead of "ognl:value" ...  
which seems like a very small thing until you see how it cleans up  
template files! It also creates one-letter prefixes for all the other  
bindings. (Specific choices here are a matter of taste -- I like the  
caps so that I don't have to twiddle the shift key back and forth to  
type quote - prefix - colon, but one could easily change this.)

Unfortunately, Tapestry is one step ahead of me, and actually seems  
to intentionally to prevent what I'm doing. (Sorry, HLS, if you are  
gritting your teeth as you read this!) In BindingSourceImpl:

     public IBinding createBinding(IComponent component, String  
bindingDescription,
             String reference, String defaultPrefix, Location location)
     {
     ...
         int colonx = reference.indexOf(':');

         if (colonx > 1)
         {
             String pathPrefix = reference.substring(0, colonx);
     ...

In other words binding prefixes are customizable, but they must be at  
least two characters.

Soooooo, I just overrode the default binding factory:

	<implementation service-id="tapestry.bindings.BindingSource">
	  <invoke-factory>
	    <construct class="InnigBindingSourceImpl">
	      <set-configuration property="contributions" configuration- 
id="tapestry.bindings.BindingFactories"/>
	    </construct>
	  </invoke-factory>
	</implementation>

public class InnigBindingSourceImpl
     implements BindingSource
     {
     private List<BindingPrefixContribution> contributions;
     private Map<String,BindingFactory> factoriesByPrefix = new  
HashMap<String,BindingFactory>();

     public void initializeService()
         {
         for(BindingPrefixContribution c : contributions)
             factoriesByPrefix.put(c.getPrefix(), c.getFactory());
         }

     public IBinding createBinding(IComponent component, String  
bindingDescription, String reference,
                                   String defaultPrefix, Location  
location)
         {
         String prefix = defaultPrefix;
         String path = reference;

         int colonx = reference.indexOf(':');
         if(colonx != -1)
             {
             String pathPrefix = reference.substring(0, colonx);

             if(factoriesByPrefix.containsKey(pathPrefix))
                 {
                 prefix = pathPrefix;
                 path = reference.substring(colonx + 1);
                 }
             else
                 throw new ApplicationRuntimeException(
                     "Unknown binding prefix \"" + pathPrefix + "\"  
in expression \"" + reference + '"');
             }

         return factoriesByPrefix.get(prefix).createBinding 
(component, bindingDescription, path, location);
         }

     public void setContributions(List<BindingPrefixContribution>  
contributions)
         { this.contributions = contributions; }
     }

(Note that this code explicitly requires the use of "literal:" or  
"-:" if a binding contains a colon. This prevents me from having a  
literal accidentally turn into some other kind of binding in a future  
upgrade.)

Combined with this, I've abandoned the use of <span> as a generic  
"Tapestry-only" tag, and am now using <if> for @If, <for> for @For,  
and <x> for @Insert.

Before:
     <span jwcid="@For" source="ognl:articles" value="ognl:article">
         <li>
             <a href="#" jwcid="@ExternalLink" page="ArticleView"  
parameters="ognl:article">
                 <span jwcid="@Insert"  
value="ognl:article.title">Article Title</span>
             </a>
         </li>
     </span>

After:
     <for jwcid="@For" source=":articles" value=":article">
         <li>
             <a href="#" jwcid="@ExternalLink" page="ArticleView"  
parameters=":article">
                 <x jwcid="@Insert" value=":article.title">Article  
Title</x>
             </a>
         </li>
     </for>

It's still not as concise as Velocity or Rails's ERB, but it *is*  
still code-free (unlike ERB) and browser-previewable (unlike both).  
I'm definitely happier with it. I still don't like the redundancy of  
<for jwcid="@For">, and don't like the verbosity of the @Insert....

Cheers,

Paul

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: A probably controversial attempt at improving template readability

Posted by DarĂ­o Vasconcelos <da...@gmail.com>.
Although I'm not using TP4 and therefore I cannot use your solution,
I'm with you on the verbosity issue. But I suppose that previewability
and speed of development come with a price. I mean, you could always
choose to fill all the detail in the .page file, and readability would
improve a lot, but at the cost of having to flip all the time between
the .html and the .page to find out what's going on.

And there's more: my guess is that for real-life problems it is very
common to have 30-50% of the HTML in components, rendering virtually
impossible to have a meaningful preview of any given file.

My dream would only be to use shorter names for some components (ie,
@Conditional) and removing all the ognl: craziness. Although I don't
know how this would be done...

regards,

On 5/7/06, Paul Cantrell <ca...@pobox.com> wrote:
> I decided that I've had it up the wazoo with typing (and looking at)
> "ognl:" all the time. Probably my least favorite things about
> Tapestry is the eye-bending verbosity of its template files -- the
> previewability is fantastic, but it comes at a *high* price in
> keystrokes and readability, IMO.
>
> I have a new approach I'm somewhat happier with, and thought I'd
> share it so that everyone else can either try it out or decry my
> foolishness, as they see fit.
>
> I added this to my hivemodule.xml:
>
>         <contribution configuration-id="tapestry.bindings.BindingFactories">
>           <binding prefix="" service-
> id="tapestry.bindings.OGNLBindingFactory"/>
>           <binding prefix="M" service-
> id="tapestry.bindings.MessageBindingFactory"/>
>           <binding prefix="-" service-
> id="tapestry.bindings.LiteralBindingFactory"/>
>           <binding prefix="A" service-
> id="tapestry.bindings.AssetBindingFactory"/>
>           <binding prefix="B" service-
> id="tapestry.bindings.BeanBindingFactory"/>
>           <binding prefix="L" service-
> id="tapestry.bindings.ListenerBindingFactory"/>
>           <binding prefix="C" service-
> id="tapestry.bindings.ComponentBindingFactory"/>
>           <binding prefix="S" service-
> id="tapestry.bindings.StateBindingFactory"/>
>           <binding prefix="H" service-
> id="tapestry.bindings.HiveMindBindingFactory"/>
>         </contribution>
>
> This makes allows me to write ":value" instead of "ognl:value" ...
> which seems like a very small thing until you see how it cleans up
> template files! It also creates one-letter prefixes for all the other
> bindings. (Specific choices here are a matter of taste -- I like the
> caps so that I don't have to twiddle the shift key back and forth to
> type quote - prefix - colon, but one could easily change this.)
>
> Unfortunately, Tapestry is one step ahead of me, and actually seems
> to intentionally to prevent what I'm doing. (Sorry, HLS, if you are
> gritting your teeth as you read this!) In BindingSourceImpl:
>
>      public IBinding createBinding(IComponent component, String
> bindingDescription,
>              String reference, String defaultPrefix, Location location)
>      {
>      ...
>          int colonx = reference.indexOf(':');
>
>          if (colonx > 1)
>          {
>              String pathPrefix = reference.substring(0, colonx);
>      ...
>
> In other words binding prefixes are customizable, but they must be at
> least two characters.
>
> Soooooo, I just overrode the default binding factory:
>
>         <implementation service-id="tapestry.bindings.BindingSource">
>           <invoke-factory>
>             <construct class="InnigBindingSourceImpl">
>               <set-configuration property="contributions" configuration-
> id="tapestry.bindings.BindingFactories"/>
>             </construct>
>           </invoke-factory>
>         </implementation>
>
> public class InnigBindingSourceImpl
>      implements BindingSource
>      {
>      private List<BindingPrefixContribution> contributions;
>      private Map<String,BindingFactory> factoriesByPrefix = new
> HashMap<String,BindingFactory>();
>
>      public void initializeService()
>          {
>          for(BindingPrefixContribution c : contributions)
>              factoriesByPrefix.put(c.getPrefix(), c.getFactory());
>          }
>
>      public IBinding createBinding(IComponent component, String
> bindingDescription, String reference,
>                                    String defaultPrefix, Location
> location)
>          {
>          String prefix = defaultPrefix;
>          String path = reference;
>
>          int colonx = reference.indexOf(':');
>          if(colonx != -1)
>              {
>              String pathPrefix = reference.substring(0, colonx);
>
>              if(factoriesByPrefix.containsKey(pathPrefix))
>                  {
>                  prefix = pathPrefix;
>                  path = reference.substring(colonx + 1);
>                  }
>              else
>                  throw new ApplicationRuntimeException(
>                      "Unknown binding prefix \"" + pathPrefix + "\"
> in expression \"" + reference + '"');
>              }
>
>          return factoriesByPrefix.get(prefix).createBinding
> (component, bindingDescription, path, location);
>          }
>
>      public void setContributions(List<BindingPrefixContribution>
> contributions)
>          { this.contributions = contributions; }
>      }
>
> (Note that this code explicitly requires the use of "literal:" or
> "-:" if a binding contains a colon. This prevents me from having a
> literal accidentally turn into some other kind of binding in a future
> upgrade.)
>
> Combined with this, I've abandoned the use of <span> as a generic
> "Tapestry-only" tag, and am now using <if> for @If, <for> for @For,
> and <x> for @Insert.
>
> Before:
>      <span jwcid="@For" source="ognl:articles" value="ognl:article">
>          <li>
>              <a href="#" jwcid="@ExternalLink" page="ArticleView"
> parameters="ognl:article">
>                  <span jwcid="@Insert"
> value="ognl:article.title">Article Title</span>
>              </a>
>          </li>
>      </span>
>
> After:
>      <for jwcid="@For" source=":articles" value=":article">
>          <li>
>              <a href="#" jwcid="@ExternalLink" page="ArticleView"
> parameters=":article">
>                  <x jwcid="@Insert" value=":article.title">Article
> Title</x>
>              </a>
>          </li>
>      </for>
>
> It's still not as concise as Velocity or Rails's ERB, but it *is*
> still code-free (unlike ERB) and browser-previewable (unlike both).
> I'm definitely happier with it. I still don't like the redundancy of
> <for jwcid="@For">, and don't like the verbosity of the @Insert....
>
> Cheers,
>
> Paul
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>


--
Times have not become more violent. They have just become more televised.
    Marilyn Manson

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org