Let’s say you need to output the number of results for a query. For example:
2 results found for your query.
Your GSP looks probably something like that:
// ... ${count} results found for your query. // ...
All is good until there is only one result:
1 results found for your query.
Oops, that looks bad 🙂
Here is one way to fix it:
// ... ${count} <%= count > 1 ? "results" : "result" %> found for your query. // ...
Wouldn’t it be nice it we could embed this behavior in some view helper function that we could reuse?
In Grails, you can do that with a custom GSP tag.
So let’s code our custom pluralize tag using TDD.
Here is a first test:
// file: test/unit/pluralize/ViewHelperTagLibSpec.groovy package pluralize import grails.test.mixin.TestFor import spock.lang.Specification import spock.lang.Unroll @TestFor(ViewHelperTagLib) class ViewHelperTagLibSpec extends Specification { @Unroll def "Should pluralize by adding 's' to the singular form"() { given: def template = '<my:pluralize count="${count}" singular="${singular}" />' expect: renderedContent == applyTemplate(template, [count:count, singular:singular]) where: renderedContent || count | singular '1 result' || 1 | 'result' '2 results' || 2 | 'result' } }
If you run this test with the command grails test-app unit:spock ViewHelperTagLib
, you should get an error since there is no ViewHelperTagLib
. So let’s create it:
// file: grails-app/taglib/pluralize/ViewHelperTagLib.groovy package pluralize class ViewHelperTagLib { static namespace = "my" def pluralize = { attrs, body -> out << body() } }
If you run the test again, you should see no more compilation errors but the tests are still failing.
Now, let’s code the pluralize behaviour:
// file: grails-app/taglib/pluralize/ViewHelperTagLib.groovy package pluralize class ViewHelperTagLib { static namespace = "my" def pluralize = { attrs, body -> out << attrs['count'] + " " + ( attrs['count'] > 1 ? attrs['singular'] + "s" : attrs['singular'] ) } }
This time, the tests pass and you can use the tag like this:
// ... <my:pluralize count="${count}" singular="result" /> found for your query. // ...
Unfortunately not all noun gets pluralized with ‘s’. For example:
1 person, 2 people 1 tooth, 2 teeth 1 mouse, 2 mice
Let’s add a second test to describe this behavior:
// file: test/unit/pluralize/ViewHelperTagLibSpec.groovy @Unroll def "Should appropriately pluralize exceptions"() { given: def template = '<my:pluralize count="${count}" singular="${singular}" plural="${plural}" />' expect: renderedContent == applyTemplate(template, [count:count, singular:singular, plural:plural]) where: renderedContent || count | singular | plural '1 person' || 1 | 'person' | 'people' '2 people' || 2 | 'person' | 'people' }
And here is our updated custom tag:
// file: grails-app/taglib/pluralize/ViewHelperTagLib.groovy package pluralize class ViewHelperTagLib { static namespace = "my" def pluralize = { attrs, body -> def plural = attrs['plural'] ?: attrs['singular'] + "s" out << attrs['count'] + " " + ( attrs['count'] > 1 ? plural : attrs['singular'] ) } }
Back to our GSP view:
// ... <my:pluralize count="${count}" singular="person" plural="people" /> found for your query. // ...
And the HTML output:
1 person found for your query. 2 people found for your query.
That's it for today.