Skip to content

Instantly share code, notes, and snippets.

@iron9light
Created September 6, 2011 18:23

Revisions

  1. iron9light created this gist Sep 6, 2011.
    114 changes: 114 additions & 0 deletions Taggable.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,114 @@
    package iron9light.lessTM.business.lib

    import scala.collection.mutable.Map
    import org.squeryl.PrimitiveTypeMode._
    import org.squeryl.{Schema, Table, KeyedEntity}
    import org.squeryl.dsl.ast.{EqualityExpression, TypedExpressionNode}
    import org.squeryl.dsl.{OneToManyRelation, ManyToOne, OneToMany, CompositeKey3}
    import java.sql.Timestamp
    import java.util.{UUID, Date}

    /**
    * @author il
    * @version 9/5/11 6:39 PM
    */

    abstract class Taggable[K, T <: Taggable[K, T]: Manifest] extends KeyedEntity[K] {self: T =>
    lazy val tags = TagModel.tags[K, T](this)
    }

    trait TagEntity[K] extends KeyedEntity[CompositeKey3[K, String, Option[String]]] {
    def itemId: K
    def tag: String
    def tagType: Option[String]

    def id = compositeKey(itemId, tag, tagType)

    def tagId = compositeKey(tag, tagType)

    // todo: implicit convertTo(tagId: CompositeKey2[String, Option[String]]): TagInfo
    }

    trait TagModel extends Schema {
    def taggable[K, T <: Taggable[K, T]](implicit convert: K => TypedExpressionNode[_], manifestT: Manifest[T], manifestTag: Manifest[K]): Table[T] = taggable[K, T](tableNameFromClass(manifestT.erasure))

    def taggable[K, T <: Taggable[K, T]](name: String)(implicit convert: K => TypedExpressionNode[_], manifestT: Manifest[T], manifestTag: Manifest[K]): Table[T] = taggable[K, T](name, None)

    def taggable[K, T <: Taggable[K, T]](name: String, prefix: String)(implicit convert: K => TypedExpressionNode[_], manifestT: Manifest[T], manifestTag: Manifest[K]): Table[T] = taggable[K, T](name, Some(prefix))

    def taggable[K, T <: Taggable[K, T]](name: String, prefix: Option[String])(implicit convert: K => TypedExpressionNode[_], manifestT: Manifest[T], manifestTag: Manifest[K]): Table[T] = {
    // case class TagInfo(itemId: K, tag: String, tagType: Option[String] = None) extends TagEntity[K]

    val (t, tTags) = prefix.map(x => (table[T](name, x), table(name + "Tag", x)(TagModel.tagManifest[K]).asInstanceOf[Table[TagEntity[K]]])).getOrElse(table[T](name), table(name + "Tag")(TagModel.tagManifest[K]).asInstanceOf[Table[TagEntity[K]]])

    // val tTags = table[TagInfo](name + "Tag", prefix)

    on(tTags) {
    tag => declare(
    tag.tagId is (unique, indexed)
    )
    }

    val tToTags = oneToManyRelation(t, tTags).via((item, tag) => new EqualityExpression(convert(item.id), convert(tag.itemId)))

    TagModel.addTagTables[K, T](t, tTags, tToTags)

    t
    }
    }

    object TagModel {
    private val map = Map[Manifest[_], (Table[_], Table[_], OneToManyRelation[_, _])]()

    def table[T <: Taggable[_, T]: Manifest] : Table[T] = map(manifest[T])._1.asInstanceOf[Table[T]]

    def tags[K, T <: Taggable[K, T]: Manifest](item: T): OneToMany[_ <: TagEntity[K]] = map(manifest[T])._3.asInstanceOf[OneToManyRelation[T, _ <: TagEntity[K]]].left(item)

    def item[T <: Taggable[_, T]: Manifest](tagInfo: TagEntity[_]): ManyToOne[T] = map(manifest[T])._3.asInstanceOf[OneToManyRelation[T, TagEntity[_]]].right(tagInfo)

    private[lib] def addTagTables[K, T <: Taggable[K, T]](t: Table[T], tTags: Table[_ <: TagEntity[K]], tToTags: OneToManyRelation[T, _ <: TagEntity[K]])(implicit manifestT: Manifest[T]) {
    map += manifest[T] -> (t, tTags, tToTags)
    }

    private[lib] def tagManifest[K](implicit m: Manifest[K]): Manifest[_] = m match {
    case _ if m == manifest[Byte] => manifest[ByteTag]
    case _ if m == manifest[Int] => manifest[IntTag]
    case _ if m <:< manifest[String] => manifest[StringTag]
    case _ if m == manifest[Double] => manifest[DoubleTag]
    case _ if m <:< manifest[BigDecimal] => manifest[BigDecimalTag]
    case _ if m == manifest[Float] => manifest[FloatTag]
    case _ if m == manifest[Long] => manifest[LongTag]
    case _ if m == manifest[Boolean] => manifest[BooleanTag]
    case _ if m <:< manifest[Date] => manifest[DateTag]
    case _ if m <:< manifest[Timestamp] => manifest[TimestampTag]
    case _ if m <:< manifest[Enumeration#Value] => manifest[EnumTag]
    case _ if m <:< manifest[UUID] => manifest[UUIDTag]
    case _ if m <:< manifest[Array[Byte]] => manifest[BytesTag]
    }

    case class ByteTag(itemId: Byte, tag: String, tagType: Option[String] = None) extends TagEntity[Byte]

    case class IntTag(itemId: Int, tag: String, tagType: Option[String] = None) extends TagEntity[Int]

    case class StringTag(itemId: String, tag: String, tagType: Option[String] = None) extends TagEntity[String]

    case class DoubleTag(itemId: Double, tag: String, tagType: Option[String] = None) extends TagEntity[Double]

    case class BigDecimalTag(itemId: BigDecimal, tag: String, tagType: Option[String] = None) extends TagEntity[BigDecimal]

    case class FloatTag(itemId: Float, tag: String, tagType: Option[String] = None) extends TagEntity[Float]

    case class LongTag(itemId: Long, tag: String, tagType: Option[String] = None) extends TagEntity[Long]

    case class BooleanTag(itemId: Boolean, tag: String, tagType: Option[String] = None) extends TagEntity[Boolean]

    case class DateTag(itemId: Date, tag: String, tagType: Option[String] = None) extends TagEntity[Date]

    case class TimestampTag(itemId: Timestamp, tag: String, tagType: Option[String] = None) extends TagEntity[Timestamp]

    case class EnumTag(itemId: Enumeration#Value, tag: String, tagType: Option[String] = None) extends TagEntity[Enumeration#Value]

    case class UUIDTag(itemId: UUID, tag: String, tagType: Option[String] = None) extends TagEntity[UUID]

    case class BytesTag(itemId: Array[Byte], tag: String, tagType: Option[String] = None) extends TagEntity[Array[Byte]]
    }