• Stars
    star
    112
  • Rank 312,240 (Top 7 %)
  • Language
    Scala
  • License
    Other
  • Created about 11 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Small Slick library for type-safe id handling

Scala Slick type-safe ids

Join the chat at https://gitter.im/VirtusLab/unicorn Coverage Status

Slick (the Scala Language-Integrated Connection Kit) is a framework for type-safe, composable data access in Scala. This library adds tools to use type-safe IDs for your classes so you can no longer join on bad id field or mess up order of fields in mappings. It also provides a way to create data access layer with methods (like querying all, querying by id, saving or deleting) for all classes with such IDs in just 4 lines of code.

Idea for type-safe ids was derived from Slick creator's presentation on ScalaDays 2013.

This library is used in Advanced play-slick Typesafe Activator template.

Unicorn is Open Source under Apache 2.0 license.

Contributors

Feel free to use it, test it and to contribute! For some helpful tips'n'tricks, see contribution guide.

Getting unicorn

For core latest version (Scala 2.12.x and Slick 3.3.x) use:

libraryDependencies += "org.virtuslab" %% "unicorn-core" % "1.3.3"

For play version (Scala 2.12.x, Slick 3.3.x, Play 2.7.x):

libraryDependencies += "org.virtuslab" %% "unicorn-play" % "1.3.3"

Or see our Maven repository.

For Slick 3.3.x and play 2.7 see version 1.3.3

For Slick 3.2.x and play 2.6 see version 1.3.2

For Slick 3.2.x and play 2.5 see version 1.2.x

For Slick 3.1.x and play 2.5 see version 1.1.x

For Slick 3.1.x and play 2.4 see version 1.0.x

For Slick 3.0.x see version 0.7.x

For Slick 2.1.x see version 0.6.x

For Slick 2.0.x see version 0.5.x.

For Slick 1.x see version 0.4.x.

Migration from older versions

See our migration guide.

Play Examples

From version 0.5.0 forward dependency on Play! framework and play-slick library is no longer necessary.

If you are using Play! anyway, examples below show how to make use of unicorn then.

Defining entities

package model

import org.virtuslab.unicorn.{BaseId, WithId}
import org.virtuslab.unicorn.LongUnicornPlayIdentifiers._

/** Id class for type-safe joins and queries. */
case class UserId(id: Long) extends AnyVal with BaseId[Long]

/** Companion object for id class, extends IdCompanion
  * and brings all required implicits to scope when needed.
  */
object UserId extends IdCompanion[UserId]

/** User entity.
  *
  * @param id user id
  * @param email user email address
  * @param lastName lastName
  * @param firstName firstName
  */
case class UserRow(id: Option[UserId],
                   email: String,
                   firstName: String,
                   lastName: String) extends WithId[Long, UserId]

Defining composable repositories

package repositories
/**
  * A place for all objects directly connected with database.
  *
  * Put your user queries here.
  * Having them in separate in this trait keeps `UserRepository` neat and tidy.
  */
trait UserBaseRepositoryComponent {
  self: UnicornWrapper[Long] =>
  import unicorn._
  import unicorn.driver.api._


  /** Table definition for users. */
  class Users(tag: Tag) extends IdTable[UserId, UserRow](tag, "USERS") {

    /** By definition id column is inserted as lowercase 'id',
      * if you want to change it, here is your setting.
      */
    protected override val idColumnName = "ID"

    def email = column[String]("EMAIL")

    def firstName = column[String]("FIRST_NAME")

    def lastName = column[String]("LAST_NAME")

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

  }

  class UserBaseRepository
    extends BaseIdRepository[UserId, UserRow, Users](TableQuery[Users])

  val userBaseRepository = new UserBaseRepository

}
@Singleton()
class UserRepository @Inject() (val unicorn: LongUnicornPlayJDBC)
  extends UserBaseRepositoryComponent with UnicornWrapper[Long] {

  import unicorn.driver.api._
  
  def save(user: UserRow): DBIO[UserId] = {
    userBaseRepository.save(user)
  }
}

