Permalink
Browse files

Merge branch 'master' into wip_canbind

  • Loading branch information...
nafg committed Oct 16, 2011
2 parents e30ee62 + e6bab09 commit 86e294e35c277434b951d70531e367d008e4dfc2
@@ -14,19 +14,18 @@
package net.liftweb
package squerylrecord
-import common.{Box, Full}
-import record.{BaseField, MetaRecord, Record, TypedField, OwnedField}
+import common.{ Box, Full }
+import record.{ BaseField, MetaRecord, Record, TypedField, OwnedField }
import record.field._
-
-import org.squeryl.internals.{FieldMetaData, PosoMetaData, FieldMetaDataFactory}
+import org.squeryl.internals.{ FieldMetaData, PosoMetaData, FieldMetaDataFactory }
import org.squeryl.annotations.Column
-
-import java.lang.reflect.{Method, Field}
+import java.lang.reflect.{ Method, Field }
import java.lang.annotation.Annotation
-import java.sql.{ResultSet, Timestamp}
-import java.util.{Calendar, Date}
-
+import java.sql.{ ResultSet, Timestamp }
+import java.util.{ Calendar, Date }
import scala.collection.immutable.Map
+import net.liftweb.util.Settable
+import net.liftweb.record.OptionalTypedField
/** FieldMetaDataFactory that allows Squeryl to use Records as model objects. */
class RecordMetaDataFactory extends FieldMetaDataFactory {
@@ -58,39 +57,40 @@ class RecordMetaDataFactory extends FieldMetaDataFactory {
/** Build a Squeryl FieldMetaData for a particular field in a Record */
def build(parentMetaData: PosoMetaData[_], name: String,
- property: (Option[Field], Option[Method], Option[Method], Set[Annotation]),
- sampleInstance4OptionTypeDeduction: AnyRef, isOptimisticCounter: Boolean): FieldMetaData = {
- if (!isRecord(parentMetaData.clasz) || isOptimisticCounter) {
- // Either this is not a Record class, in which case we'll
- //treat it as a normal class in primitive type mode, or the field
- //was mixed in by the Optimisitic trait and is not a Record field.
- return SquerylRecord.posoMetaDataFactory.build(parentMetaData, name, property,
- sampleInstance4OptionTypeDeduction, isOptimisticCounter)
- }
+ property: (Option[Field], Option[Method], Option[Method], Set[Annotation]),
+ sampleInstance4OptionTypeDeduction: AnyRef, isOptimisticCounter: Boolean): FieldMetaData = {
+ if (!isRecord(parentMetaData.clasz) || isOptimisticCounter) {
+ // Either this is not a Record class, in which case we'll
+ //treat it as a normal class in primitive type mode, or the field
+ //was mixed in by the Optimisitic trait and is not a Record field.
+ return SquerylRecord.posoMetaDataFactory.build(parentMetaData, name, property,
+ sampleInstance4OptionTypeDeduction, isOptimisticCounter)
+ }
val metaField = findMetaField(parentMetaData.clasz, name)
val (field, getter, setter, annotations) = property
+
val colAnnotation = annotations.find(a => a.isInstanceOf[Column]).map(a => a.asInstanceOf[Column])
val fieldsValueType = metaField match {
- case (f: SquerylRecordField) => f.classOfPersistentField
- case (_: BooleanTypedField) => classOf[Boolean]
+ case (f: SquerylRecordField) => f.classOfPersistentField
+ case (_: BooleanTypedField) => classOf[Boolean]
case (_: DateTimeTypedField) => classOf[Timestamp]
- case (_: DoubleTypedField) => classOf[Double]
- case (_: IntTypedField) => classOf[java.lang.Integer]
- case (_: LongTypedField) => classOf[java.lang.Long]
- case (_: DecimalTypedField) => classOf[BigDecimal]
- case (_: TimeZoneTypedField) => classOf[String]
- case (_: StringTypedField) => classOf[String]
- case (_: PasswordTypedField) => classOf[String]
- case (_: BinaryTypedField) => classOf[Array[Byte]]
- case (_: LocaleTypedField) => classOf[String]
- case (_: EnumTypedField[_]) => classOf[Enumeration#Value]
- case (_: EnumNameTypedField[_]) => classOf[Enumeration#Value]
+ case (_: DoubleTypedField) => classOf[Double]
+ case (_: IntTypedField) => classOf[java.lang.Integer]
+ case (_: LongTypedField) => classOf[java.lang.Long]
+ case (_: DecimalTypedField) => classOf[BigDecimal]
+ case (_: TimeZoneTypedField) => classOf[String]
+ case (_: StringTypedField) => classOf[String]
+ case (_: PasswordTypedField) => classOf[String]
+ case (_: BinaryTypedField) => classOf[Array[Byte]]
+ case (_: LocaleTypedField) => classOf[String]
+ case (_: EnumTypedField[_]) => classOf[Int]
+ case (_: EnumNameTypedField[_]) => classOf[String]
case _ => error("Unsupported field type. Consider implementing " +
- "SquerylRecordField for defining the persistent class." +
- "Field: " + metaField)
+ "SquerylRecordField for defining the persistent class." +
+ "Field: " + metaField)
}
new FieldMetaData(
@@ -111,67 +111,80 @@ class RecordMetaDataFactory extends FieldMetaDataFactory {
import java.math.MathContext
val fieldLength =
metaField match {
- case (stringTypedField: StringTypedField) => Some(stringTypedField.maxLength)
- case decimalField: DecimalField[_] => {
- val precision = decimalField.context.getPrecision();
- if(precision != 0)
- Some(precision)
- else
- None
- }
- case decimalField: OptionalDecimalField[_] => {
- val precision = decimalField.context.getPrecision();
- if(precision != 0)
- Some(precision)
- else
- None
- }
- case _ => None
- }
+ case (stringTypedField: StringTypedField) => Some(stringTypedField.maxLength)
+ case decimalField: DecimalField[_] => {
+ val precision = decimalField.context.getPrecision();
+ if (precision != 0)
+ Some(precision)
+ else
+ None
+ }
+ case decimalField: OptionalDecimalField[_] => {
+ val precision = decimalField.context.getPrecision();
+ if (precision != 0)
+ Some(precision)
+ else
+ None
+ }
+ case _ => None
+ }
fieldLength getOrElse super.length
}
-
+
override def scale = {
- val fieldScale =
+ val fieldScale =
metaField match {
- case decimalField: DecimalField[_] => Some(decimalField.scale)
- case decimalField: OptionalDecimalField[_] => Some(decimalField.scale)
- case _ => None
- }
+ case decimalField: DecimalField[_] => Some(decimalField.scale)
+ case decimalField: OptionalDecimalField[_] => Some(decimalField.scale)
+ case _ => None
+ }
fieldScale getOrElse super.scale
}
- private def fieldFor(o: AnyRef) = getter.get.invoke(o).asInstanceOf[TypedField[AnyRef]]
+ private def fieldFor(o: AnyRef) = getter.get.invoke(o).asInstanceOf[TypedField[_ <: AnyRef]]
- override def setFromResultSet(target: AnyRef, rs: ResultSet, index: Int) =
- fieldFor(target).setFromAny(Box!!resultSetHandler(rs, index))
+ override def set(target: AnyRef, value: AnyRef) = {
+ val typedField: TypedField[_] = fieldFor(target)
+ typedField.setFromAny(Box !! value)
+ }
- override def get(o: AnyRef) = fieldFor(o).valueBox match {
- case Full(c: Calendar) => new Timestamp(c.getTime.getTime)
- case Full(other) => other
- case _ => null
+ override def setFromResultSet(target: AnyRef, rs: ResultSet, index: Int) = set(target, resultSetHandler(rs, index))
+
+ override def get(o: AnyRef) = fieldFor(o) match {
+ case enumField: EnumTypedField[_] => enumField.valueBox match {
+ case Full(enum: Enumeration#Value) => enum.id: java.lang.Integer
+ case _ => null
+ }
+ case enumNameField: EnumNameTypedField[_] => enumNameField valueBox match {
+ case Full(enum: Enumeration#Value) => enum.toString
+ case _ => null
+ }
+ case other => other.valueBox match {
+ case Full(c: Calendar) => new Timestamp(c.getTime.getTime)
+ case Full(other) => other
+ case _ => null
+ }
}
}
}
-
+
/**
* Checks if the given class is a subclass of Record. A special handling is only
* needed for such subtypes. For other classes, use the standard squeryl methods.
*/
private def isRecord(clasz: Class[_]) = {
classOf[Record[_]].isAssignableFrom(clasz)
}
-
/**
* For records, the constructor must not be used directly when
* constructing Objects. Instead, the createRecord method must be called.
*/
def createPosoFactory(posoMetaData: PosoMetaData[_]): () => AnyRef = {
- if (!isRecord(posoMetaData.clasz)) {
- // No record class - use standard poso meta data factory
- return SquerylRecord.posoMetaDataFactory.createPosoFactory(posoMetaData);
- }
+ if (!isRecord(posoMetaData.clasz)) {
+ // No record class - use standard poso meta data factory
+ return SquerylRecord.posoMetaDataFactory.createPosoFactory(posoMetaData);
+ }
// Extract the MetaRecord for the companion object. This
// is done only once for each class.
@@ -180,15 +193,15 @@ class RecordMetaDataFactory extends FieldMetaDataFactory {
() => metaRecord.createRecord.asInstanceOf[AnyRef]
}
-
+
/**
* There needs to be a special handling for squeryl-record when single fields are selected.
- *
+ *
* The problem was that record fields reference the record itself and thus Squeryl was of the
* opinion that the whole record should be returned, as well as the selected field.
* It is described in detail in this bug report:
* https://www.assembla.com/spaces/liftweb/tickets/876-record-squeryl-selecting-unspecified-columns-in-generated-sql
- *
+ *
* By overriding this function, the reference to the record is excluded from
* the reference finding algorithm in Squeryl.
*/
@@ -90,6 +90,7 @@ object Company extends Company with MetaRecord[Company] with CRUDify[Long, Compa
}
object EmployeeRole extends Enumeration {
+
type EmployeeRole = Value
val Programmer, Manager = Value
@@ -217,12 +218,15 @@ object MySchema extends Schema {
}
object TestData {
+
val c1 = Company.createRecord.name("First Company USA").
created(Calendar.getInstance()).
country(Countries.USA).postCode("12345")
+
val c2 = Company.createRecord.name("Second Company USA").
created(Calendar.getInstance()).
country(Countries.USA).postCode("54321")
+
val c3 = Company.createRecord.name("Company or Employee").
created(Calendar.getInstance()).
country(Countries.Canada).postCode("1234")
@@ -245,10 +249,20 @@ object MySchema extends Schema {
admin(true).departmentNumber(1).role(EmployeeRole.Manager).
photo(Array[Byte](1))
- lazy val allEmployees = List(e1, e2)
+ lazy val e3 = Employee.createRecord.companyId(c2.idField.is).
+ name("Joe Shmo").
+ email("joe@shmo.com").salary(BigDecimal("100000.00")).
+ locale(java.util.Locale.US.toString()).
+ timeZone("America/Los_Angeles").password("test").
+ admin(false).departmentNumber(1).role(EmployeeRole.Programmer).
+ photo(Array[Byte](1))
+
+ lazy val allEmployees = List(e1, e2, e3)
val r1 = Room.createRecord.name("Room 1")
+
val r2 = Room.createRecord.name("Room 2")
+
val r3 = Room.createRecord.name("Room 3")
val allRooms = List(r1, r2, r3)
@@ -76,13 +76,10 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
forExample("support normal joins") in {
transaction {
val companiesWithEmployees = from(companies, employees)((c, e) =>
- where(c.id === e.id)
- select ((c, e)))
- val ids = companiesWithEmployees.map(entry => (entry._1.id,
- entry._2.id))
- ids must haveSize(2)
- ids must containAll(List((td.c1.id, td.e1.id),
- (td.c2.id, td.e2.id)))
+ where(c.id === e.companyId.get)
+ select ((c.id, e.id))).toList
+ companiesWithEmployees must haveSize(td.allEmployees.size)
+ companiesWithEmployees must containAll(td.allEmployees map { e => (e.companyId.get, e.id) })
}
}
@@ -93,7 +90,7 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
on (c.id === e.map(_.companyId))
)
- companiesWithEmployees must haveSize(3)
+ companiesWithEmployees must haveSize(4)
// One company doesn't have an employee, two have
companiesWithEmployees.filter(ce => ce._2.isEmpty) must haveSize(1)
@@ -113,11 +110,8 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
on (e1.admin === e2.map(_.admin))
)
- // two employees, both have distinct admin settings
- employeesWithSameAdminSetting must haveSize(2)
employeesWithSameAdminSetting.foreach { ee =>
ee._2 must not(beEmpty)
- ee._1.id must_== ee._2.get.id
}
val companiesWithSameCreationDate = join(companies, companies.leftOuter)((c1, c2) =>
@@ -147,6 +141,8 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
val employees = company.get.employees
employees must haveSize(1)
checkEmployeesEqual(td.e1, employees.head)
+ employees.associate(td.e3)
+ td.e3.companyId.get must_== company.get.id
}
}
@@ -209,13 +205,6 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
}
}
- forExample("support associate with one-to-many relations") >> {
- transactionWithRollback {
- //td.c3.employees.associate(td.e2)
- //td.e2.company.id must_== td.c3.id
- }
- }
-
forExample("support many to many relations") >> {
transactionWithRollback {
td.e1.rooms must haveSize(2)
@@ -278,15 +267,16 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
// Boolean fields:
val empAdmin: BooleanField[Employee] = from(employees)(e => where(e.admin in
- from(employees)(e2 => where(e2.id === td.e1.id) select (e2.admin)))
+ from(employees)(e2 => where(e2.id === td.e2.id) select (e2.admin)))
select (e.admin)).single
- empAdmin.is must_== td.e1.admin.is
+ empAdmin.is must_== td.e2.admin.is
// Enum fields:
- val empRole: EnumNameField[_, _] = from(employees)(e => where(e.role in
- from(employees)(e2 => where(e2.id === td.e1.id) select (e2.role)))
- select (e.role)).single
- empRole.is must_== td.e1.role.is
+ val empRoleQuery = from(employees)(e => where(e.role in
+ from(employees)(e2 => where(e2.id === td.e2.id) select (e2.role)))
+ select (e.role.get))
+ val empRole = empRoleQuery.single
+ empRole must_== td.e2.role.is
}
}

0 comments on commit 86e294e

Please sign in to comment.