Skip to content

Instantly share code, notes, and snippets.

View adamw's full-sized avatar

Adam Warski adamw

View GitHub Profile
@adamw
adamw / t4.scala
Created September 19, 2019 16:11
def insertPerson(p: Person): ConnectionIO[Int] =
sql"INSERT INTO persons(name, age) VALUES(${p.name}, ${p.age})".update.run
def countPersons: ConnectionIO[Int] =
sql"SELECT COUNT(*) FROM persons".query[Int].unique
val insertAndCount =
insertPerson(Person("Pedro", 81)).flatMap(_ => countPersons)
@adamw
adamw / t3.scala
Last active September 20, 2019 06:16
val transactor: Transactor[IO] = ???
val result: IO[List[Person]] = personsQuery.transact(transactor)
val persons: List[Person] = result.unsafeRunSync()
@adamw
adamw / t2.scala
Last active September 20, 2019 06:16
// details on creating a transactor are omitted
val transactor: Transactor[IO] = ???
val result: IO[List[Person]] = personsQuery.transact(transactor)
@adamw
adamw / t1.scala
Last active September 19, 2019 16:10
import doobie._
import doobie.implicits._
case class Person(name: String, age: Int)
val personsQuery: ConnectionIO[List[Person]] =
sql"SELECT p.name, p.age FROM persons p".query[Person].to[List]
@adamw
adamw / workshop.md
Last active October 11, 2020 07:57

Functional Scala CRUD

We'll be adding a feature to Bootzooka, a skeleton Scala microservice/web application project.

This will involve using doobie, monix, tapir, http4s, circe and compile-time dependency injection.

If you'd like to follow along:

  1. make sure you have Java & sbt installed. You can use sdkman for Java and popular package manages for sbt (e.g. brew)
  2. git clone https://github.com/softwaremill/bootzooka.git
val TestConfig: Config = DefaultConfig
.modify(_.email.emailSendInterval)
.setTo(100.milliseconds)
def changePassword(userId: Id @@ User,
currentPassword: String,
newPassword: String): ConnectionIO[Unit] =
for {
user <- userOrNotFound(UserModel.findById(userId))
_ <- verifyPassword(user, currentPassword)
_ = logger.debug(s"Changing password for user: $userId")
_ <- UserModel.updatePassword(userId, User.hashPassword(newPassword))
} yield ()
object UserModel {
def findById(id: Id @@ User): ConnectionIO[Option[User]] = {
findBy(fr"id = $id")
}
def findByEmail(email: String @@ LowerCased): ConnectionIO[Option[User]] = {
findBy(fr"email_lowercase = $email")
}
private def findBy(by: Fragment): ConnectionIO[Option[User]] = {
import io.circe.Decoder
import com.softwaremill.bootzooka.infrastructure.Json._
case class Register_IN(login: String, email: String, password: String)
implicitly[Decoder[Register_IN]]
import com.softwaremill.tagging.@@
import io.circe.generic.AutoDerivation
import io.circe.java8.time.{JavaTimeDecoders, JavaTimeEncoders}
import io.circe.{Decoder, Encoder, Printer}
object Json extends AutoDerivation with JavaTimeDecoders with JavaTimeEncoders {
val noNullsPrinter: Printer = Printer.noSpaces.copy(dropNullValues = true)
implicit def taggedStringEncoder[U]: Encoder[String @@ U] =
Encoder.encodeString.asInstanceOf[Encoder[String @@ U]]