Form-binder is a micro data binding and validating framework, very easy to use and hack.
It was initially created for my
Scalatra
-based project, but you can easily integrate it with other frameworks.
- very lightweight, only ~900 lines codes (framework + built-in extensions)
- easy use, no verbose codes, and what you see is what you get
- high customizable, you can extend almost every executing point
- easily extensible, every extension interface is an alias of
FunctionN
- immutable, you can share mapping definition object safely
Steps:
- define your binder
- define your mappings
- prepare your data
- bind and consume
p.s. every points above (1)/(2)/(3)/(4)/ are all extendable and you can easily customize it.
To use form-binder
, pls add the dependency to your sbt project file:
libraryDependencies += "com.github.tminglei" %% "form-binder" % "0.12.2"
Then you can integrate it with your framework to simplify normal usage.
Here's the way in my Scalatra
project:
First, I defined a FormBindSupport
trait,
trait MyFormBindSupport extends I18nSupport { self: ScalatraBase =>
import MyFormBindSupport._
before() {
request(BindMessagesKey) = Messages(locale, bundlePath = "bind-messages")
}
def binder(implicit request: HttpServletRequest) = FormBinder(bindMessages.get, errsTree())
///
private def bindMessages(implicit request: HttpServletRequest): Messages = if (request == null) {
throw new ScalatraException("There needs to be a request in scope to call bindMessages")
} else {
request.get(BindMessagesKey).map(_.asInstanceOf[Messages]).orNull
}
}
Then mix it to my xxxServlet, and use it like this,
import com.github.tminglei.bind.simple._
class SampleServlet extends ScalatraServlet with MyFormBindSupport {
get("/:id") {
val mappings = tmapping(
"id" -> long()
)
binder.bind(mappings, params).fold(
errors => holt(400, errors),
{ case (id) =>
Ok(toJson(repos.features.get(id)))
}
)
}
}
p.s. you can check more integration sample codes under /integrations.
The core of form-binder
is Mapping
, tree structure mappings. With depth-first algorithm, it was used to validate data and construct the result value object.
[1] binder: facade, used to bind and trigger processing, two major methods: bind
, validate
[2] messages: used to provide error messages
[3] mapping: holding constraints, processors, and maybe child mapping, etc. used to validate/convert data, two types of mappings: field
and group
[4] data: inputting data map
binder bind method signature (return an Either
and let user to continue processing):
//bind mappings to data, and return an either, holding validation errors (left) or converted value (right)
def bind[T](mapping: Mapping[T], data: Map[String, String], root: String = ""): Either[R, T]
binder validate method signature (validate only and not consume converted data):
//return (maybe processed) errors
def validate[T](mapping: Mapping[T], data: Map[String, String], root: String = "")
Check here for built-in mappings.
(1) ErrProcessor: used to process error seq, like converting it to json
(2) PreProcessor: used to pre-process data, like omitting $
from $3,013
(3) Constraint: used to validate raw string data
(4) ExtraConstraint: used to validate converted value
* Check here for built-in
PreProcessor
/ErrProcessor
.
**Check here for built-inConstraint
/ExtraConstraint
.
- label:
feature
, readable name for current group/field - mapTo:
feature
, map converted value to another type - i18n:
feature
, labels starting with@
will be used as a message key to fetch a i18n value frommessages
- eagerCheck:
option
, check errors as more as possible; defaultfalse
, return right after a validation error found - skipUntouched:
option
, skip checking untouched empty field/values; defaultfalse
, won't skip untouched - touchedChecker:
function
, check whether a field was touched by user; if yes, required fields can't be empty
p.s. for more dev and usage details, pls check the source codes and test cases.
[TODO]
To hack it and make your contribution, you can setup it like this:
$ git clone https://github.com/tminglei/form-binder.git
$ cd form-binder
$ sbt
...
To run the tests, pls execute:
$ sbt test
Play!
framework development team, for the original idea and implementation;- Naoki Takezoe and his
scalatra-forms
, aplay-data
implementation for scalatra.
The BSD License, Minglei Tu <[email protected]>