From 0f4c231589c925b8a717f9f05ee20f08649c2baa Mon Sep 17 00:00:00 2001 From: j Date: Fri, 16 Sep 2016 16:35:52 -0700 Subject: [PATCH 1/2] add ensime files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 96ef862..21e7aba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ .idea/ +.ensime* From f22c681355b56dd226739ab4fc07bfe4e24aa8c3 Mon Sep 17 00:00:00 2001 From: j Date: Fri, 16 Sep 2016 16:36:35 -0700 Subject: [PATCH 2/2] implicit conversions for all jsonapiobject values --- .../implicits/AttributeConversions.scala | 18 ++---- .../JsonApiObjectValueConversions.scala | 42 ++++++++++++-- .../implicits/UnconvertibleTypeError.scala | 9 +++ .../org/zalando/jsonapi/model/package.scala | 9 +++ .../implicits/AttributeConversionsSpec.scala | 46 ++++++++++----- .../JsonApiObjectValueConversionsSpec.scala | 57 ++++++++++++++++--- 6 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 src/main/scala/org/zalando/jsonapi/model/implicits/UnconvertibleTypeError.scala diff --git a/src/main/scala/org/zalando/jsonapi/model/implicits/AttributeConversions.scala b/src/main/scala/org/zalando/jsonapi/model/implicits/AttributeConversions.scala index 2104d39..40222f6 100644 --- a/src/main/scala/org/zalando/jsonapi/model/implicits/AttributeConversions.scala +++ b/src/main/scala/org/zalando/jsonapi/model/implicits/AttributeConversions.scala @@ -6,17 +6,11 @@ import org.zalando.jsonapi.model.Attribute import org.zalando.jsonapi.model.implicits.JsonApiObjectValueConversions._ object AttributeConversions { - implicit def convertToStringAttribute(nameString: (String, String)) = Attribute(nameString._1, nameString._2) - implicit def convertToIntAttribute(nameInt: (String, Int)) = Attribute(nameInt._1, nameInt._2) - implicit def convertToLongAttribute(nameLong: (String, Long)) = Attribute(nameLong._1, nameLong._2) - implicit def convertToDoubleAttribute(nameDouble: (String, Double)) = Attribute(nameDouble._1, nameDouble._2) - implicit def convertToFloatAttribute(nameFloat: (String, Float)) = Attribute(nameFloat._1, nameFloat._2) - implicit def convertToBooleanAttribute(nameInt: (String, Boolean)) = Attribute(nameInt._1, nameInt._2) + implicit def convertPairToOptionalAttribute(pair: (String, Option[_])): Option[Attribute] = { + pair._2.map(Attribute(pair._1, _)) + } - implicit def convertToOptionalStringAttribute(nameString: (String, Option[String])) = nameString._2.map(nameString._1 -> _:Attribute) - implicit def convertToOptionalIntAttribute(nameInt: (String, Option[Int])) = nameInt._2.map(nameInt._1 -> _:Attribute) - implicit def convertToOptionalLongAttribute(nameLong: (String, Option[Long])) = nameLong._2.map(nameLong._1 -> _:Attribute) - implicit def convertToOptionalDoubleAttribute(nameDouble: (String, Option[Double])) = nameDouble._2.map(nameDouble._1 -> _:Attribute) - implicit def convertToOptionalFloatAttribute(nameFloat: (String, Option[Float])) = nameFloat._2.map(nameFloat._1 -> _:Attribute) - implicit def convertToOptionalBooleanAttribute(nameInt: (String, Option[Boolean])) = nameInt._2.map(nameInt._1 -> _:Attribute) + implicit def convertPairToAttribute(pair: (String, _)): Attribute = { + Attribute(pair._1, pair._2) + } } diff --git a/src/main/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversions.scala b/src/main/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversions.scala index 3d0b3a3..d170bd3 100644 --- a/src/main/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversions.scala +++ b/src/main/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversions.scala @@ -2,13 +2,43 @@ package org.zalando.jsonapi.model.implicits import scala.language.implicitConversions +import org.zalando.jsonapi.model.Attribute import org.zalando.jsonapi.model.JsonApiObject._ object JsonApiObjectValueConversions { - implicit def convertStringToStringValue(string: String) = StringValue(string) - implicit def convertIntToNumberValue(int: Int) = NumberValue(int) - implicit def convertLongToNumberValue(long: Long) = NumberValue(long) - implicit def convertDoubleToNumberValue(double: Double) = NumberValue(double) - implicit def convertFloatToNumberValue(float: Float) = NumberValue(float) - implicit def convertBooleanToBooleanValue(boolean: Boolean) = BooleanValue(boolean) + implicit def convertAnyToValue(any: Any): Value = { + any match { + case(string: String) ⇒ + StringValue(string) + case(int: Int) ⇒ + NumberValue(int) + case(long: Long) ⇒ + NumberValue(long) + case(double: Double) ⇒ + NumberValue(double) + case(float: Float) ⇒ + NumberValue(float) + case true ⇒ + TrueValue + case false ⇒ + FalseValue + case null ⇒ + NullValue + case value: Value ⇒ + value + case(map: Map[_,_]) ⇒ + JsObjectValue( + map.map { + case(name: String, value) ⇒ + Attribute(name, convertAnyToValue(value)) + case _ ⇒ + throw UnconvertibleTypeError("Maps must have string keys to be converted to JsonApiObject Values") + }.toList + ) + case(seq: Seq[_]) ⇒ + JsArrayValue(seq.map(convertAnyToValue)) + case _ ⇒ + throw UnconvertibleTypeError(any) + } + } } diff --git a/src/main/scala/org/zalando/jsonapi/model/implicits/UnconvertibleTypeError.scala b/src/main/scala/org/zalando/jsonapi/model/implicits/UnconvertibleTypeError.scala new file mode 100644 index 0000000..bdca1e0 --- /dev/null +++ b/src/main/scala/org/zalando/jsonapi/model/implicits/UnconvertibleTypeError.scala @@ -0,0 +1,9 @@ +package org.zalando.jsonapi.model.implicits + +case class UnconvertibleTypeError(msg: String) extends Exception(msg) + +object UnconvertibleTypeError { + def apply(any: Any): UnconvertibleTypeError = { + UnconvertibleTypeError(s"Can not convert ${any.getClass.getName} to JsonApiObject Value") + } +} diff --git a/src/main/scala/org/zalando/jsonapi/model/package.scala b/src/main/scala/org/zalando/jsonapi/model/package.scala index 56d45aa..fd3d911 100644 --- a/src/main/scala/org/zalando/jsonapi/model/package.scala +++ b/src/main/scala/org/zalando/jsonapi/model/package.scala @@ -203,6 +203,15 @@ package object model { */ case object NullValue extends Value + /** + * An attribute value that is true + */ + val TrueValue = BooleanValue(true) + + /** + * An attribute value that is false + */ + val FalseValue = BooleanValue(false) } /** diff --git a/src/test/scala/org/zalando/jsonapi/model/implicits/AttributeConversionsSpec.scala b/src/test/scala/org/zalando/jsonapi/model/implicits/AttributeConversionsSpec.scala index 2b96955..9797598 100644 --- a/src/test/scala/org/zalando/jsonapi/model/implicits/AttributeConversionsSpec.scala +++ b/src/test/scala/org/zalando/jsonapi/model/implicits/AttributeConversionsSpec.scala @@ -8,31 +8,49 @@ import org.zalando.jsonapi.model.implicits.AttributeConversions._ class AttributeConversionsSpec extends WordSpec with Matchers { "scala tuples" should { "be converted to string attributes" in { - convertToStringAttribute("name" -> "string") should be(Attribute("name", StringValue("string"))) + convertPairToAttribute("name" → "string") should be(Attribute("name", StringValue("string"))) } "be converted to number attributes" in { - convertToIntAttribute("name" -> 42) should be(Attribute("name", NumberValue(42))) - convertToLongAttribute("name" -> 42l) should be(Attribute("name", NumberValue(42))) - convertToFloatAttribute("name" -> 42f) should be(Attribute("name", NumberValue(42))) - convertToDoubleAttribute("name" -> 42d) should be(Attribute("name", NumberValue(42))) + convertPairToAttribute("name" → 42) should be(Attribute("name", NumberValue(42))) + convertPairToAttribute("name" → 42l) should be(Attribute("name", NumberValue(42))) + convertPairToAttribute("name" → 42f) should be(Attribute("name", NumberValue(42))) + convertPairToAttribute("name" → 42d) should be(Attribute("name", NumberValue(42))) } "be converted to boolean attributes" in { - convertToBooleanAttribute("name" -> true) should be(Attribute("name", BooleanValue(true))) - convertToBooleanAttribute("name" -> false) should be(Attribute("name", BooleanValue(false))) + convertPairToAttribute("name" → true) should be(Attribute("name", TrueValue)) + convertPairToAttribute("name" → false) should be(Attribute("name", FalseValue)) + } + "be converted to null attributes" in { + convertPairToAttribute(("name" → null)) should be(Attribute("name", NullValue)) + } + "be converted to js object attributes" in { + convertPairToAttribute(("name" → Map("null" → null))) should be(Attribute("name", JsObjectValue(List(Attribute("null", NullValue))))) + } + "be converted to js array attributes" in { + convertPairToAttribute(("name" → List(null))) should be(Attribute("name", JsArrayValue(List(NullValue)))) } "be converted to optional string attributes" in { - convertToOptionalStringAttribute("name" -> Option("string")) should be(Option(Attribute("name", StringValue("string")))) + convertPairToOptionalAttribute("name" → Option("string")) should be(Option(Attribute("name", StringValue("string")))) } "be converted to optional number attributes" in { - convertToOptionalIntAttribute("name" -> Option(42)) should be(Option(Attribute("name", NumberValue(42)))) - convertToOptionalLongAttribute("name" -> Option(42l)) should be(Option(Attribute("name", NumberValue(42)))) - convertToOptionalFloatAttribute("name" -> Option(42f)) should be(Option(Attribute("name", NumberValue(42)))) - convertToOptionalDoubleAttribute("name" -> Option(42d)) should be(Option(Attribute("name", NumberValue(42)))) + convertPairToOptionalAttribute("name" → Option(42)) should be(Option(Attribute("name", NumberValue(42)))) + convertPairToOptionalAttribute("name" → Option(42l)) should be(Option(Attribute("name", NumberValue(42)))) + convertPairToOptionalAttribute("name" → Option(42f)) should be(Option(Attribute("name", NumberValue(42)))) + convertPairToOptionalAttribute("name" → Option(42d)) should be(Option(Attribute("name", NumberValue(42)))) } "be converted to optional boolean attributes" in { - convertToOptionalBooleanAttribute("name" -> Option(true)) should be(Option(Attribute("name", BooleanValue(true)))) - convertToOptionalBooleanAttribute("name" -> Option(false)) should be(Option(Attribute("name", BooleanValue(false)))) + convertPairToOptionalAttribute("name" → Option(true)) should be(Option(Attribute("name", BooleanValue(true)))) + convertPairToOptionalAttribute("name" → Option(false)) should be(Option(Attribute("name", BooleanValue(false)))) + } + "be converted to optional null attributes" in { + convertPairToOptionalAttribute(("name" → Option(null))) should be(None) + } + "be converted to optional js object attributes" in { + convertPairToOptionalAttribute(("name" → Option(Map("null" → null)))) should be(Option(Attribute("name", JsObjectValue(List(Attribute("null", NullValue)))))) + } + "be converted to optional js array attributes" in { + convertPairToOptionalAttribute(("name" → Option(List(null)))) should be(Option(Attribute("name", JsArrayValue(List(NullValue))))) } } } diff --git a/src/test/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversionsSpec.scala b/src/test/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversionsSpec.scala index e1e809c..d15a37b 100644 --- a/src/test/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversionsSpec.scala +++ b/src/test/scala/org/zalando/jsonapi/model/implicits/JsonApiObjectValueConversionsSpec.scala @@ -1,24 +1,65 @@ - package org.zalando.jsonapi.model.implicits import org.scalatest.{Matchers, WordSpec} +import org.zalando.jsonapi.model.Attribute import org.zalando.jsonapi.model.JsonApiObject._ import org.zalando.jsonapi.model.implicits.JsonApiObjectValueConversions._ class JsonApiObjectValueConversionsSpec extends WordSpec with Matchers { "scala values" should { "be converted to string values" in { - convertStringToStringValue("string") should be(StringValue("string")) + convertAnyToValue("string") should be(StringValue("string")) } "be converted to number values" in { - convertIntToNumberValue(42) should be(NumberValue(42)) - convertLongToNumberValue(42l) should be(NumberValue(42)) - convertFloatToNumberValue(42f) should be(NumberValue(42)) - convertDoubleToNumberValue(42d) should be(NumberValue(42)) + convertAnyToValue(42) should be(NumberValue(42)) + convertAnyToValue(42l) should be(NumberValue(42)) + convertAnyToValue(42f) should be(NumberValue(42)) + convertAnyToValue(42d) should be(NumberValue(42)) } "be converted to boolean values" in { - convertBooleanToBooleanValue(true) should be(BooleanValue(true)) - convertBooleanToBooleanValue(false) should be(BooleanValue(false)) + convertAnyToValue(true) should be(TrueValue) + convertAnyToValue(false) should be(FalseValue) + } + "be converted to js array values" in { + convertAnyToValue(Seq("one", 2, Map("3" → 4d), false, null)) should be(JsArrayValue(List( + StringValue("one"), + NumberValue(2), + JsObjectValue(List(Attribute("3", NumberValue(4d)))), + FalseValue, + NullValue + ))) + } + "be converted to js object values" in { + convertAnyToValue(Map( + "one" → 2, + "3" → List(4f, true, null) + )) should be(JsObjectValue(List( + Attribute("one", NumberValue(2)), + Attribute("3", JsArrayValue(List( + NumberValue(4f), + TrueValue, + NullValue + ))) + ))) + } + "be converted to null values" in { + convertAnyToValue(null) should be(NullValue) + } + "be left alone" in { + for { + value ← Seq(NullValue, TrueValue, FalseValue, StringValue("value"), NumberValue(1)) + } { + convertAnyToValue(value) should be (value) + } + } + "throw an error for unconvertible types" in { + the[UnconvertibleTypeError] thrownBy { + convertAnyToValue(Map(1 → 2)) + } should have message "Maps must have string keys to be converted to JsonApiObject Values" + + the[UnconvertibleTypeError] thrownBy { + convertAnyToValue(new java.util.Date) + } should have message "Can not convert java.util.Date to JsonApiObject Value" } } }