Usage

package repositories

import model.UserRow

import scala.concurrent.ExecutionContext.Implicits.global

class UsersRepositoryTest extends BasePlayTest with UserBaseRepositoryComponent {

  "Users Repository" should "save and query users" in runWithRollback {

    val user = UserRow(None, "[email protected]", "Krzysztof", "Nowak")
    val action = for {
      _ <- userBaseRepository.create
      userId <- userBaseRepository.save(user)
      userOpt <- userBaseRepository.findById(userId)
    } yield userOpt

    action.map { userOpt =>
      userOpt.map(_.email) shouldEqual Some(user.email)
      userOpt.map(_.firstName) shouldEqual Some(user.firstName)
      userOpt.map(_.lastName) shouldEqual Some(user.lastName)
      userOpt.flatMap(_.id) should not be (None)
    }
  }
}

Core Examples

If you do not want to include Play! but still want to use unicorn, unicorn-core will make it available for you.

Preparing Unicorn to work

First you have to bake your own cake to provide unicorn with proper driver (in example case H2), as also build new object for Long ID support in entities:

package infra

object LongUnicornIdentifiers extends Identifiers[Long] {
  override def ordering: Ordering[Long] = implicitly[Ordering[Long]]

  override type IdCompanion[Id <: BaseId[Long]] = CoreCompanion[Id]
}

object Unicorn
    extends LongUnicornCore
    with HasJdbcDriver {

  override lazy val driver = H2Driver
}

Then you can use that cake to import driver and types provided by unicorn as shown in next sections.

Defining entities

package model

import infra.LongUnicornIdentifiers._
import infra.Unicorn.driver.api._
import slick.lifted.Tag

/** Id class for type-safe joins and queries. */
case class UserId(id: Long) extends AnyVal with BaseId[Long]

/** Companion object for id class, extends IdMapping
  * and brings all required implicits to scope when needed.
  */
object UserId extends IdCompanion[UserId]

/** User entity.  */
case class UserRow(id: Option[UserId],
                email: String,
                firstName: String,
                lastName: String) extends WithId[Long, UserId]

/** Table definition for users. */
class Users(tag: Tag) extends IdTable[UserId, UserRow](tag, "USERS") {

  // use this property if you want to change name of `id` column to uppercase
  // you need this on H2 for example
  override val idColumnName = "ID"

  def email = column[String]("EMAIL")

  def firstName = column[String]("FIRST_NAME")

  def lastName = column[String]("LAST_NAME")

  override def * = (id.?, email, firstName, lastName) <> (UserRow.tupled, UserRow.unapply)
}

Defining repositories

package repositories

import infra.Unicorn._
import infra.Unicorn.driver.api._
import model._

/**
 * Repository for users.
 *
 * It brings all base repository methods with it from [[BaseIdRepository]], but you can add yours as well.
 *
 * Use your favourite DI method to instantiate it in your application.
 */
class UsersRepository extends BaseIdRepository[UserId, UserRow, Users](TableQuery[Users])

Usage

package repositories

import model.UserRow
import scala.concurrent.ExecutionContext.Implicits.global


class UsersRepositoryTest extends BaseTest[Long] {

  val usersRepository: UsersRepository = new UsersRepository

  "Users Service" should "save and query users" in runWithRollback {
    val user = UserRow(None, "[email protected]", "Krzysztof", "Nowak")

    val actions = for {
      _ <- usersRepository.create
      userId <- usersRepository.save(user)
      user <- usersRepository.findById(userId)
    } yield user

    actions map { userOpt =>
      userOpt shouldBe defined

      userOpt.value should have(
        'email(user.email),
        'firstName(user.firstName),
        'lastName(user.lastName)
      )
      userOpt.value.id shouldBe defined
    }
  }
}

