2017-11-17 5 views
2

아무도 셰이프리스를 사용하여이 테스트를 수행하는 방법을 알고 있지 않습니까? 나는 특정 인스턴스의 독립적 인 경우 클래스 A, 대한 JsonSchema 창출을하고 있어요로셰이프를 사용하여 유형 수준 필터링

package net.jtownson.swakka.jsonschema 

import org.scalatest.FlatSpec 
import org.scalatest.Matchers._ 

class OptionalFieldSpec extends FlatSpec { 

    case class A(i: Int, j: Option[Int]) 

    "an extractor of some kind" should "get the (non)optional fields from a case class" in { 

    extractNonOptionalFieldNames[A] shouldBe List("i") 

    extractOptionalFieldNames[A] shouldBe List("j") 

    } 

    def extractNonOptionalFieldNames[T <: Product](/* implicit typeclass instances? */): List[String] = ??? 

    def extractOptionalFieldNames[T <: Product]: List[String] = ??? 

} 

나는 더 런타임 A의 인스턴스 또는 그 상응하는 일반 의약품이 없습니다. 스키마에는 이 필요하며 비 필수 필드 목록 인 필드가 필요합니다. 예 : 이 같은

{ 
    "type" -> "object", 
    "required" -> ["i"], 
    "properties" -> { 
    "i" -> { 
     "type" -> "integer", 
     "format" -> "int32" 
    } 
    } 
} 
+0

당신이 원하는'LabelledGeneric'을 - 당신에게 유형 레벨 필드 이름의 증거뿐만 아니라 자신의 유형을 줄 것이다. 그 너머에, 그냥 그 HList를 필터링하려는 것 같습니다. – Alec

답변

3

뭔가 :

trait FieldNameExtractor[T] extends Serializable { 
    import shapeless.ops.hlist.{RightFolder, ToTraversable} 
    import shapeless.ops.record.Keys 
    import shapeless.{HList, HNil, LabelledGeneric, Poly2} 

    /** 
    * Extracts filtered field names for type [[T]], 
    * given a polymorphic function that acts as the type filter 
    */ 
    def extract[L <: HList, R <: HList, O <: HList](op: Poly2)(
     implicit lgen: LabelledGeneric.Aux[T, L], 
     folder: RightFolder.Aux[L, HNil.type, op.type, R], 
     keys: Keys.Aux[R, O], 
     traversable: ToTraversable.Aux[O, List, Symbol] 
): List[String] = { 
    val result = keys().to[List] 
    result.map(_.name) 
    } 
} 

object FieldNameExtractor { 
    def apply[T] = new FieldNameExtractor[T] {} 
} 

사용법 :

import org.scalatest.FlatSpec 
import org.scalatest.Matchers._ 

class Test extends FlatSpec { 
    /* type filters */ 
    import shapeless.{HList, Poly2} 
    import shapeless.labelled.KeyTag, shapeless.tag.Tagged 

    type FilterO[A, T] = Option[A] with KeyTag[Symbol with Tagged[T], Option[A]] 

    trait Ignore extends Poly2 { 
    implicit def default[A, L <: HList] = at[A, L]((_, l) => l) 
    } 
    trait Accept extends Poly2 { 
    implicit def default[A, L <: HList] = at[A, L](_ :: _) 
    } 

    object allOptions extends Ignore { 
    implicit def option[A, T, L <: HList] = at[FilterO[A, T], L](_ :: _) 
    } 
    object noOptions extends Accept { 
    implicit def option[A, T, L <: HList] = at[FilterO[A, T], L]((_, l) => l) 
    } 

    "an extractor of some kind" should "get the (non)optional fields from a case class" in { 
    case class A(i: Int, j: Option[Int], k: String) 

    val fne = FieldNameExtractor[A] 
    fne.extract(noOptions) shouldBe List("i", "k") // extractNonOptionalFieldNames 
    fne.extract(allOptions) shouldBe List("j")  // extractOptionalFieldNames 
    } 
} 
+0

아름다운. 감사. 사실, 당신이 게시 한 첫 번째 솔루션이 좋다고 생각했습니다. 몇 줄의 코드가 적었습니다. –

+0

나는 그것을 더 재사용 할 수 있고 아름답게 만들었다. 모든 "마법"은'extract' 메쏘드에서 발생합니다; 예 : 'extractOptionalFieldNames' 구현은 필요한 매개 변수를 전달합니다 : 사용자 정의 유형의 값과 필터 역할을하는 다형 함수 ('Poly' impl) –

+0

'A'의 런타임 인스턴스가 필요하지 않도록 수정되었습니다. –

2
여기

하나 개의 방법이 사용 유형 클래스 :

import shapeless._ 
import shapeless.labelled.FieldType 


trait OptionExtractor[A] { 
    type B <: HList 
} 

trait LowPriorityOptionExtractor { 

    implicit def hconsExtractor[K, V, T <: HList](implicit 
                 extractor: OptionExtractor[T]): 
    OptionExtractor.Aux[FieldType[K, V] :: T, extractor.B] = new OptionExtractor[FieldType[K, V] :: T] { 
    type B = extractor.B 
    } 

} 

object OptionExtractor extends LowPriorityOptionExtractor { 

    type Aux[A, B0 <: HList] = OptionExtractor[A] {type B = B0} 

    def apply[A](implicit extractor: OptionExtractor[A]): OptionExtractor.Aux[A, extractor.B] = extractor 

    implicit val hnilOptionExtractor: OptionExtractor.Aux[HNil, HNil] = new OptionExtractor[HNil] { 
    type B = HNil 
    } 

    implicit def hconsOptionExtractor[K, V, T <: HList](implicit extractor: OptionExtractor[T]): 
    OptionExtractor.Aux[FieldType[K, Option[V]] :: T, K :: extractor.B] = new OptionExtractor[FieldType[K, Option[V]] :: T] { 
    type B = K :: extractor.B 
    } 

} 

해야 할 몇 가지가있다 설명 :

  • A의 런타임 인스턴스가 없다고 언급 했으므로. 되돌리려는 유형 수준 표현은 무엇입니까? 이 솔루션에서 나는 방금 옵션 장치에 대한 증인으로 HList을 반환했습니다. 나는 List[String] 표현이 충분하지 않다고 생각하지 않는다. 왜냐하면 비 선택적 값을 필터링하면 아무것도하지 않는 것과 같은 타입을 가지기 때문이다.
  • 유형 클래스에는 우선 순위가 있으므로 필터링 옵션은 역순으로 동일한 옵션이됩니다.

이처럼 사용할 수 있습니다 :

case class A(i: Int, j: Option[Int], k: Option[Long]) 
val x = LabelledGeneric[A] 
type filteredType = OptionExtractor[x.Repr] 
//type B = Symbol with shapeless.tag.Tagged[String("j")] :: Symbol with shapeless.tag.Tagged[String("k")] :: shapeless.HNil 
+0

건배, Jamborta. (나는 당신의 솔루션을 upvoted하지만 아직 충분히 셀 수있는 거리의 신용을 가지고 있지 않다. 나는 더 많은 시니어 일 때 다시 돌아올 것이다 :-) –

관련 문제