Show your objects baby!

We have been having some projects lately that needed a lot of admin CRUDs and pages showing a list of attributes, and we were getting bored of copy and paste code like this in our show pages, for every single attribute:

Name
<%= @person.name %>

We had already created some helper to do the work for us, but having this helper being copied from one project to another wasn’t that DRY. That’s when we decided to create ShowFor.

ShowFor is a DSL to help you showing a list of attributes, with I18n, perfect for show pages in CRUD interfaces. It allows you to replace code/html duplication in your views using a nice syntax. Let’s see what we can do.

Attributes

Let’s imagine we have a Person model, which has first_name, last_name, age, photo, and confirmed attributes. The following lines show a list of values for a specific person:

<% show_for @person do |p| %>
  <%= p.attribute :first_name %>
  <%= p.attribute :last_name %>
  <%= p.attribute :confirmed? %>
  <%= p.attribute :created_at, :format => :short %>
  <%= p.attribute :age, :if_blank => "No age" %>

  <% p.attribute :photo do %>
    <%= image_tag(@person.photo_url) %>
  <% end %>
<% end %>

Here is an example output you will get:

First name
Carlos

Last name
Antonio

Confirmed?
Yes

Created at
08 Mar 11:30

Age
24

Photo
Rails

As you can see, you are going to get a default html markup, with classes and ids to help you design with CSS. And if you noticed, we are using extra options in some attributes, lets take a look at some of them:

  • :format lets you pass any format that will be used together with i18n, for date/time attributes only, as you would use with the l helper.
  • :if_blank specifies what to show if the attribute is blank. It may have a default value from i18n.
  • do...end: using blocks you can handle the content for that specific attribute by yourself, as we have done in this example for the photo attribute.

Boolean attributes, as our :confirmed, also have a default for true and false values, and can be configured through i18n. If you want to say “Yup” and “Nope” instead of “Yes” and “No” respectively, just change your i18n file. You can also pass a :escape option to not escape the value (true by default).

Associations

ShowFor also works with associations. For instance, we can easily add that a Person belongs to a City with a name attribute and that it has and belongs to many Tags. To handle the former, we can do:

<%= p.association :city %>

ShowFor will guess the right attribute to show by looking into all possibilities configured in ShowFor.association_methods and in this case choosing :name. But of course, you can change it as you need, on-the-fly:

<%= p.association :city, :using => :full_name %>
<%= p.attribute :full_name, :in => :city %>

Both calls above will have the same output, just choose the one which suits you better.

Handling collections is easy as belongs_to associations. You can just pass the association to ShowFor and it will know whether it’s a collection or not, generating a list of elements using ul and li tags.

<%= p.association :tags %>

However, if you want to render the collection inline, you can use :to_sentence or :join as options:

<%= p.association :tags, :to_sentence => true %>
<%= p.association :tags, :join => ',' %>

It’s also possible to pass a block to the collection. ShowFor will create the wrapper tag (ul by default in this case) and will yield each element in the collection for you to handle it:

