Общая проверка поля с помощью трейтов в Lift

Я пытаюсь определить trait Required, чтобы инкапсулировать логику для проверки наличия требуемых Record Field, однако мне не удалось выяснить, каким должен быть тип self. Моя цель - написать что-нибудь как можно ближе, например, к object foo extends SomeField(...) with Required, однако я понимаю, что мне, возможно, придется явно передать некоторые параметры типа в Required.

Мой некомпетентный подход был таков:

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
  }
}

однако это приводит к ошибкам компиляции и предупреждениям, связанным с экзистенциальными типами из:

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

не говоря уже о том, что это так далеко от лаконичного with Required.

РЕДАКТИРОВАТЬ:

Вместо этого я придумал:

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
  }
}

однако он не позволяет мне добавлять required к super.validations, потому что ожидает this.type.ValueType => List[FieldError], а не String => List[FieldError], что я считаю странным, потому что в случае Field[String, ...] ValueType равно String.

Если я изменю required на ValueType => ..., он компилируется, но with Required[SomeModel] выдает ошибки:

аргументы типа [String, OwnerType] не соответствуют границам параметра типа поля [ThisType, OwnerType ‹: net.liftweb.record.Record [OwnerType]]

... даже несмотря на то, что StringField.ThisType это String, а String.OwnerType является подклассом Record[SomeModel], а SomeModel является подклассом MongoRecord[SomeModel]. -Я потерялся.

P.S. Это связано с Lift Record: пустое значение для обязательного поля, но без ошибок проверки


person Erik Kaplun    schedule 14.03.2014    source источник


Ответы (1)


Следующее компилируется в консоли SBT.

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]
person Dave Whittaker    schedule 14.03.2014
comment
Это действительно очень мило! Не могли бы вы объяснить, как можно понять, что это было правильное решение? Требуется ли для этого более глубокое понимание того, как Lift Record был построен / разработан? Или это просто отсутствие опыта работы с Lift / Scala заставило меня не понять, что мне нужно использовать StringTypedField в качестве самотипа? (Я действительно придумал рабочий миксин, но он был намного сложнее) - person Erik Kaplun; 14.03.2014
comment
И то и другое понемногу. Работа с набором текста - это определенно вещь Scala, но знание того, что проще использовать StringTypedField, дает знакомство с Lift. - person Dave Whittaker; 15.03.2014
comment
Похоже, это приводит к бесконечной рекурсии; чтобы исправить это, я заменил тип self на extends StringTypedField и вместо этого вызвал super.valiations. (не стесняйтесь вносить мои правки в ответ и удалять мою заметку). - person Erik Kaplun; 15.03.2014
comment
Обновлено. Я хотел избежать явного наследования, но полагаю, что нет способа сослаться на метод проверки типа self. Если вы укажете super без расширения трейта, он не будет компилироваться. Полагаю, это по крайней мере сработает. - person Dave Whittaker; 17.03.2014
comment
Это определенно работает ... однако, тем временем, я понял, что, в конце концов, легче собрать большую часть логики проверки для полей любого типа в одну функцию, которая просто выполняет итерацию по rec.fields, проверяет атрибут required_? и действует соответственно , со специальными случаями, определенными с помощью _3 _... в противном случае будет слишком много хлопот с системой типов и слишком мало пользы (например, pastebin. com / P2pNg05b) - person Erik Kaplun; 17.03.2014