ff8d393251d4dae6 Contact model
authorTomas Zeman <tzeman@volny.cz>
Fri, 10 Feb 2012 09:53:05 +0100
changeset 8 828565e7f571
parent 7 8ef5e77ad79e
child 9 fe20033afdf1
ff8d393251d4dae6 Contact model
src/main/scala/fis/base/model/BaseSchema.scala
src/main/scala/fis/base/model/Entity.scala
src/main/scala/fis/crm/model/Contact.scala
src/main/scala/fis/crm/model/CrmSchema.scala
src/test/scala/fis/base/model/AbstractTest.scala
src/test/scala/fis/base/model/CodeListSpec.scala
src/test/scala/fis/base/model/H2Vendor.scala
src/test/scala/fis/base/model/SeqIdPostgreSqlAdapterSpec.scala
src/test/scala/fis/crm/model/ContactSpec.scala
src/test/scala/fis/crm/model/CrmSchemaSpec.scala
--- a/src/main/scala/fis/base/model/BaseSchema.scala	Fri Feb 10 09:53:04 2012 +0100
+++ b/src/main/scala/fis/base/model/BaseSchema.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -15,22 +15,70 @@
  */
 package fis.base.model
 
-import org.squeryl.{Schema, Table}
 import net.liftweb.squerylrecord.RecordTypeMode._
+import net.liftweb.util.Helpers.{snakify, tryo}
+import org.squeryl.{Schema, Session, Table}
+import org.squeryl.internals.StatementWriter
+import scala.collection.mutable.ListBuffer
 
 trait BaseSchema extends Schema {
-  val codeListItems = entityTable[CodeListItem]
+  val entityTableName = "entity"
+  val entities = new Table[EntityUnion](entityTableName) {
+    val tables = new ListBuffer[String]
+  }
+
+  val codeListItems = entityTable[CodeListItem]("code_list_item")
+
+  override def tableNameFromClass(c: Class[_]): String =
+    snakify(c.getSimpleName)
+
+  protected def entityTable[T <: Entity[_]](name: String)(implicit manifestT:
+    Manifest[T]): Table[T] = {
+
+    val tbl = table(name)(manifestT)
+    on(tbl) { t => declare(t.id.is(autoIncremented("entity_id_seq"))) }
+    entities.tables += name
+    tbl
+  }
 
-  protected def entityTable[T <: Entity[_]]()(implicit manifestT: Manifest[T]):
-    Table[T] = {
+  override def create = {
+    table[EntityUnion](entityTableName) // register entity table as the last one
+    super.create
+  }
 
-    val tbl = table()
-    on(tbl) { t => declare(t.id.is(autoIncremented("entity_id_seq"))) }
-    tbl
+  override protected def drop = {
+    val dba = Session.currentSession.databaseAdapter
+    val sw = new StatementWriter(dba)
+    sw.write("drop view " + entityTableName + " cascade")
+    ex(sw)
+    super.drop
+  }
+
+  private def ex(sw: StatementWriter) = {
+    val sql = sw.statement
+    val cs = Session.currentSession
+    cs.log(sql)
+    val s = cs.connection.createStatement
+    tryo { s.execute(sql) }
+    tryo { s.close }
   }
 }
 
 object BaseSchema extends BaseSchema
 
+class EntityViewH2Adapter extends SeqIdH2Adapter {
+  override def writeCreateTable[T](t: Table[T], sw: StatementWriter,
+    schema: Schema) = schema match {
+    case sch: BaseSchema if t.name == sch.entityTableName =>
+      sw.write("create view " + t.name + " as ")
+      val it = sch.entities.tables.iterator
+      while (it.hasNext) {
+        sw.write("select id, name, note from " + it.next)
+        if (it.hasNext)
+          sw.write(" union ")
+      }
+    case _ => super.writeCreateTable(t, sw, schema)
+  }
+}
 
 // vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/base/model/Entity.scala	Fri Feb 10 09:53:04 2012 +0100
+++ b/src/main/scala/fis/base/model/Entity.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -15,7 +15,7 @@
  */
 package fis.base.model
 
-import net.liftweb.record.Record
+import net.liftweb.record.{MetaRecord, Record}
 import net.liftweb.record.field._
 import net.liftweb.squerylrecord.KeyedRecord
 import org.squeryl.annotations.Column
@@ -54,6 +54,18 @@
   def entity: T
 }
 
