Skip to content
This repository
Browse code

Issue 1343 - Ensure equals method works properly with all Fields

  • Loading branch information...
commit f030513b9228ca1a0478180e4575762b98596c75 1 parent 36d59de
Tim Nelson eltimn authored

Showing 14 changed files with 413 additions and 259 deletions. Show diff stats Hide diff stats

  1. +23 36 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/BsonRecord.scala
  2. +4 2 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/MongoMetaRecord.scala
  3. +2 2 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/JsonObjectField.scala
  4. +4 4 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/PatternField.scala
  5. +46 0 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/BsonRecordSpec.scala
  6. +33 51 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala
  7. +92 70 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala
  8. +62 15 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala
  9. +23 15 persistence/record/src/main/scala/net/liftweb/record/Field.scala
  10. +21 0 persistence/record/src/main/scala/net/liftweb/record/MetaRecord.scala
  11. +28 4 persistence/record/src/main/scala/net/liftweb/record/Record.scala
  12. +63 41 persistence/record/src/test/scala/net/liftweb/record/FieldSpec.scala
  13. +11 17 persistence/record/src/test/scala/net/liftweb/record/Fixtures.scala
  14. +1 2  persistence/record/src/test/scala/net/liftweb/record/RecordSpec.scala
59 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/BsonRecord.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2011 WorldWide Conferencing, LLC
  2 + * Copyright 2011-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -36,31 +36,18 @@ trait BsonRecord[MyType <: BsonRecord[MyType]] extends Record[MyType] {
36 36 def meta: BsonMetaRecord[MyType]
37 37
38 38 /**
39   - * Encode a record instance into a DBObject
40   - */
  39 + * Encode a record instance into a DBObject
  40 + */
41 41 def asDBObject: DBObject = meta.asDBObject(this)
42 42
43 43 /**
44   - * Set the fields of this record from the given DBObject
45   - */
  44 + * Set the fields of this record from the given DBObject
  45 + */
46 46 def setFieldsFromDBObject(dbo: DBObject): Unit = meta.setFieldsFromDBObject(this, dbo)
47 47
48   - override def toString = {
49   - val fieldList = this.fields.map(f => "%s=%s" format (f.name,
50   - f.valueBox match {
51   - case Full(c: java.util.Calendar) => c.getTime().toString()
52   - case Full(null) => ""
53   - case Full(v) => v.toString
54   - case _ => ""
55   - }))
56   -
57   - "%s={%s}" format (this.getClass.toString, fieldList.mkString(", "))
58   - }
59   -
60   -
61 48 /**
62   - * Save the instance and return the instance
63   - */
  49 + * Save the instance and return the instance
  50 + */
64 51 override def saveTheRecord(): Box[MyType] = throw new BackingStoreException("BSON Records don't save themselves")
65 52 }
66 53
@@ -69,10 +56,10 @@ trait BsonMetaRecord[BaseRecord <: BsonRecord[BaseRecord]] extends MetaRecord[Ba
69 56 self: BaseRecord =>
70 57
71 58 /**
72   - * Create a BasicDBObject from the field names and values.
73   - * - MongoFieldFlavor types (List) are converted to DBObjects
74   - * using asDBObject
75   - */
  59 + * Create a BasicDBObject from the field names and values.
  60 + * - MongoFieldFlavor types (List) are converted to DBObjects
  61 + * using asDBObject
  62 + */
76 63 def asDBObject(inst: BaseRecord): DBObject = {
77 64 val dbo = BasicDBObjectBuilder.start // use this so regex patterns can be stored.
78 65
@@ -116,11 +103,11 @@ trait BsonMetaRecord[BaseRecord <: BsonRecord[BaseRecord]] extends MetaRecord[Ba
116 103 }
117 104
118 105 /**
119   - * Creates a new record, then sets the fields with the given DBObject.
120   - *
121   - * @param dbo - the DBObject
122   - * @return Box[BaseRecord]
123   - */
  106 + * Creates a new record, then sets the fields with the given DBObject.
  107 + *
  108 + * @param dbo - the DBObject
  109 + * @return Box[BaseRecord]
  110 + */
124 111 def fromDBObject(dbo: DBObject): BaseRecord = {
125 112 val inst: BaseRecord = createRecord
126 113 setFieldsFromDBObject(inst, dbo)
@@ -128,13 +115,13 @@ trait BsonMetaRecord[BaseRecord <: BsonRecord[BaseRecord]] extends MetaRecord[Ba
128 115 }
129 116
130 117 /**
131   - * Populate the inst's fields with the values from a DBObject. Values are set
132   - * using setFromAny passing it the DBObject returned from Mongo.
133   - *
134   - * @param inst - the record that will be populated
135   - * @param dbo - The DBObject
136   - * @return Unit
137   - */
  118 + * Populate the inst's fields with the values from a DBObject. Values are set
  119 + * using setFromAny passing it the DBObject returned from Mongo.
  120 + *
  121 + * @param inst - the record that will be populated
  122 + * @param dbo - The DBObject
  123 + * @return Unit
  124 + */
138 125 def setFieldsFromDBObject(inst: BaseRecord, dbo: DBObject): Unit = {
139 126 for (k <- dbo.keySet; field <- inst.fieldByName(k.toString)) {
140 127 field.setFromAny(dbo.get(k.toString))
6 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/MongoMetaRecord.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2010-2011 WorldWide Conferencing, LLC
  2 + * Copyright 2010-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -317,7 +317,9 @@ trait MongoMetaRecord[BaseRecord <: MongoRecord[BaseRecord]]
317 317 }
318 318
319 319 /**
320   - * Update only the dirty fields
  320 + * Update only the dirty fields.
  321 + *
  322 + * Note: PatternField will always set the dirty flag when set.
321 323 */
322 324 def update(inst: BaseRecord): Unit = {
323 325 val dirtyFields = fields(inst).filter(_.dirty_?)
4 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/JsonObjectField.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   -* Copyright 2010-2011 WorldWide Conferencing, LLC
  2 +* Copyright 2010-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ abstract class JsonObjectField[OwnerType <: BsonRecord[OwnerType], JObjectType <
42 42 override def toForm: Box[NodeSeq] = Empty // FIXME
43 43
44 44 /** Encode the field value into a JValue */
45   - def asJValue: JValue = value.asJObject
  45 + def asJValue: JValue = valueBox.map(_.asJObject) openOr (JNothing: JValue)
46 46
47 47 /*
48 48 * Decode the JValue and set the field to the decoded value.
8 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/PatternField.scala
@@ -11,10 +11,10 @@
11 11 * limitations under the License.
12 12 */
13 13
14   -package net.liftweb
15   -package mongodb
16   -package record
17   -package field
  14 +package net.liftweb
  15 +package mongodb
  16 +package record
  17 +package field
18 18
19 19 import java.util.regex.Pattern
20 20 import scala.xml.NodeSeq
46 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/BsonRecordSpec.scala
... ... @@ -0,0 +1,46 @@
  1 +/*
  2 + * Copyright 2010-2012 WorldWide Conferencing, LLC
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package net.liftweb
  18 +package mongodb
  19 +package record
  20 +
  21 +import org.specs2.mutable.Specification
  22 +
  23 +class BsonRecordSpec extends Specification with MongoTestKit {
  24 + "BsonRecordSpec Specification".title
  25 + import fixtures._
  26 +
  27 + override def before = {
  28 + super.before
  29 + checkMongoIsRunning
  30 + }
  31 +
  32 + "BsonRecord" should {
  33 + "compare properly" in {
  34 +
  35 + val subRec = SubSubRecord.createRecord.name("subrecord")
  36 + val subRec2 = SubSubRecord.createRecord.name("subrecord")
  37 +
  38 + (subRec == subRec2) must_== true
  39 +
  40 + subRec2.name("subrecord2")
  41 +
  42 + (subRec == subRec2) must_== false
  43 +
  44 + }
  45 + }
  46 +}
84 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2010-2011 WorldWide Conferencing, LLC
  2 + * Copyright 2010-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -132,25 +132,6 @@ class FieldTypeTestRecord private () extends MongoRecord[FieldTypeTestRecord] wi
132 132 object legacyOptionalTimeZoneField extends TimeZoneField(this) { override def optional_? = true }
133 133 object optionalTimeZoneField extends OptionalTimeZoneField(this)
134 134
135   - override def equals(other: Any): Boolean = other match {
136   - case that: FieldTypeTestRecord =>
137   - this.id.value == that.id.value &&
138   - this.mandatoryBooleanField.value == that.mandatoryBooleanField.value &&
139   - this.mandatoryCountryField.value == that.mandatoryCountryField.value &&
140   - this.mandatoryDecimalField.value == that.mandatoryDecimalField.value &&
141   - this.mandatoryDoubleField.value == that.mandatoryDoubleField.value &&
142   - this.mandatoryEmailField.value == that.mandatoryEmailField.value &&
143   - this.mandatoryEnumField.value == that.mandatoryEnumField.value &&
144   - this.mandatoryIntField.value == that.mandatoryIntField.value &&
145   - this.mandatoryLocaleField.value == that.mandatoryLocaleField.value &&
146   - this.mandatoryLongField.value == that.mandatoryLongField.value &&
147   - this.mandatoryPostalCodeField.value == that.mandatoryPostalCodeField.value &&
148   - this.mandatoryStringField.value == that.mandatoryStringField.value &&
149   - this.mandatoryTextareaField.value == that.mandatoryTextareaField.value &&
150   - this.mandatoryTimeZoneField.value == that.mandatoryTimeZoneField.value
151   - case _ => false
152   - }
153   -
154 135 def dirtyFields = this.allFields.filter(_.dirty_?)
155 136 }
156 137
@@ -238,9 +219,6 @@ class MongoFieldTypeTestRecord private () extends MongoRecord[MongoFieldTypeTest
238 219 object mandatoryObjectIdField extends ObjectIdField(this)
239 220 object legacyOptionalObjectIdField extends ObjectIdField(this) { override def optional_? = true }
240 221
241   - object mandatoryPatternField extends PatternField(this)
242   - object legacyOptionalPatternField extends PatternField(this) { override def optional_? = true }
243   -
244 222 object mandatoryUUIDField extends UUIDField(this)
245 223 object legacyOptionalUUIDField extends UUIDField(this) { override def optional_? = true }
246 224
@@ -248,19 +226,6 @@ class MongoFieldTypeTestRecord private () extends MongoRecord[MongoFieldTypeTest
248 226 override def formats = owner.meta.formats
249 227 }
250 228
251   - override def equals(other: Any): Boolean = other match {
252   - case that: MongoFieldTypeTestRecord =>
253   - this.id.value == that.id.value &&
254   - this.mandatoryDateField.value == that.mandatoryDateField.value &&
255   - this.mandatoryJsonObjectField.value == that.mandatoryJsonObjectField.value &&
256   - this.mandatoryObjectIdField.value == that.mandatoryObjectIdField.value &&
257   - this.mandatoryPatternField.value.pattern == that.mandatoryPatternField.value.pattern &&
258   - this.mandatoryPatternField.value.flags == that.mandatoryPatternField.value.flags &&
259   - this.mandatoryUUIDField.value == that.mandatoryUUIDField.value &&
260   - this.mandatoryMongoCaseClassField.value == that.mandatoryMongoCaseClassField.value
261   - case _ => false
262   - }
263   -
264 229 def dirtyFields = this.allFields.filter(_.dirty_?)
265 230 }
266 231
@@ -268,6 +233,38 @@ object MongoFieldTypeTestRecord extends MongoFieldTypeTestRecord with MongoMetaR
268 233 override def formats = allFormats + new EnumSerializer(MyTestEnum)
269 234 }
270 235
  236 +class PatternFieldTestRecord private () extends MongoRecord[PatternFieldTestRecord] with ObjectIdPk[PatternFieldTestRecord] {
  237 + def meta = PatternFieldTestRecord
  238 +
  239 + import java.util.regex.Pattern
  240 +
  241 + object mandatoryPatternField extends PatternField(this)
  242 + object legacyOptionalPatternField extends PatternField(this) { override def optional_? = true }
  243 +
  244 + /**
  245 + * Pattern.equals doesn't work properly, so a custom equals is required with PatternField
  246 + */
  247 + override def equals(other: Any): Boolean = {
  248 + other match {
  249 + case that: PatternFieldTestRecord =>
  250 + that.fields.corresponds(this.fields) { (a,b) =>
  251 + (a.name == b.name) && ((a.valueBox, b.valueBox) match {
  252 + case (Full(ap: Pattern), Full(bp: Pattern)) =>
  253 + ap.pattern == bp.pattern && ap.flags == bp.flags
  254 + case _ => a.valueBox == b.valueBox
  255 + })
  256 + }
  257 + case _ => false
  258 + }
  259 + }
  260 +
  261 + def dirtyFields = this.allFields.filter(_.dirty_?)
  262 +}
  263 +
  264 +object PatternFieldTestRecord extends PatternFieldTestRecord with MongoMetaRecord[PatternFieldTestRecord] {
  265 + override def formats = allFormats
  266 +}
  267 +
271 268 class PasswordTestRecord private () extends MongoRecord[PasswordTestRecord] with ObjectIdPk[PasswordTestRecord] {
272 269 def meta = PasswordTestRecord
273 270
@@ -379,12 +376,6 @@ class SubSubRecord private () extends BsonRecord[SubSubRecord] {
379 376 def meta = SubSubRecord
380 377
381 378 object name extends StringField(this, 12)
382   -
383   - override def equals(other: Any): Boolean = other match {
384   - case that: SubSubRecord =>
385   - this.name.value == that.name.value
386   - case _ => false
387   - }
388 379 }
389 380 object SubSubRecord extends SubSubRecord with BsonMetaRecord[SubSubRecord] {
390 381 override def formats = allFormats
@@ -431,11 +422,6 @@ class NullTestRecord private () extends MongoRecord[NullTestRecord] with IntPk[N
431 422 def defaultValue = JsonObj("1", null)
432 423 }
433 424 object jsonobjlist extends MongoJsonObjectListField[NullTestRecord, JsonObj](this, JsonObj)
434   -
435   - override def equals(other: Any): Boolean = other match {
436   - case that: NullTestRecord => this.toString == that.toString
437   - case _ => false
438   - }
439 425 }
440 426
441 427 object NullTestRecord extends NullTestRecord with MongoMetaRecord[NullTestRecord]
@@ -454,10 +440,6 @@ class BoxTestRecord private () extends MongoRecord[BoxTestRecord] with LongPk[Bo
454 440 }
455 441 object jsonobjlist extends MongoJsonObjectListField[BoxTestRecord, BoxTestJsonObj](this, BoxTestJsonObj)
456 442
457   - override def equals(other: Any): Boolean = other match {
458   - case that: BoxTestRecord => this.toString == that.toString
459   - case _ => false
460   - }
461 443 }
462 444 object BoxTestRecord extends BoxTestRecord with MongoMetaRecord[BoxTestRecord] {
463 445 override def formats = super.formats + new JsonBoxSerializer
162 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2006-2011 WorldWide Conferencing, LLC
  2 + * Copyright 2006-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ package net.liftweb
18 18 package mongodb
19 19 package record
20 20
21   -import java.util.{Date, UUID}
  21 +import java.util.{Calendar, Date, UUID}
22 22 import java.util.regex.Pattern
23 23
24 24 import org.bson.types.ObjectId
@@ -54,12 +54,13 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
54 54
55 55 def passBasicTests[A](
56 56 example: A,
  57 + example2: A,
57 58 mandatory: MandatoryTypedField[A],
58 59 legacyOptional: MandatoryTypedField[A],
59 60 canCheckDefaultValues: Boolean = true
60 61 )(implicit m: scala.reflect.Manifest[A]): Unit = {
61 62
62   - def commonBehaviorsForAllFlavors(field: MandatoryTypedField[A]) = {
  63 + def commonBehaviorsForAllFlavors(field: MandatoryTypedField[A]): Unit = {
63 64
64 65 "which have the correct initial value" in {
65 66 field.value must be_==(field.defaultValue).when(canCheckDefaultValues)
@@ -88,6 +89,30 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
88 89 // Failure("my failure") must_== Failure("my failure")
89 90 pending
90 91 }
  92 +
  93 + "which are only flagged as dirty_? when setBox is called with a different value" in {
  94 + field.clear
  95 + field match {
  96 + case owned: OwnedField[_] => owned.owner.runSafe {
  97 + field.resetDirty
  98 + }
  99 + case _ => field.resetDirty
  100 + }
  101 + field.dirty_? must_== false
  102 + val valueBox = field.valueBox
  103 + field.setBox(valueBox)
  104 + field.dirty_? must_== false
  105 + val exampleBox = Full(example)
  106 + (valueBox === exampleBox) must_== false
  107 + field.setBox(exampleBox)
  108 + field.dirty_? must_== true
  109 + val exampleBox2 = Full(example2)
  110 + (exampleBox === exampleBox2) must_== false
  111 + field.setBox(exampleBox2)
  112 + field.dirty_? must_== true
  113 + field.setBox(valueBox)
  114 + success
  115 + }
91 116 }
92 117
93 118 "support mandatory fields" in {
@@ -191,7 +216,9 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
191 216 val rec = MongoFieldTypeTestRecord.createRecord
192 217 val now = new Date
193 218 val nowStr = rec.meta.formats.dateFormat.format(now)
194   - passBasicTests(now, rec.mandatoryDateField, rec.legacyOptionalDateField, false)
  219 + val now2 = Calendar.getInstance()
  220 + now2.add(Calendar.DATE, 1)
  221 + passBasicTests(now, now2.getTime, rec.mandatoryDateField, rec.legacyOptionalDateField, false)
195 222 passConversionTests(
196 223 now,
197 224 rec.mandatoryDateField,
@@ -204,8 +231,9 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
204 231 "JsonObjectField" should {
205 232 val rec = MongoFieldTypeTestRecord.createRecord
206 233 val ttjo = TypeTestJsonObject(1, "jsonobj1", Map("x" -> "a"))
  234 + val ttjo2 = TypeTestJsonObject(2, "jsonobj2", Map("x" -> "b"))
207 235 val json = ("intField" -> 1) ~ ("stringField" -> "jsonobj1") ~ ("mapField" -> (("x" -> "a")))
208   - passBasicTests(ttjo, rec.mandatoryJsonObjectField, rec.legacyOptionalJsonObjectField)
  236 + passBasicTests(ttjo, ttjo2, rec.mandatoryJsonObjectField, rec.legacyOptionalJsonObjectField)
209 237 passConversionTests(
210 238 ttjo,
211 239 rec.mandatoryJsonObjectField,
@@ -220,7 +248,8 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
220 248 "ObjectIdField" should {
221 249 val rec = MongoFieldTypeTestRecord.createRecord
222 250 val oid = ObjectId.get
223   - passBasicTests(oid, rec.mandatoryObjectIdField, rec.legacyOptionalObjectIdField, false)
  251 + val oid2 = ObjectId.get
  252 + passBasicTests(oid, oid2, rec.mandatoryObjectIdField, rec.legacyOptionalObjectIdField, false)
224 253 passConversionTests(
225 254 oid,
226 255 rec.mandatoryObjectIdField,
@@ -231,9 +260,10 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
231 260 }
232 261
233 262 "PatternField" should {
234   - val rec = MongoFieldTypeTestRecord.createRecord
  263 + val rec = PatternFieldTestRecord.createRecord
235 264 val ptrn = Pattern.compile("^Mo", Pattern.CASE_INSENSITIVE)
236   - passBasicTests(ptrn, rec.mandatoryPatternField, rec.legacyOptionalPatternField, false)
  265 + val ptrn2 = Pattern.compile("^MON", Pattern.CASE_INSENSITIVE)
  266 + passBasicTests(ptrn, ptrn2, rec.mandatoryPatternField, rec.legacyOptionalPatternField, false)
237 267 passConversionTests(
238 268 ptrn,
239 269 rec.mandatoryPatternField,
@@ -246,7 +276,8 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
246 276 "UUIDField" should {
247 277 val rec = MongoFieldTypeTestRecord.createRecord
248 278 val uuid = UUID.randomUUID
249   - passBasicTests(uuid, rec.mandatoryUUIDField, rec.legacyOptionalUUIDField, false)
  279 + val uuid2 = UUID.randomUUID
  280 + passBasicTests(uuid, uuid2, rec.mandatoryUUIDField, rec.legacyOptionalUUIDField, false)
250 281 passConversionTests(
251 282 uuid,
252 283 rec.mandatoryUUIDField,
@@ -280,7 +311,8 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
280 311 "function correctly" in {
281 312 val rec = ListTestRecord.createRecord
282 313 val lst = List("abc", "def", "ghi")
283   - passBasicTests(lst, rec.mandatoryStringListField, rec.legacyOptionalStringListField)
  314 + val lst2 = List("ab", "de", "gh")
  315 + passBasicTests(lst, lst2, rec.mandatoryStringListField, rec.legacyOptionalStringListField)
284 316 passConversionTests(
285 317 lst,
286 318 rec.mandatoryStringListField,
@@ -295,7 +327,8 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
295 327 "function correctly" in {
296 328 val rec = ListTestRecord.createRecord
297 329 val lst = List(4, 5, 6)
298   - passBasicTests(lst, rec.mandatoryIntListField, rec.legacyOptionalIntListField)
  330 + val lst2 = List(1, 2, 3)
  331 + passBasicTests(lst, lst2, rec.mandatoryIntListField, rec.legacyOptionalIntListField)
299 332 passConversionTests(
300 333 lst,
301 334 rec.mandatoryIntListField,
@@ -310,11 +343,12 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
310 343 "function correctly" in {
311 344 val rec = ListTestRecord.createRecord
312 345 val lst = List(TypeTestJsonObject(1, "jsonobj1", Map("x" -> "1")), TypeTestJsonObject(2, "jsonobj2", Map("x" -> "2")))
  346 + val lst2 = List(TypeTestJsonObject(3, "jsonobj3", Map("x" -> "3")), TypeTestJsonObject(4, "jsonobj4", Map("x" -> "4")))
313 347 val json = List(
314 348 ("intField" -> 1) ~ ("stringField" -> "jsonobj1") ~ ("mapField" -> (("x" -> "1"))),
315 349 ("intField" -> 2) ~ ("stringField" -> "jsonobj2") ~ ("mapField" -> (("x" -> "2")))
316 350 )
317   - passBasicTests(lst, rec.mandatoryMongoJsonObjectListField, rec.legacyOptionalMongoJsonObjectListField)
  351 + passBasicTests(lst, lst2, rec.mandatoryMongoJsonObjectListField, rec.legacyOptionalMongoJsonObjectListField)
318 352 passConversionTests(
319 353 lst,
320 354 rec.mandatoryMongoJsonObjectListField,
@@ -340,7 +374,8 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
340 374 "function correctly" in {
341 375 val rec = MapTestRecord.createRecord
342 376 val map = Map("a" -> "abc", "b" -> "def", "c" -> "ghi")
343   - passBasicTests(map, rec.mandatoryStringMapField, rec.legacyOptionalStringMapField)
  377 + val map2 = Map("a" -> "ab", "b" -> "de", "c" -> "gh")
  378 + passBasicTests(map, map2, rec.mandatoryStringMapField, rec.legacyOptionalStringMapField)
344 379 passConversionTests(
345 380 map,
346 381 rec.mandatoryStringMapField,
@@ -359,7 +394,8 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
359 394 "function correctly" in {
360 395 val rec = MapTestRecord.createRecord
361 396 val map = Map("a" -> 4, "b" -> 5, "c" -> 6)
362   - passBasicTests(map, rec.mandatoryIntMapField, rec.legacyOptionalIntMapField)
  397 + val map2 = Map("a" -> 1, "b" -> 2, "c" -> 3)
  398 + passBasicTests(map, map2, rec.mandatoryIntMapField, rec.legacyOptionalIntMapField)
363 399 passConversionTests(
364 400 map,
365 401 rec.mandatoryIntMapField,
@@ -378,32 +414,27 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
378 414 "function correctly" in {
379 415 val rec = SubRecordTestRecord.createRecord
380 416 val subRec = SubRecord.createRecord.name("subrecord")
  417 + val subRec2 = SubRecord.createRecord.name("subrecord2")
381 418
382 419 val srJson =
383   - JObject(List(
384   - JField("name", JString("subrecord")),
385   - JField("subsub", JObject(List(
386   - JField("name", JString(""))
387   - ))),
388   - JField("subsublist", JArray(List())),
389   - JField("when", JObject(List(
390   - JField("$dt", JString(rec.meta.formats.dateFormat.format(subRec.when.value)))
391   - ))),
392   - JField("slist", JArray(List())),
393   - JField("smap", JObject(List())),
394   - JField("oid", JObject(List(JField("$oid", JString(subRec.oid.value.toString))))),
395   - JField("pattern", JObject(List(
396   - JField("$regex", JString(subRec.pattern.value.pattern)),
397   - JField("$flags", JInt(subRec.pattern.value.flags))
398   - ))),
399   - JField("uuid", JObject(List(JField("$uuid", JString(subRec.uuid.value.toString)))))
400   - ))
  420 + ("name" -> "subrecord") ~
  421 + ("subsub" -> ("name" -> "")) ~
  422 + ("subsublist" -> JArray(Nil)) ~
  423 + ("when" -> ("$dt" -> rec.meta.formats.dateFormat.format(subRec.when.value))) ~
  424 + ("slist" -> JArray(Nil)) ~
  425 + ("smap" -> JObject(Nil)) ~
  426 + ("oid" -> ("$oid" -> subRec.oid.value.toString)) ~
  427 + ("pattern" ->
  428 + ("$regex" -> subRec.pattern.value.pattern) ~
  429 + ("$flags" -> subRec.pattern.value.flags)
  430 + ) ~
  431 + ("uuid" -> ("$uuid" -> subRec.uuid.value.toString))
401 432
402 433 val srJsExp = new JsExp {
403 434 def toJsCmd = Printer.compact(render(srJson))
404 435 }
405 436
406   - passBasicTests(subRec, rec.mandatoryBsonRecordField, rec.legacyOptionalBsonRecordField, false)
  437 + passBasicTests(subRec, subRec2, rec.mandatoryBsonRecordField, rec.legacyOptionalBsonRecordField, false)
407 438 passConversionTests(
408 439 subRec,
409 440 rec.mandatoryBsonRecordField,
@@ -418,44 +449,35 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
418 449 "function correctly" in {
419 450 val rec = SubRecordTestRecord.createRecord
420 451 val lst = List(SubRecord.createRecord.name("subrec1"), SubRecord.createRecord.name("subrec2"))
  452 + val lst2 = List(SubRecord.createRecord.name("subrec3"), SubRecord.createRecord.name("subrec4"))
421 453 val sr1Json =
422   - JObject(List(
423   - JField("name", JString("subrec1")),
424   - JField("subsub", JObject(List(
425   - JField("name", JString(""))
426   - ))),
427   - JField("subsublist", JArray(List())),
428   - JField("when", JObject(List(
429   - JField("$dt", JString(rec.meta.formats.dateFormat.format(lst(0).when.value)))
430   - ))),
431   - JField("slist", JArray(List())),
432   - JField("smap", JObject(List())),
433   - JField("oid", JObject(List(JField("$oid", JString(lst(0).oid.value.toString))))),
434   - JField("pattern", JObject(List(
435   - JField("$regex", JString(lst(0).pattern.value.pattern)),
436   - JField("$flags", JInt(lst(0).pattern.value.flags))
437   - ))),
438   - JField("uuid", JObject(List(JField("$uuid", JString(lst(0).uuid.value.toString)))))
439   - ))
  454 + ("name" -> "subrec1") ~
  455 + ("subsub" -> ("name" -> "")) ~
  456 + ("subsublist" -> JArray(Nil)) ~
  457 + ("when" -> ("$dt" -> rec.meta.formats.dateFormat.format(lst(0).when.value))) ~
  458 + ("slist" -> JArray(Nil)) ~
  459 + ("smap" -> JObject(Nil)) ~
  460 + ("oid" -> ("$oid" -> lst(0).oid.value.toString)) ~
  461 + ("pattern" ->
  462 + ("$regex" -> lst(0).pattern.value.pattern) ~
  463 + ("$flags" -> lst(0).pattern.value.flags)
  464 + ) ~
  465 + ("uuid" -> ("$uuid" -> lst(0).uuid.value.toString))
  466 +
440 467 val sr2Json =
441   - JObject(List(
442   - JField("name", JString("subrec2")),
443   - JField("subsub", JObject(List(
444   - JField("name", JString(""))
445   - ))),
446   - JField("subsublist", JArray(List())),
447   - JField("when", JObject(List(
448   - JField("$dt", JString(rec.meta.formats.dateFormat.format(lst(1).when.value)))
449   - ))),
450   - JField("slist", JArray(List())),
451   - JField("smap", JObject(List())),
452   - JField("oid", JObject(List(JField("$oid", JString(lst(1).oid.value.toString))))),
453   - JField("pattern", JObject(List(
454   - JField("$regex", JString(lst(1).pattern.value.pattern)),
455   - JField("$flags", JInt(lst(1).pattern.value.flags))
456   - ))),
457   - JField("uuid", JObject(List(JField("$uuid", JString(lst(1).uuid.value.toString)))))
458   - ))
  468 + ("name" -> "subrec2") ~
  469 + ("subsub" -> ("name" -> "")) ~
  470 + ("subsublist" -> JArray(Nil)) ~
  471 + ("when" -> ("$dt" -> rec.meta.formats.dateFormat.format(lst(1).when.value))) ~
  472 + ("slist" -> JArray(Nil)) ~
  473 + ("smap" -> JObject(Nil)) ~
  474 + ("oid" -> ("$oid" -> lst(1).oid.value.toString)) ~
  475 + ("pattern" ->
  476 + ("$regex" -> lst(1).pattern.value.pattern) ~
  477 + ("$flags" -> lst(1).pattern.value.flags)
  478 + ) ~
  479 + ("uuid" -> ("$uuid" -> lst(1).uuid.value.toString))
  480 +
459 481 val sr1JsExp = new JsExp {
460 482 def toJsCmd = compact(render(sr1Json))
461 483 }
@@ -463,7 +485,7 @@ object MongoFieldSpec extends Specification with MongoTestKit with AroundExample
463 485 def toJsCmd = compact(render(sr2Json))
464 486 }
465 487
466   - passBasicTests(lst, rec.mandatoryBsonRecordListField, rec.legacyOptionalBsonRecordListField)
  488 + passBasicTests(lst, lst2, rec.mandatoryBsonRecordListField, rec.legacyOptionalBsonRecordListField)
467 489 passConversionTests(
468 490 lst,
469 491 rec.mandatoryBsonRecordListField,
77 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2010-2011 WorldWide Conferencing, LLC
  2 + * Copyright 2010-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ class MongoRecordSpec extends Specification with MongoTestKit {
53 53 val rec = MongoFieldTypeTestRecord.createRecord
54 54 val allExpectedFieldNames: List[String] = "_id" :: "mandatoryMongoCaseClassField" ::
55 55 (for {
56   - typeName <- "Date JsonObject ObjectId Pattern UUID".split(" ")
  56 + typeName <- "Date JsonObject ObjectId UUID".split(" ")
57 57 flavor <- "mandatory legacyOptional".split(" ")
58 58 } yield flavor + typeName + "Field").toList
59 59
@@ -196,7 +196,6 @@ class MongoRecordSpec extends Specification with MongoTestKit {
196 196 .mandatoryDateField(new Date)
197 197 .mandatoryJsonObjectField(TypeTestJsonObject(1, "jsonobj1", Map("x" -> "1")))
198 198 .mandatoryObjectIdField(ObjectId.get)
199   - .mandatoryPatternField(Pattern.compile("^Mo", Pattern.CASE_INSENSITIVE))
200 199 .mandatoryUUIDField(UUID.randomUUID)
201 200 .mandatoryMongoCaseClassField(MongoCaseClassTestObject(1,"str",MyTestEnum.TWO))
202 201
@@ -205,15 +204,21 @@ class MongoRecordSpec extends Specification with MongoTestKit {
205 204 ("mandatoryDateField" -> ("$dt" -> mfttr.meta.formats.dateFormat.format(mfttr.mandatoryDateField.value))) ~
206 205 ("legacyOptionalDateField" -> (None: Option[JObject])) ~
207 206 ("mandatoryJsonObjectField" -> (("intField" -> 1) ~ ("stringField" -> "jsonobj1") ~ ("mapField" -> ("x" -> "1")))) ~
208   - ("legacyOptionalJsonObjectField" -> (("intField" -> 0) ~ ("stringField" -> "") ~ ("mapField" -> JObject(Nil)))) ~
  207 + ("legacyOptionalJsonObjectField" -> (None: Option[JObject])) ~
209 208 ("mandatoryObjectIdField", ("$oid" -> mfttr.mandatoryObjectIdField.value.toString)) ~
210 209 ("legacyOptionalObjectIdField" -> (None: Option[JObject])) ~
211   - ("mandatoryPatternField" -> (("$regex" -> mfttr.mandatoryPatternField.value.pattern) ~ ("$flags" -> mfttr.mandatoryPatternField.value.flags))) ~
212   - ("legacyOptionalPatternField" -> (None: Option[JObject])) ~
213 210 ("mandatoryUUIDField" -> ("$uuid" -> mfttr.mandatoryUUIDField.value.toString)) ~
214 211 ("legacyOptionalUUIDField" -> (None: Option[JObject])) ~
215 212 ("mandatoryMongoCaseClassField" -> ("intField" -> 1) ~ ("stringField" -> "str") ~ ("enum" -> 1))
216 213
  214 + val pftr = PatternFieldTestRecord.createRecord
  215 + .mandatoryPatternField(Pattern.compile("^Mo", Pattern.CASE_INSENSITIVE))
  216 +
  217 + val pftrJson =
  218 + ("_id" -> ("$oid" -> pftr.id.toString)) ~
  219 + ("mandatoryPatternField" -> (("$regex" -> pftr.mandatoryPatternField.value.pattern) ~ ("$flags" -> pftr.mandatoryPatternField.value.flags))) ~
  220 + ("legacyOptionalPatternField" -> (None: Option[JObject]))
  221 +
217 222 val ltr = ListTestRecord.createRecord
218 223 .mandatoryStringListField(List("abc", "def", "ghi"))
219 224 .mandatoryIntListField(List(4, 5, 6))
@@ -371,6 +376,14 @@ class MongoRecordSpec extends Specification with MongoTestKit {
371 376 tr mustEqual mfttr
372 377 }
373 378
  379 + pftr.save
  380 +
  381 + val pftrFromDb = PatternFieldTestRecord.find(pftr.id.value)
  382 + pftrFromDb.isDefined must_== true
  383 + pftrFromDb foreach { tr =>
  384 + tr mustEqual pftr
  385 + }
  386 +
374 387 ltr.save
375 388
376 389 val ltrFromDb = ListTestRecord.find(ltr.id.value)
@@ -406,6 +419,15 @@ class MongoRecordSpec extends Specification with MongoTestKit {
406 419 tr mustEqual mfttrDef
407 420 }
408 421
  422 + val pftrDef = PatternFieldTestRecord.createRecord
  423 + pftrDef.save
  424 +
  425 + val pftrFromDb = PatternFieldTestRecord.find(pftrDef.id.value)
  426 + pftrFromDb.isDefined must_== true
  427 + pftrFromDb foreach { tr =>
  428 + tr mustEqual pftrDef
  429 + }
  430 +
409 431 val ltrDef = ListTestRecord.createRecord
410 432 ltrDef.save
411 433
@@ -437,6 +459,8 @@ class MongoRecordSpec extends Specification with MongoTestKit {
437 459 "convert Mongo type fields to JValue" in {
438 460 mfttr.asJValue mustEqual mfttrJson
439 461
  462 + pftr.asJValue mustEqual pftrJson
  463 +
440 464 ltr.asJValue mustEqual ltrJson
441 465
442 466 mtr.asJValue mustEqual mtrJson
@@ -456,6 +480,12 @@ class MongoRecordSpec extends Specification with MongoTestKit {
456 480 tr mustEqual mfttr
457 481 }
458 482
  483 + val pftrFromJson = PatternFieldTestRecord.fromJsonString(compact(render(pftrJson)))
  484 + pftrFromJson.isDefined must_== true
  485 + pftrFromJson foreach { tr =>
  486 + tr mustEqual pftr
  487 + }
  488 +
459 489 val ltrFromJson = ListTestRecord.fromJsonString(compact(render(ltrJson)))
460 490 ltrFromJson.isDefined must_== true
461 491 ltrFromJson foreach { tr =>
@@ -691,7 +721,6 @@ class MongoRecordSpec extends Specification with MongoTestKit {
691 721 rec.dirtyFields.length must_== 0
692 722 }
693 723
694   -
695 724 val fttr2 = FieldTypeTestRecord.createRecord.save
696 725
697 726 fttr2.legacyOptionalStringField("legacy optional string")
@@ -730,9 +759,6 @@ class MongoRecordSpec extends Specification with MongoTestKit {
730 759 mfttr.mandatoryObjectIdField(ObjectId.get)
731 760 mfttr.mandatoryObjectIdField.dirty_? must_== true
732 761
733   - mfttr.mandatoryPatternField(Pattern.compile("^Mon", Pattern.CASE_INSENSITIVE))
734   - mfttr.mandatoryPatternField.dirty_? must_== true
735   -
736 762 mfttr.mandatoryUUIDField(UUID.randomUUID)
737 763 mfttr.mandatoryUUIDField.dirty_? must_== true
738 764
@@ -742,7 +768,7 @@ class MongoRecordSpec extends Specification with MongoTestKit {
742 768 mfttr.legacyOptionalObjectIdField(Empty)
743 769 mfttr.legacyOptionalObjectIdField.dirty_? must_== true
744 770
745   - mfttr.dirtyFields.length must_== 7
  771 + mfttr.dirtyFields.length must_== 6
746 772 mfttr.update
747 773 mfttr.dirtyFields.length must_== 0
748 774
@@ -773,6 +799,25 @@ class MongoRecordSpec extends Specification with MongoTestKit {
773 799 }
774 800 }
775 801
  802 + /* save throws an exception here but not above ???
  803 + "update dirty fields for a PatternFieldTestRecord" in {
  804 + val pftrd = PatternFieldTestRecord.createRecord.save
  805 +
  806 + pftrd.mandatoryPatternField(Pattern.compile("^Mon", Pattern.CASE_INSENSITIVE))
  807 + pftrd.mandatoryPatternField.dirty_? must_== true
  808 +
  809 + pftrd.dirtyFields.length must_== 1
  810 + pftrd.update
  811 + pftrd.dirtyFields.length must_== 0
  812 +
  813 + val fromDb = PatternFieldTestRecord.find(pftrd.id.is)
  814 + fromDb.isDefined must_== true
  815 + fromDb foreach { rec =>
  816 + rec must_== pftrd
  817 + rec.dirtyFields.length must_== 0
  818 + }
  819 + }*/
  820 +
776 821 "update dirty fields for a ListTestRecord" in {
777 822 val ltr = ListTestRecord.createRecord.save
778 823
@@ -822,8 +867,6 @@ class MongoRecordSpec extends Specification with MongoTestKit {
822 867 }
823 868
824 869 "update dirty fields for a SubRecordTestRecord" in {
825   - val srtr = SubRecordTestRecord.createRecord.save
826   -
827 870 val ssr1 = SubSubRecord.createRecord.name("SubSubRecord1")
828 871 val ssr2 = SubSubRecord.createRecord.name("SubSubRecord2")
829 872
@@ -835,9 +878,13 @@ class MongoRecordSpec extends Specification with MongoTestKit {
835 878 .smap(Map("a" -> "s1", "b" -> "s2"))
836 879 .pattern(Pattern.compile("^Mon", Pattern.CASE_INSENSITIVE))
837 880
838   - val sr2 = SubRecord.createRecord.name("SubRecord2")
  881 + val srtr = SubRecordTestRecord.createRecord
  882 + .mandatoryBsonRecordField(sr1)
  883 + .save
  884 +
  885 + val sr2 = sr1.copy.name("SubRecord2")
839 886
840   - srtr.mandatoryBsonRecordField(sr1)
  887 + srtr.mandatoryBsonRecordField(sr2)
841 888 srtr.mandatoryBsonRecordField.dirty_? must_== true
842 889
843 890 srtr.mandatoryBsonRecordListField(List(sr1,sr2))
38 persistence/record/src/main/scala/net/liftweb/record/Field.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2007-2011 WorldWide Conferencing, LLC
  2 + * Copyright 2007-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -39,6 +39,11 @@ trait BaseField extends FieldIdentifier with util.BaseField {
39 39 def dirty_? : Boolean = dirty
40 40
41 41 /**
  42 + * Should the dirty flag always be set when setBox is called
  43 + */
  44 + def forceDirty_? : Boolean = false
  45 +
  46 + /**
42 47 * Should the field be ignored by the OR Mapper?
43 48 */
44 49 def ignoreField_? = false
@@ -146,7 +151,7 @@ trait OwnedField[OwnerType <: Record[OwnerType]] extends BaseField {
146 151
147 152 /** Refined trait for fields holding a particular value type */
148 153 trait TypedField[ThisType] extends BaseField {
149   -
  154 +
150 155 /*
151 156 * Unless overriden, MyType is equal to ThisType. Available for
152 157 * backwards compatibility
@@ -225,19 +230,22 @@ trait TypedField[ThisType] extends BaseField {
225 230 case (f: Failure) => set_!(f) // preserve failures set in
226 231 case _ => Failure(notOptionalErrorMessage)
227 232 }
228   - if(!dirty_?) {
229   - val same = (oldValue, data) match {
230   - case (Full(ov), Full(nv)) => ov == nv
231   - case (a, b) => a == b
232   - }
233   - dirty_?(!same)
  233 + if (forceDirty_?) {
  234 + dirty_?(true)
  235 + }
  236 + else if (!dirty_?) {
  237 + val same = (oldValue, data) match {
  238 + case (Full(ov), Full(nv)) => ov == nv
  239 + case (a, b) => a == b
  240 + }
  241 + dirty_?(!same)
234 242 }
235 243 data
236 244 }
237 245
238 246 // Helper methods for things to easily use mixins and so on that use ValueType instead of Box[MyType], regardless of the optional-ness of the field
239 247 protected def toValueType(in: Box[MyType]): ValueType
240   -
  248 +
241 249 protected def toBoxMyType(in: ValueType): Box[MyType]
242 250
243 251 protected def set_!(in: Box[MyType]): Box[MyType] = runFilters(in, setFilterBox)
@@ -326,7 +334,7 @@ trait TypedField[ThisType] extends BaseField {
326 334 }
327 335
328 336 trait MandatoryTypedField[ThisType] extends TypedField[ThisType] with Product1[ThisType] {
329   -
  337 +
330 338 /**
331 339 * ValueType represents the type that users will work with. For MandatoryTypeField, this is
332 340 * equal to ThisType.
@@ -353,7 +361,7 @@ trait MandatoryTypedField[ThisType] extends TypedField[ThisType] with Product1[T
353 361 def value: MyType = valueBox openOr defaultValue
354 362
355 363 def get: MyType = value
356   -
  364 +
357 365 def is: MyType = value
358 366
359 367 protected def liftSetFilterToBox(in: Box[MyType]): Box[MyType] = in.map(v => setFilter.foldLeft(v)((prev, f) => f(prev)))
@@ -373,7 +381,7 @@ trait MandatoryTypedField[ThisType] extends TypedField[ThisType] with Product1[T
373 381 }
374 382
375 383 trait OptionalTypedField[ThisType] extends TypedField[ThisType] with Product1[Box[ThisType]] {
376   -
  384 +
377 385 /**
378 386 * ValueType represents the type that users will work with. For OptionalTypedField, this is
379 387 * equal to Option[ThisType].
@@ -395,16 +403,16 @@ trait OptionalTypedField[ThisType] extends TypedField[ThisType] with Product1[Bo
395 403 def set(in: Option[MyType]): Option[MyType] = setBox(in) or defaultValueBox
396 404
397 405 def toValueType(in: Box[MyType]) = in
398   -
  406 +
399 407 def toBoxMyType(in: ValueType) = in
400 408
401 409 def value: Option[MyType] = valueBox
402 410
403 411 def get: Option[MyType] = value
404   -
  412 +
405 413 def is: Option[MyType] = value
406 414
407   - protected def liftSetFilterToBox(in: Box[MyType]): Box[MyType] = setFilter.foldLeft(in){ (prev, f) =>
  415 + protected def liftSetFilterToBox(in: Box[MyType]): Box[MyType] = setFilter.foldLeft(in){ (prev, f) =>
408 416 prev match {
409 417 case fail: Failure => fail //stop on failure, otherwise some filters will clober it to Empty
410 418 case other => f(other)
21 persistence/record/src/main/scala/net/liftweb/record/MetaRecord.scala
@@ -441,6 +441,27 @@ trait MetaRecord[BaseRecord <: Record[BaseRecord]] {
441 441 }
442 442
443 443 /**
  444 + * Populate the fields of the record with values from an existing record
  445 + *
  446 + * @param inst - The record to populate
  447 + * @param rec - The Record to read from
  448 + */
  449 + def setFieldsFromRecord(inst: BaseRecord, rec: BaseRecord) {
  450 + for {
  451 + fh <- fieldList
  452 + fld <- rec.fieldByName(fh.name)
  453 + } {
  454 + fh.field(inst).setFromAny(fld.valueBox)
  455 + }
  456 + }
  457 +
  458 + def copy(rec: BaseRecord): BaseRecord = {
  459 + val inst = createRecord
  460 + setFieldsFromRecord(inst, rec)
  461 + inst
  462 + }
  463 +
  464 + /**
444 465 * Defined the order of the fields in this record
445 466 *
446 467 * @return a List of Field
32 persistence/record/src/main/scala/net/liftweb/record/Record.scala
... ... @@ -1,5 +1,5 @@
1 1 /*
2   - * Copyright 2007-2011 WorldWide Conferencing, LLC
  2 + * Copyright 2007-2012 WorldWide Conferencing, LLC
3 3 *
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
@@ -14,8 +14,8 @@
14 14 * limitations under the License.
15 15 */
16 16
17   -package net.liftweb
18   -package record
  17 +package net.liftweb
  18 +package record
19 19
20 20 import common._
21 21 import http.js.{JsExp, JsObj}
@@ -82,7 +82,7 @@ trait Record[MyType <: Record[MyType]] extends FieldContainer {