为一组案例动态填充一些属性 类
Dynamically populate some attributes for a set of case classes
- 我有一组模型 类,这些模型的子集有 2 个属性,分别称为
createdBy
和 modifiedBy
。
- 我只需要为具有这些属性的对象填充这些属性。目前我正在使用一些样板代码进行模式匹配。
case class DataSourceInstanceRow(id: Int, value: String, createdBy: Option[String], modifiedBy: Option[String])
case class FormDefinitionRow(id: Int, formData: String, createdBy: Option[String], modifiedBy: Option[String])
case class DecisionTableDefinitionRow(id: Int, rows: Int, definitions: List[String], createdBy: Option[String], modifiedBy: Option[String])
case class ReportDef(id: Int, reportType: Int, reportName: String)
def populateLogs[T](t: T, user: String): T = {
t match {
case ds: DataSourceInstanceRow =>
if(ds.id == -1) ds.copy(modifiedBy = Some(user), createdBy = Some(user)).asInstanceOf[T]
else ds.copy(modifiedBy = Some(user)).asInstanceOf[T]
case fd: FormDefinitionRow =>
if(fd.id == -1) fd.copy(modifiedBy = Some(user), createdBy = Some(user)).asInstanceOf[T]
else fd.copy(modifiedBy = Some(user)).asInstanceOf[T]
case dtd: DecisionTableDefinitionRow =>
if(dtd.id == -1) dtd.copy(modifiedBy = Some(user), createdBy = Some(user)).asInstanceOf[T]
else dtd.copy(modifiedBy = Some(user)).asInstanceOf[T]
case o => o
}
}
DataSourceInstanceRow
、FormDefinitionRow
、DecisiontableDefinitionRow
具有 modifiedBy
和 createdBy
属性。但不是 ReportDef
我如何使用 shapeless 创建一个抽象来从上面的模式匹配中删除样板文件?
你可以用 Shapeless 的 Updater
做这种事情:
import shapeless.{ LabelledGeneric, HList, Witness }
import shapeless.labelled.{FieldType, field}
import shapeless.ops.record.Updater
type CreatedBy = Witness.`'createdBy`.T
type ModifiedBy = Witness.`'modifiedBy`.T
def populateLogs[T, R <: HList](t: T, user: String)(implicit
gen: LabelledGeneric.Aux[T, R],
cb: Updater.Aux[R, FieldType[CreatedBy, Option[String]], R] = null,
mb: Updater.Aux[R, FieldType[ModifiedBy, Option[String]], R] = null
): T = (
for {
createdBy <- Option(cb)
modifiedBy <- Option(mb)
} yield gen.from(
createdBy(modifiedBy(gen.to(t), field(Some(user))), field(Some(user)))
)
).getOrElse(t)
然后:
scala> populateLogs(DataSourceInstanceRow(1, "abc", None, None), "foo")
res0: DataSourceInstanceRow = DataSourceInstanceRow(1,abc,Some(foo),Some(foo))
scala> populateLogs(ReportDef(1, 2, "abc"), "foo")
res1: ReportDef = ReportDef(1,2,abc)
此实现使用了一个技巧,您可以将 null
默认值放在隐式参数上,编译器将在找不到隐式参数时使用它。它很简单而且工作得很好,但有些人讨厌它。一种更有原则的方法使用隐式优先级排序:
trait UpdateBoth[T] extends ((T, String) => T)
object UpdateBoth extends LowPriorityUpdateBothInstances {
implicit def updateWithFields[T, R <: HList](implicit
gen: LabelledGeneric.Aux[T, R],
cb: Updater.Aux[R, FieldType[CreatedBy, Option[String]], R],
mb: Updater.Aux[R, FieldType[ModifiedBy, Option[String]], R]
): UpdateBoth[T] = (t, user) =>
gen.from(cb(mb(gen.to(t), field(Some(user))), field(Some(user))))
}
trait LowPriorityUpdateBothInstances {
implicit def updateAny[T]: UpdateBoth[T] = (t, _) => t
}
def populateLogs[T](t: T, user: String)(implicit update: UpdateBoth[T]): T =
update(t, user)
这将以完全相同的方式工作。
- 我有一组模型 类,这些模型的子集有 2 个属性,分别称为
createdBy
和modifiedBy
。 - 我只需要为具有这些属性的对象填充这些属性。目前我正在使用一些样板代码进行模式匹配。
case class DataSourceInstanceRow(id: Int, value: String, createdBy: Option[String], modifiedBy: Option[String])
case class FormDefinitionRow(id: Int, formData: String, createdBy: Option[String], modifiedBy: Option[String])
case class DecisionTableDefinitionRow(id: Int, rows: Int, definitions: List[String], createdBy: Option[String], modifiedBy: Option[String])
case class ReportDef(id: Int, reportType: Int, reportName: String)
def populateLogs[T](t: T, user: String): T = {
t match {
case ds: DataSourceInstanceRow =>
if(ds.id == -1) ds.copy(modifiedBy = Some(user), createdBy = Some(user)).asInstanceOf[T]
else ds.copy(modifiedBy = Some(user)).asInstanceOf[T]
case fd: FormDefinitionRow =>
if(fd.id == -1) fd.copy(modifiedBy = Some(user), createdBy = Some(user)).asInstanceOf[T]
else fd.copy(modifiedBy = Some(user)).asInstanceOf[T]
case dtd: DecisionTableDefinitionRow =>
if(dtd.id == -1) dtd.copy(modifiedBy = Some(user), createdBy = Some(user)).asInstanceOf[T]
else dtd.copy(modifiedBy = Some(user)).asInstanceOf[T]
case o => o
}
}
DataSourceInstanceRow
、FormDefinitionRow
、DecisiontableDefinitionRow
具有modifiedBy
和createdBy
属性。但不是ReportDef
我如何使用 shapeless 创建一个抽象来从上面的模式匹配中删除样板文件?
你可以用 Shapeless 的 Updater
做这种事情:
import shapeless.{ LabelledGeneric, HList, Witness }
import shapeless.labelled.{FieldType, field}
import shapeless.ops.record.Updater
type CreatedBy = Witness.`'createdBy`.T
type ModifiedBy = Witness.`'modifiedBy`.T
def populateLogs[T, R <: HList](t: T, user: String)(implicit
gen: LabelledGeneric.Aux[T, R],
cb: Updater.Aux[R, FieldType[CreatedBy, Option[String]], R] = null,
mb: Updater.Aux[R, FieldType[ModifiedBy, Option[String]], R] = null
): T = (
for {
createdBy <- Option(cb)
modifiedBy <- Option(mb)
} yield gen.from(
createdBy(modifiedBy(gen.to(t), field(Some(user))), field(Some(user)))
)
).getOrElse(t)
然后:
scala> populateLogs(DataSourceInstanceRow(1, "abc", None, None), "foo")
res0: DataSourceInstanceRow = DataSourceInstanceRow(1,abc,Some(foo),Some(foo))
scala> populateLogs(ReportDef(1, 2, "abc"), "foo")
res1: ReportDef = ReportDef(1,2,abc)
此实现使用了一个技巧,您可以将 null
默认值放在隐式参数上,编译器将在找不到隐式参数时使用它。它很简单而且工作得很好,但有些人讨厌它。一种更有原则的方法使用隐式优先级排序:
trait UpdateBoth[T] extends ((T, String) => T)
object UpdateBoth extends LowPriorityUpdateBothInstances {
implicit def updateWithFields[T, R <: HList](implicit
gen: LabelledGeneric.Aux[T, R],
cb: Updater.Aux[R, FieldType[CreatedBy, Option[String]], R],
mb: Updater.Aux[R, FieldType[ModifiedBy, Option[String]], R]
): UpdateBoth[T] = (t, user) =>
gen.from(cb(mb(gen.to(t), field(Some(user))), field(Some(user))))
}
trait LowPriorityUpdateBothInstances {
implicit def updateAny[T]: UpdateBoth[T] = (t, _) => t
}
def populateLogs[T](t: T, user: String)(implicit update: UpdateBoth[T]): T =
update(t, user)
这将以完全相同的方式工作。