Michael Koziarski wrote:> What about if you change this to:
>
> <%= text_field ("user", "username") %>
>
> That''s the ''rails way'' to handle this.
To explain this would probably get a bit off-topic, but what the heck.
I''m always interested in feedback. I was not completely honest in what
the code actually is. I removed aspects that were not important. The
actual code is:
<textbox name="username" value="<%=@usr.username%>"
/>
Of course this isn''t (X)HTML. This is because I don''t use the
helper
functions provide by rails. Instead I use a XSLT library that I have
built over time with a number of other frameworks. From my experience
there are 4 basic way to generate standard chunks of HTML content. The
first is the way you described above. It is the functional method. So:
text_field "user", "username"
and obviously you get more complex as these "widgets" get more
complex.
I have seen all sorts of complex components developed with this simple
functional interface. There are a couple of problems with this
interface. The biggest is that over time you keep adding more options to
deal with more situations. Pretty soon you have a function that looks like:
text_field "user", "username", "Enter Username",
true, false,
"onClick=\"my_func()\""
Now what does each of these arguments mean? You end up having to look at
the function prototype often to remember what they are and what order
they go in (unless you have a IDE that does some sort of intellisense).
Even if you know what they mean because you use them so often do you
really want to type those arguments every time. It would be nice to have
defaults. And something like:
text_field "user", "username", nil, nil, nil,
"onClick=\"my_func()\""
isn''t really much better. So the next stage up is using named arguments
(or hashes in Ruby) with good defaults. With this you end up with:
text_field( object => "user",
attribute => "username",
default => "Enter Username",
editable => true,
required => false,
htmlExtra => "onClick=\"my_func()\"" )
This works pretty good. Especially if the passed in hash is merged with
a good set of defaults so you can do something like:
text_field( object => "user", attribute => "username"
)
and rely on the defaults for everything else. This way the simple case
is easy, but the hard case is still possible. Also the order of the
arguments don''t matter anymore.
So what is the problem with this method? What if you want a more complex
object that cannot be specified easily with a simple hash. For example,
what if you want to create a list component which displays a list of
items. This component will handle putting things like header cells for
each column as well as a title to the list. Also it could support things
like putting a checkbox on each item so you can select items to take an
action on. It would also provide useful features like alternating rows
to different colors, allowing you to click anywhere on the list row to
enable the checkbox, and specify a series of actions that can be taken
for selected objects. It will also set things up so that when you submit
an action it will direct it to the right script (controller and action).
So we are talking about a highly interactive list instead of just a
plain HTML table. How do you specify this with the hash interface? It
because difficult as you continue to add features to your component. So
the next step up is a "Builder" object interface. So you do something
like this:
<%
list = HTMLList.new
list.odd_color = "#d4e0dd"
list.even_color = "#de03df"
new_action = HTMLListAction.new
new_action.action = ''new''
new_action.text = ''New User''
list.add_action new_action
edit_action = HTMLListAction.new
edit_action.action = ''edit''
edit_action.text = ''Edit User''
list.add_action edit_action
delete_action = HTMLListAction.new
delete_action.action = ''delete''
delete_action.text = ''Delete User''
delete_action.vertical_location = :bottom
list.add_action delete_action
list.add_column( "Username" );
list.add_column( "Active" );
list.add_row( ''eric'', true );
list.add_row( ''hunter'', false );
%>
<%=list.output%>
I am just making up this interface on the fly. Obviously you can make it
better or worse, but the concept is the same. You create objects to
generate content. Objects have default values for attributes and you can
set the optional attributes. When you have you object built you call
some sort of output method which generates your HTML content from the
information you have given it.
This seems to solve all our problems, but introduces a new one. We can
do complex stuff but the easy stuff has now become hard. What if you
just want to output what we started with? You end up with:
<%
username = TextBox.new
username.object = @usr
username.attribute = :username
%>
<%=username.output%>
This type of mess really gets in the way of your view code and makes
things confusing.
The last way (and my preferred way) is to use a tag based markup method.
So if you want to output a calender input box to enter the user''s date
of birth you have:
<calendar name="dob" value="<%=@usr.dob%>" />
This directly matches traditional HTML which you are already using:
<textarea
name="description"><%=description%></textarea>
It will allow you to have defaults and put things in any order you want
like the hash method. So you could have:
<textfield name="user" value="<%=@usr.username%>"
required="false"
editable="true" onclick="my_func()" />
for the complex case but also just:
<textfield name="user" value="<%=@usr.username%>"
/>
for the easy case. It also handles the really complex case like the list
component. So you can have:
<list odd="#d4e0dd" even="#de03df">
<action name="new" label="New User" />
<action name="edit" label="Edit User" />
<action name="delete" label="Delete User"
vpos="bottom" />
<header>Username</header>
<header>Active</header>
<row>
<column>eric</column>
<column><checkbox checked="true"/></column>
</row>
<row>
<column>hunter</column>
<column><checkbox checked="false"/></column>
</row>
</list>
Obviously the content for the list can be generate from an array of
objects with a little ERB sprinked about much like the rest of your
content is generated by tags with ERB sprinkled throughout.
In addition to it working for all situations you get the following
additional benefits.
First, my special tags are cross language and cross framework. I have
used them in Perl, ASP, PHP and many other environments. Some of my tags
are tightly bound to a particular language or framework but many can be
used in any system.
Second, some browsers (Mozilla and IE recent versions) support
processing XSLT on the client side. So you can configure things to just
send the content and XSLT files and the pure HTML content will be
generated client-side. This reduces your load on the server and reduces
your bandwidth (after the first download of your XSLT files it doesn''t
have to download them again if they are used on other pages or the same
page is displayed again). So basically instead of sending the final
display you are simply sending the content (a mixture of HTML and your
own tags) and you tell the client how to make the final display for
themselves. After the first hit you simply send content. The browser
already knows how to generate the final output.
Also for backwards compatibility you can process server-side if their
User-Agent string is an older browser. So there is no backwards
compatibility issue. You can even use the user-agent string (and other
info) to generate different implementations of a widget (if they don''t
support javascript then simply send the dropdown lists, but if they do
then send them the implementation from
http://www.dynarch.com/projects/calendar/ for a little added pizazz.
I use mod_xslt as an output filter for Apache so it doesn''t matter what
language (Ruby, Perl, PHP) or framework (Rails, PEAR, etc.) I am
developing in I can use my special tags. Some tags are general purpose
components but other tags are specific to an application or industry
domain. But if you wanted a pure Ruby way you could find a XSLT ruby
library (if there isn''t one I would imagine you could bind to libxslt
easily) and put it as a "after_filter" on ActionController. Put it in
the ApplicationController and you are set to go.
There are a couple of downside. Unless you go completely server-side and
use XSLT extensions you cannot interact with server resources very well.
I find ERB makes up for this issue fine. Also it takes a while to get
used to XSLT. It is more functional in concept and if you aren''t used
to
that way of programming it can cause some aggravation. Also browser
support for XSLT is a bit buggy currently so you may have to
occasionally work around an issue or take things completely server-side.
So this is why I am not using the "Rails way". This is my first Rails
app so I am still working out the details of using XSLT in Rails but so
far it seems to work quite good.
Eric
_______________________________________________
Rails mailing list
Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org
http://lists.rubyonrails.org/mailman/listinfo/rails