sardok
sardok

Reputation: 1116

Generic mapping function to handle enumeration types

I have following enumeration types.

class Payment extends Enumeration {
  type Payment = Value
  val Cash, CreditCard, Unspecified = Value
}
object Payment extends Payment

class Ticket extends Enumeration {
  type Ticket = Value
  val Normal, Discount, Other = Value
}
object Ticket extends Ticket

I declared enumerations as classes instead of objects as i will need the type information later (for MappedColumnType).

Here is the non working version of the slick table:

class Stats(tag: Tag) extends Table[Stat](tag, "STATS") {

  implicit def enumToInt[E <: Enumeration : ClassTag, V : ClassTag](implicit conv: V => E#Value): BaseColumnType[V] =
    MappedColumnType.base[V, Int] (
      value => value.id,
      num => implicitly[ClassTag[E]].runtimeClass.newInstance().asInstanceOf[E](num).asInstanceOf[V]
    )

  def entityid = column[Int]("ENTITYID")
  def email = column[String]("EMAIL")
  def payment = column[Payment]("PAYMENT")
  def ticket = column[Ticket]("TICKET")
  def phone = column[String]("PHONE")
  def ticket_source = column[TicketSource]("TICKETSOURCE")

  def * = (entityid, email, payment, ticket, phone, ticket_source) <>
    (Stat.tupled, Stat.unapply)
}

My goal is defining a generic mapper function to handle all of enumeration as the conversion methods are valid for all type of enumerations (value to id, id to value).

In mapper function i tried to capture ClassTag information and create a running instance to call the apply method which returns a enumeration Value. I had to add .asinstanceOf[V] to the result of apply method, otherwise error is risen about E#Value does not match with expected value of V. Now, this what i get as an error message:

Tables.scala:45: could not find implicit value for parameter tm: scala.slick.ast.TypedType[com.yokyer.tiyatrosever.models.Payment]
[error]   def payment = column[Payment]("PAYMENT")
[error]                                ^
Tables.scala:46: could not find implicit value for parameter tm: scala.slick.ast.TypedType[com.yokyer.tiyatrosever.models.Ticket]
[error]   def ticket = column[Ticket]("TICKET")

I am new to scala, i put bunch of recipes about reflections put together and wondering if i am doing something fundamentally wrong.

Upvotes: 2

Views: 846

Answers (1)

rfranco
rfranco

Reputation: 46

you need to create a MappedColumnType

import profile.simple._

def enumValueMapper(enum: Enumeration) = MappedColumnType.base[enum.Value, String](
    e => e.toString,
    s => Try(enum.withName(s)).getOrElse(throw new IllegalArgumentException
                                        (s"enumeration $s doesn't exist $enum 
                                        [${enum.values.mkString(",")}]"))
)

def enumIdMapper(enum: Enumeration) = MappedColumnType.base[enum.Value, Int](
    e => e.id,
    i => enum.apply(i)
)

then you can use

// It convert Enum to Int
implicit val paymentMapper = enumIdMapper(Payment) 

Or

// It convert Enum to String
implicit val paymentMapper = enumValueMapper(Payment) 

Upvotes: 3

Related Questions