Code lists refactoring
authorTomas Zeman <tzeman@volny.cz>
Tue, 03 Apr 2012 11:47:03 +0200
changeset 31 0b74e7bbe6ff
parent 30 eba7121e29cd
child 32 4874ab3a7e86
Code lists refactoring
src/main/scala/fis/base/model/BaseSchema.scala
src/main/scala/fis/base/model/CodeList.scala
src/main/scala/fis/base/model/codeLists.scala
src/main/scala/fis/cl/model/CodeList.scala
src/main/scala/fis/cl/model/CodeListCrud.scala
src/main/scala/fis/cl/model/CodeListItem.scala
src/main/scala/fis/cl/model/CodeListItemField.scala
src/main/scala/fis/cl/model/CodeListSchema.scala
src/main/scala/fis/top/model/FisDbSchema.scala
src/test/scala/fis/base/model/CodeListSpec.scala
src/test/scala/fis/cl/model/CodeListSpec.scala
src/test/scala/fis/crm/model/ContactSpec.scala
--- a/src/main/scala/fis/base/model/BaseSchema.scala	Tue Apr 03 11:47:02 2012 +0200
+++ b/src/main/scala/fis/base/model/BaseSchema.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -15,11 +15,17 @@
  */
 package fis.base.model
 
+import net.liftweb.squerylrecord.RecordTypeMode._
 import net.liftweb.util.Helpers.snakify
