Skip to content

Commit

Permalink
Merge branch 'master' into wip_canbind
Browse files Browse the repository at this point in the history
  • Loading branch information
Naftoli Gugenheim committed Oct 16, 2011
2 parents e30ee62 + e6bab09 commit 86e294e
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 97 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -14,19 +14,18 @@
package net.liftweb package net.liftweb
package squerylrecord package squerylrecord


import common.{Box, Full} import common.{ Box, Full }
import record.{BaseField, MetaRecord, Record, TypedField, OwnedField} import record.{ BaseField, MetaRecord, Record, TypedField, OwnedField }
import record.field._ import record.field._

import org.squeryl.internals.{ FieldMetaData, PosoMetaData, FieldMetaDataFactory }
import org.squeryl.internals.{FieldMetaData, PosoMetaData, FieldMetaDataFactory}
import org.squeryl.annotations.Column import org.squeryl.annotations.Column

import java.lang.reflect.{ Method, Field }
import java.lang.reflect.{Method, Field}
import java.lang.annotation.Annotation import java.lang.annotation.Annotation
import java.sql.{ResultSet, Timestamp} import java.sql.{ ResultSet, Timestamp }
import java.util.{Calendar, Date} import java.util.{ Calendar, Date }

import scala.collection.immutable.Map 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. */ /** FieldMetaDataFactory that allows Squeryl to use Records as model objects. */
class RecordMetaDataFactory extends FieldMetaDataFactory { class RecordMetaDataFactory extends FieldMetaDataFactory {
Expand Down Expand Up @@ -58,39 +57,40 @@ class RecordMetaDataFactory extends FieldMetaDataFactory {


/** Build a Squeryl FieldMetaData for a particular field in a Record */ /** Build a Squeryl FieldMetaData for a particular field in a Record */
def build(parentMetaData: PosoMetaData[_], name: String, def build(parentMetaData: PosoMetaData[_], name: String,
property: (Option[Field], Option[Method], Option[Method], Set[Annotation]), property: (Option[Field], Option[Method], Option[Method], Set[Annotation]),
sampleInstance4OptionTypeDeduction: AnyRef, isOptimisticCounter: Boolean): FieldMetaData = { sampleInstance4OptionTypeDeduction: AnyRef, isOptimisticCounter: Boolean): FieldMetaData = {
if (!isRecord(parentMetaData.clasz) || isOptimisticCounter) { if (!isRecord(parentMetaData.clasz) || isOptimisticCounter) {
// Either this is not a Record class, in which case we'll // 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 //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. //was mixed in by the Optimisitic trait and is not a Record field.
return SquerylRecord.posoMetaDataFactory.build(parentMetaData, name, property, return SquerylRecord.posoMetaDataFactory.build(parentMetaData, name, property,
sampleInstance4OptionTypeDeduction, isOptimisticCounter) sampleInstance4OptionTypeDeduction, isOptimisticCounter)
} }


val metaField = findMetaField(parentMetaData.clasz, name) val metaField = findMetaField(parentMetaData.clasz, name)


val (field, getter, setter, annotations) = property val (field, getter, setter, annotations) = property

val colAnnotation = annotations.find(a => a.isInstanceOf[Column]).map(a => a.asInstanceOf[Column]) val colAnnotation = annotations.find(a => a.isInstanceOf[Column]).map(a => a.asInstanceOf[Column])


val fieldsValueType = metaField match { val fieldsValueType = metaField match {
case (f: SquerylRecordField) => f.classOfPersistentField case (f: SquerylRecordField) => f.classOfPersistentField
case (_: BooleanTypedField) => classOf[Boolean] case (_: BooleanTypedField) => classOf[Boolean]
case (_: DateTimeTypedField) => classOf[Timestamp] case (_: DateTimeTypedField) => classOf[Timestamp]
case (_: DoubleTypedField) => classOf[Double] case (_: DoubleTypedField) => classOf[Double]
case (_: IntTypedField) => classOf[java.lang.Integer] case (_: IntTypedField) => classOf[java.lang.Integer]
case (_: LongTypedField) => classOf[java.lang.Long] case (_: LongTypedField) => classOf[java.lang.Long]
case (_: DecimalTypedField) => classOf[BigDecimal] case (_: DecimalTypedField) => classOf[BigDecimal]
case (_: TimeZoneTypedField) => classOf[String] case (_: TimeZoneTypedField) => classOf[String]
case (_: StringTypedField) => classOf[String] case (_: StringTypedField) => classOf[String]
case (_: PasswordTypedField) => classOf[String] case (_: PasswordTypedField) => classOf[String]
case (_: BinaryTypedField) => classOf[Array[Byte]] case (_: BinaryTypedField) => classOf[Array[Byte]]
case (_: LocaleTypedField) => classOf[String] case (_: LocaleTypedField) => classOf[String]
case (_: EnumTypedField[_]) => classOf[Enumeration#Value] case (_: EnumTypedField[_]) => classOf[Int]
case (_: EnumNameTypedField[_]) => classOf[Enumeration#Value] case (_: EnumNameTypedField[_]) => classOf[String]
case _ => error("Unsupported field type. Consider implementing " + case _ => error("Unsupported field type. Consider implementing " +
"SquerylRecordField for defining the persistent class." + "SquerylRecordField for defining the persistent class." +
"Field: " + metaField) "Field: " + metaField)
} }


new FieldMetaData( new FieldMetaData(
Expand All @@ -111,67 +111,80 @@ class RecordMetaDataFactory extends FieldMetaDataFactory {
import java.math.MathContext import java.math.MathContext
val fieldLength = val fieldLength =
metaField match { metaField match {
case (stringTypedField: StringTypedField) => Some(stringTypedField.maxLength) case (stringTypedField: StringTypedField) => Some(stringTypedField.maxLength)
case decimalField: DecimalField[_] => { case decimalField: DecimalField[_] => {
val precision = decimalField.context.getPrecision(); val precision = decimalField.context.getPrecision();
if(precision != 0) if (precision != 0)
Some(precision) Some(precision)
else else
None None
} }
case decimalField: OptionalDecimalField[_] => { case decimalField: OptionalDecimalField[_] => {
val precision = decimalField.context.getPrecision(); val precision = decimalField.context.getPrecision();
if(precision != 0) if (precision != 0)
Some(precision) Some(precision)
else else
None None
} }
case _ => None case _ => None
} }
fieldLength getOrElse super.length fieldLength getOrElse super.length
} }

override def scale = { override def scale = {
val fieldScale = val fieldScale =
metaField match { metaField match {
case decimalField: DecimalField[_] => Some(decimalField.scale) case decimalField: DecimalField[_] => Some(decimalField.scale)
case decimalField: OptionalDecimalField[_] => Some(decimalField.scale) case decimalField: OptionalDecimalField[_] => Some(decimalField.scale)
case _ => None case _ => None
} }
fieldScale getOrElse super.scale 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) = override def set(target: AnyRef, value: AnyRef) = {
fieldFor(target).setFromAny(Box!!resultSetHandler(rs, index)) val typedField: TypedField[_] = fieldFor(target)
typedField.setFromAny(Box !! value)
}


override def get(o: AnyRef) = fieldFor(o).valueBox match { override def setFromResultSet(target: AnyRef, rs: ResultSet, index: Int) = set(target, resultSetHandler(rs, index))
case Full(c: Calendar) => new Timestamp(c.getTime.getTime)
case Full(other) => other override def get(o: AnyRef) = fieldFor(o) match {
case _ => null 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 * 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. * needed for such subtypes. For other classes, use the standard squeryl methods.
*/ */
private def isRecord(clasz: Class[_]) = { private def isRecord(clasz: Class[_]) = {
classOf[Record[_]].isAssignableFrom(clasz) classOf[Record[_]].isAssignableFrom(clasz)
} }



/** /**
* For records, the constructor must not be used directly when * For records, the constructor must not be used directly when
* constructing Objects. Instead, the createRecord method must be called. * constructing Objects. Instead, the createRecord method must be called.
*/ */
def createPosoFactory(posoMetaData: PosoMetaData[_]): () => AnyRef = { def createPosoFactory(posoMetaData: PosoMetaData[_]): () => AnyRef = {
if (!isRecord(posoMetaData.clasz)) { if (!isRecord(posoMetaData.clasz)) {
// No record class - use standard poso meta data factory // No record class - use standard poso meta data factory
return SquerylRecord.posoMetaDataFactory.createPosoFactory(posoMetaData); return SquerylRecord.posoMetaDataFactory.createPosoFactory(posoMetaData);
} }


// Extract the MetaRecord for the companion object. This // Extract the MetaRecord for the companion object. This
// is done only once for each class. // is done only once for each class.
Expand All @@ -180,15 +193,15 @@ class RecordMetaDataFactory extends FieldMetaDataFactory {


() => metaRecord.createRecord.asInstanceOf[AnyRef] () => metaRecord.createRecord.asInstanceOf[AnyRef]
} }

/** /**
* There needs to be a special handling for squeryl-record when single fields are selected. * 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 * 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. * opinion that the whole record should be returned, as well as the selected field.
* It is described in detail in this bug report: * 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 * 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 * By overriding this function, the reference to the record is excluded from
* the reference finding algorithm in Squeryl. * the reference finding algorithm in Squeryl.
*/ */
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ object Company extends Company with MetaRecord[Company] with CRUDify[Long, Compa
} }


object EmployeeRole extends Enumeration { object EmployeeRole extends Enumeration {

type EmployeeRole = Value type EmployeeRole = Value


val Programmer, Manager = Value val Programmer, Manager = Value
Expand Down Expand Up @@ -217,12 +218,15 @@ object MySchema extends Schema {
} }


object TestData { object TestData {

val c1 = Company.createRecord.name("First Company USA"). val c1 = Company.createRecord.name("First Company USA").
created(Calendar.getInstance()). created(Calendar.getInstance()).
country(Countries.USA).postCode("12345") country(Countries.USA).postCode("12345")

val c2 = Company.createRecord.name("Second Company USA"). val c2 = Company.createRecord.name("Second Company USA").
created(Calendar.getInstance()). created(Calendar.getInstance()).
country(Countries.USA).postCode("54321") country(Countries.USA).postCode("54321")

val c3 = Company.createRecord.name("Company or Employee"). val c3 = Company.createRecord.name("Company or Employee").
created(Calendar.getInstance()). created(Calendar.getInstance()).
country(Countries.Canada).postCode("1234") country(Countries.Canada).postCode("1234")
Expand All @@ -245,10 +249,20 @@ object MySchema extends Schema {
admin(true).departmentNumber(1).role(EmployeeRole.Manager). admin(true).departmentNumber(1).role(EmployeeRole.Manager).
photo(Array[Byte](1)) 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 r1 = Room.createRecord.name("Room 1")

val r2 = Room.createRecord.name("Room 2") val r2 = Room.createRecord.name("Room 2")

val r3 = Room.createRecord.name("Room 3") val r3 = Room.createRecord.name("Room 3")


val allRooms = List(r1, r2, r3) val allRooms = List(r1, r2, r3)
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -76,13 +76,10 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
forExample("support normal joins") in { forExample("support normal joins") in {
transaction { transaction {
val companiesWithEmployees = from(companies, employees)((c, e) => val companiesWithEmployees = from(companies, employees)((c, e) =>
where(c.id === e.id) where(c.id === e.companyId.get)
select ((c, e))) select ((c.id, e.id))).toList
val ids = companiesWithEmployees.map(entry => (entry._1.id, companiesWithEmployees must haveSize(td.allEmployees.size)
entry._2.id)) companiesWithEmployees must containAll(td.allEmployees map { e => (e.companyId.get, e.id) })
ids must haveSize(2)
ids must containAll(List((td.c1.id, td.e1.id),
(td.c2.id, td.e2.id)))
} }
} }


Expand All @@ -93,7 +90,7 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
on (c.id === e.map(_.companyId)) on (c.id === e.map(_.companyId))
) )


companiesWithEmployees must haveSize(3) companiesWithEmployees must haveSize(4)
// One company doesn't have an employee, two have // One company doesn't have an employee, two have
companiesWithEmployees.filter(ce => ce._2.isEmpty) must haveSize(1) companiesWithEmployees.filter(ce => ce._2.isEmpty) must haveSize(1)


Expand All @@ -113,11 +110,8 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
on (e1.admin === e2.map(_.admin)) on (e1.admin === e2.map(_.admin))
) )


// two employees, both have distinct admin settings
employeesWithSameAdminSetting must haveSize(2)
employeesWithSameAdminSetting.foreach { ee => employeesWithSameAdminSetting.foreach { ee =>
ee._2 must not(beEmpty) ee._2 must not(beEmpty)
ee._1.id must_== ee._2.get.id
} }


val companiesWithSameCreationDate = join(companies, companies.leftOuter)((c1, c2) => val companiesWithSameCreationDate = join(companies, companies.leftOuter)((c1, c2) =>
Expand Down Expand Up @@ -147,6 +141,8 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {
val employees = company.get.employees val employees = company.get.employees
employees must haveSize(1) employees must haveSize(1)
checkEmployeesEqual(td.e1, employees.head) checkEmployeesEqual(td.e1, employees.head)
employees.associate(td.e3)
td.e3.companyId.get must_== company.get.id
} }
} }


Expand Down Expand Up @@ -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") >> { forExample("support many to many relations") >> {
transactionWithRollback { transactionWithRollback {
td.e1.rooms must haveSize(2) td.e1.rooms must haveSize(2)
Expand Down Expand Up @@ -278,15 +267,16 @@ object SquerylRecordSpec extends Specification("SquerylRecord Specification") {


// Boolean fields: // Boolean fields:
val empAdmin: BooleanField[Employee] = from(employees)(e => where(e.admin in 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 select (e.admin)).single
empAdmin.is must_== td.e1.admin.is empAdmin.is must_== td.e2.admin.is


// Enum fields: // Enum fields:
val empRole: EnumNameField[_, _] = from(employees)(e => where(e.role in val empRoleQuery = from(employees)(e => where(e.role in
from(employees)(e2 => where(e2.id === td.e1.id) select (e2.role))) from(employees)(e2 => where(e2.id === td.e2.id) select (e2.role)))
select (e.role)).single select (e.role.get))
empRole.is must_== td.e1.role.is val empRole = empRoleQuery.single
empRole must_== td.e2.role.is
} }


} }
Expand Down

0 comments on commit 86e294e

Please sign in to comment.