Defining custom underlying type

All reviews examples used Long as underlying Id type. From version 0.6.0 there is possibility to define own.

Let's use String as our type for id. So we should bake unicorn with String parametrization.

Play example

@Singleton()
class StringUnicornPlay @Inject() (databaseConfigProvider: DatabaseConfigProvider)
  extends UnicornPlay[String](databaseConfigProvider.get[JdbcProfile])


object StringUnicornPlayIdentifiers extends PlayIdentifiersImpl[String] {
  override val ordering: Ordering[String] = implicitly[Ordering[String]]
  override type IdCompanion[Id <: BaseId[String]] = PlayCompanion[Id]
}

Core example

object StringUnicornIdentifiers extends Identifiers[String] {
  override def ordering: Ordering[String] = implicitly[Ordering[String]]

  override type IdCompanion[Id <: BaseId[Long]] = CoreCompanion[Id]
}

object Unicorn
    extends UnicornCore[String]
    with HasJdbcDriver {

  override lazy val driver = H2Driver
}

Usage is same as in Long example. Main difference is that you should import classes from self-baked cake. The only concern is that id is auto-increment so we can't use arbitrary type there. We plan to solve this problem in next versions.

More Repositories

1

git-machete

Probably the sharpest git repository organizer & rebase/merge workflow automation tool you've ever seen
Python
906
star
2

scala-cli

Scala CLI is a command-line tool to interact with the Scala language. It lets you compile, run, test, and package your Scala code (and more!)
Scala
544
star
3

graphbuddy

Graph Buddy helps you to understand the code better
HTML
149
star
4

render

Universal data-driven template for generating textual output, as a static binary and a library
Go
140
star
5

iskra

Typesafe wrapper for Apache Spark DataFrame API
Scala
136
star
6

git-machete-intellij-plugin

Probably the sharpest git repository organizer & rebase/merge workflow automation tool you've ever seen
Java
134
star
7

besom

Besom - a Pulumi SDK for Scala. Also, incidentally, a broom made of twigs tied round a stick. Brooms and besoms are used for protection, to ward off evil spirits, and cleansing of ritual spaces.
Scala
124
star
8

pandas-stubs

Pandas type stubs. Helps you type-check your code.
Python
120
star
9

scala-yaml

Scala
92
star
10

Inkuire

Hoogle-like searches for Scala 3 and Kotlin
Scala
91
star
11

avocADO

Safe compile-time parallelization of for-comprehensions for Scala 3
Scala
87
star
12

jenkins-operator

Kubernetes native Jenkins Operator, moved to https://github.com/jenkinsci/kubernetes-operator
Go
82
star
13

bazel-steward

A bot to keep Bazel dependencies up to date
Kotlin
60
star
14

pretty-stacktraces

Scala
58
star
15

tetrisly-react

Tetrisly offers user-friendly components designed for effortless integration. Plus, it's fully compatible with Tetrisly for Figma with a seamless design and development experience in mind.
TypeScript
41
star
16

crypt

Universal cryptographic tool with AWS KMS, GCP KMS, GnuPG and Azure Key Vault support
Go
33
star
17

infrastructure-as-types

Infrastructure as Types - modern infrastructure declaration and deployment toolkit
Scala
26
star
18

ide-probe

Scala
26
star
19

beholder

Small slick lib for create views on on database
Scala
26
star
20

akka-serialization-helper

Serialization toolbox for Akka messages, events and persistent state that helps achieve compile-time guarantee on serializability. No more errors in the runtime!
Scala
26
star
21

activator-play-advanced-slick

Typesafe Activator template for advanced play-slick project
HTML
20
star
22

kubedrainer

Simple Kubernetes Node Drainer
Go
20
star
23

contextbuddy

Platform documentation
CSS
16
star
24

strapi-plugin-content-manager-extension-hierarchical

strapi-plugin-content-manager-extension-hierarchical
JavaScript
15
star
25

