Add typed attribute converters for session, request, flash and servletContext#15715
Add typed attribute converters for session, request, flash and servletContext#15715codeconsole wants to merge 4 commits into
Conversation
|
@codeconsole Can this target 8? |
@matrei done, how about a review ;P |
4f61d56 to
e5df31d
Compare
…, flash and servletContext
Extract the value-level conversion logic into an internal helper class
`org.grails.util.TypeConverters` (toByte/toInteger/toLong/toBoolean/
toStringValue/toDate/toList, etc., each with a default-value overload), have the
existing `TypeConvertingMap` instance `getX` methods delegate to it, and add a
`string`/`getString` converter.
Expose the full set of converters (`string`, `byte`, `char`, `short`, `int`,
`long`, `double`, `float`, `boolean`, `list`, `date`) directly on `HttpSession`,
`HttpServletRequest`, `ServletContext` and the `flash` scope (`FlashScope`) by
calling the internal converters on the attribute value, so attribute access is
statically compilable and type-safe under `@CompileStatic`/`@GrailsCompileStatic`,
e.g.:
session.int('age', 21)
session.string('tz', 'America/Los_Angeles')
flash.string('notice')
Both the conversion logic and the default-value handling live in the single
internal `TypeConverters` class, shared by the map and the extensions, with no
new conversion methods added to the public `TypeConvertingMap` API. Calling the
internal converters directly avoids any per-access allocation. The only
exception is the boolean default, whose "is the value present" rule differs by
holder (map containsKey vs attribute set) and so stays per-site by design.
Includes tests covering conversion, null-safety, defaults and static resolution,
plus documentation.
e5df31d to
0ecfc36
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15715 +/- ##
==================================================
+ Coverage 48.3483% 48.3575% +0.0092%
- Complexity 15104 15181 +77
==================================================
Files 1870 1872 +2
Lines 85459 85571 +112
Branches 14901 14899 -2
==================================================
+ Hits 41318 41380 +62
- Misses 37805 37851 +46
- Partials 6336 6340 +4
🚀 New features to boost your workflow:
|
Guard SimpleDateFormat parsing in toDate against a null toString() (which throws NullPointerException, not the caught ParseException) and name all empty catch variables 'ignored'. Adds test coverage for the null toString() edge case across every converter.
The class-level @SuppressWarnings({"rawtypes","unchecked"}) was copied from AbstractTypeConvertingMap, where it covers raw Map usage. In TypeConverters the only raw-type usage was toList; genericize it to return List<Object> using a typed ArrayList copy so the suppression is no longer needed, and remove it.
Relocate the internal helper out of the legacy org.grails.util package into the project's org.apache.grails namespace: .core for gradle-project uniqueness, .internal to mark it as non-public (mirroring the old grails.* reservation), and .util since it was extracted from util-package code. Update all imports in AbstractTypeConvertingMap, the servlet extensions and the test.
|
@sbglasius @jdaugherty @matrei annoying that I got bonus changes from other peoples work when I switched the base to 8.0.x, but I replayed everything on top of a fresh 8.0.x then force pushed this PR. Should be clean now. |
✅ All tests passed ✅Test Summary
🏷️ Commit: 60980ef Learn more about TestLens at testlens.app. |
Summary
Adds null-safe, typed conversion methods to the
session,request,flashandservletContextobjects, mirroring the existingparams.int('id', 1)API. Also adds a missingstring/getStringconverter toTypeConvertingMapitself.The full converter set is available —
string,byte,char,short,int,long,double,float,boolean,list,date— each with an optional default-value overload, exactly as onparams.Motivation
Under
@CompileStatic/@GrailsCompileStatic, dynamic attribute access such assession.userTimeZoneIddoes not compile, and the explicit form requires a cast at every call site:These converters give the same null-safe, typed, default-aware ergonomics as
params, and they compile under static compilation. They are equally useful in dynamic code for the parsing/defaulting behaviour (just likeparams.int(...)).Design
org.grails.util.TypeConverters, which converts a rawObjectvalue to the target type (with an optional default). BothTypeConvertingMap's instancegetXmethods and the new attribute extensions delegate to it, so the logic lives in a single place and no new conversion methods are added to the publicTypeConvertingMapAPI.getX(name, default)semantics are preserved, soparamsbehaviour is unchanged (verified byGrailsParameterMapBindingSpec).booleandefault: "is the value present" differs by holder (mapcontainsKeyvs. attribute set), which cannot be expressed from a value alone, so it stays per-site and is documented inline.stringconverter returns the first element when the attribute is an array, mirroring the single-value semantics of the other converters; uselistfor all values.Tests & docs
getString/string.HttpSessionExtensionSpec,HttpServletRequestExtensionSpec,ServletContextExtensionSpec,FlashScopeExtensionSpec) covering conversion, null-safety, defaults, and a@CompileStaticstatic-resolution guard.GrailsParameterMapBindingSpecpasses unchanged (regression guard for the core refactor).