Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When overloading `apply` method: Slick error message 'value tupled is not a member of object'

Tags:

scala

slick

I need an ability to create a User object by providing all the values except id in certain cases, such that the User object takes care of assigning itself an auto-generated value.

For this I have overloaded the apply method in the companion object, like shown below. But this is causing the compile time error: value tupled is not a member of object.

Solutions mentioned on StackOverflow and other blogs aren't working, such as: http://queirozf.com/entries/slick-error-message-value-tupled-is-not-a-member-of-object

case class User(id: Long, firstName: String, lastName: String, mobile: Long, email: String)

object User {
  private val seq = new AtomicLong

  def apply(firstName: String, lastName: String, mobile: Long, email: String): User = {
    User(seq.incrementAndGet(), firstName, lastName, mobile, email)
  }
}

class UserTableDef(tag: Tag) extends Table[User](tag, "user") {

  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def firstName = column[String]("first_name")
  def lastName = column[String]("last_name")
  def mobile = column[Long]("mobile")
  def email = column[String]("email")

  override def * =
    (id, firstName, lastName, mobile, email) <> (User.tupled, User.unapply)

}
like image 976
iamsmkr Avatar asked Dec 19 '22 11:12

iamsmkr


2 Answers

The source of your problem is that overloaded apply def.

tupled does not work with case class's with less than 2 parameters or overloaded apply.

As far as slick's * (or all) mapping and <> is concerned, it is supposed to be like,

def * = (tupleMember1, tupleMember2, ...) <> (func1, func2)

Such that,

  • func1 takes that tuple (tupleMember1, tupleMember2, ...) as input and returns an instance of mapped class/case class.
  • func1 takes an instance of mapped class/case class and returns that tuple (tupleMember1, tupleMember2, ...).

So you can provide any function... which meets these requirements.

case class User(id: Long, firstName: String, lastName: String, mobile: Long, email: String)

object User {
  private val seq = new AtomicLong

  def apply(firstName: String, lastName: String, mobile: Long, email: String): User = {
    User(seq.incrementAndGet(), firstName, lastName, mobile, email)
  }

  def mapperTo(
    id: Long, firstName: String,
    lastName: String, mobile: Long, email: String
  ) = apply(id, firstName, lastName, mobile, email)

}

class UserTableDef(tag: Tag) extends Table[User](tag, "user") {

  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def firstName = column[String]("first_name")
  def lastName = column[String]("last_name")
  def mobile = column[Long]("mobile")
  def email = column[String]("email")

  override def * =
    (id, firstName, lastName, mobile, email) <> ((User.mapperTo _).tupled, User.unapply)

}
like image 52
sarveshseri Avatar answered Mar 08 '23 22:03

sarveshseri


One possible solution is to push the secondary constructor to the case class definition itself and then use the work around mentioned in the blog post specified in the question.

You can then create User objects without specifying id, however, you may need still to use new keyword, like so new User(firstName, lastName, mobile, email).

case class User(id: Long, firstName: String, lastName: String, mobile: Long, email: String) {

  def this(firstName: String, lastName: String, mobile: Long, email: String) =
    this(User.seq.incrementAndGet(), firstName, lastName, mobile, email)
}

object User {
  private val seq = new AtomicLong
}

class UserTableDef(tag: Tag) extends Table[User](tag, "user") {

  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def firstName = column[String]("first_name")
  def lastName = column[String]("last_name")
  def mobile = column[Long]("mobile")
  def email = column[String]("email")

  override def * =
    (id, firstName, lastName, mobile, email) <> ((User.apply _).tupled, User.unapply)

}
like image 42
iamsmkr Avatar answered Mar 08 '23 23:03

iamsmkr