community-build3

Scala
14
star
26

scala-packager

Scala
13
star
27

pekko-serialization-helper

Serialization toolbox for Pekko messages, events and persistent state that helps achieve compile-time guarantee on serializability. No more errors in the runtime!
Scala
13
star
28

using_directives

Java
12
star
29

coursier-m1

A small repo to release coursier using self-hosted Mac M1 runner
Shell
11
star
30

vss

Scala
10
star
31

scg-cli

scg-cli is a CLI tool for Semantic Code Graph analysis
Scala
9
star
32

ReactSphere-reactive-beyond-hype

Repo for presentation on ReactSphere: Reactive beyond hype
HCL
8
star
33

akka-workshop-client

Base code for akka workshop.
Scala
8
star
34

scala-compose

Scala
7
star
35

scala.today

Scala
7
star
36

scala-snippet-checker

TypeScript
6
star
37

codetale

CodeTale - documentation & issue tracking
Dockerfile
6
star
38

genesis

Common sbt settings for sbt-based projects
Scala
5
star
39

pyspark-workshop

HTML
5
star
40

data_lake_navigation_atlas

Code for blogpost Navigation in the data lake using Atlas
Scala
5
star
41

ddd-public-materials

All public materials for community prepared by The DDD guild from Virtuslab
Kotlin
5
star
42

kleisli-examples

Examples from blog post on Kleisli arrows
Scala
5
star
43

tips

CSS
4
star
44

go-extended

Things missing or not belonging in the standard library
Go
3
star
45

mesos-on-vagrant

Just a Vagrant file and Ansible playbook for deploying Mesos cluster for testing
3
star
46

scala-workshop

Scala
3
star
47

talk-scala-akka-play

Introductory talk to Scala, Akka and Play! framework
JavaScript
3
star
48

base-types-kt

Library with common types for Kotlin supporting domain-driven functional programming
Kotlin
3
star
49

Edison-BlinkOnboard

Example Akka system for blinking LED at Intel Edison
Scala
3
star
50

jira-stats

Exports some metrics from jira via REST api - currently calculated dev days per ticket
Go
2
star
51

runscope-agent

Containerized Runscope Agent (Dockerfile)
Makefile
2
star
52

homebrew-cloud

This repository contains a collection of Homebrew formulas.
Ruby
2
star
53

scala-cli-packages

Shell
2
star
54

spark_sql_under_the_hood

Code for blogpost: Spark SQL under the hood
Scala
2
star
55

homebrew-scala-cli

Ruby
1
star
56

scalacamp

ScalaCamp.pl site source code
HTML
1
star
57

homebrew-git-machete

1
star
58

scala-workshop-bootstrap

Shell
1
star
59

gpki

Git Public Key Infrastructure
Python
1
star
60

jenkins-operator-assets

Hosting Jenkins Operator assests like images or CSS files.
CSS
1
star
61

virtusity-workshop-graphql

TypeScript
1
star
62

dokka-site

Kotlin
1
star
63

kibana-rpm-packaging

1
star
64

kubectl-deploy

Simple kubectl plugin for rendering and applying Kubernetes manifests
Go
1
star
65

akka-http-kubernetes.g8

Scala
1
star
66

scala-cli.g8

Scala
1
star
67

homebrew-scala-experimental

Ruby
1
star
68

scg-scala

Scala compiler plugin for Semantic Code Graph generation
Scala
1
star
69

ide-probe-tests

Scala
1
star
70

Ariadne-Bootloader

A little less unfinished TFTP bootloader for Arduino Leonardo Ethernet
Arduino
1
star
71

ScalaTastiesScrapper

Scala
1
star
72

scala3-workshop

Scala
1
star
73

aws-cli

Yet Another Dockerized AWS CLI
Makefile
1
star
74

besom-ask-me

Scala
1
star
75

shuttlecraft

Scala
1
star