psisoyev
psisoyev

Reputation: 2168

Scala: Update class field value

I have:

case class One(someParam: String) {
private val _defaultTimeout = readFromConfig("defaultTimeout")
val timeout: Timeout = akka.util.Timeout(_defaultTimeout seconds)
val info: Option[Info] = Await.result(someSmartService.getInformationForSomething(someParam)), timeout.duration)
}

I'm building a service, which will obscure (encrypt) some sensitive data. I'm doing it in a such way:

def encrypt(oldOne: One): One = {
  val encryptedSomeParam = EncryptService.getHash(oldOne.someParam)
  val encryptedInfo = encryptInfo(oldOne.info)
  // what to do with that? ^^
  one.copy(someParam = encryptedSomeParam)
}

Also, I need to encrypt some data inside this "info" field of class One. The issue is that it is a val and I cannot reassign the value of a val. Is there an easy way how to do that? For now I'm thinking about changing it to a var, but I think it's not the best way to do that. Also, I cannot write encrypted data to this value from the beginning like this:

val info: Option[Info] = EncryptionService.encrypt(someSmartService.getInformationForSomething(someParam))

As this field is used in other places where I need the fields to be not encrypted. I want to encrypt sensitive data before the persistence of the object to a database.

Any ideas?

Thanks in advance!

EDIT: I know, that this looks like a bad design, so if someone has a better idea how to deal with it I'm looking forward to hear from you :)

Upvotes: 0

Views: 2120

Answers (1)

Malte Schwerhoff
Malte Schwerhoff

Reputation: 12852

Why not make info a case class argument as well?

case class One(someParam: String, info: Option[Info])

You could implement a default value for info by defining the companion object like

object One {
  def apply(someParam: String): One = One(someParam, someSmartService.getInformationForSomething(someParam))
}

That would allow you to work with Ones as follows:

One("foo")
One("foo", Some(...))
One(encryptedSomeParam, encryptedInfo)
One("plaintext").copy(someParam = encryptedSomeParam, info = encryptedInfo)

EDIT 1: Lazy info

Case classes cannot have lazy val arguments, i.e., neither info: => Option[String] nor lazy val info: Option[String] is allowed as an argument type.

You could make info a parameter-less function, though

case class One(someParam: String, info: () => Option[String])

object One {
  def apply(someParam: String): One = One(someParam, () => Some(someParam))
}

and then use it as

One("hi", () => Some("foo"))
println(One("hi", () => None).info())

This is obviously not ideal since it is not possible to introduce these changes without breaking code client code. Better solutions are welcome.

EDIT 2: Lazy info, no case class

If you don't insist on One being a case class (for example, because you really need copy), you could use a regular class with lazy values and a companion object for easy use:

class One(_someParam: String, _info: => Option[String]) {
  val someParam = _someParam
  lazy val info = _info
}

object One {
  def apply(someParam: String): One = new One(someParam, Await.result(...))
  def apply(someParam: String, info: => Option[String]): One = new One(someParam, info)
  def unapply(one: One) = Some((one.someParam, one.info))
}

Upvotes: 2

Related Questions