diff --git a/persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/MongoCaseClassField.scala b/persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/MongoCaseClassField.scala index fed5a8d221..a3ff4a8e2d 100644 --- a/persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/MongoCaseClassField.scala +++ b/persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/MongoCaseClassField.scala @@ -11,10 +11,10 @@ * limitations under the License. */ -package net.liftweb -package mongodb -package record -package field +package net.liftweb +package mongodb +package record +package field import net.liftweb.record._ import net.liftweb.record.RecordHelpers.jvalueToJsExp @@ -31,8 +31,10 @@ import net.liftweb.http.js.JsExp class MongoCaseClassField[OwnerType <: Record[OwnerType],CaseType](rec: OwnerType)( implicit mf: Manifest[CaseType]) extends Field[CaseType, OwnerType] with MandatoryTypedField[CaseType] with MongoFieldFlavor[CaseType] { - - implicit val formats = net.liftweb.json.DefaultFormats + + // override this for custom formats + def formats: Formats = DefaultFormats + implicit lazy val _formats = formats override type MyType = CaseType @@ -60,7 +62,7 @@ class MongoCaseClassField[OwnerType <: Record[OwnerType],CaseType](rec: OwnerTyp val jvalue = JObjectParser.serialize(dbo) setFromJValue(jvalue) } - + override def setFromString(in: String): Box[CaseType] = { Helpers.tryo{ JsonParser.parse(in).extract[CaseType] } } @@ -76,22 +78,24 @@ class MongoCaseClassField[OwnerType <: Record[OwnerType],CaseType](rec: OwnerTyp } class MongoCaseClassListField[OwnerType <: Record[OwnerType],CaseType](rec: OwnerType)( implicit mf: Manifest[CaseType]) extends Field[List[CaseType], OwnerType] with MandatoryTypedField[List[CaseType]] with MongoFieldFlavor[List[CaseType]] { - - implicit val formats = net.liftweb.json.DefaultFormats - + + // override this for custom formats + def formats: Formats = DefaultFormats + implicit lazy val _formats = formats + override type MyType = List[CaseType] - + def owner = rec def asXHtml = Text(value.toString) - + def toForm: Box[NodeSeq] = Empty override def defaultValue: MyType = Nil override def optional_? = true - + def asJValue = JArray(value.map(v => Extraction.decompose(v))) - + def setFromJValue(jvalue: JValue): Box[MyType] = jvalue match { case JArray(contents) => setBox(Full(contents.flatMap(s => Helpers.tryo[CaseType]{ s.extract[CaseType] }))) case _ => setBox(Empty) @@ -99,12 +103,12 @@ class MongoCaseClassListField[OwnerType <: Record[OwnerType],CaseType](rec: Owne def asDBObject: DBObject = { val dbl = new BasicDBList - + asJValue match { - case JArray(list) => + case JArray(list) => list.foreach(v => dbl.add(JObjectParser.parse(v.asInstanceOf[JObject]))) } - + dbl } diff --git a/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala b/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala index e7ff2d4d8c..ec88b17892 100644 --- a/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala +++ b/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala @@ -22,7 +22,8 @@ package fixtures import field._ import common.{Box, Empty, Failure, Full} -import json.ext.JsonBoxSerializer +import json._ +import json.ext.{EnumSerializer, JsonBoxSerializer} import http.SHtml import util.FieldError @@ -243,6 +244,10 @@ class MongoFieldTypeTestRecord private () extends MongoRecord[MongoFieldTypeTest object mandatoryUUIDField extends UUIDField(this) object legacyOptionalUUIDField extends UUIDField(this) { override def optional_? = true } + object mandatoryMongoCaseClassField extends MongoCaseClassField[MongoFieldTypeTestRecord, MongoCaseClassTestObject](this) { + override def formats = owner.meta.formats + } + override def equals(other: Any): Boolean = other match { case that: MongoFieldTypeTestRecord => this.id.value == that.id.value && @@ -251,7 +256,8 @@ class MongoFieldTypeTestRecord private () extends MongoRecord[MongoFieldTypeTest this.mandatoryObjectIdField.value == that.mandatoryObjectIdField.value && this.mandatoryPatternField.value.pattern == that.mandatoryPatternField.value.pattern && this.mandatoryPatternField.value.flags == that.mandatoryPatternField.value.flags && - this.mandatoryUUIDField.value == that.mandatoryUUIDField.value + this.mandatoryUUIDField.value == that.mandatoryUUIDField.value && + this.mandatoryMongoCaseClassField.value == that.mandatoryMongoCaseClassField.value case _ => false } @@ -259,7 +265,7 @@ class MongoFieldTypeTestRecord private () extends MongoRecord[MongoFieldTypeTest } object MongoFieldTypeTestRecord extends MongoFieldTypeTestRecord with MongoMetaRecord[MongoFieldTypeTestRecord] { - override def formats = allFormats + override def formats = allFormats + new EnumSerializer(MyTestEnum) } class PasswordTestRecord private () extends MongoRecord[PasswordTestRecord] with ObjectIdPk[PasswordTestRecord] { @@ -269,7 +275,7 @@ class PasswordTestRecord private () extends MongoRecord[PasswordTestRecord] with } object PasswordTestRecord extends PasswordTestRecord with MongoMetaRecord[PasswordTestRecord] -case class MongoCaseClassTestObject(intField: Int, stringField: String) +case class MongoCaseClassTestObject(intField: Int, stringField: String, enum: MyTestEnum.Value) class ListTestRecord private () extends MongoRecord[ListTestRecord] with UUIDPk[ListTestRecord] { def meta = ListTestRecord @@ -283,7 +289,9 @@ class ListTestRecord private () extends MongoRecord[ListTestRecord] with UUIDPk[ object mandatoryMongoJsonObjectListField extends MongoJsonObjectListField(this, TypeTestJsonObject) object legacyOptionalMongoJsonObjectListField extends MongoJsonObjectListField(this, TypeTestJsonObject) { override def optional_? = true } - object mongoCaseClassListField extends MongoCaseClassListField[ListTestRecord, MongoCaseClassTestObject](this) + object mongoCaseClassListField extends MongoCaseClassListField[ListTestRecord, MongoCaseClassTestObject](this) { + override def formats = owner.meta.formats + } // TODO: More List types @@ -292,14 +300,15 @@ class ListTestRecord private () extends MongoRecord[ListTestRecord] with UUIDPk[ this.id.value == that.id.value && this.mandatoryStringListField.value == that.mandatoryStringListField.value && this.mandatoryIntListField.value == that.mandatoryIntListField.value && - this.mandatoryMongoJsonObjectListField.value == that.mandatoryMongoJsonObjectListField.value + this.mandatoryMongoJsonObjectListField.value == that.mandatoryMongoJsonObjectListField.value && + this.mongoCaseClassListField.value == that.mongoCaseClassListField.value case _ => false } def dirtyFields = this.allFields.filter(_.dirty_?) } object ListTestRecord extends ListTestRecord with MongoMetaRecord[ListTestRecord] { - override def formats = allFormats + override def formats = allFormats + new EnumSerializer(MyTestEnum) } class MapTestRecord private () extends MongoRecord[MapTestRecord] with StringPk[MapTestRecord] { diff --git a/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala b/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala index 98babc7d28..512126dead 100644 --- a/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala +++ b/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala @@ -323,7 +323,7 @@ object MongoFieldSpec extends Specification("MongoField Specification") with Mon "MongoCaseClassListField" should { "setFromAny a List" in { val rec = ListTestRecord.createRecord - val lst = List(MongoCaseClassTestObject(1,"str1")) + val lst = List(MongoCaseClassTestObject(1,"str1", MyTestEnum.THREE)) rec.mongoCaseClassListField.setFromAny(lst) rec.mongoCaseClassListField.value must_== lst } diff --git a/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala b/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala index 8cfe355f04..4b692f6c69 100644 --- a/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala +++ b/persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala @@ -37,17 +37,17 @@ import com.mongodb._ * Systems under specification for MongoRecord. */ object MongoRecordSpec extends Specification("MongoRecord Specification") with MongoTestKit { - import fixtures._ "MongoRecord field introspection" should { checkMongoIsRunning val rec = MongoFieldTypeTestRecord.createRecord - val allExpectedFieldNames: List[String] = "_id" :: (for { - typeName <- "Date JsonObject ObjectId Pattern UUID".split(" ") - flavor <- "mandatory legacyOptional".split(" ") - } yield flavor + typeName + "Field").toList + val allExpectedFieldNames: List[String] = "_id" :: "mandatoryMongoCaseClassField" :: + (for { + typeName <- "Date JsonObject ObjectId Pattern UUID".split(" ") + flavor <- "mandatory legacyOptional".split(" ") + } yield flavor + typeName + "Field").toList "introspect only the expected fields" in { rec.fields().map(_.name).filterNot(allExpectedFieldNames.contains(_)) must_== Nil @@ -194,6 +194,7 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M .mandatoryObjectIdField(ObjectId.get) .mandatoryPatternField(Pattern.compile("^Mo", Pattern.CASE_INSENSITIVE)) .mandatoryUUIDField(UUID.randomUUID) + .mandatoryMongoCaseClassField(MongoCaseClassTestObject(1,"str",MyTestEnum.TWO)) val mfttrJson = ("_id" -> ("$oid" -> mfttr.id.toString)) ~ @@ -206,13 +207,14 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M ("mandatoryPatternField" -> (("$regex" -> mfttr.mandatoryPatternField.value.pattern) ~ ("$flags" -> mfttr.mandatoryPatternField.value.flags))) ~ ("legacyOptionalPatternField" -> (None: Option[JObject])) ~ ("mandatoryUUIDField" -> ("$uuid" -> mfttr.mandatoryUUIDField.value.toString)) ~ - ("legacyOptionalUUIDField" -> (None: Option[JObject])) + ("legacyOptionalUUIDField" -> (None: Option[JObject])) ~ + ("mandatoryMongoCaseClassField" -> ("intField" -> 1) ~ ("stringField" -> "str") ~ ("enum" -> 1)) val ltr = ListTestRecord.createRecord .mandatoryStringListField(List("abc", "def", "ghi")) .mandatoryIntListField(List(4, 5, 6)) .mandatoryMongoJsonObjectListField(List(TypeTestJsonObject(1, "jsonobj1", Map("x" -> "1")), TypeTestJsonObject(2, "jsonobj2", Map("x" -> "2")))) - .mongoCaseClassListField(List(MongoCaseClassTestObject(1,"str"))) + .mongoCaseClassListField(List(MongoCaseClassTestObject(1,"str",MyTestEnum.TWO))) val ltrJson = ("_id" -> ("$uuid" -> ltr.id.toString)) ~ @@ -226,7 +228,7 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M )) ~ ("legacyOptionalMongoJsonObjectListField" -> List[JObject]()) ~ ("mongoCaseClassListField" -> List( - ("intField" -> 1) ~ ("stringField" -> "str") + ("intField" -> 1) ~ ("stringField" -> "str") ~ ("enum" -> 1) )) val mtr = MapTestRecord.createRecord @@ -685,7 +687,7 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M fttr.legacyOptionalStringField(Empty) fttr.legacyOptionalStringField.dirty_? must_== true - fttr.dirtyFields.length must_== 7 + fttr.dirtyFields.length must_== 9 fttr.update fttr.dirtyFields.length must_== 0 @@ -792,7 +794,7 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M ltr.mandatoryMongoJsonObjectListField(List(TypeTestJsonObject(1, "jsonobj1", Map("x" -> "1")), TypeTestJsonObject(2, "jsonobj2", Map("x" -> "2")))) ltr.mandatoryMongoJsonObjectListField.dirty_? must_== true - ltr.mongoCaseClassListField(List(MongoCaseClassTestObject(1,"str"))) + ltr.mongoCaseClassListField(List(MongoCaseClassTestObject(1,"str",MyTestEnum.TWO))) ltr.mongoCaseClassListField.dirty_? must_== true ltr.dirtyFields.length must_== 4 diff --git a/persistence/mongodb/src/main/scala/net/liftweb/mongodb/JObjectParser.scala b/persistence/mongodb/src/main/scala/net/liftweb/mongodb/JObjectParser.scala index d664aee401..d03ffa6d3d 100644 --- a/persistence/mongodb/src/main/scala/net/liftweb/mongodb/JObjectParser.scala +++ b/persistence/mongodb/src/main/scala/net/liftweb/mongodb/JObjectParser.scala @@ -36,10 +36,12 @@ object JObjectParser extends SimpleInjector { * * JObjectParser.stringProcessor.default.set((s: String) => s) */ - val stringProcessor = new Inject(() => (s: String) => { + val stringProcessor = new Inject(() => defaultStringProcessor _) {} + + def defaultStringProcessor(s: String): Object = { if (ObjectId.isValid(s)) new ObjectId(s) else s - }) {} + } /* * Parse a JObject into a DBObject diff --git a/persistence/mongodb/src/test/scala/net/liftweb/mongodb/JObjectParserSpec.scala b/persistence/mongodb/src/test/scala/net/liftweb/mongodb/JObjectParserSpec.scala index 6aa12477de..5d65b85b8b 100644 --- a/persistence/mongodb/src/test/scala/net/liftweb/mongodb/JObjectParserSpec.scala +++ b/persistence/mongodb/src/test/scala/net/liftweb/mongodb/JObjectParserSpec.scala @@ -45,14 +45,14 @@ object JObjectParserSpec extends Specification("JObjectParser Specification") { } } "not convert strings to ObjectId when configured not to" in { - JObjectParser.stringProcessor.default.set((s: String) => s) - - val (oid, dbo) = buildTestData - val xval = tryo(dbo.get("x").asInstanceOf[String]) - - xval must notBeEmpty - xval.foreach { x => - x must_== oid.toString + JObjectParser.stringProcessor.doWith((s: String) => s) { + val (oid, dbo) = buildTestData + val xval = tryo(dbo.get("x").asInstanceOf[String]) + + xval must notBeEmpty + xval.foreach { x => + x must_== oid.toString + } } } }