- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
[lib.web] v.0.2: validators rewritten, state management, forms
Mon, 2009-09-28, 21:18
Hello everyone!
I'd like to announce version 0.2 of my stateless web framework lib.web.
New features briefly described below are:
- rewritten request parameter validators;
- generalized state management;
- forms and multipage forms module.
It can be downloaded from http://dimgel.ru/lib.web/files/
Examples can be tried at http://dimgel.ru:8080/lib.web/example/
I don't yet consider existing API stable and worth using in real
projects, so I haven't yet bothered with git and github. Forms took more
time than I expected. In 1-3 months I hope to finish some stuff (like
file uploads), cleanup, publish v.0.3 and start a public ride with issue
tracking, etc. For now I'd just appreciate any feedback. I also need
help with maven configuration (please see readme, section "how to run
examples").
***** VALIDATORS REWRITTEN *****
With validators framework presented in v.0.1, I could create
VRangeInt(min: Int, max: Int) and write something like this:
param(VRequired & VInt & VRangeInt, CInt)
Not only one had to clone VRange validators for param(VRequired &
VDouble & VRangeDouble, CDouble) or for param(VRequired & VIntTrim &
VRangeIntTrim, CIntTrim), it also brings a significant overhead because
VInt, VRangeInt and CInt all perform integer parsing. So the problem is
that validators are separate from converters and don't exchange
validated data among themselves.
In v.0.2, there are no separate converters. Validators are also
converters; they return validated/converted data along with validation
error list. The root Validator trait is declared like this:
package ru.dimgel.lib.web.param
trait Validator[In,Out] {
def validate(in: In): Param.Result[Out]
}
So VInt, for example, takes String input and returns Integer result; now
VRange maybe declared as generic VRange[T <% Ordered[T]] which performs
only test without conversion (its In and Out types are equal). This also
makes unnecessary to clone trimming and non-trimming validators; only
first validator must trim and pass that trimmed data forth.
The other problem solved is necessity of different parameter emptiness
semantics for different validation scenarios. I removed method
Param.isEmpty() and made it very easy to define custom Required and
Optional validators which implement emptiness tests. Finally, because
validators can be now chained based on their input and output types
compatibility, there's no need for my old weird Tags anymore.
Examples e03param and e04paramcustom are rewritten and now demonstrate
all new stuff.
***** STATE MANAGEMENT *****
When working on multipage forms, I had to track form context (data
submitted on previous form pages). For small forms with small total data
size, I planned next solution: serialize all data into base64-encoded
string and pass that string through a single hidden form field.
Generalization of this idea is next interface:
package ru.dimgel.lib.web.state
trait StateHandler {
def put(obj: AnyRef): String
def get(key: String): AnyRef
def remove(key: String)
}
This looks like collection, but implementations may be 100% stateless
(put() serializes, get() deserializes, remove() does nothing), 100%
stateful (HashMap which returns GUID keys), or anything in the middle.
Very flexible. Do you want stateful or stateless site? Just
choose/create appropriate state handler implementation.
Currently StateSerializer used in multipage form example
(http://dimgel.ru:8080/lib.web/example/en/e26formmultipage/) uses java
serialization which produces rather heavy strings. I was thinking about
JSONSserializer which could return shorter strings without all that Java
metadata. But I've put it off for now: there were questions about simple
ways of mapping possibly complex objects. This flesh is not urgent.
See example e05state.
***** FORMS *****
Forms look like this:
@serializable class LoginDTO {
var email: String = _
var password: String = _
var remember: Boolean = _
}
object LoginForm extends Form[LoginDTO] (Map("method" -> "post")) {
// short form for common use case:
val email: FText(VRequired & VEmail)
// full form for flexibility:
val password: Field(WPassword, CString, VRequired)
val remember: FCheckBox()
override protected
def htmlImp(errors:Form.ErrorList): NodeSeq = {
def err(f: Field.Generic) = if (errors.ok(f)) "" else "err"
{
Email{email.w}
Password{password.w}
{remember.w} Remember me
}
}
override def submit(param: ParamMap, default: LoginDTO) = {
//...
}
}
Here you can see that I rely on reflection quite a lot. I tried many
various ways but finally had to choose between concise source code and
total strict typization. I've chosen conciseness.
See examples e20widgets, e22form, e23formfields, e26formmultipage.
Tue, 2009-09-29, 13:47
#2
Re: [lib.web] v.0.2: validators rewritten, state management, for
ArtemGr wrote:
> Stateless form stuff looks interesting,
> but I do not like the idea of working under framework,
> I like the idea of useful libraries more,
> so that I could use the neat stuff where I want, not where I must.
Yeah, I agree: whatever framework you take, you face its limits very
soon. So my point about forms module was:
1. It must suit my own needs as far as I can recall my own experience.
Of course I tried to generalize it but in some situations I've chosen
simplicity over flexibility.
2. It's independent from lib.web core so those who don't like it may
forget it. More, if anyone would share some another cute forms stuff I
can add it as form2 package or so. =)
3) At last, while working on forms I could create really good validators
framework which can be easily extended to fit exactly to one's needs.
Request parameter validation is usually asspain, too. One of my primary
goals was to create validators flexible enough to be used in different
contexts - particularly, with and without forms.
Unfortunately, I could not create forms exactly as I dreemed them. I
claimed lib.web to be thin strictly typed wrapper over thick and
powerful things like Scala, Java codebase and servlets. My slogan here
is "write IN language, not USING language" (c) anti-McConnell. But forms
arrived to be neither thin nor strictly typed. I only hope that time
will show they're not completely useless. :)
Tue, 2009-09-29, 14:07
#3
Re: [lib.web] v.0.2: validators rewritten, state management, for
ArtemGr wrote:
> Stateless form stuff looks interesting,
> but I do not like the idea of working under framework,
> I like the idea of useful libraries more,
> so that I could use the neat stuff where I want, not where I must.
BTW, you gave me good idea: in readme I noted that validators maybe used
on their own, but I'll probably add ability to create Param and ParamMap
classes directly from HttpServletRequest. It would make validators
really useful independently of lib.web core.
P.S. In my previous answer, I occasionally deleted "forms are always
asspain" after "yeah I agree". Overproofreading. %)
Wed, 2009-09-30, 09:47
#4
Re: [lib.web] v.0.2: validators rewritten, state management, for
Dmitry Grigoriev writes:
> BTW, you gave me good idea: in readme I noted that validators maybe used
> on their own, but I'll probably add ability to create Param and ParamMap
> classes directly from HttpServletRequest. It would make validators
> really useful independently of lib.web core.
That was the first thing I looked for in the code.
"Where do I put my HttpServletRequest in?"
I saw you have your own generalizations around request and response,
but without standard ServletRequest support it looks at least like
Scala would look without JCL.
Wed, 2009-09-30, 10:17
#5
Re: [lib.web] v.0.2: validators rewritten, state management, for
ArtemGr wrote:
> I saw you have your own generalizations around request and response,
> but without standard ServletRequest support it looks at least like
> Scala would look without JCL.
OK, I'll do that. Thanks for feedback. :)
Dmitry Grigoriev writes:
> In 1-3 months I hope to finish some stuff (like
> file uploads), cleanup, publish v.0.3 and start a public ride with issue
> tracking, etc. For now I'd just appreciate any feedback.
Stateless form stuff looks interesting,
but I do not like the idea of working under framework,
I like the idea of useful libraries more,
so that I could use the neat stuff where I want, not where I must.