From 418cdb53df326a9b899cb446e62c11c4ff2d4153 Mon Sep 17 00:00:00 2001 From: Donald Oellerich Date: Mon, 21 Apr 2014 17:00:53 -0400 Subject: [PATCH 1/5] add a unit test for custom period formatter --- .../jodatime/taglib/PeriodTagLibSpec.groovy | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy b/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy index f025f7d..d33e1d6 100644 --- a/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy +++ b/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy @@ -18,6 +18,7 @@ package grails.plugin.jodatime.taglib import grails.test.mixin.TestFor import org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException import org.joda.time.Period +import org.joda.time.format.PeriodFormatterBuilder import spock.lang.Issue import spock.lang.Specification import spock.lang.Unroll @@ -264,4 +265,17 @@ class PeriodTagLibSpec extends Specification { value = new Period().withHours(1) } -} \ No newline at end of file + void 'formatPeriod accepts formatter attribute'() { + expect: + applyTemplate('', [value: value, formatter: formatter]) == "16d, 2h and 2m" + + where: + value = new Period().withWeeks(2).withHours(50).withMinutes(2).withSeconds(2) + formatter = new PeriodFormatterBuilder() + .appendDays().appendSuffix("d").appendSeparator(", ") + .appendHours().appendSuffix("h").appendSeparator(" and ") + .appendMinutes().appendSuffix("m") + .toFormatter() + } + +} From 5d8b2f6f80040b91b81bdbf2d817f4fc0f2f2f26 Mon Sep 17 00:00:00 2001 From: Donald Oellerich Date: Mon, 21 Apr 2014 17:01:11 -0400 Subject: [PATCH 2/5] add support for a formatter attr --- .../taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy b/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy index 87ae3c9..7cce7cd 100644 --- a/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy +++ b/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy @@ -63,7 +63,7 @@ class PeriodTagLib { value = safeNormalize(value, periodType) } - def formatter = PeriodFormat.wordBased(request.locale) + def formatter = attrs.formatter ?: PeriodFormat.wordBased(request.locale) out << formatter.print(value) } @@ -109,4 +109,4 @@ class PeriodTagLib { message(code: "${DurationFieldType.name}.$fieldType.name", default: defaultLabel) } -} \ No newline at end of file +} From f36d34696425bd77752d9a7aeda9edf5d6c7d662 Mon Sep 17 00:00:00 2001 From: Donald Oellerich Date: Tue, 22 Apr 2014 14:15:28 -0400 Subject: [PATCH 3/5] Refactor to a util method so a custom formatter can be more easily used --- .../jodatime/taglib/PeriodTagLib.groovy | 56 ++-------------- .../plugin/jodatime/util/PeriodUtils.groovy | 66 +++++++++++++++++++ .../jodatime/taglib/PeriodTagLibSpec.groovy | 13 ---- .../jodatime/util/PeriodUtilsSpec.groovy | 37 +++++++++++ 4 files changed, 108 insertions(+), 64 deletions(-) create mode 100644 src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy create mode 100644 test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy diff --git a/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy b/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy index 7cce7cd..8cef0be 100644 --- a/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy +++ b/grails-app/taglib/grails/plugin/jodatime/taglib/PeriodTagLib.groovy @@ -17,11 +17,9 @@ package grails.plugin.jodatime.taglib import org.joda.time.Duration import org.joda.time.DurationFieldType -import org.joda.time.Period import org.joda.time.PeriodType import org.joda.time.format.PeriodFormat -import static org.joda.time.DurationFieldType.months -import static org.joda.time.DurationFieldType.years +import grails.plugin.jodatime.util.PeriodUtils class PeriodTagLib { @@ -32,7 +30,7 @@ class PeriodTagLib { def id = attrs.id ?: name def value = attrs.value - def periodType = getPeriodType(attrs.fields, DEFAULT_PERIOD_TYPE) + def periodType = PeriodUtils.getPeriodType(attrs.fields, DEFAULT_PERIOD_TYPE) if (value instanceof Duration) { value = value.toPeriod(periodType) @@ -50,58 +48,14 @@ class PeriodTagLib { } def formatPeriod = {attrs -> - def value = attrs.value - if (!value) { + if (!attrs.value) { throwTagError("'value' attribute is required") } - def periodType = getPeriodType(attrs.fields, PeriodType.standard()) - - if (value instanceof Duration) { - value = value.toPeriod(periodType) - } else { - value = safeNormalize(value, periodType) - } - - def formatter = attrs.formatter ?: PeriodFormat.wordBased(request.locale) - - out << formatter.print(value) - } - - private PeriodType getPeriodType(String fields, PeriodType defaultPeriodType) { - PeriodType periodType - if (fields) { - periodType = getPeriodTypeForFields(fields) - } else if (grailsApplication.config.jodatime?.periodpicker?.default?.fields) { - periodType = getPeriodTypeForFields(grailsApplication.config.jodatime.periodpicker.default.fields) - } else { - periodType = defaultPeriodType - } - return periodType - } - - private static final PeriodType DEFAULT_PERIOD_TYPE = getPeriodTypeForFields("hours,minutes,seconds") - - private static PeriodType getPeriodTypeForFields(String fields) { - def fieldTypes = fields.split(/\s*,\s*/).collect { DurationFieldType."$it"() } as DurationFieldType[] - return PeriodType.forFields(fieldTypes) + out << PeriodUtils.formatPeriod(attrs.value, attrs.fields, request.locale) } - /** - * PeriodFormat.print will throw UnsupportedOperationException if years or months are present in period but - * not supported by the formatter so we trim those fields off to avoid the problem. - */ - private Period safeNormalize(Period value, PeriodType periodType) { - if (!periodType.isSupported(years()) && years() in value.getFieldTypes()) { - log.warn "Omitting years from value '$value' as format '$periodType' does not support years" - value = value.withYears(0) - } - if (!periodType.isSupported(months()) && months() in value.getFieldTypes()) { - log.warn "Omitting months from value '$value' as format '$periodType' does not support months" - value = value.withMonths(0) - } - return value.normalizedStandard(periodType) - } + private static final PeriodType DEFAULT_PERIOD_TYPE = PeriodUtils.getPeriodTypeForFields("hours,minutes,seconds") private String getLabelFor(DurationFieldType fieldType) { def bundle = ResourceBundle.getBundle("${PeriodFormat.package.name}.messages", request.locale) diff --git a/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy b/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy new file mode 100644 index 0000000..b2c1f34 --- /dev/null +++ b/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy @@ -0,0 +1,66 @@ +package grails.plugin.jodatime.util + +import grails.util.Holders +import org.apache.log4j.Logger +import org.joda.time.Duration +import org.joda.time.DurationFieldType +import org.joda.time.Period +import org.joda.time.PeriodType +import org.joda.time.format.PeriodFormat +import org.joda.time.format.PeriodFormatter +import static org.joda.time.DurationFieldType.months +import static org.joda.time.DurationFieldType.years + +class PeriodUtils { + + private static final Logger log = Logger.getLogger(getClass()) + + static formatPeriod(value, String fields, Locale locale, PeriodFormatter formatter=null) { + def periodType = getPeriodType(fields, PeriodType.standard()) + + if (value instanceof Duration) { + value = value.toPeriod(periodType) + } else { + value = safeNormalize(value, periodType) + } + + if (!formatter) { + formatter = PeriodFormat.wordBased(locale) + } + + return formatter.print(value) + } + + static PeriodType getPeriodType(String fields, PeriodType defaultPeriodType) { + PeriodType periodType + if (fields) { + periodType = getPeriodTypeForFields(fields) + } else if (Holders.config.jodatime?.periodpicker?.default?.fields) { + periodType = getPeriodTypeForFields(Holders.config.jodatime.periodpicker.default.fields) + } else { + periodType = defaultPeriodType + } + return periodType + } + + private static PeriodType getPeriodTypeForFields(String fields) { + def fieldTypes = fields.split(/\s*,\s*/).collect { DurationFieldType."$it"() } as DurationFieldType[] + return PeriodType.forFields(fieldTypes) + } + + /** + * PeriodFormat.print will throw UnsupportedOperationException if years or months are present in period but + * not supported by the formatter so we trim those fields off to avoid the problem. + */ + private static Period safeNormalize(Period value, PeriodType periodType) { + if (!periodType.isSupported(years()) && years() in value.getFieldTypes()) { + log.warn "Omitting years from value '$value' as format '$periodType' does not support years" + value = value.withYears(0) + } + if (!periodType.isSupported(months()) && months() in value.getFieldTypes()) { + log.warn "Omitting months from value '$value' as format '$periodType' does not support months" + value = value.withMonths(0) + } + return value.normalizedStandard(periodType) + } +} diff --git a/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy b/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy index d33e1d6..b9db478 100644 --- a/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy +++ b/test/unit/grails/plugin/jodatime/taglib/PeriodTagLibSpec.groovy @@ -265,17 +265,4 @@ class PeriodTagLibSpec extends Specification { value = new Period().withHours(1) } - void 'formatPeriod accepts formatter attribute'() { - expect: - applyTemplate('', [value: value, formatter: formatter]) == "16d, 2h and 2m" - - where: - value = new Period().withWeeks(2).withHours(50).withMinutes(2).withSeconds(2) - formatter = new PeriodFormatterBuilder() - .appendDays().appendSuffix("d").appendSeparator(", ") - .appendHours().appendSuffix("h").appendSeparator(" and ") - .appendMinutes().appendSuffix("m") - .toFormatter() - } - } diff --git a/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy b/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy new file mode 100644 index 0000000..6201fdd --- /dev/null +++ b/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy @@ -0,0 +1,37 @@ +/* + * Copyright 2014 Donald Oellerich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package grails.plugin.jodatime.util + +import spock.lang.Specification +import org.joda.time.Period +import org.joda.time.format.PeriodFormatterBuilder + +class PeriodUtilsSpec extends Specification { + + void 'formatPeriod accepts formatter attribute'() { + given: + def value = new Period().withWeeks(2).withHours(50).withMinutes(2).withSeconds(2) + def formatter = new PeriodFormatterBuilder() + .appendDays().appendSuffix("d").appendSeparator(", ") + .appendHours().appendSuffix("h").appendSeparator(" and ") + .appendMinutes().appendSuffix("m") + .toFormatter() + + expect: + PeriodUtils.formatPeriod(value, "days,hours,minutes", Locale.US, formatter) == "16d, 2h and 2m" + } +} From 748ad637a6c7aa689d804a47f6acd7c09cf9a1de Mon Sep 17 00:00:00 2001 From: Donald Oellerich Date: Tue, 22 Apr 2014 14:33:10 -0400 Subject: [PATCH 4/5] add javadoc --- .../grails/plugin/jodatime/util/PeriodUtils.groovy | 11 +++++++++++ .../plugin/jodatime/util/PeriodUtilsSpec.groovy | 13 +++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy b/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy index b2c1f34..4fdf9ef 100644 --- a/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy +++ b/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy @@ -11,10 +11,21 @@ import org.joda.time.format.PeriodFormatter import static org.joda.time.DurationFieldType.months import static org.joda.time.DurationFieldType.years +/** + * Helper methods for Periods. + */ class PeriodUtils { private static final Logger log = Logger.getLogger(getClass()) + /** + * Format a Duration or Period for display + * @param value the Period or Duration to format + * @param fields a comma separated list of fields to display + * @param locale the locale for the default formatter + * @param formatter an optional formatter to use in place of the default word based formatter + * @return the formatted string + */ static formatPeriod(value, String fields, Locale locale, PeriodFormatter formatter=null) { def periodType = getPeriodType(fields, PeriodType.standard()) diff --git a/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy b/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy index 6201fdd..46b88e9 100644 --- a/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy +++ b/test/unit/grails/plugin/jodatime/util/PeriodUtilsSpec.groovy @@ -19,19 +19,20 @@ package grails.plugin.jodatime.util import spock.lang.Specification import org.joda.time.Period import org.joda.time.format.PeriodFormatterBuilder +import static java.util.Locale.ENGLISH class PeriodUtilsSpec extends Specification { void 'formatPeriod accepts formatter attribute'() { given: def value = new Period().withWeeks(2).withHours(50).withMinutes(2).withSeconds(2) - def formatter = new PeriodFormatterBuilder() - .appendDays().appendSuffix("d").appendSeparator(", ") - .appendHours().appendSuffix("h").appendSeparator(" and ") - .appendMinutes().appendSuffix("m") - .toFormatter() + def formatter = new PeriodFormatterBuilder() + .appendDays().appendSuffix("d").appendSeparator(", ") + .appendHours().appendSuffix("h").appendSeparator(" and ") + .appendMinutes().appendSuffix("m") + .toFormatter() expect: - PeriodUtils.formatPeriod(value, "days,hours,minutes", Locale.US, formatter) == "16d, 2h and 2m" + PeriodUtils.formatPeriod(value, "days,hours,minutes", ENGLISH, formatter) == "16d, 2h and 2m" } } From dbbb0b94c88ae71472c36c96281621233a18b3e0 Mon Sep 17 00:00:00 2001 From: Donald Oellerich Date: Tue, 22 Apr 2014 14:37:43 -0400 Subject: [PATCH 5/5] add missing license --- .../plugin/jodatime/util/PeriodUtils.groovy | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy b/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy index 4fdf9ef..c5bece7 100644 --- a/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy +++ b/src/groovy/grails/plugin/jodatime/util/PeriodUtils.groovy @@ -1,3 +1,18 @@ +/* + * Copyright 2010 Rob Fletcher + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package grails.plugin.jodatime.util import grails.util.Holders