Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ lazy val core = (project in file("core"))
)
)

lazy val catsEffectsIOModule = (project in file("modules/cats-effect-io"))
.settings(commonSettings)
.settings(
name := "query-cats-effect-io",
libraryDependencies ++= Seq(Dependencies.catsEffect)
)
.dependsOn(core % "test->test;compile->compile")

lazy val playSqlModule = (project in file("modules/play-sql"))
.settings(commonSettings)
.settings(
Expand Down Expand Up @@ -142,4 +150,10 @@ lazy val todoAppExample = (project in file("examples/todo-app"))

lazy val root: Project = project
.in(file("."))
.aggregate(core, playSqlModule, sampleAppExample, todoAppExample)
.aggregate(
core,
playSqlModule,
catsEffectsIOModule,
sampleAppExample,
todoAppExample
)
47 changes: 0 additions & 47 deletions core/src/main/scala/core/database/ComposeWithCompletion.scala

This file was deleted.

22 changes: 22 additions & 0 deletions core/src/main/scala/core/database/LiftAsync.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.zengularity.querymonad.core.database

// import scala.concurrent.{ExecutionContext, Future}
import scala.language.higherKinds

/**
* Heavily inspired from work done by @cchantep in Acolyte (see acolyte.reactivemongo.ComposeWithCompletion)
*/
trait LiftAsync[F[_], M[_], A] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semantic is always async ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes (but no in term of cats-effect's Async typeclass), since the essence of this typeclass is to wrap the computation in an async context (Future or IO). Maybe it can be confusing, so feel free to suggest any name which could express it better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Task ? Effect ?

type Outer

def apply[Resource](
loaner: WithResource[F, Resource],
f: Resource => M[A]
): F[Outer]
}

object LiftAsync {

type Aux[F[_], M[_], A, B] = LiftAsync[F, M, A] { type Outer = B }

}
21 changes: 10 additions & 11 deletions core/src/main/scala/core/database/QueryRunner.scala
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
package com.zengularity.querymonad.core.database

import scala.concurrent.Future
import scala.language.higherKinds

/**
* A class who can run a Query.
*/
sealed trait QueryRunner[Resource] {
sealed trait QueryRunner[F[_], Resource] {
def apply[M[_], T](query: QueryT[M, Resource, T])(
implicit compose: ComposeWithCompletion[M, T]
): Future[compose.Outer]
implicit lift: LiftAsync[F, M, T]
): F[lift.Outer]
}

object QueryRunner {
private class DefaultRunner[Resource](wr: WithResource[Resource])
extends QueryRunner[Resource] {
private class DefaultRunner[F[_], Resource](wr: WithResource[F, Resource])
extends QueryRunner[F, Resource] {
def apply[M[_], T](
query: QueryT[M, Resource, T]
)(implicit compose: ComposeWithCompletion[M, T]): Future[compose.Outer] =
compose(wr, query.run)
)(implicit lift: LiftAsync[F, M, T]): F[lift.Outer] =
lift(wr, query.run)
}

// Default factory
def apply[Resource](
wr: WithResource[Resource]
): QueryRunner[Resource] =
def apply[F[_], Resource](
wr: WithResource[F, Resource]
): QueryRunner[F, Resource] =
new DefaultRunner(wr)
}
7 changes: 4 additions & 3 deletions core/src/main/scala/core/database/WithResource.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.zengularity.querymonad.core.database

import scala.concurrent.Future
// import scala.concurrent.Future
import scala.language.higherKinds

trait WithResource[Resource] {
def apply[A](f: Resource => Future[A]): Future[A]
trait WithResource[F[_], Resource] {
def apply[A](f: Resource => F[A]): F[A]
}
38 changes: 38 additions & 0 deletions core/src/main/scala/module/future/LiftAsyncFuture.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.zengularity.querymonad.module.future

import scala.concurrent.{ExecutionContext, Future}
import scala.language.higherKinds

import com.zengularity.querymonad.core.database.LiftAsync

trait LiftAsyncFuture extends LowPriority {

implicit def futureOut[A]: LiftAsync.Aux[Future, Future, A, A] =
new LiftAsync[Future, Future, A] {
type Outer = A

def apply[In](
loaner: WithResourceF[In],
f: In => Future[A]
): Future[Outer] = loaner(f)

override val toString = "futureOut"
}

}

trait LowPriority { _: LiftAsyncFuture =>

implicit def pureOut[F[_], A](
implicit ec: ExecutionContext
): LiftAsync.Aux[Future, F, A, F[A]] =
new LiftAsync[Future, F, A] {
type Outer = F[A]

def apply[In](loaner: WithResourceF[In], f: In => F[A]): Future[Outer] =
loaner(r => Future(f(r)))

override val toString = "pureOut"
}

}
20 changes: 20 additions & 0 deletions core/src/main/scala/module/future/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.zengularity.querymonad.module

import scala.concurrent.Future

import com.zengularity.querymonad.core.database.{QueryRunner, WithResource}

package object future {

type WithResourceF[Resource] = WithResource[Future, Resource]

type QueryRunnerF[Resource] = QueryRunner[Future, Resource]

object QueryRunnerF {
def apply[Resource](wc: WithResourceF[Resource]): QueryRunnerF[Resource] =
QueryRunnerF[Resource](wc)
}

object implicits extends LiftAsyncFuture

}
21 changes: 16 additions & 5 deletions core/src/main/scala/module/sql/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.zengularity.querymonad.module

import java.sql.Connection

// import scala.concurrent.ExecutionContext
import scala.language.higherKinds
import scala.concurrent.Future

import cats.Applicative

Expand Down Expand Up @@ -60,13 +60,24 @@ package object sql {
type SqlQueryE[A, Err] = QueryE[Connection, A, Err]

// Query runner aliases
type WithSqlConnection = WithResource[Connection]
type WithSqlConnection[F[_]] = WithResource[F, Connection]

type SqlQueryRunner = QueryRunner[Connection]
type SqlQueryRunner[F[_]] = QueryRunner[F, Connection]

object SqlQueryRunner {
def apply(wc: WithSqlConnection): SqlQueryRunner =
QueryRunner[Connection](wc)
def apply[F[_]](wc: WithSqlConnection[F]): SqlQueryRunner[F] =
QueryRunner[F, Connection](wc)
}

object future {
type WithSqlConnectionF = WithSqlConnection[Future]

type SqlQueryRunnerF = SqlQueryRunner[Future]

object SqlQueryRunnerF {
def apply(wc: WithSqlConnectionF): SqlQueryRunnerF =
SqlQueryRunner(wc)
}
}

}
21 changes: 11 additions & 10 deletions core/src/test/scala/module/sql/SqlQueryRunnerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import org.specs2.mutable.Specification
import com.zengularity.querymonad.module.sql.{
SqlQuery,
SqlQueryRunner,
SqlQueryT,
WithSqlConnection
SqlQueryT
}
import com.zengularity.querymonad.module.future.implicits._
import com.zengularity.querymonad.module.sql.future.WithSqlConnectionF
import com.zengularity.querymonad.test.module.sql.models.{Material, Professor}
import com.zengularity.querymonad.test.module.sql.utils.SqlConnectionFactory

Expand All @@ -23,7 +24,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {
"SqlQueryRunner" should {
// execute lift Queries
"return integer value lift in Query using pure" in {
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(AcolyteQueryResult.Nil)
val runner = SqlQueryRunner(withSqlConnection)
val query = SqlQuery.pure(1)
Expand All @@ -32,7 +33,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {
}

"return optional value lift in Query using liftF" in {
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(AcolyteQueryResult.Nil)
val runner = SqlQueryRunner(withSqlConnection)
val query = SqlQueryT.liftF(Seq(1))
Expand All @@ -42,7 +43,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {

// execute single query
"retrieve professor with id 1" in {
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(Professor.resultSet)
val runner = SqlQueryRunner(withSqlConnection)
val result = runner(Professor.fetchProfessor(1)).map(_.get)
Expand All @@ -53,7 +54,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {
}

"retrieve material with id 1" in {
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(Material.resultSet)
val runner = SqlQueryRunner(withSqlConnection)
val result = runner(Material.fetchMaterial(1)).map(_.get)
Expand All @@ -64,7 +65,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {
}

"not retrieve professor with id 2" in {
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(AcolyteQueryResult.Nil)
val runner = SqlQueryRunner(withSqlConnection)
val query = for {
Expand All @@ -87,7 +88,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {
case _ =>
AcolyteQueryResult.Nil
}
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(handler)
val runner = SqlQueryRunner(withSqlConnection)
val query = for {
Expand All @@ -109,7 +110,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {
case _ =>
AcolyteQueryResult.Nil
}
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(handler)
val runner = SqlQueryRunner(withSqlConnection)
val query = for {
Expand All @@ -132,7 +133,7 @@ class SqlQueryRunnerSpec(implicit ee: ExecutionEnv) extends Specification {

val queryResult: AcolyteQueryResult =
(RowLists.rowList1(classOf[Int] -> "res").append(5))
val withSqlConnection: WithSqlConnection =
val withSqlConnection: WithSqlConnectionF =
SqlConnectionFactory.withSqlConnection(queryResult)
val runner = SqlQueryRunner(withSqlConnection)
val query =
Expand Down
10 changes: 5 additions & 5 deletions core/src/test/scala/module/sql/utils/SqlConnectionFactory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@ import acolyte.jdbc.{
ScalaCompositeHandler
}

import com.zengularity.querymonad.module.sql.WithSqlConnection
import com.zengularity.querymonad.module.sql.future.WithSqlConnectionF

object SqlConnectionFactory {

def withSqlConnection[A <: AcolyteQueryResult](
resultsSet: A
): WithSqlConnection =
new WithSqlConnection {
): WithSqlConnectionF =
new WithSqlConnectionF {
def apply[B](f: Connection => Future[B]): Future[B] =
AcolyteDSL.withQueryResult(resultsSet) { connection =>
f(connection).andThen { case _ => connection.close() }
}

}

def withSqlConnection(handler: ScalaCompositeHandler): WithSqlConnection =
new WithSqlConnection {
def withSqlConnection(handler: ScalaCompositeHandler): WithSqlConnectionF =
new WithSqlConnectionF {
def apply[B](f: Connection => Future[B]): Future[B] = {
val con = AcolyteDSL.connection(handler)
f(con).andThen { case _ => con.close() }
Expand Down
1 change: 1 addition & 0 deletions examples/sample-app/app/wiring/AppLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.zengularity.querymonad.module.sql.{
SqlQueryRunner,
SqlQueryT
}
import com.zengularity.querymonad.module.future.implicits._
import com.zengularity.querymonad.module.playsql.database.WithPlayTransaction

class AppComponents(context: Context)
Expand Down
6 changes: 4 additions & 2 deletions examples/todo-app/app/controller/TodoController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import play.api.libs.json.Json
import com.zengularity.querymonad.examples.todoapp.controller.model.AddTodoPayload
import com.zengularity.querymonad.examples.todoapp.model.{Todo, User}
import com.zengularity.querymonad.examples.todoapp.store.{TodoStore, UserStore}
import com.zengularity.querymonad.module.sql.{SqlQueryRunner, SqlQueryT}
import com.zengularity.querymonad.module.future.implicits._
import com.zengularity.querymonad.module.sql.SqlQueryT
import com.zengularity.querymonad.module.sql.future.SqlQueryRunnerF

class TodoController(
runner: SqlQueryRunner,
runner: SqlQueryRunnerF,
todoStore: TodoStore,
userStore: UserStore,
cc: ControllerComponents
Expand Down
Loading