<% a.association :tags do |tag| %>
  
  • <%= link_to tag.name, tag %>
  • <% end %>

    Labels

    You may have noticed ShowFor has a default label using the strong html tag. It also exposes you this as a helper, so you can use it whenever you wish:

      <%= p.label :first_name %>
      <%= p.label :age, :id => 'person_age' %>
    
      First name
      Age
    

    Installation

    ShowFor is already compatible with Rails 3, at the time of this writing in version 0.2.0. You can just follow the instructions in the README to get it installed.

    If you are using Rails 2.3.x, take a look at the 0.1 branch, and follow the installations in the README for this branch to get it up and running.

    Please don’t forget to run the generator and take a look at the initializer, it will allows you configure several parts of ShowFor.

    script/generate show_for_install
    

    Closing

    ShowFor helps you to show your objects easily with a default html markup, and can be totally configured to suit your needs. It has been helping us in every project, and we hope it may help you too. If you have any doubt, please take a look at the README, there are a lot of examples and documentation there.

    And what about you? Do you have any helper you use every single day that might be transformed in a plugin/gem? Do not hesitate on doing this, we would be glad to see your work.

    Enjoy!

    • Rafael Rosa

      Hi guys,

      That’s a nice DSL, but I was wondering: is it correct to use instead of something like ? I’m not sure, but the strong means “important text” while the label tag means “this is the name/title of the thing ahead” which is appropriate usability wise and compatible with screen readers and stuff like that. I would rather do something like:

      ‘something’%>

      to generate

      First nameCarlos

      Besides, I’d add a :break option to enable/disable the . Hope this helps.

      Regards

    • Rafael Rosa

      Hi guys,

      That’s a nice DSL, but I was wondering: is it correct to use instead of something like ? I’m not sure, but the strong means “important text” while the label tag means “this is the name/title of the thing ahead” which is appropriate usability wise and compatible with screen readers and stuff like that. I would rather do something like:

      ‘something’%>

      to generate

      First nameCarlos

      Besides, I’d add a :break option to enable/disable the . Hope this helps.

      Regards

    • Rafael Rosa

      It seems the comments are not escaping the markup, sorry :(

    • Rafael Rosa

      It seems the comments are not escaping the markup, sorry :(

    • http://www.cristalab.com/devatwork Alvaro Pereyra (Yaraher)

      I’d agree with Rafael’s comment. Why use strong instead of the proper (and dare I say, semantic) “label” tag?.

      Besides that, I like a lot. Feels “formtasticly” in spirit. Great work!

      What about adding a attributes method? So your first example would become something like (using HAML to try to evade that markup eating of wordpress :P):

      show_for @person do |p|
      = p.attributes :first_name, :last_name, :confirmed?
      = p.attribute :created_at, :format => :short
      = p.attribute :age, :if_blank => “No age”

    • http://www.cristalab.com/devatwork Alvaro Pereyra (Yaraher)

      I’d agree with Rafael’s comment. Why use strong instead of the proper (and dare I say, semantic) “label” tag?.

      Besides that, I like a lot. Feels “formtasticly” in spirit. Great work!

      What about adding a attributes method? So your first example would become something like (using HAML to try to evade that markup eating of wordpress :P):

      show_for @person do |p|
      = p.attributes :first_name, :last_name, :confirmed?
      = p.attribute :created_at, :format => :short
      = p.attribute :age, :if_blank => “No age”

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Rafael really thanks for the tips. And we are going to take a look at the markup inside the comments :)..

      @Rafael and @Alvaro, the initializer ShowFor installs inside your config/initializers has some configurations that allows you to change each piece of code it uses, for instance the label_tag, wrapper_tag, and default separator (ie br tag). Perhaps I missed explaining a bit about it in the post.
      I think it should do the trick of overriding the :strong tag for :label for instance. Anyway, I agree label is “the name/title of the thing ahead” (I liked it =D).

      @Alvaro a patch to add :attributes would be appreciated =). Thanks.

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Rafael really thanks for the tips. And we are going to take a look at the markup inside the comments :)..

      @Rafael and @Alvaro, the initializer ShowFor installs inside your config/initializers has some configurations that allows you to change each piece of code it uses, for instance the label_tag, wrapper_tag, and default separator (ie br tag). Perhaps I missed explaining a bit about it in the post.
      I think it should do the trick of overriding the :strong tag for :label for instance. Anyway, I agree label is “the name/title of the thing ahead” (I liked it =D).

      @Alvaro a patch to add :attributes would be appreciated =). Thanks.

    • http://billsaysthis.com BillSaysThis

      I’m getting a generator not found (using latest Rails 3):

      wgd1 root# gem install show_for
      Successfully installed show_for-0.2.0
      1 gem installed
      Installing ri documentation for show_for-0.2.0…
      Updating class cache with 4063 classes…
      Installing RDoc documentation for show_for-0.2.0…

      wgd1$ rails generate show_for_install
      Could not find generator show_for_install.
      wgd1$ rails g
      Please choose a generator below.
      Rails:
      … as expected
      ActiveRecord:
      … as expected
      Erb:
      … as expected
      Rspec:
      … as expected
      TestUnit:
      … as expected

    • http://billsaysthis.com BillSaysThis

      I’m getting a generator not found (using latest Rails 3):

      wgd1 root# gem install show_for
      Successfully installed show_for-0.2.0
      1 gem installed
      Installing ri documentation for show_for-0.2.0…
      Updating class cache with 4063 classes…
      Installing RDoc documentation for show_for-0.2.0…

      wgd1$ rails generate show_for_install
      Could not find generator show_for_install.
      wgd1$ rails g
      Please choose a generator below.
      Rails:
      … as expected
      ActiveRecord:
      … as expected
      Erb:
      … as expected
      Rspec:
      … as expected
      TestUnit:
      … as expected

    • http://simplesideias.com.br Nando Vieira

      @Rafael and @Alvaro: even the label tag isn’t the recommend one. The label is meant to be associated with a form element.
      I think DL can be used, but is not that easy to style it. I’d probably go with p > strong + text.

    • http://simplesideias.com.br Nando Vieira

      @Rafael and @Alvaro: even the label tag isn’t the recommend one. The label is meant to be associated with a form element.
      I think DL can be used, but is not that easy to style it. I’d probably go with p > strong + text.

    • José Valim

      Agreed, label is def. not the recommended one. First show_for versions used b tags instead of strong. And then I got a few pull requests asking to change to strong. Good news is, it’s easy to customize in your app. :)

      @BillSaysThis I’m investigating. :)

    • José Valim

      Agreed, label is def. not the recommended one. First show_for versions used b tags instead of strong. And then I got a few pull requests asking to change to strong. Good news is, it’s easy to customize in your app. :)

      @BillSaysThis I’m investigating. :)

    • Rafael Rosa

      Hi,

      @Nando is right, the DL, DT and DD tags are the recommended ones for this job, label is exclusive for form. My bad :)

      http://www.w3.org/TR/html5/semantics.html#the-dl-element

      The styling may be a little trick, but perhaps it worth the trouble to define a correct default.

      Regards

    • Rafael Rosa

      Hi,

      @Nando is right, the DL, DT and DD tags are the recommended ones for this job, label is exclusive for form. My bad :)

      http://www.w3.org/TR/html5/semantics.html#the-dl-element

      The styling may be a little trick, but perhaps it worth the trouble to define a correct default.

      Regards

    • http://craigbuchek.com Craig Buchek

      Cool. I’ve been wanting to do this myself for quite some time. Especially since Streamlined is no longer maintained. Now all we is ListFor.

      One suggestion — it’d be better if we didn’t have to put each line in a separate ERB tag. Even if the entire file was all Ruby, it’d be OK by me.

    • http://craigbuchek.com Craig Buchek

      Cool. I’ve been wanting to do this myself for quite some time. Especially since Streamlined is no longer maintained. Now all we is ListFor.

      One suggestion — it’d be better if we didn’t have to put each line in a separate ERB tag. Even if the entire file was all Ruby, it’d be OK by me.

    • http://billsaysthis.com BillSaysThis

      How do I change the label printed above an association list for individual fields?

    • http://billsaysthis.com BillSaysThis

      How do I change the label printed above an association list for individual fields?

    • José Valim

      @BillSaysThis I didn’t understand what you want to achieve. More info please? :)

    • José Valim

      @BillSaysThis I didn’t understand what you want to achieve. More info please? :)

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Craig Thanks.. By “put each line in a separate ERB tag” you mean what @Alvaro said, using :attributes? If so, a patch would be really cool =).

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Craig Thanks.. By “put each line in a separate ERB tag” you mean what @Alvaro said, using :attributes? If so, a patch would be really cool =).

    • Pingback: Caffeine Driven Development » Blog Archive » L33t Links #85

    • http://www.cristalab.com/devatwork Alvaro Pereyra (Yaraher)

      @Carlos Antonio: I bit the bullet and tried to make it work. Just sent you an email with the patch for an idea of how to implement it. It’s quite basic, but it works :)

    • http://www.cristalab.com/devatwork Alvaro Pereyra (Yaraher)

      @Carlos Antonio: I bit the bullet and tried to make it work. Just sent you an email with the patch for an idea of how to implement it. It’s quite basic, but it works :)

    • http://www.cristalab.com/devatwork Alvaro Pereyra (Yaraher)

      BTW, I just realized I had the “label” thing totally wrong, as you’ve said before. For a moment, I though we were talking about form fields. Duh!.

      I agree that DL would be the best way to work it. Maybe I’ll try to play with it this noon. Shouldn’t be that hard to accomplish.

    • http://www.cristalab.com/devatwork Alvaro Pereyra (Yaraher)

      BTW, I just realized I had the “label” thing totally wrong, as you’ve said before. For a moment, I though we were talking about form fields. Duh!.

      I agree that DL would be the best way to work it. Maybe I’ll try to play with it this noon. Shouldn’t be that hard to accomplish.

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Alvaro Hey, I’m going to take a look at it ok, thanks :).

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Alvaro Hey, I’m going to take a look at it ok, thanks :).

    • http://billsaysthis.com BillSaysThis

      @josevalim I mean if I code, say, :full_name %> I get a paragraph above the list of people with the word People in bold. I would like a different text to appear in place of People.

      Similarly, for specific fields I would like to use a label other than the human version of the field name.

    • http://billsaysthis.com BillSaysThis

      @josevalim I mean if I code, say, :full_name %> I get a paragraph above the list of people with the word People in bold. I would like a different text to appear in place of People.

      Similarly, for specific fields I would like to use a label other than the human version of the field name.

    • http://billsaysthis.com BillSaysThis

      Sorry, code block above didn’t come out correctly, should be:

      :full_name %>

    • http://billsaysthis.com BillSaysThis

      Sorry, code block above didn’t come out correctly, should be:

      :full_name %>

    • http://billsaysthis.com BillSaysThis

      Ugg, still no good. Here’s without the angle bracket:

      Sorry, code block above didn’t come out correctly, should be:

      % = d.association :people, :using => :full_name %

    • http://billsaysthis.com BillSaysThis

      Ugg, still no good. Here’s without the angle bracket:

      Sorry, code block above didn’t come out correctly, should be:

      % = d.association :people, :using => :full_name %

    • José Valim

      Bill, I’m almost sure that you can given the :label option:

      <%= d.association :people, :using => :full_name, :label => “OMG People” %>

    • José Valim

      Bill, I’m almost sure that you can given the :label option:

      <%= d.association :people, :using => :full_name, :label => “OMG People” %>

    • http://billsaysthis.com BillSaysThis

      Thanks, the label option wasn’t clear to me.

    • http://billsaysthis.com BillSaysThis

      Thanks, the label option wasn’t clear to me.

    • http://billsaysthis.com BillSaysThis

      Next issue, again I’m sure I just haven’t read the docs closely enough ;)

      I want to use a block with the association so I can print something a bit more sophisticated for each item. If I use a helper nothing shows up, even in a very simple case as below nothing shows up (< removed to hopefully print code properly).

      % d.association :people, :label => ‘Staffers’, :label_tag => ‘h2′ do |person| %>
      li> %= person.full_name %> /li>
      % end %>

    • http://billsaysthis.com BillSaysThis

      Next issue, again I’m sure I just haven’t read the docs closely enough ;)

      I want to use a block with the association so I can print something a bit more sophisticated for each item. If I use a helper nothing shows up, even in a very simple case as below nothing shows up (< removed to hopefully print code properly).

      % d.association :people, :label => ‘Staffers’, :label_tag => ‘h2′ do |person| %>
      li> %= person.full_name %> /li>
      % end %>

    • Ingemar

      It would be neat if one could just pass in the record and then it would output all attributes listed in attr_accessible

    • Ingemar

      It would be neat if one could just pass in the record and then it would output all attributes listed in attr_accessible

    • Vineyard

      Does this work with MongoMapper?

    • Vineyard

      Does this work with MongoMapper?

    • http://carlosantoniodasilva.wordpress.com Carlos Antonio

      @Vineyard yes, it should work normally.. There is nothing in ShowFor talking about your database, it’s only a better way of showing data.

    • http://carlosantoniodasilva.wordpress.com Carlos Antônio

      @Vineyard yes, it should work normally.. There is nothing in ShowFor talking about your database, it’s only a better way of showing data.