Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding of formats in MongoCaseClassField #1275

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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] }
}
Expand All @@ -76,35 +78,37 @@ 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)
}

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
}

Expand Down
Expand Up @@ -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

Expand Down Expand Up @@ -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 &&
Expand All @@ -251,15 +256,16 @@ 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
}

def dirtyFields = this.allFields.filter(_.dirty_?)
}

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] {
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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] {
Expand Down
Expand Up @@ -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
}
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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)) ~
Expand All @@ -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)) ~
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -36,10 +36,12 @@ object JObjectParser extends SimpleInjector {
*
* <code>JObjectParser.stringProcessor.default.set((s: String) => s)</code>
*/
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
Expand Down
Expand Up @@ -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
}
}
}
}
Expand Down