scala

Case classes in Scala!

At first, when I got introduced to case classes in Scala, I thought, why then hell does Scala have another way of creating classes? My initial google search taught me that case classes are classes specifically meant for holding data with minimum ceremony.

The java way of doing this would be to (Usual way of creating model classes);

  • Define a class with bunch of private fields.
  • Write constructor with all these fields as the parameters.
  • Define getters and setters for each of the fields.
  • Make the class extend the Serializable interface.

Scala does this all underneath the hood for you if you create a case class. That is right! case class is just Java classes on steroids. Well, since we are talking about Scala, how is it different from normal classes in Scala?

The fun thing here is case classes can also be considered as Scala classes on steroids, how you might ask. Case classes, would also do the following things besides just creating a class;

  • Creates the class along with a companion object.
  • A default toString method that includes all the fields and not the annoying @ and hex strings.
Normal class would give you the hex string while the case class object would give a more meaningful toString
  • A copy method that allows to create a new copy of the object with the exact same fields.
  • equals and hashCode methods would be overridden will be dependent on the fields instead of the meaningless object reference. This helps a lot with the objects being able to be added into List and Maps.

Finally, if your case class does not require any fields but just methods, we can go ahead and just create a case object directly.

Would just create an object with all the features specified above

Seems like this is not just a thing in Scala. Kotlin, a language heavily inspired by Scala also has this construct, but they call it data classes(https://kotlinlang.org/docs/reference/data-classes.html).

So, I hope its now a bit more clear as to why the hell is there another way to create classes and objects in Scala. Use them exactly by knowing where and when to use them.

Standard
implicit
scala

How to create extension methods in Scala using implicits

I guess my recent love for scala is quite evident lately. I began to fall in love with the conciseness and expressive nature of scala. But more than that, there is one thing that I am currently infatuated with; implicits – The magic keyword in scala.

Scala uses implicit's heavily to improve the conciseness of many design patterns that are native to functional programming and hence plays a key role in the core design of the language and many libraries in the community.

Let’s get started on how to put implicits into action and create an extension method to a class using implicit‘s.

So, what is this extension method in first place?

Extension methods are like extending a feature to a class that is otherwise in-extensible. Example of an extension method would be able to extend the functionality of Int class by creating a method that takes in a single digit as a number and returns the digit as a String. Something similar to the snippet shown below.

1.digitToWord() // This is an extension method on Int class

Clearly, this is not possible in statically typed language as the language would not allow you to add methods to the standard library and would fail to compile.

But scala has this magic language feature called implicit specially for performing a trick like this. Let us see how that is done.

implicit class ExtendedInt(val num: Int) {
  def digitToWord: String = {
    num match {
      case 1 => "One"
      case 2 => "Two"
      case 3 => "Three"
      case 4 => "Four"
      case 5 => "Five"
      case 6 => "Six"
      case 7 => "Seven"
      case 8 => "Eight"
      case 9 => "Nine"
      case 0 => "Zero"
      case _ => "Not a valid single digit"
    }
  }
}

Scala allows you to create an implicit class which has a method that returns a String. When we define this in the scope, scala compiler keeps track of this transformation from Int to String in the context and tries to use it whenever appropriate.

So, when you say,

1.digitToWord() // Implicit magic aka extension method call

Scala compiler will automatically check to see if its able to find one unique implicit that can take in an Int, construct an ExtendedInt and call digitToWord(). Since we already defined our implicit class Extendednt to take an Int to create an instance of ExtendedInt, compiler silently performs the following transformation for you.

new ExtendedInt(1).digitToWord() // After the implicit magic worn out

Cool isn’t it? Implicit is a complicated feature and would sound like something which makes the code very difficult to debug. Yes, that is true! Implicits are a very powerful feature indeed and often face a lot of criticisms. One has to use implicits with a word of caution and careful enough to stay responsible because, as Spiderman says,

“With great power comes great responsibility!”

Also yeah, Happy new year!

Standard
cassandra, libraries, scala

Access Cassandra using phantom – A type-safe scala library

This post is a quick tutorial on how to use Scala for interacting with Cassandra database. We will look into how to use phantom library for achieving this. phantom is a library that is written in Scala and acts as a wrapper on top of the cassandra-driver (Java driver for Cassandra).

The library phantom offers a very concise and typesafe way to write and design Cassandra interactions using Scala. In this post, I will be sharing my 2 cents for people getting started in using this library.

Prerequisites

Before getting started, I would like to make a few assumptions to keep this post concise and short as much as possible. My assumptions are that;

  1. You know what Cassandra is.
  2. You have Cassandra running locally or on a cluster for this tutorial.
  3. You have a piece of basic knowledge of what is Scala and sbt.

In this post, let’s write a simple database client for accessing User information. For simplicity sake, our User model would just have id, firstName, lastName and emailId as its fields. Since cassandra is all about designing your data-model based on your query use-case, we shall have two use-cases for accessing data and hence shall define two tables in cassandra.

The tables would have the following schema.

CREATE KEYSPACE user_keyspace WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};
CREATE TABLE user_keyspace.user_by_first_name (
firstname text PRIMARY KEY,
email text,
id uuid,
lastname text
);
CREATE TABLE user_keyspace.user_by_id (
id uuid PRIMARY KEY,
email text,
firstname text,
lastname text
);
view raw keyspace.cql hosted with ❤ by GitHub

