Generic field validation with traits in Lift

2019-08-08 17:49发布

问题:

I'm trying to define a trait Required to encapsulate the logic to validate the presence of required Record Fields, however, I haven't been able to figure out what the self type should be. My goal is to be able to write something as close as possible to e.g. object foo extends SomeField(...) with Required, however I do realize I might have to explicitly pass in some type parameters to Required.

My incompetent take so far has been:

import net.liftweb.record.Field import net.liftweb.util.FieldError

trait Required[ThisType, OwnerType] {
  this: Field[ThisType, OwnerType] =>
  def errMsg = "is required"

  override def validations = {
    val required =
      (x: String) => if (x.isEmpty) List(FieldError(this, errMsg)) else Nil
    // this asInstanceOf call also seems fishy
    // --why's it even required if we already have the self type in place?
    required :: super.asInstanceOf[Field[ThisType, OwnerType]].validations
  }
}

however, this leads to compilation errors and warnings related to existential types from:

myfield = object SomeField(...) with Required[SomeField[SomeModel], SomeModel]

not to mention it's as far as it gets from the concise with Required.

EDIT:

I've come up with this instead:

trait Required[OwnerType] extends Field[String, OwnerType] {
  def errMsg = "is required"

  override def validations = {
    val required =
      (x: String) => if (x.isEmpty) List(FieldError(this, errMsg)) else Nil
    required :: super.validations
  }
}

however, it doesn't allow me to prepend required to super.validations because it expects this.type.ValueType => List[FieldError] not String => List[FieldError], which I find odd because in the case of Field[String, ...], ValueType is String.

If I change required to be ValueType => ..., it compiles, but with Required[SomeModel] errors out with:

type arguments [String,OwnerType] do not conform to trait Field's type parameter bounds [ThisType,OwnerType <: net.liftweb.record.Record[OwnerType]]

...even though StringField.ThisType is String and String.OwnerType is a subclass of Record[SomeModel], SomeModel being a subclass of MongoRecord[SomeModel]. —I'm lost.

P.S. This is related to Lift Record: empty value for required field but no validation errors

回答1:

The following compiles in the SBT console.

import net.liftweb.util._
import net.liftweb.record._
import net.liftweb.record.field._

trait Required extends StringTypedField {

  override def validations: List[ValidationFunction] = valMinLen(1, "Required!") _ :: super.validations

}

class TestRecord extends Record[TestRecord] {

    val meta = TestRecord

    object testField extends StringField(this, 255) with Required

}

object TestRecord extends TestRecord with MetaRecord[TestRecord]