awesome_fields Gets A FormBuilder, New headerize Plugin
June 16th, 2008
I haven’t been able to work on Quill too much lately, in part because I lost my stylus somewhere along the way (new one should be forthcoming today, actually) and in part because my day job involves Rails at the moment, so that’s the world my mind has been in. What I have been working on is my awesome_fields plugin and a new plugin, headerize. Actually, the `work’ I have done on these has been extracting some stuff that I commonly do in my Rails projects into plugins. There’s very little that is technically new code from me there, though the headerize plugin is well spec’ed-out.
Anyway, so awesome_fields has received a LinedBuilder for creating better marked-up forms with less effort, and headerize was created to facilitate putting all your stylesheets and scripts in one place (typically the document head) while still being able to include them from the view. That way, the stylesheet that needs to be included by the _form partial is included there, so that it is spatially near to the place where it is used, while the actual markup for it goes in the head, where it belongs. So I’ll hit these two separately…
awesome_fields and the LinedBuilder
Let’s face it, one thing Rails sucks at is facilitating the creation of labeled form fields. Outputting errors for form fields is also a bit repetitive out of the box. It seems like this begs for a bit of DRY goodness that can live in a custom FormBuilder. Enter the LinedBuilder, whose purpose is to make explicit labels and errors as much a part of the turbulent past as possible.
Basically, the LinedBuilder gives you the usual methods, such as text_field, text_area, etc, but it gives them labels, wraps them in a div, and automatically includes errors in them if necessary. So let’s say I had my form:
<% form_for @model do |f| %>
<p>
<%= error_message_on 'model', 'name' %>
<label for="model_name">Name: </label>
<%= f.text_field 'name' %>
</p>
<p>
<%= error_message_on 'model', 'author' %>
<label for="model_author">Author: </label>
<%= f.text_field 'author' %>
</p>
<% end %>
That’s a lot of typing, plus it’s repetitive. With awesome_fields (and therefore LinedBuilder):
<% lined_form_for @model do |f| %>
<%= f.text_field 'name' %>
<%= f.text_field 'author' %>
<% end %>
Dearie me, that does look cleaner. Behind the scenes, this means we will get:
<form ...>
<div class="form_line">
<label for="model_name">Name: </label>
<input type="text" id="model_name" name="model[name]" value="..." />
</div>
<div class="form_line">
<label for="model_author">Author: </label>
<input type="text" id="model_author" name="model[author]" value="..." />
</div>
</form>
And, if there is an error on the name field from validates_presence_of, we get:
<form ...>
<div class="form_line_with_errors">
<div class="field_error">Name should not be blank.</div>
<label for="model_name">Name: </label>
<input type="text" id="model_name" name="model[name]" value="..." />
</div>
<div class="form_line">
<label for="model_author">Author: </label>
<input type="text" id="model_author" name="model[author]" value="..." />
</div>
</form>
As an additional bonus, say validates_presence_of and validates_numericality_of both fired, the error becomes:
<div class="form_line_with_errors">
<div class="field_error">Name should not be blank and should be a number.</div>
<label for="model_name">Name: </label>
<input type="text" id="model_name" name="model[name]" value="..." />
</div>
This is because, if the object returned by errors.[] responds to to_sentence, then it is converted into a sentence. Since Rails defines an excellent such method for arrays, we get the above pretty output.
So that’s the LinedBuilder in a nutshell. There are a few options you can pass. If using the automatic label (which is the humanized name of the field) doesn’t cut it, you can pass in a :label option with the text you want to use. If you don’t want a label, you can pass in a :no_label option set to true. And finally, if you need to distinguish between labels for long and short fields, you can pass in a :long option set to true. If you do this with a call to text_area, it will also adjust the default number of rows and columns.
For more information, see the RDocs in the source file . To install the plugin on Rails <=2.0:
git clone git://github.com/Shadowfiend/awesome_fields.git vendor/plugins/awesome_fields
If you are using git, make sure to:
rm -R vendor/plugins/awesome_fields/.git
So that you can check in the code. If you’re running Rails 2.1, then you can hit up:
script/plugin install git://github.com/Shadowfiend/awesome_fields.git
headerize—Javascript and Stylesheets Where They Belong
In addition to the changes to awesome_fields, headerize is a new plugin that lets you put your JS and stylesheet tags where they belong (in the head of your document, technically, though some people put JS at the bottom as a loading speed optimization) while letting you include them wherever you want.
When would you use this? For Javascript, if you’re trying to write unobtrusive Javascript, you usually want to include a file for your particular view. So say you’ve got a form, and you want to make it remote. But, you want this not to clutter up your HTML. So, you including a JS file called remote_forms.js that has the unobtrusive javascript to make things work.
Trouble is, you don’t want to include it on every page. One way to do this is to include it anyway and just use a CSS class or somesuch to ensure that only forms with the `remote’ class get the behavior. But another way is just to include the script only on pages that need it. The same goes for stylesheets: if there’s no form on a page, why include the styles for it?
Since the layout in Rails is separate from the view you are rendering, however, it is annoying to include anything per-view. headerize takes care of this problem. After installing it, it makes two helpers available: add_javascript and add_stylesheet (both exist in pluralized forms for readability when taking care of multiple items). These each behave exactly like javascript_include_tag and stylesheet_link_tag, only they don’t drop their links into the HTML right where they are called. Instead, wherever you want your stylesheets and Javascript, you insert the output of javascript_includes and stylesheet_links.
For example, to deal with the above situation, I could have a layout application.html.haml (in HAML):
!!! XML
!!!
%html
%head
%title My Site
= stylesheet_links
= javascript_includes
%body
= yield
And, in your new view, say new.html.erb (in ERB):
<% add_stylesheet 'forms'
add_javascript 'remote_forms' %>
<% form_for @model do |f| %>
<%= f.field_for :name %>
<%= f.field_for :date %>
<% end %>
That’s it. When the view is rendered, the layout will have the right stylesheets and Javascript includes right there, easy as pie. Note that this has some implications on fragment caching (since the links and includes are built every time, if you fragment cache the portion including the stylesheet calls and it is not re-evaluated, then the stylesheets and includes will not function correctly). Also note that these functions can be called from partials, so for example an _form partial can always include the relevant stylesheets and forms and no matter where it is rendered from, the correct stuff will be included.
Also worth mentioning, as presented before, the calls to add_stylesheet and add_javascript are exactly the same as their inline counterparts, so you could easily do any of these:
<% add_stylesheet 'print', :media => 'print'
add_javascripts 'remote_forms', 'remote_links', :cache => 'remote_stuff' %>
As before, for more information, see the RDocs in the source . To install the plugin on Rails <=2.0:
git clone git://github.com/Shadowfiend/headerize.git vendor/plugins/headerize
Again, if you are using git, make sure to:
rm -R vendor/plugins/awesome_fields/.git
So that you can check in the code. If you’re running Rails 2.1, then you can hit up:
script/plugin install git://github.com/Shadowfiend/headerize.git
And that’s all folks! Hope some of you get some use out of these plugins!
Leave a Reply