Make sure your database has this schema configured before proceeding further.


Step 0: Add phantom library to the dependencies

Let us start by creating a simple sbt scala project and add the following dependency to build.sbt file to the libraryDependencies.

"com.outworkers" % "phantom-dsl_2.12" % "2.30.0"

Step 1: Define your model class

In cassandra, it is totally acceptable to denormalize the data and have multiple tables for the same model. phantom is designed to allow this. We define one case class as the data holder and several model classes that correspond to our different use cases. Let us go ahead and define the model and case classes for our User data-model.

// Base model used as DTO
case class User(id: UUID, firstName: String, lastName: String, email: String)
view raw User.scala hosted with ❤ by GitHub

Now, we define the tables that correspond to the tables in Cassandra. This allows phantom to construct the queries for us in a typesafe manner.

The following are the use cases for which we would need clients;

  1. To access the data using the user id. (user_by_id table)
  2. To access the data using the user’s first name. (user_by_first_name table)

We create appropriate models that reflect these tables in cassandra and define low-level queries using phantom-dsl to allow phantom to construct queries for us so we don’t have to write any cql statements ever in our application.

// Query based model
abstract class UserById extends Table[UserById, User] {
// Override table metadata
override def tableName: String = "user_by_id"
// Define the table schema
object id extends UUIDColumn with PartitionKey
object fName extends StringColumn {
override def name: String = "firstName"
}
object lName extends StringColumn {
override def name: String = "lastName"
}
object email extends StringColumn
// Define low level queries
def createUserById(uuid: UUID, firstName: String, lastName: String, emailId: String): Future[ResultSet] = {
insert
.value(_.id, uuid)
.value(_.fName, firstName)
.value(_.lName, lastName)
.value(_.email, emailId)
.future()
}
def getUserById(uuid: UUID): Future[Option[User]] = select
.all()
.where(_.id eqs uuid)
.one()
}
abstract class UserByFirstName extends Table[UserByFirstName, User] {
// Override table metadata
override def tableName: String = "user_by_first_name"
// Define the table schema
object fName extends StringColumn with PartitionKey {
override def name: String = "firstName"
}
object id extends UUIDColumn
object lName extends StringColumn {
override def name: String = "lastName"
}
object email extends StringColumn
// Define low level queries
def createUserByUserName(uuid: UUID, firstName: String, lastName: String, emailId: String): Future[ResultSet] = {
insert
.value(_.id, uuid)
.value(_.fName, firstName)
.value(_.lName, lastName)
.value(_.email, emailId)
.future()
}
def getUserByFirstName(firstName: String): Future[Option[User]] = select
.all()
.where(_.fName eqs firstName)
.one()
}
view raw UserModel.scala hosted with ❤ by GitHub

