Skip to content
This repository has been archived by the owner on Jun 23, 2020. It is now read-only.

Commit

Permalink
#15: [refactor] Better naming; transformer -> basic type transformer,…
Browse files Browse the repository at this point in the history
… as it is only one component of a "transformer" (before - FullTransformer). We are usually interested in a transformer, as it serializes all the way to json.
  • Loading branch information
adamw committed Mar 12, 2015
1 parent 75963c3 commit 9ca9419
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 101 deletions.
8 changes: 4 additions & 4 deletions docs/backend/formdef/typetransformations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Form definition: Supported types, type transformers
Fields of basic types (``String``, ``Int``, ``Long``, ``Float``, ``Double`` and ``Boolean``), ``UUID`` and ``Date``
are supported out-of-the-box, and can be directly edited in form fields.

If you have a more complex type, you need to provide an implicit implementation of a ``Transformer[U, S]``,
where ``U`` is your type, and ``S`` is one of the basic types. For convenience, you can extend
``StringTransformer[U]`` etc.
If you have a more complex type which serializes to a basic type, you need to provide an implicit implementation of a
``BasicTypeTransformer[U, S]``, where ``U`` is your type, and ``S`` is one of the basic types. For convenience, you can
extend ``StringTransformer[U]`` etc.

In the transformer, you need to implement a method which serializes your type to a basic type, and another
method which deserializes a basic type into your type, or returns a form error (conversion error).
Expand All @@ -18,7 +18,7 @@ You may wish to add render hints on some types of transformation automatically.
``def renderHint: Option[RenderHint with BasicFieldCompatible]`` in your transformer.

Example: Joda-Time DateTime transformer
----------------------------------
---------------------------------------

The `Joda-Time <http://www.joda.org/joda-time>`_ DateTime transformer can look like this::

Expand Down
4 changes: 2 additions & 2 deletions supler/src/main/scala/org/supler/Message.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package org.supler

import org.supler.transformation.Transformer
import org.supler.transformation.{BasicTypeTransformer}

case class Message(key: String, params: Any*)

object Message {
def apply[U](v: U, params: Any*)(implicit transformer: Transformer[U, String]) =
def apply[U](v: U, params: Any*)(implicit transformer: BasicTypeTransformer[U, String]) =
new Message(transformer.serialize(v), params: _*)
}
6 changes: 3 additions & 3 deletions supler/src/main/scala/org/supler/Supler.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.supler

import org.supler.field._
import org.supler.transformation.FullTransformer
import org.supler.transformation.Transformer
import org.supler.validation._

import scala.language.experimental.macros
Expand All @@ -15,7 +15,7 @@ object Supler extends Validators with RenderHints {
def form[T](rows: Supler[T] => List[Row[T]]): Form[T] = macro SuplerFormMacros.form_impl[T]

def field[T, U](param: T => U)
(implicit transformer: FullTransformer[U, _]): BasicField[T, U] =
(implicit transformer: Transformer[U, _]): BasicField[T, U] =
macro SuplerFieldMacros.field_impl[T, U]

/**
Expand Down Expand Up @@ -79,7 +79,7 @@ object Supler extends Validators with RenderHints {

trait Supler[T] extends Validators {
def field[U](param: T => U)
(implicit transformer: FullTransformer[U, _]): BasicField[T, U] =
(implicit transformer: Transformer[U, _]): BasicField[T, U] =
macro SuplerFieldMacros.field_impl[T, U]

/**
Expand Down
6 changes: 3 additions & 3 deletions supler/src/main/scala/org/supler/SuplerFieldMacros.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package org.supler

import org.supler.field._
import org.supler.transformation.FullTransformer
import org.supler.transformation.Transformer

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object SuplerFieldMacros {
def field_impl[T: c.WeakTypeTag, U: c.WeakTypeTag](c: blackbox.Context)
(param: c.Expr[T => U])
(transformer: c.Expr[FullTransformer[U, _]]): c.Expr[BasicField[T, U]] = {
(transformer: c.Expr[Transformer[U, _]]): c.Expr[BasicField[T, U]] = {

import c.universe._

Expand Down Expand Up @@ -122,7 +122,7 @@ object SuplerFieldMacros {

object FactoryMethods {
def newBasicField[T, U, S](fieldName: String, read: T => U, write: (T, U) => T, required: Boolean,
transformer: FullTransformer[U, S], emptyValue: Option[U]): BasicField[T, U] = {
transformer: Transformer[U, S], emptyValue: Option[U]): BasicField[T, U] = {

BasicField[T, U](fieldName, read, write, List(), None, required, transformer, transformer.renderHint, emptyValue,
AlwaysCondition, AlwaysCondition)
Expand Down
4 changes: 2 additions & 2 deletions supler/src/main/scala/org/supler/field/BasicField.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.supler.field
import org.json4s.JsonAST._
import org.supler._
import org.supler.validation._
import org.supler.transformation.FullTransformer
import org.supler.transformation.Transformer

case class BasicField[T, U](
name: String,
Expand All @@ -12,7 +12,7 @@ case class BasicField[T, U](
validators: List[Validator[T, U]],
label: Option[String],
required: Boolean,
transformer: FullTransformer[U, _],
transformer: Transformer[U, _],
renderHint: Option[RenderHint with BasicFieldCompatible],
emptyValue: Option[U],
enabledIf: T => Boolean,
Expand Down
4 changes: 2 additions & 2 deletions supler/src/main/scala/org/supler/field/StaticField.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.supler.field

import org.json4s.JValue
import org.json4s.JsonAST._
import org.supler.transformation.FullTransformer
import org.supler.transformation.Transformer
import org.supler.validation.{PartiallyAppliedObj, ValidationScope}
import org.supler.{FieldPath, Message}

Expand All @@ -21,7 +21,7 @@ case class StaticField[T](
val enabledIf: T => Boolean = AlwaysCondition
def includeIf(condition: T => Boolean): StaticField[T] = this.copy(includeIf = condition)

private val transformer = implicitly[FullTransformer[String, String]]
private val transformer = implicitly[Transformer[String, String]]

override protected def generateJSONData(obj: T) = {
val msg = createMessage(obj)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.supler.transformation

import java.text.{ParseException, SimpleDateFormat}
import java.util.{Date, UUID}

import org.supler.Supler
import org.supler.field.{BasicFieldCompatible, RenderHint}

trait BasicTypeTransformer[U, S] {
def serialize(u: U): S
def deserialize(s: S): Either[String, U]
def renderHint: Option[RenderHint with BasicFieldCompatible] = None
}

// convenience traits for extension by custom transformers; one for each of the types which have a json transformer
trait StringTransformer[U] extends BasicTypeTransformer[U, String]
trait IntTransformer[U] extends BasicTypeTransformer[U, Int]
trait LongTransformer[U] extends BasicTypeTransformer[U, Long]
trait FloatTransformer[U] extends BasicTypeTransformer[U, Float]
trait DoubleTransformer[U] extends BasicTypeTransformer[U, Double]
trait BooleanTransformer[U] extends BasicTypeTransformer[U, Boolean]

object BasicTypeTransformer {
trait IdentityTransformer[U] extends BasicTypeTransformer[U, U] {
override def serialize(u: U) = u
override def deserialize(u: U) = Right(u)
}

// if the type has a corresponding json transformer, no need to transform further
implicit object StringIdTransformer extends IdentityTransformer[String]
implicit object IntIdTransformer extends IdentityTransformer[Int]
implicit object LongIdTransformer extends IdentityTransformer[Long]
implicit object FloatIdTransformer extends IdentityTransformer[Float]
implicit object DoubleIdTransformer extends IdentityTransformer[Double]
implicit object BooleanIdTransformer extends IdentityTransformer[Boolean]

implicit object UUIDTransformer extends StringTransformer[UUID] {
override def serialize(u: UUID) = u.toString

override def deserialize(s: String) = try {
Right(UUID.fromString(s))
}
catch {
case e: IllegalArgumentException => Left("error_illegalUUIDformat")
}
}

val ISODateFormat = new SimpleDateFormat("yyyy-MM-dd")

implicit object DateTransformer extends StringTransformer[Date] {
override def serialize(d: Date) = ISODateFormat.format(d)

override def deserialize(d: String) = try {
Right(ISODateFormat.parse(d))
}
catch {
case e: ParseException => Left("error_illegalDateformat")
}

override def renderHint = Some(Supler.asDate())
}

implicit def optionTransformer[U, S](implicit base: BasicTypeTransformer[U, S]): BasicTypeTransformer[Option[U], Option[S]] =
new BasicTypeTransformer[Option[U], Option[S]] {
override def serialize(u: Option[U]) = u.map(base.serialize)
override def deserialize(s: Option[S]) = s.map(base.deserialize) match {
case None => Right(None)
case Some(d) => d.right.map(Some(_))
}
override def renderHint = base.renderHint
}
}

This file was deleted.

77 changes: 14 additions & 63 deletions supler/src/main/scala/org/supler/transformation/Transformer.scala
Original file line number Diff line number Diff line change
@@ -1,72 +1,23 @@
package org.supler.transformation

import java.text.{ParseException, SimpleDateFormat}
import java.util.{Date, UUID}

import org.supler.Supler
import org.json4s.JValue
import org.supler.field.{BasicFieldCompatible, RenderHint}

trait Transformer[U, S] {
def serialize(u: U): S
def deserialize(s: S): Either[String, U]
def renderHint: Option[RenderHint with BasicFieldCompatible] = None
}

// convenience traits for extension by custom transformers; one for each of the types which have a json transformer
trait StringTransformer[U] extends Transformer[U, String]
trait IntTransformer[U] extends Transformer[U, Int]
trait LongTransformer[U] extends Transformer[U, Long]
trait FloatTransformer[U] extends Transformer[U, Float]
trait DoubleTransformer[U] extends Transformer[U, Double]
trait BooleanTransformer[U] extends Transformer[U, Boolean]

object Transformer {
trait IdentityTransformer[U] extends Transformer[U, U] {
override def serialize(u: U) = u
override def deserialize(u: U) = Right(u)
}

// if the type has a corresponding json transformer, no need to transform further
implicit object StringIdTransformer extends IdentityTransformer[String]
implicit object IntIdTransformer extends IdentityTransformer[Int]
implicit object LongIdTransformer extends IdentityTransformer[Long]
implicit object FloatIdTransformer extends IdentityTransformer[Float]
implicit object DoubleIdTransformer extends IdentityTransformer[Double]
implicit object BooleanIdTransformer extends IdentityTransformer[Boolean]
class Transformer[U, S](basicTypeTransformer: BasicTypeTransformer[U, S], jsonTransformer: JsonTransformer[S]) {
def serialize(u: U): Option[JValue] = jsonTransformer.toJValue(basicTypeTransformer.serialize(u))

implicit object UUIDTransformer extends StringTransformer[UUID] {
override def serialize(u: UUID) = u.toString
def deserialize(jvalue: JValue): Either[String, U] = for {
s <- jsonTransformer.fromJValue.lift(jvalue).toRight("cannot convert json value").right
u <- basicTypeTransformer.deserialize(s).right
} yield u

override def deserialize(s: String) = try {
Right(UUID.fromString(s))
}
catch {
case e: IllegalArgumentException => Left("error_illegalUUIDformat")
}
}
def jsonSchemaName = jsonTransformer.jsonSchemaName

val ISODateFormat = new SimpleDateFormat("yyyy-MM-dd")

implicit object DateTransformer extends StringTransformer[Date] {
override def serialize(d: Date) = ISODateFormat.format(d)

override def deserialize(d: String) = try {
Right(ISODateFormat.parse(d))
}
catch {
case e: ParseException => Left("error_illegalDateformat")
}

override def renderHint = Some(Supler.asDate())
}
def renderHint: Option[RenderHint with BasicFieldCompatible] = basicTypeTransformer.renderHint
}

implicit def optionTransformer[U, S](implicit base: Transformer[U, S]): Transformer[Option[U], Option[S]] =
new Transformer[Option[U], Option[S]] {
override def serialize(u: Option[U]) = u.map(base.serialize)
override def deserialize(s: Option[S]) = s.map(base.deserialize) match {
case None => Right(None)
case Some(d) => d.right.map(Some(_))
}
override def renderHint = base.renderHint
}
object Transformer {
implicit def create[U, S](
implicit basicTypeTransformer: BasicTypeTransformer[U, S], jsonTransformer: JsonTransformer[S]): Transformer[U, S] =
new Transformer[U, S](basicTypeTransformer, jsonTransformer)
}

0 comments on commit 9ca9419

Please sign in to comment.