I am trying to create a custom field type in Squeryl. This field represents an Isin code, so it is backed up by a string field. Following the example on the documentation, I have added a simple validation before creating a new isin (never mind what an Isin code is or how the validation procedure works):
trait Domain[A] { self: CustomType[A] =>
def validate(a: A): Unit
validate(value)
}
class Isin(v: String) extends StringField(v) with Domain[String] {
println("instantiating Isin")
override def validate(s: String) {
println("calling validate with " + s)
assert(checkIsin(s))
}
private def checkIsin(isin: String): Boolean = {
// never mind the exact procedure
}
}
I have added some println
to find out what is going on. I use this field inside a model like
case class Asset(
val id: Long = 0,
val isin: Isin
) extends KeyedEntity[Long]
object Asset {
import Database.assets
def create(isinCode: String) {
inTransaction {
assets.insert(new Asset(isin = new Isin(isinCode)))
}
}
}
Now, when I call Asset.create("US0378331005")
(a valid ISIN) I get an exception. In the stacktrace it turns out that this exception is due to a call to the init
method on a null
value, which is supposedly passed to checkIsin
. Indeed, the println
statements print
calling validate with US0378331005
Instantiating Isin
calling validate with
So it seems that the validate
method is actually invoked twice, but the second time it gets a null
value.
What is going on wrong?
There are several problems here. First off, you seem to be using Lift Record and, if that's the case, you are implementing your validation logic incorrectly. The correct way to validate a Record field is to override
def validations: List[ValidationFunction]
where ValidationFunction is a type alias
type ValidationFunction = ValueType => List[FieldError]
and in your case ValueType == String.
The next issue is your Domain trait. Because your call to validate is inlined into the class definition, it will be called when your field is constructed. Obviously there is no value set at that point, so that's why you are seeing the println that references an empty value. I imagine that the order you see them in has something to do with your application flow. You have an existing record that is validated, and after that a new Record is created which triggers the next 2 println statements.