-import org.squeryl.Schema
+import org.squeryl._
 
 trait BaseSchema extends Schema {
-  val codeListItems = table[CodeListItem]
+  protected def tableWithSeq[T <: KeyedEntity[Long]]()(
+    implicit manifestT: Manifest[T]): Table[T] = {
+    val tbl = table()(manifestT)
+    on(tbl)(t => declare(t.id.is(autoIncremented(tbl.name + "_id_seq"))))
+    tbl
+  }
 
   override def tableNameFromClass(c: Class[_]): String =
     snakify(c.getSimpleName)
--- a/src/main/scala/fis/base/model/CodeList.scala	Tue Apr 03 11:47:02 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * 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.record.{Field, MetaRecord, Record}
-import net.liftweb.record.field._
-import net.liftweb.squerylrecord.KeyedRecord
-import net.liftweb.squerylrecord.RecordTypeMode._
-import scala.xml.{NodeSeq, Text}
-
-class CodeListItem private () extends Record[CodeListItem] with
-  Entity[CodeListItem] {
-
-  def meta = CodeListItem
-
-  /** I18n flag for this item's name. */
-  val i18n = new BooleanField(this)
-  /** Default item flag. */
-  val dflt = new BooleanField(this)
-  /** Reference to code list id (enum id). */
-  val codeListType = new IntField(this)
-
-  override def toXHtml = Text(linkName)
-}
-
-object CodeListItem extends CodeListItem with MetaRecord[CodeListItem] with
-  MetaEntity[CodeListItem] {
-
-  def getTable = BaseSchema.codeListItems
-
-  def items(codeListType: Int) = from(getTable) { cli =>
-    where(cli.codeListType === codeListType)
-    select(cli)
-  }
-
-  def defaultItem(codeListType: Int): Box[CodeListItem] =
-    items(codeListType) find (_.dflt.get)
-}
-
-trait CodeList[T] {
-  def items: Iterable[T]
-  def defaultItem: Box[T]
-  def item(id: Long): Box[T]
-}
-
-class CodeListImpl[T](val id: CodeListRegister.CodeListRegister,
-  tr: CodeListItem => T) extends CodeList[T] {
-
-  def items = CodeListItem.items(id.id) map (tr(_))
-  def defaultItem = CodeListItem.defaultItem(id.id) map (tr(_))
-  def item(id: Long) = CodeListItem.findByKey(id) map (tr(_))
-}
-
-import net.liftweb.http.SHtml
-import net.liftweb.util.Helpers._
-import CodeListRegister.CodeListRegister
-
-class CodeListItemField[T, OwnerType <: Record[OwnerType]](own: OwnerType,
-  codeListType: CodeListRegister, tr: CodeListItem => T)
-  extends AbstractCodeListItemField[T, OwnerType](own, codeListType) {
-
-  def cvt: Box[T] = item map { tr(_) }
-}
-
-abstract class AbstractCodeListItemField[T, OwnerType <: Record[OwnerType]](
-  own: OwnerType, codeListType: CodeListRegister) extends LongField(own) with
-  Converter0[Box[T]] {
-
-  def item: Box[CodeListItem] = CodeListItem.findByKey(get)
-
-  override def defaultValueBox: Box[Long] =
-    CodeListItem.defaultItem(codeListType.id).map { _.id }
-
-  override def toXHtml = item map { _.toXHtml } openOr NodeSeq.Empty
-
-  protected def buildDisplayList: List[(Long, String)] =
-    CodeListItem.items(codeListType.id).toList.map { i =>
-      (i.id, i.linkName)
-    }
-
-  private def elem = SHtml.selectObj[Long](buildDisplayList, Full(get),
-    set(_)) % ("tabindex" -> tabIndex.toString)
-
-  override def toForm: Box[NodeSeq] = uniqueFieldId match {
-    case Full(id) => Full(elem % ("id" -> id))
-    case _ => Full(elem)
-  }
-}
- 
-// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/base/model/codeLists.scala	Tue Apr 03 11:47:02 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-/*
- * 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
-
-object CodeListRegister extends Enumeration {
-  type CodeListRegister = Val
-  val TestCL = new Val(-1, "TestCL")
-
-}
-
-// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/cl/model/CodeList.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012 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.cl.model
+
+import net.liftweb.common._
+import net.liftweb.squerylrecord.RecordTypeMode._
+
+/**
+ * Code list API.
+ */
+trait CodeListLike[T] {
+  def items: Iterable[T]
+  def dflt: Box[T]
+  def apply(id: Long): Option[T]
+}
+
+case class CodeList(codeList: Symbol) extends CodeListLike[CodeListItem] with
+  CodeListCrud {
+
+  lazy val codeListStr = codeList.name
+
+  def apply(id: Long) = CodeListSchema.cli.lookup(id)
+
+  def items = from(table)(
+    i => where(i.codeList === codeListStr and i.deleted === false)
+    select(i)
+    orderBy(i.rank asc)
+  )
+
+  def dflt = from(table)(
+    i => where(i.codeList === codeListStr and i.dflt === true)
+    select(i)
+  ).page(0, 1) headOption
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/cl/model/CodeListCrud.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2012 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.cl.model
+
+import fis.base.model.RecordCrud
+
+trait CodeListCrud extends RecordCrud[CodeListItem] {
+  val table = CodeListSchema.cli
+}
+
+object CodeListCrud extends CodeListCrud
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/cl/model/CodeListItem.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011-2012 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.cl.model
+
+import fis.base.model.Entity
+import net.liftweb.common._
+import net.liftweb.record.{Field, MetaRecord, Record}
+import net.liftweb.record.field._
+import net.liftweb.squerylrecord.RecordTypeMode._
+import scala.xml.{NodeSeq, Text}
+
+class CodeListItem private () extends Record[CodeListItem] with
+  Entity[CodeListItem] {
+
+  def meta = CodeListItem
+
+  /** I18n flag for this item's name. */
+  val i18n = new BooleanField(this)
+  /** Default item flag. */
+  val dflt = new BooleanField(this)
+  /** Reference to code list id. */
+  val codeList = new StringField(this, 40) {
+    override def setFromAny(in: Any): Box[String] = in match {
+      case s: Symbol => setFromString(s.name)
+      case x => super.setFromAny(x)
+    }
+    def apply(in: Symbol): CodeListItem = super.apply(in.name)
+  }
+  /** Deleted flag. */
+  val deleted = new BooleanField(this)
+  /** Rank - ordering.*/
+  val rank = new IntField(this)
+
+  override def toXHtml = Text(linkName)
+
+  val s1 = new StringField(this, 200)
+  val s2 = new StringField(this, 200)
+  val s3 = new StringField(this, 200)
+
+  val l1 = new LongField(this)
+  val l2 = new LongField(this)
+  val l3 = new LongField(this)
+
+  val i1 = new IntField(this)
+  val i2 = new IntField(this)
+  val i3 = new IntField(this)
+
+  val os1 = new OptionalStringField(this, 200)
+  val os2 = new OptionalStringField(this, 200)
+  val os3 = new OptionalStringField(this, 200)
+
+  val ol1 = new OptionalLongField(this)
+  val ol2 = new OptionalLongField(this)
+  val ol3 = new OptionalLongField(this)
+
+  val oi1 = new OptionalIntField(this)
+  val oi2 = new OptionalIntField(this)
+  val oi3 = new OptionalIntField(this)
+}
+
+object CodeListItem extends CodeListItem with MetaRecord[CodeListItem]
+
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/cl/model/CodeListItemField.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 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.cl.model
+
+import net.tz.lift.model.FieldLabel
+import net.liftweb.common._
+import net.liftweb.http.SHtml
+import net.liftweb.record.{Field, MetaRecord, Record}
+import net.liftweb.record.field._
+import net.liftweb.util.Helpers._
+import scala.xml.{Elem, NodeSeq, Text}
+
+class CodeListItemField[T <: Record[T]](own: T, val cl: CodeList,
+  val dispF: CodeListItem => String, val htmlF: CodeListItem => NodeSeq)
+  extends LongField(own) with FieldLabel {
+
+  def this(o: T, id: Symbol) = this(o, CodeList(id), _.linkName, _.toXHtml)
+
+  def item: Box[CodeListItem] = cl(get)
+
+  override def defaultValueBox: Box[Long] = cl.dflt.map { _.id }
+
+  override def asHtml = item map { htmlF } openOr NodeSeq.Empty
+
+  protected def buildDisplayList: List[(Long, String)] =
+    cl.items.toList.map { i => (i.id, dispF(i)) }
+
+  private def elem = SHtml.selectObj[Long](buildDisplayList, Full(get),
+    set(_)) % ("tabindex" -> tabIndex.toString)
+
+  override def toForm: Box[NodeSeq] = uniqueFieldId match {
+    case Full(id) => Full(elem % ("id" -> id))
+    case _ => Full(elem)
+  }
+}
+
+class OptionalCodeListItemField[T <: Record[T]](own: T, val cl: CodeList,
+  val dispF: CodeListItem => String, val htmlF: CodeListItem => NodeSeq)
+  extends OptionalLongField(own) with FieldLabel {
+
+  def this(o: T, id: Symbol) = this(o, CodeList(id), _.linkName, _.toXHtml)
+
+  def item: Box[CodeListItem] = get flatMap { cl(_) }
+
+  override def defaultValueBox: Box[Long] = cl.dflt.map { _.id }
+
+  override def asHtml = item map { htmlF } openOr NodeSeq.Empty
+
+  protected def buildDisplayList: List[(Box[Long], String)] =
+    (Empty, "---") ::
+    cl.items.toList.map { i => (Full(i.id), dispF(i)) }
+
+  private def elem = SHtml.selectObj[Box[Long]](buildDisplayList, Full(get),
+    set(_)) % ("tabindex" -> tabIndex.toString)
+
+  override def toForm: Box[NodeSeq] = uniqueFieldId match {
+    case Full(id) => Full(elem % ("id" -> id))
+    case _ => Full(elem)
+  }
+}
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/cl/model/CodeListSchema.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012 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.cl.model
+
+import fis.base.model.BaseSchema
+import net.liftweb.squerylrecord.RecordTypeMode._
+
+trait CodeListSchema extends BaseSchema {
+  val cli = tableWithSeq[CodeListItem]
+  on(cli)(t => declare(
+    t.codeList  defineAs(indexed("code_list_item_code_list_idx"))
+  ))
+}
+
+object CodeListSchema extends CodeListSchema
+
+
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/top/model/FisDbSchema.scala	Tue Apr 03 11:47:02 2012 +0200
+++ b/src/main/scala/fis/top/model/FisDbSchema.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -16,10 +16,12 @@
 package fis.top.model
 
 import fis.base.model.BaseSchema
+import fis.cl.model.CodeListSchema
 import fis.crm.model.CrmSchema
 import fis.geo.model.GeoSchema
 
 trait FisDbSchema extends BaseSchema
+  with CodeListSchema
   with CrmSchema
   with GeoSchema
 
--- a/src/test/scala/fis/base/model/CodeListSpec.scala	Tue Apr 03 11:47:02 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright 2011-2012 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 org.scalatest._
-
-class CodeListSpec extends AbstractTest {
-
-  import net.liftweb.squerylrecord.RecordTypeMode._
-  import net.liftweb.squerylrecord.SquerylRecord
-  import BaseSchema._
-
-  override def beforeAll {
-    super.beforeAll
-    doInDB { schema.drop }
-    doInDB { schema.create }
-  }
-
-  "CodeListItem" should "create/retrieve item" in {
-    doInDB {
-      val cli1 = CodeListItem.createRecord.name("cli1").codeListType(1)
-      codeListItems.insert(cli1)
-      cli1.id should equal (1)
-      val l1 = from(codeListItems)(cli => select(cli)).toList
-      l1.size should equal (1)
-      val cl = new CodeListImpl(CodeListRegister.TestCL, { i => i } )
-      val l2 = cl.items
-      l2.size should equal (1)
-      l2.head should equal (cli1)
-    }
-  }
-
-  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/cl/model/CodeListSpec.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2011-2012 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.cl.model
+
+import fis.base.model.{AbstractTest, DropAndCreate}
+import net.liftweb.common._
+import net.liftweb.squerylrecord.RecordTypeMode._
+import net.liftweb.squerylrecord.SquerylRecord
+import org.scalatest._
+
+class CodeListSpec extends AbstractTest with BeforeAndAfterAll {
+
+  override def beforeAll {
+    super.beforeAll
+    doInDB { schema.drop }
+    doInDB { schema.create }
+  }
+
+  "CodeListItem" should "create/retrieve item" in { doInDB {
+      val cli1 = CodeListItem.createRecord.name("cli1").codeList('test).rank(1)
+      CodeListCrud.save(cli1) should be ('defined)
+      debug("Code list item: id=%d, name=%s".format(cli1.id, cli1.linkName))
+      cli1.id should equal (1)
+      val cli2 = CodeListItem.createRecord.name("cli1").codeList('test).rank(2)
+      CodeListCrud.save(cli2) should be ('defined)
+      val cl = CodeList('test)
+      val l = cl.items
+      l.size should equal (2)
+      l.head should equal (cli1)
+  }}
+
+  object schema extends CodeListSchema with DropAndCreate
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/test/scala/fis/crm/model/ContactSpec.scala	Tue Apr 03 11:47:02 2012 +0200
+++ b/src/test/scala/fis/crm/model/ContactSpec.scala	Tue Apr 03 11:47:03 2012 +0200
@@ -32,8 +32,10 @@
   "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)
     /*