+class EntityUnion private() extends Record[EntityUnion] with
+  Entity[EntityUnion] {
+  def meta = EntityUnion
+  //val entityType = new LongField(this)
+}
+
+object EntityUnion extends EntityUnion with MetaRecord[EntityUnion] with
+  MetaEntity[EntityUnion] {
+
+  def getTable = BaseSchema.entities
+}
+
 /**
  * Converter interface mixin.
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/model/Contact.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fis.crm.model
+
+import fis.base.model.{Entity, MetaEntity}
+import net.liftweb.record.{MetaRecord, Record}
+import net.liftweb.record.field._
+
+class Contact private() extends Record[Contact] with Entity[Contact] {
+
+  def meta = Contact
+
+  val firstName = new StringField(this, 80, "")
+  val lastName = new StringField(this, 80, "")
+  val position = new OptionalStringField(this, 40)
+  val workMail = new EmailField(this, 256)
+  val privateMail = new OptionalEmailField(this, 256)
+  val otherMail = new OptionalEmailField(this, 256)
+  val workMobile = new StringField(this, 40)
+  val privateMobile = new OptionalStringField(this, 40)
+  val otherMobile = new OptionalStringField(this, 40)
+  val workPhone = new OptionalStringField(this, 40)
+  val privatePhone = new OptionalStringField(this, 40)
+  val fax = new OptionalStringField(this, 40)
+
+  override def linkName = lastName.get + " " + firstName.get
+
+  lazy val entities = CrmSchema.entityContacts.right(this)
+}
+
+object Contact extends Contact with MetaRecord[Contact] with
+  MetaEntity[Contact] {
+
+  def getTable = CrmSchema.contacts
+}
+
+import org.squeryl.KeyedEntity
+import org.squeryl.dsl.CompositeKey3
+
+case class EntityContact(val entityId: Long, val contactId: Long,
+  val typeId: Long) extends KeyedEntity[CompositeKey3[Long, Long, Long]] {
+
+  def id = CompositeKey3(entityId, contactId, typeId)
+
+  lazy val contactType =
+    CrmSchema.contactTypeToEntityContacts.rightStateful(this)
+}
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/model/CrmSchema.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fis.crm.model
+
+import fis.base.model.BaseSchema
+import net.liftweb.squerylrecord.RecordTypeMode._
+
+trait CrmSchema extends BaseSchema {
+
+  val contacts = entityTable[Contact]("contact")
+
+  val entityContacts = manyToManyRelation(entities, contacts).
+    via[EntityContact]((e, c, ec) => (
+      e.id === ec.entityId,
+      c.id === ec.contactId
+    ))
+
+  entityContacts.leftForeignKeyDeclaration.unConstrainReference
+
+  val contactTypeToEntityContacts = oneToManyRelation(codeListItems,
+    entityContacts).via((cli, ec) => cli.id === ec.typeId)
+}
+
+object CrmSchema extends CrmSchema
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/scala/fis/base/model/AbstractTest.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fis.base.model
+
+import net.liftweb.common._
+import net.liftweb.mapper.DB
+import net.liftweb.squerylrecord.SquerylRecord
+import org.scalatest._
+import org.scalatest.matchers.ShouldMatchers
+
+abstract class AbstractTest extends FlatSpec with ShouldMatchers with Logger
+  with BeforeAndAfterEach with BeforeAndAfterAll {
+
+  val db = this.getClass.getSimpleName
+  val h2vendor = H2Vendor(db)
+  val ci = TestConnectionIdentifier(db)
+
+  override def beforeAll() {
+    beforeEach()
+  }
+
+  override def beforeEach() {
+    super.beforeEach
+    DB.defineConnectionManager(ci, h2vendor)
+    SquerylRecord.init(() => new EntityViewH2Adapter)
+  }
+
+  override def afterEach() {
+    h2vendor.closeAllConnections_!
+  }
+
+  def doInDB(block: => Any) {
+    DB.use(ci) { _ => block }
+  }
+
+}
+
+import org.squeryl.Schema
+
+trait DropAndCreate { self: Schema =>
+  def dropAndCreate {
+    drop
+    create
+  }
+}
+
+
+// vim: set ts=2 sw=2 et:
--- a/src/test/scala/fis/base/model/CodeListSpec.scala	Fri Feb 10 09:53:04 2012 +0100
+++ b/src/test/scala/fis/base/model/CodeListSpec.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -17,23 +17,16 @@
 
 import net.liftweb.common._
 import org.scalatest._
-import org.scalatest.matchers.ShouldMatchers
 
-class CodeListSpec extends FlatSpec with ShouldMatchers with Logger
-  with BeforeAndAfterAllFunctions {
+class CodeListSpec extends AbstractTest {
 
-  import net.liftweb.mapper.{DB, DefaultConnectionIdentifier, StandardDBVendor}
   import net.liftweb.squerylrecord.RecordTypeMode._
   import net.liftweb.squerylrecord.SquerylRecord
-  import TestBaseSchema._
+  import BaseSchema._
 
-  val dbVendor = new StandardDBVendor("org.h2.Driver",
-    "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", Empty, Empty)
-  DB.defineConnectionManager(DefaultConnectionIdentifier, dbVendor)
-  SquerylRecord.init(() => new SeqIdH2Adapter)
-
-  beforeAll {
-    doInDB { dropAndCreate }
+  override def beforeAll {
+    super.beforeAll
+    doInDB { schema.dropAndCreate }
   }
 
   "CodeListItem" should "create/retrieve item" in {
@@ -50,16 +43,7 @@
     }
   }
 
-  def doInDB(block: => Any) {
-    DB.use(DefaultConnectionIdentifier) { _ => block }
-  }
-}
-
-object TestBaseSchema extends BaseSchema {
-  def dropAndCreate {
-    drop
-    create
-  }
+  object schema extends BaseSchema with DropAndCreate
 }
 
 // vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/scala/fis/base/model/H2Vendor.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fis.base.model
+
+import net.liftweb.common._
+import net.liftweb.mapper.ConnectionIdentifier
+import net.tz.lift.util.StandardDBVendor
+
+case class H2Vendor(val db: String) extends StandardDBVendor("org.h2.Driver",
+  "jdbc:h2:mem:"+ db + ";DB_CLOSE_DELAY=-1", Empty, Empty)
+
+case class TestConnectionIdentifier(val jndiName: String) extends
+  ConnectionIdentifier
+
+// vim: set ts=2 sw=2 et:
--- a/src/test/scala/fis/base/model/SeqIdPostgreSqlAdapterSpec.scala	Fri Feb 10 09:53:04 2012 +0100
+++ b/src/test/scala/fis/base/model/SeqIdPostgreSqlAdapterSpec.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -23,7 +23,7 @@
   with Logger with BeforeAndAfterAllFunctions {
 
   import java.sql.Connection
-  import net.liftweb.mapper.{DB, DefaultConnectionIdentifier}
+  import net.liftweb.mapper.DB
   import net.liftweb.squerylrecord.SquerylRecord
   import net.tz.lift.util.StandardDBVendor
   import org.squeryl.PrimitiveTypeMode._
@@ -36,7 +36,7 @@
       c.prepareStatement(testQuery).executeQuery
     }
   }
-  DB.defineConnectionManager(DefaultConnectionIdentifier, dbVendor)
+  DB.defineConnectionManager(PgsqlConnectionIdentifier, dbVendor)
   SquerylRecord.init(() => new SeqIdPostgreSqlAdapter)
 
   beforeAll {
@@ -70,13 +70,19 @@
   }
 
   def doInDB(block: => Any) {
-    DB.use(DefaultConnectionIdentifier) { c =>
+    DB.use(PgsqlConnectionIdentifier) { c =>
       block
       c.commit
     }
   }
 }
 
+import net.liftweb.db.ConnectionIdentifier
+
+case object PgsqlConnectionIdentifier extends ConnectionIdentifier {
+  val jndiName = "pgsql"
+}
+
 import org.squeryl.{KeyedEntity, Schema}
 import org.squeryl.PrimitiveTypeMode._
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/scala/fis/crm/model/ContactSpec.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fis.crm.model
+
+import fis.base.model._
+import net.liftweb.common._
+import net.liftweb.util._
+import net.liftweb.util.Helpers._
+import org.scalatest._
+
+class ContactSpec extends AbstractTest {
+  import CrmSchema._
+
+  override def beforeAll() {
+    super.beforeAll
+    doInDB { schema.dropAndCreate }
+  }
+
+  "EntityContact" should "create" in { doInDB {
+    val c1 = Contact.createRecord.name("c1")
+    contacts.insert(c1)
+    val t1 = CodeListItem.createRecord.name("t1")
+    codeListItems.insert(t1)
+    val c2 = Contact.createRecord.name("c2") // acts here as entity
+    contacts.insert(c2)
+    entityContacts.insert(EntityContact(c2.id, c1.id, t1.id))
+
+    val el1 = c1.entities.toList
+    el1.size should equal (1)
+    el1.head.id should equal (c2.id)
+    el1.head.name.get should equal ("c2")
+    val al1 = c1.entities.associations.toList
+    al1.size should equal (1)
+    al1.head.contactType.one should equal (Some(t1))
+  }}
+
+  object schema extends CrmSchema with DropAndCreate
+
+}
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/scala/fis/crm/model/CrmSchemaSpec.scala	Fri Feb 10 09:53:05 2012 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package fis.crm.model
+
+import fis.base.model._
+import net.liftweb.common._
+import net.liftweb.util._
+import net.liftweb.util.Helpers._
+
+class CrmSchemaSpec extends AbstractTest {
+
+  "CrmSchema" should "create" in { doInDB {
+    schema.dropAndCreate
+  }}
+
+  object schema extends CrmSchema with DropAndCreate
+
+}
+
+
+
+// vim: set ts=2 sw=2 et: