Skip to content

Commit

Permalink
Adapted to new package and some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
raulraja committed Jul 6, 2016
1 parent 300cfdd commit 307849a
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 38 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Expand Up @@ -5,7 +5,7 @@ lazy val fetch = (project in file("."))
organization := "org.scala-exercises", organization := "org.scala-exercises",
name := "exercises-fetch", name := "exercises-fetch",
scalaVersion := "2.11.7", scalaVersion := "2.11.7",
version := "0.1.1", version := "0.2.1-SNAPSHOT",
resolvers ++= Seq( resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"), Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases") Resolver.sonatypeRepo("releases")
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
@@ -1,2 +1,2 @@
addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.1.1", "0.13", "2.10") addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.2.1-SNAPSHOT", "0.13", "2.10")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
4 changes: 3 additions & 1 deletion src/main/scala/fetchlib/CachingSection.scala
Expand Up @@ -11,6 +11,8 @@ import cats.std.list._
import cats.syntax.cartesian._ import cats.syntax.cartesian._
import cats.syntax.traverse._ import cats.syntax.traverse._


import org.scalaexercises.definitions._

/** /**
* = Caching = * = Caching =
* *
Expand All @@ -20,7 +22,7 @@ import cats.syntax.traverse._
* *
* @param name caching * @param name caching
*/ */
object CachingSection extends FlatSpec with Matchers with exercise.Section { object CachingSection extends FlatSpec with Matchers with Section {


import FetchTutorialHelper._ import FetchTutorialHelper._


Expand Down
5 changes: 4 additions & 1 deletion src/main/scala/fetchlib/CatsSection.scala
Expand Up @@ -9,6 +9,9 @@ import fetch.unsafe.implicits._
import fetch.syntax._ import fetch.syntax._
import scala.util.Try import scala.util.Try



import org.scalaexercises.definitions._

/** /**
* = cats = * = cats =
* *
Expand All @@ -25,7 +28,7 @@ import scala.util.Try
* *
* @param name cats * @param name cats
*/ */
object CatsSection extends FlatSpec with Matchers with exercise.Section { object CatsSection extends FlatSpec with Matchers with Section {


/** /**
* = Applicative = * = Applicative =
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/fetchlib/ConcurrencyMonadsSection.scala
Expand Up @@ -8,6 +8,8 @@ import cats._
import fetch.syntax._ import fetch.syntax._
import scala.util.Try import scala.util.Try


import org.scalaexercises.definitions._

/** /**
* = Concurrency monads = * = Concurrency monads =
* *
Expand Down Expand Up @@ -39,7 +41,7 @@ import scala.util.Try
* *
* @param name concurrency_monads * @param name concurrency_monads
*/ */
object ConcurrencyMonadsSection extends FlatSpec with Matchers with exercise.Section { object ConcurrencyMonadsSection extends FlatSpec with Matchers with Section {


import FetchTutorialHelper._ import FetchTutorialHelper._


Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/fetchlib/ErrorHandlingSection.scala
Expand Up @@ -9,6 +9,8 @@ import fetch.unsafe.implicits._
import fetch.syntax._ import fetch.syntax._
import scala.util.Try import scala.util.Try


import org.scalaexercises.definitions._

/** /**
* *
* = Error handling = * = Error handling =
Expand All @@ -18,7 +20,7 @@ import scala.util.Try
* *
* @param name error_handling * @param name error_handling
*/ */
object ErrorHandlingSection extends FlatSpec with Matchers with exercise.Section { object ErrorHandlingSection extends FlatSpec with Matchers with Section {


import FetchTutorialHelper._ import FetchTutorialHelper._


Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/fetchlib/FetchLibrary.scala
@@ -1,10 +1,12 @@
package fetchlib package fetchlib


import org.scalaexercises.definitions._

/** Fetch is a library for making access to data both simple & efficient. /** Fetch is a library for making access to data both simple & efficient.
* *
* @param name fetch * @param name fetch
*/ */
object FetchLibrary extends exercise.Library { object FetchLibrary extends Library {
override def owner = "scala-exercises" override def owner = "scala-exercises"
override def repository = "exercises-fetch" override def repository = "exercises-fetch"


Expand Down
23 changes: 8 additions & 15 deletions src/main/scala/fetchlib/FetchTutorialHelper.scala
Expand Up @@ -8,14 +8,6 @@ object FetchTutorialHelper {
type UserId = Int type UserId = Int
case class User(id: UserId, username: String) case class User(id: UserId, username: String)


def latency[A](result: A, msg: String) = {
val id = Thread.currentThread.getId
println(s"~~> [$id] $msg")
Thread.sleep(100)
println(s"<~~ [$id] $msg")
result
}

import cats.data.NonEmptyList import cats.data.NonEmptyList


val userDatabase: Map[UserId, User] = Map( val userDatabase: Map[UserId, User] = Map(
Expand All @@ -28,12 +20,12 @@ object FetchTutorialHelper {
implicit object UserSource extends DataSource[UserId, User] { implicit object UserSource extends DataSource[UserId, User] {
override def fetchOne(id: UserId): Query[Option[User]] = { override def fetchOne(id: UserId): Query[Option[User]] = {
Query.sync({ Query.sync({
latency(userDatabase.get(id), s"One User $id") userDatabase.get(id)
}) })
} }
override def fetchMany(ids: NonEmptyList[UserId]): Query[Map[UserId, User]] = { override def fetchMany(ids: NonEmptyList[UserId]): Query[Map[UserId, User]] = {
Query.sync({ Query.sync({
latency(userDatabase.filterKeys(ids.unwrap.contains), s"Many Users $ids") userDatabase.filterKeys(ids.unwrap.contains)
}) })
} }
} }
Expand All @@ -52,12 +44,12 @@ object FetchTutorialHelper {
implicit object PostSource extends DataSource[PostId, Post] { implicit object PostSource extends DataSource[PostId, Post] {
override def fetchOne(id: PostId): Query[Option[Post]] = { override def fetchOne(id: PostId): Query[Option[Post]] = {
Query.sync({ Query.sync({
latency(postDatabase.get(id), s"One Post $id") postDatabase.get(id)
}) })
} }
override def fetchMany(ids: NonEmptyList[PostId]): Query[Map[PostId, Post]] = { override def fetchMany(ids: NonEmptyList[PostId]): Query[Map[PostId, Post]] = {
Query.sync({ Query.sync({
latency(postDatabase.filterKeys(ids.unwrap.contains), s"Many Posts $ids") postDatabase.filterKeys(ids.unwrap.contains)
}) })
} }
} }
Expand All @@ -72,13 +64,13 @@ object FetchTutorialHelper {
override def fetchOne(id: Post): Query[Option[PostTopic]] = { override def fetchOne(id: Post): Query[Option[PostTopic]] = {
Query.sync({ Query.sync({
val topic = if (id.id % 2 == 0) "monad" else "applicative" val topic = if (id.id % 2 == 0) "monad" else "applicative"
latency(Option(topic), s"One Post Topic $id") Option(topic)
}) })
} }
override def fetchMany(ids: NonEmptyList[Post]): Query[Map[Post, PostTopic]] = { override def fetchMany(ids: NonEmptyList[Post]): Query[Map[Post, PostTopic]] = {
Query.sync({ Query.sync({
val result = ids.unwrap.map(id => (id, if (id.id % 2 == 0) "monad" else "applicative")).toMap val result = ids.unwrap.map(id => (id, if (id.id % 2 == 0) "monad" else "applicative")).toMap
latency(result, s"Many Post Topics $ids") result
}) })
} }
} }
Expand All @@ -97,6 +89,7 @@ object FetchTutorialHelper {
val fetchError: Fetch[User] = (new Exception("Oh noes")).fetch val fetchError: Fetch[User] = (new Exception("Oh noes")).fetch


import cats.syntax.cartesian._ import cats.syntax.cartesian._
import cats.syntax.traverse._


val postsByAuthor: Fetch[List[Post]] = for { val postsByAuthor: Fetch[List[Post]] = for {
posts <- List(1, 2).traverse(getPost) posts <- List(1, 2).traverse(getPost)
Expand All @@ -112,11 +105,11 @@ object FetchTutorialHelper {


val homePage = (postsByAuthor |@| postTopics).tupled val homePage = (postsByAuthor |@| postTopics).tupled


import cats.{Eval, Now, Later, Always}
import monix.eval.Task import monix.eval.Task


import monix.execution.Cancelable import monix.execution.Cancelable
import scala.concurrent.duration._ import scala.concurrent.duration._
import fetch.monixTask.implicits._


def queryToTask[A](q: Query[A]): Task[A] = q match { def queryToTask[A](q: Query[A]): Task[A] = q match {
case Sync(e) => evalToTask(e) case Sync(e) => evalToTask(e)
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/fetchlib/SyntaxSection.scala
Expand Up @@ -9,12 +9,14 @@ import fetch.unsafe.implicits._
import fetch.syntax._ import fetch.syntax._
import scala.util.Try import scala.util.Try


import org.scalaexercises.definitions._

/** /**
* = Syntax = * = Syntax =
* *
* @param name syntax * @param name syntax
*/ */
object SyntaxSection extends FlatSpec with Matchers with exercise.Section { object SyntaxSection extends FlatSpec with Matchers with Section {


import FetchTutorialHelper._ import FetchTutorialHelper._


Expand Down
Expand Up @@ -10,6 +10,9 @@ import fetch.syntax._
import cats.std.list._ import cats.std.list._
import cats.syntax.cartesian._ import cats.syntax.cartesian._
import cats.syntax.traverse._ import cats.syntax.traverse._

import org.scalaexercises.definitions._

/** /**
* = Introduction = * = Introduction =
* *
Expand Down Expand Up @@ -72,17 +75,6 @@ import cats.syntax.traverse._
* case class User(id: UserId, username: String) * case class User(id: UserId, username: String)
* }}} * }}}
* *
* We'll simulate unpredictable latency with this function.
*
* {{{
* def latency[A](result: A, msg: String) = {
* val id = Thread.currentThread.getId
* println(s"~~> [$id] $msg")
* Thread.sleep(100)
* println(s"<~~ [$id] $msg")
* result
* }
* }}}
* *
* And now we're ready to write our user data source; we'll emulate a database with an in-memory map. * And now we're ready to write our user data source; we'll emulate a database with an in-memory map.
* *
Expand All @@ -102,12 +94,12 @@ import cats.syntax.traverse._
* implicit object UserSource extends DataSource[UserId, User]{ * implicit object UserSource extends DataSource[UserId, User]{
* override def fetchOne(id: UserId): Query[Option[User]] = { * override def fetchOne(id: UserId): Query[Option[User]] = {
* Query.sync({ * Query.sync({
* latency(userDatabase.get(id), s"One User $id") * userDatabase.get(id)
* }) * })
* } * }
* override def fetchMany(ids: NonEmptyList[UserId]): Query[Map[UserId, User]] = { * override def fetchMany(ids: NonEmptyList[UserId]): Query[Map[UserId, User]] = {
* Query.sync({ * Query.sync({
* latency(userDatabase.filterKeys(ids.unwrap.contains), s"Many Users $ids") * userDatabase.filterKeys(ids.unwrap.contains)
* }) * })
* } * }
* } * }
Expand All @@ -122,7 +114,7 @@ import cats.syntax.traverse._
* *
* @param name usage * @param name usage
*/ */
object UsageSection extends FlatSpec with Matchers with exercise.Section { object UsageSection extends FlatSpec with Matchers with Section {


import FetchTutorialHelper._ import FetchTutorialHelper._


Expand Down Expand Up @@ -164,7 +156,6 @@ object UsageSection extends FlatSpec with Matchers with exercise.Section {
*/ */
def creatingAndRunning(res0: User) = { def creatingAndRunning(res0: User) = {
val fetchUser: Fetch[User] = getUser(1) val fetchUser: Fetch[User] = getUser(1)

fetchUser.runA[Id] should be(res0) fetchUser.runA[Id] should be(res0)
} }


Expand Down
56 changes: 56 additions & 0 deletions src/test/scala/exercises/Test.scala
@@ -0,0 +1,56 @@
package exercises

import cats.data.Xor

import shapeless._
import shapeless.ops.function._

import org.scalacheck.{ Prop, Arbitrary }
import org.scalacheck.Gen
import Prop.forAll

import org.scalatest.Spec
import org.scalatest.exceptions._
import org.scalatest.prop.Checkers

import org.scalacheck.Shapeless._

object Test {

def testSuccess[F, R, L <: HList](method: F, answer: L)(
implicit
A: Arbitrary[L],
fntop: FnToProduct.Aux[F, L R]
): Prop = {
val rightGen = genRightAnswer(answer)
val rightProp = forAll(rightGen)({ p

val result = Xor.catchOnly[GeneratorDrivenPropertyCheckFailedException]({ fntop(method)(p) })
result match {
case Xor.Left(exc) exc.cause match {
case Some(originalException) throw originalException
case _ false
}
case _ true
}
})

val wrongGen = genWrongAnswer(answer)
val wrongProp = forAll(wrongGen)({ p
Xor.catchNonFatal({ fntop(method)(p) }).isLeft
})

Prop.all(rightProp, wrongProp)
}

def genRightAnswer[L <: HList](answer: L): Gen[L] = {
Gen.const(answer)
}

def genWrongAnswer[L <: HList](l: L)(
implicit
A: Arbitrary[L]
): Gen[L] = {
A.arbitrary.suchThat(_ != l)
}
}
33 changes: 33 additions & 0 deletions src/test/scala/exercises/fetchlib/QuickStart.scala
@@ -0,0 +1,33 @@
package exercises

import fetchlib._
import shapeless.HNil

import org.scalatest.Spec
import org.scalatest.prop.Checkers

import org.scalacheck.Shapeless._

class QuickStartSpec extends Spec with Checkers {

import FetchTutorialHelper._
import UsageSection._
import Test._

def `Creating And Running` =
check(testSuccess(creatingAndRunning _, userDatabase(1) :: HNil))

def `Sequencing Strategy` =
check(testSuccess(sequencing _, (userDatabase(1), userDatabase(2)) :: 2 :: HNil))

def `Batching Strategy` =
check(testSuccess(batching _, (userDatabase(1), userDatabase(2)) :: 1 :: HNil))

def `Deduplication Strategy` =
check(testSuccess(deduplication _, (userDatabase(1), userDatabase(1)) :: 1 :: HNil))

def `Caching Strategy` =
check(testSuccess(caching _, (userDatabase(1), userDatabase(1)) :: 1 :: HNil))


}

0 comments on commit 307849a

Please sign in to comment.