Skip to content

Commit

Permalink
Introduce finch-generic module
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-murzinov authored and vkostyukov committed Mar 31, 2017
1 parent 5584ddb commit 2a836c0
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 17 deletions.
12 changes: 9 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ lazy val finch = project.in(file("."))
"""
|import io.finch._
|import io.finch.circe._
|import io.finch.generic._
|import io.finch.items._
|import com.twitter.util.{Future, Await}
|import com.twitter.concurrent.AsyncStream
Expand All @@ -178,15 +179,20 @@ lazy val finch = project.in(file("."))
"io.spray" %% "spray-json" % sprayVersion
))
.aggregate(
core, argonaut, jackson, json4s, circe, playjson, sprayjson, benchmarks, test, jsonTest, oauth2,
examples, sse
core, generic, argonaut, jackson, json4s, circe, playjson, sprayjson, benchmarks, test, jsonTest,
oauth2, examples, sse
)
.dependsOn(core, circe)
.dependsOn(core, generic, circe)

lazy val core = project
.settings(moduleName := "finch-core")
.settings(allSettings)

lazy val generic = project
.settings(moduleName := "finch-generic")
.settings(allSettings)
.dependsOn(core % "compile->compile;test->test")

lazy val test = project
.settings(moduleName := "finch-test")
.settings(allSettings)
Expand Down
12 changes: 0 additions & 12 deletions core/src/main/scala/io/finch/Endpoint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -582,18 +582,6 @@ object Endpoint {
}
}

class GenericDerivation[A] {
def fromParams[Repr <: HList](implicit
gen: LabelledGeneric.Aux[A, Repr],
fp: FromParams[Repr]
): Endpoint[A] = fp.endpoint.map(gen.from)
}

/**
* Generically derive a very basic instance of [[Endpoint]] for a given type `A`.
*/
def derive[A]: GenericDerivation[A] = new GenericDerivation[A]

implicit val endpointInstance: Alternative[Endpoint] = new Alternative[Endpoint] {
final override def ap[A, B](ff: Endpoint[A => B])(fa: Endpoint[A]): Endpoint[B] =
ff.productWith(fa)((f, a) => f(a))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.finch.internal
package io.finch.generic

import scala.reflect.ClassTag

Expand Down Expand Up @@ -32,7 +32,7 @@ object FromParams {
}
}

private[internal] object Extractor extends Poly1 {
private[generic] object Extractor extends Poly1 {

implicit def optionalExtractor[V](implicit
dh: DecodeEntity[V],
Expand Down
11 changes: 11 additions & 0 deletions generic/src/main/scala/io/finch/generic/GenericDerivation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.finch.generic

import io.finch._
import shapeless._

final class GenericDerivation[A] {
def fromParams[Repr <: HList](implicit
gen: LabelledGeneric.Aux[A, Repr],
fp: FromParams[Repr]
): Endpoint[A] = fp.endpoint.map(gen.from)
}
8 changes: 8 additions & 0 deletions generic/src/main/scala/io/finch/generic/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.finch

package object generic {
/**
* Generically derive a very basic instance of [[Endpoint]] for a given type `A`.
*/
def deriveEndpoint[A]: GenericDerivation[A] = new GenericDerivation[A]
}
36 changes: 36 additions & 0 deletions generic/src/test/scala/io/finch/generic/DerivedEndpointLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.finch

import cats.Eq
import cats.instances.AllInstances
import cats.laws._
import cats.laws.discipline._
import org.scalacheck.{Arbitrary, Prop}
import org.typelevel.discipline.Laws

trait DerivedEndpointLaws[A] extends Laws with MissingInstances with AllInstances {

def endpoint: Endpoint[A]
def toParams: A => Seq[(String, String)]

def roundTrip(a: A): IsEq[A] = {
val i = Input.get("/", toParams(a): _*)
endpoint(i).awaitValueUnsafe().get <-> a
}

def evaluating(implicit A: Arbitrary[A], eq: Eq[A]): RuleSet =
new DefaultRuleSet(
name = "evaluating",
parent = None,
"roundTrip" -> Prop.forAll { (a: A) => roundTrip(a) }
)
}

object DerivedEndpointLaws {
def apply[A](
e: Endpoint[A],
tp: A => Seq[(String, String)]
): DerivedEndpointLaws[A] = new DerivedEndpointLaws[A] {
val endpoint: Endpoint[A] = e
val toParams = tp
}
}
28 changes: 28 additions & 0 deletions generic/src/test/scala/io/finch/generic/GenericSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.finch.generic

import cats.kernel.Eq
import io.finch._
import org.scalacheck.Arbitrary

class GenericSpec extends FinchSpec {

behavior of "generic"

case class Foo(a: String, b: Int)

val e: Endpoint[Foo] = deriveEndpoint[Foo].fromParams

implicit val eq: Eq[Foo] = Eq.fromUniversalEquals

implicit val arbitraryFoo: Arbitrary[Foo] = Arbitrary(for {
s <- Arbitrary.arbitrary[String]
i <- Arbitrary.arbitrary[Int]
} yield Foo(s, i))

val f: Foo => Seq[(String, String)] = foo => Seq(
("a" -> foo.a),
("b" -> foo.b.toString)
)

checkAll("DerivedEndpoint[Foo]", DerivedEndpointLaws[Foo](e, f).evaluating)
}

0 comments on commit 2a836c0

Please sign in to comment.