Ответ 1
Я не пробовал это, но как насчет черты, в которую вы смешиваете:
trait CommonFields { this: Table[_] =>
def id = column[String]("id", O.PrimaryKey)
def livemode = column[Boolean]("livemode", O.NotNull)
def created = column[DateTime]("created", O.NotNull)
def createdBy = column[Account]("created_by", O.NotNull)
protected common_* = id ~ livemode ~ created ~ createdBy
}
Затем вы можете сделать:
object AsTable extends Table[(String,Boolean,DateTime,Account,String)]("a_table")
with CommonFields {
def foo = column[String]("foo", O.NotNull)
def * = common_* ~ foo
}
Единственное, что вам нужно будет повторить сейчас, это тип элементов.
UPDATE
Если вы хотите выполнить сопоставление объектов и:
- Вы сопоставляете классы case
- Поля в классах вашего случая находятся в том же порядке
Просто выполните:
case class A(
id: String,
livemode: Boolean,
created: DateTime,
createdBy: Account,
foo: String)
object AsTable extends Table[A]("a_table") with CommonFields {
def foo = column[String]("foo", O.NotNull)
def * = common_* ~ foo <> (A.apply _, A.unapply _)
}
Это представляется наиболее экономичным решением (вместо этого пытается определить *
в CommonFields
и добавление параметра типа). Тем не менее, это требует, чтобы вы изменили все классы case, если ваши поля изменились.
Мы могли бы попытаться смягчить это, используя композицию в классах case:
case class Common(
id: String,
livemode: Boolean,
created: DateTime,
createdBy: Account)
case class A(
common: Common,
foo: String)
Однако при построении функции сопоставления нам (где-то) придется преобразовать кортежи формы:
(CT_1, CT_2, ... CT_N, ST_1, ST_2, ..., ST_M)
CT
Общий тип (известный в CommonFields
) ST
Конкретный тип (известный в AsTable
)
To:
(CT_1, CT_2, ... CT_N), (ST_1, ST_2, ..., ST_M)
Чтобы передать их подпрограммам, индивидуально преобразующим Common
и A
в и из их кортежей.
Мы должны сделать это, не зная числа или точные типы либо CT
(при реализации в AsTable
), либо ST
(при реализации в CommonFields
). Кортежи в стандартной библиотеке Scala не могут этого сделать. Для этого вам нужно будет использовать HLists
для примера, предложенного shapeless.
Несомненно, стоит ли это усилий.
Основной контур может выглядеть так (без всякого неявного беспорядка, который потребуется). Этот код не будет компилироваться следующим образом.
trait CommonFields { this: Table[_] =>
// like before
type ElList = String :: Boolean :: DateTime :: Account :: HNil
protected def toCommon(els: ElList) = Common.apply.tupled(els.tupled)
protected def fromCommon(c: Common) = HList(Common.unapply(c))
}
object AsTable extends Table[A] with CommonFields {
def foo = column[String]("foo", O.NotNull)
def * = common_* ~ foo <> (x => toA(HList(x)), x => fromA(x) tupled)
// convert HList to A
protected def toA[L <: HList](els: L) = {
// Values for Common
val c_els = els.take[Length[ElList]]
// Values for A
val a_els = toCommon(c_els) :: els.drop[Length[ElList]]
A.apply.tupled(a_els.tupled)
}
// convert A to HList
protected def fromA(a: A) =
fromCommon(a.common) :: HList(A.unapply(a)).drop[One]
}
Используя еще одну магию типа, вы можете решить две последние проблемы:
- Поместите
toA
иfromA
в базовый признак (используя параметры типа в признаке или используя элементы абстрактного типа) - Избегайте определения
ElList
явно, извлекая его изCommon.apply
с помощью этой техники