You can access the state of the project until this step in GitHub here.

Now that we have our models and low level queries defined, we need to design an effective way to manage our session and database instances.

Step 2: Encapsulate database instances in a Provider

Since we have interactions with multiple tables for a single model User, phantom provides a way to encapsulate these database instances at one place and manage it for us. This way, our implementations won’t be scattered around and will be effectively managed with the appropriate session object.

So, in this step, we define an encapsulation for all the database instances by extending phantom‘s Database class. This is where we create instances for the models we defined in the above step.

import com.outworkers.phantom.dsl._
// This class will encapsulate all the valid database instances
class UserDatabase(implicit cassandraConnection: CassandraConnection)
extends Database[UserDatabase](connector = cassandraConnection) {
object userByIdInstance extends UserById with Connector
object userByFirstName extends UserByFirstName with Connector
}
// This trait will act as a database instance provider.
trait UserDbProvider extends DatabaseProvider[UserDatabase]

Notice that we also defined a trait extending DatabaseProvider[UserDatabase] in the above snippet. We will in the next step discuss how this trait is useful.

You can access the state of the project until this step here.

Step 3: Orchestrate your queries using DatabaseProvider

Now, that you have your model and database instances in place, exposing these methods may not be a good design. What if you need some kind of data-validation/data-enrichment before accessing these methods. Well, to be very specific in our case, we would need to enter data into two tables when a User record is created. To serve such purposes, we need an extra layer of abstraction for accessing the queries we defined. This is the exact purpose of DatabaseProvider trait.

We define our database access layer (like a service) by extending the trait DatabaseProvider and orchestrate our low-level queries so that the application can access data with the right abstraction.

import com.outworkers.phantom.dsl._
import scala.concurrent.Future
trait UserDatabaseService extends UserDbProvider {
// Orchestrate your low level queries appropriately.
def createUser(uuid: UUID, firstName: String, lastName: String, email:String): Future[Option[UUID]] =
database.userByIdInstance.createUserById(uuid, firstName, lastName, email)
.flatMap(_ => {
database.userByFirstName.createUserByUserName(uuid, firstName, lastName, email)
.map(rs => if(rs.wasApplied) Some(uuid) else None)
})
def selectUserById(uuid: UUID): Future[Option[User]] = database.userByIdInstance.getUserById(uuid)
def selectUserByFirstName(firstName: String): Future[Option[User]] = database.userByFirstName.getUserByFirstName(firstName)
}

You can see that we were able to combine both the inserts (to user_by_id & user_by_first_name) in one method call. We could have definitely, done some sort of validation or data-transformation if we wanted to here.

You can access the state of the project until this step here.

Step 4: Putting everything together

We are all set to the database service we created so far. Lets see how this is done.

We start out by creating our CassandraConnection instance. This is how we can inject out cassandra configuration into phantom and let it manage the database session for us.

implicit val cassandraConnection: CassandraConnection = {
ContactPoint.local.keySpace("user_keyspace")
}

Here we are using cassandra installed locally, hence we used ContactPoint.local. Ideally in real-world we would have to use ContactPoints(hosts).

Then we create our database encapsulation instance and the service object.

object UserDatabase extends UserDatabase
object databaseService extends UserDatabaseService {
override def database: UserDatabase = UserDatabase
}
view raw Service.scala hosted with ❤ by GitHub

Now, it is as simple as just calling a bunch of methods to test out if our database interactions work.

val userPair = for{
uuid <- databaseService.createUser(UUID.nameUUIDFromBytes("Sumanth".getBytes), "Sumanth", "Kumar", "sumanth@indywiz.com")
userByFirstName <- databaseService.selectUserByFirstName("Sumanth")
userById <- databaseService.selectUserById(uuid.get)
} yield (userById, userByFirstName)
val res = userPair.map {
case (userById, userByFirstName) =>
logger.info(s"User by Id : ${userById.get}")
logger.info(s"User by first name: ${userByFirstName.get}")
}
view raw Call.scala hosted with ❤ by GitHub

You can find the final state of the project here.

We might have had a lot of constructs like Database, DatabaseProvider, etc, bundled with phantom-dsl. But in my opinion, this is something that makes it more than just yet another scala dsl-library. It is because of these design constructs, phantom implicitly promotes good design for people using it.

Hope you found my rambling useful.

Standard
asynchronous, scala

Testing futures in scala using scala test

Non-blocking external calls (web service) calls often would return a Future which will resolve to some value in the future. Testing such values in Java is pretty sad. Testing futures in java will usually involve performing a get()to resolve a future and then perform the assertions. I am pretty new to scala and was curious if I would have to do the same. Fortunately with Scala this is not the case. I will walk you thru my experience with an example and lets see how we do it in scala. In this post, we will be using scala-test library as our testing library and use FlatSpec type of testing.

Let’s start by defining our class to test. 

import scala.concurrent.{ExecutionContext, Future}
class NetworkCallExample {
implicit val ec: ExecutionContext = ExecutionContext.Implicits.global
private def getResponseFromServer(url: String): Future[Int] = {
Future {
Thread.sleep(5000)
10
}
}
// Dummy method to simulate a network call
def statusOfSomeOtherResponse(url: String): Future[Int] = {
println("Sending out request to some other url")
Future {
Thread.sleep(1000)
200
}
}
//Another dummy call
def lengthOfTheResponse(url: String): Future[Int] = {
println("Sending out request asynchronously to the server")
getResponseFromServer(url)
}
}

The above code is a simple scala class that will simulate a dummy network call by just sleeping for an arbitrary amount time.

lengthOfTheResponse(url: String): Future[Int] is a function that waits for 5 seconds and returns the number 10. 

Similarly, statusOfSomeOtherResponse(url: String): Future[Int]is another function that waits for 1 second and returns the number 200. Let’s see how we write tests for these function. 

import org.scalatest.FlatSpec
import org.scalatest.concurrent.ScalaFutures
class NetworkCallExampleTest extends FlatSpec with ScalaFutures {
it should "verify the future returns when complete" in {
val networkCallExample = new NetworkCallExample
//This will fail with a time out error
whenReady(networkCallExample.lengthOfTheResponse("testurl")) { res =>
assert(res == 10)
}
}
}

The above example looks great but will fail with the following error.

A timeout occurred waiting for a future to complete. Queried 10 times, sleeping 15 milliseconds between each query.

The default behavior of whenReady is to keep polling 10 times by waiting for 15 milliseconds in between to see if the future is resolved. If not, we get the above error. This clearly is not a consistent solution as we are depending on a finite time and polling to figure out the future result. Sure, we can configure the above whenReady method with a higher timeout and retry but there are better ways to test. Let’s see another approach.

class NetworkCallExampleTest extends AsyncFlatSpec {
it should "verify the future returns with for and yield" in {
val networkCallExample = new NetworkCallExample
for{
res <- networkCallExample.lengthOfTheResponse("testurl")
} yield {
assert(res == 10)
}
}
}

The above example will first execute the lengthOfTheResponse("testurl") resolve the future and yield the result Intinto resvariable. We then assert the result. But there is a gotcha we need to keep in mind. It is important that we use the AsyncFlatSpec instead of FlatSpec

We can also do more fluent version with multiple network calls. 

class NetworkCallExampleTest extends AsyncFlatSpec {
it should "verify the future returns with for and yield" in {
val networkCallExample = new NetworkCallExample
// More fluent version
for {
_ <- networkCallExample.lengthOfTheResponse("testurl")
status <- networkCallExample.statusOfSomeOtherResponse("some other url")
} yield {
assert(status == 200)
}
}
}

This way, we can kind of chain the futures in the order that we want to test and perform assertions accordingly. This will ensure that the futures resolve appropriately.

Let me know what you think of this and would certainly like to learn other approach to test futures.

Standard