List of LongKeyedMetaMappers

31 views
Skip to first unread message

Adam Coimbra

unread,
Mar 19, 2012, 8:13:03 PM3/19/12
to lif...@googlegroups.com
Hello,
I am trying to pass to a function a List of mappers much like the last argument[s] to schemifier. However, schemifier's argument is of type BaseMapper, whereas my argument must only take mappers with a certain trait mixed in. My function will simply take a list rather than being variadic. Therefore I assume that my argument should have a type definition of something like 
    def my_func(mappers: List[LongKeyedMetaMapper with my_trait])
However, I need to pass type parameters to LongKeyedMetaMapper and my_trait (which takes the same type parameters as CRUDify). I have been reading about and experimenting with abstract classes to this end for hours now and have had no success. If someone could point me in the right direction, or even better give me an example of a type definition like the one I'm searching for, I would greatly appreciate it. I have also considered the possibility that I would be better off making my argument of type List[MetaMapper] and casting it to a LongKeyedMetaMapper with my_trait, but this would obviously require type definitions that are beyond me as well.
Any help would be great.
Thanks,
Adam

Andrew Mullins

unread,
Mar 19, 2012, 10:20:47 PM3/19/12
to lif...@googlegroups.com
You might want something like:

def myFunc[M <: LongKeyedMapper[M]](mappers: List[LongKeyedMetaMapper[M] with my_trait[Long, M]])

This is a method that accepts type parameters. MyMapper must be a subclass of LongKeyedMapper (since LongKeyedMetaMapper's type argument must be of type LongKeyedMapper). You could also write this as:

def myFunc[K <: AnyRef, M <: KeyedMapper[K, M]](mappers: List[KeyedMetaMapper[K , M] with my_trait[K, M]])

... if your Mappers have different key types.

Adam Coimbra

unread,
Mar 19, 2012, 11:02:42 PM3/19/12
to lif...@googlegroups.com
Thank you so much for your reply!

Do you know if the type definition need to explicitly cover all traits (my mappers also use CRUDify, IdPk, CreatedUpdated) or if it will infer that the only trait that "matters" is the one specified in the type definition (my_trait)?
Because I'm getting an error along the lines of "no type parameters for method myFunc ... exist so that it can be applied to arguments ... because argument expression's type is not compatible with formal parameter type;"

If you have another moment, it would be fantastic if you could send me an example of how to use the code you sent (e.g. myFunc(List(foo,bar,User)) ) given mappers like:

class foo extends LongKeyedMapper[ foo  ] with CreatedUpdated with IdPK { ... }
object foo extends  foo  with LongKeyedMetaMapper[ foo  ] with CRUDify[Long,  foo  ] with RESTify[Long,  foo  ] { ... }
 
and
 
class bar extends LongKeyedMapper[ bar] with CreatedUpdated with IdPK { ... }
object  bar  extends   bar  with LongKeyedMetaMapper[  bar  ] with CRUDify[Long,   bar  ] with RESTify[Long,   bar  ] { ... }

and (not sure if this is even possible)
 
class User extends MegaProtoUser[User]  { ... }
object User extends User with MetaMegaProtoUser[User] with RESTify[Long, User] { ... }

 
Thanks again.
Adam 

Andrew Mullins

unread,
Mar 20, 2012, 11:45:17 AM3/20/12
to lif...@googlegroups.com
Do you need the myFunc mappers parameter to be a list of KeyedMetaMappers -or- KeyedMappers (companion objects vs instances)?
What does you `my_trait` trait look like?
You only need to specify the trait(s) that are needed within myFunc.

class foo extends LongKeyedMapper[foo] with CreatedUpdated with IdPK { ... }
object foo extends foo with LongKeyedMetaMapper[foo] with CRUDify[Long, foo  ] with RESTify[Long, foo] { ... }

class bar extends LongKeyedMapper[ bar] with CreatedUpdated with IdPK { ... }
object bar extends bar with LongKeyedMetaMapper[bar] with CRUDify[Long, bar] with RESTify[Long, bar] { ... }

class User extends MegaProtoUser[User]  { ... }
object User extends User with MetaMegaProtoUser[User] with RESTify[Long, User] { ... }

class MySnippet {
  private def myFunc[K, M <: KeyedMapper[K, M]](mappers: List[KeyedMapper[K, M] with my_trait[K, M]]) = {
    // return something
  }
  //- or -
  private def myMetaFunc[K, M <: KeyedMetaMapper[K, M]](mappers: List[KeyedMetaMapper[K, M] with my_trait[K, M]]) = {
    // return something
  }

  def render = {
    val data: List[KeyedMapper[Long, _] with my_trait[Long, _]] = List(aFoo, aBar, aUser)

    "li" #> myFunc(data)

Adam Coimbra

unread,
Mar 20, 2012, 7:49:21 PM3/20/12
to lif...@googlegroups.com
The logic needs the mappers to be singletons. My_trait is defined identically to CRUDify. What I'm attempting to build is a generic REST api. I don't really expect you or anyone to look through this as it's pretty huge, but I wanted to follow up. The commented code at the top of RestAPI is what I'm having so much trouble with. It would eventually replace the serve { } portion beneath it.


object RestAPI extends RestHelper {

 /*
  var mappers:List[LongKeyedMetaMapper[_] with RESTify[Long, _]] = List(foo,bar)
  def add(tbl:String,h:PartialFunction[Req, () => Box[LiftResponse]]) {
    serve(h)
  }
  def setMappers {
       mappers.map(m => {
         val name = m.getSingleton.dbTableName.toString
           serve( {case Req("api" :: name :: "findBy" :: key :: _, "xml", GetRequest) => findBy(m.getSingleton,key) } )
           serve( {case Req("api" :: name :: "findBySql" :: key :: _, "xml", GetRequest) => findBySql(m.getSingleton,key) } )
           serve( {case Req("api" :: name :: "create" :: key :: _, "xml", GetRequest) =>  create(m.getSingleton,key) } )
           serve( {case Req("api" :: name :: AsLong(id) :: "update" :: key :: _, "xml", GetRequest) => update(m.getSingleton,id,key) } )
           serve( {case Req("api" :: name :: AsLong(id) :: "delete" :: key :: _, "xml", GetRequest) =>  delete(m.getSingleton,id,key) } )
           serve( {case Req("api" :: name :: AsLong(id) :: "get" :: key :: _, "xml", GetRequest) =>  get(m.getSingleton,id,key) } )
           serve( {case Req("api" :: name :: "meta" :: key :: _, "xml", GetRequest) =>  metaData(m.getSingleton,key) } )
      }) 
    }    */



  def authenticate(key: String):Boolean = if (key == "secret") true else false
  serve {

       case Req("api" :: "user" :: "findBy" :: key :: _, "xml", GetRequest) => user_findBy(User,key)
       case Req("api" :: "user" :: "findBySql" :: key :: _, "xml", GetRequest) => user_findBySql(User,key)
       case Req("api" :: "user" :: "create" :: key :: _, "xml", GetRequest) =>  user_create(User,key)
       case Req("api" :: "user" :: AsLong(id) :: "update" :: key :: _, "xml", GetRequest) => user_update(User,id,key)
       case Req("api" :: "user" :: AsLong(id) :: "delete" :: key :: _, "xml", GetRequest) =>  user_delete(User,id,key)
       case Req("api" :: "user" :: AsLong(id) :: "get" :: key :: _, "xml", GetRequest) =>  user_get(User,id,key)
       case Req("api" :: "user" :: "meta" :: key :: _, "xml", GetRequest) =>  user_metaData(User,key)

       case Req("api" :: "foo" :: "findBy" :: key :: _, "xml", GetRequest) => findBy(foo,key)
       case Req("api" :: "foo" :: "findBySql" :: key :: _, "xml", GetRequest) =>  findBySql(foo,key)
       case Req("api" :: "foo" :: "create" :: key :: _, "xml", GetRequest) =>  create(foo,key)
       case Req("api" :: "foo" :: AsLong(id) :: "update" :: key :: _, "xml", GetRequest) => update(foo,id,key)
       case Req("api" :: "foo" :: AsLong(id) :: "delete" :: key :: _, "xml", GetRequest) => delete(foo,id,key)
       case Req("api" :: "foo" :: AsLong(id) :: "get" :: key :: _, "xml", GetRequest) => get(foo,id,key)
       case Req("api" :: "foo" :: "meta" :: key :: _, "xml", GetRequest) => metaData(foo,key)

  }

  def findBy[M <: LongKeyedMapper[M]](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],key:String):LiftResponse = { if (RestAPI.authenticate(key)) {
      var u = m.findAll(m.generateQuery:_*) //Query Mapper
      if (u.isEmpty) PlainTextResponse("Not found") else XmlResponse(<results>{u.map(f => f.toXml)}</results>)
  } else PlainTextResponse("Auth fail or wrong db table name") }

  def findBySql[M <: LongKeyedMapper[M]](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],key:String):LiftResponse = { if (RestAPI.authenticate(key))  {
       if (S.param("sql").isEmpty) PlainTextResponse("Param 'sql' not found")
       else {
          try {
             var u = m.findAll(BySql(S.param("sql").open_!,IHaveValidatedThisSQL("adam","2012-03-19")))
             XmlResponse(<results>{u.map(f => f.toXml)}</results>)
          } catch {
            case e: Exception => PlainTextResponse("Bad SQL: " + e.toString)
          }
       }
  } else PlainTextResponse("Auth fail or wrong db table name")  }

  def create[M <: LongKeyedMapper[M]](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],key:String):LiftResponse = { if (RestAPI.authenticate(key)) {
     var u = m.createInstance
     if (m.setFieldsFromParams(u)) XmlResponse(u.toXml) else PlainTextResponse("Forgot a non_null field or did not set any fields") } else PlainTextResponse("Auth fail or wrong db table name")  }

  def update[M <: LongKeyedMapper[M] with IdPK](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],id:Long,key:String):LiftResponse = { if (RestAPI.authenticate(key))  {
     try {
         var ubox = m.find(BySql("id="+id,IHaveValidatedThisSQL("adam",""))) //dangerous

         if (!ubox.isEmpty) {
           val  u = ubox.open_!
           if (m.setFieldsFromParams(u)) XmlResponse(u.toXml) else PlainTextResponse("Forgot a non_null field or did not set any fields")
         } else PlainTextResponse("Not found")
     } catch {
         case e: Exception => PlainTextResponse("Bad SQL: " + e.toString)
     }
  } else PlainTextResponse("Auth fail or wrong db table name") }

  def delete[M <: LongKeyedMapper[M] with IdPK](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],id:Long,key:String):LiftResponse = {  if (RestAPI.authenticate(key)) { // this will eventually archive instead of delete
     try { if ((!S.param("auth").isEmpty) && (id >= 0))
        if (S.param("auth").open_! == "true") {
          val toDelete = m.find(BySql("id="+id,IHaveValidatedThisSQL("adam","")))
          if (!toDelete.isEmpty)
            if (toDelete.open_!.delete_!)
              PlainTextResponse("Delete successful")
            else PlainTextResponse("Delete unsuccessful")
          else PlainTextResponse("Item not found")
        } else PlainTextResponse("Not authorized to delete")
      else PlainTextResponse("Not authorized or ID wrong") } catch { case e:Exception => PlainTextResponse(e.toString)}
   }  else PlainTextResponse("Auth fail or wrong db table name")  }

  def get[M <: LongKeyedMapper[M] with IdPK](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],id:Long,key:String):LiftResponse = {  if (RestAPI.authenticate(key)) {
      try {
         var ubox = m.find(BySql("id="+id,IHaveValidatedThisSQL("adam",""))) //dangerous
         if (!ubox.isEmpty) {
            XmlResponse(ubox.open_!.toXml)
         } else PlainTextResponse("Not found")
      } catch { case e: Exception => PlainTextResponse(e.toString) }
  } else PlainTextResponse("Forgot a non_null field or did not set any fields") }

  def metaData[M <: LongKeyedMapper[M] with IdPK](m: LongKeyedMetaMapper[M] with RESTify[Long,  M],key:String):LiftResponse = {  if (RestAPI.authenticate(key)) XmlResponse(
     <model dbTableName={m.dbTableName.toString}>
        <fields>
          {m.mappedFields.map(f => <field dbColumnName={f.dbColumnName.toString} type={f.targetSQLType.toString} not_null={f.dbNotNull_?.toString} /> )}
          </fields>
     </model>
  ) else PlainTextResponse("Forgot a non_null field or did not set any fields") }


  //and, annoyingly, because the user mapper is a different type, we have another set of these for user
  def user_findBy[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],key:String):LiftResponse = {  if (RestAPI.authenticate(key)) {
      var u = m.findAll(m.generateQuery:_*) //Query Mapper
      if (u.isEmpty) PlainTextResponse("Not found") else XmlResponse(<results>{u.map(f => f.toXml)}</results>)
  }  else PlainTextResponse("Auth fail or wrong db table name") }

  def user_findBySql[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],key:String):LiftResponse = {  if (RestAPI.authenticate(key)) {
     if (S.param("sql").isEmpty) PlainTextResponse("Param 'sql' not found")
       else {
          try {
             var u = m.findAll(BySql(S.param("sql").open_!,IHaveValidatedThisSQL("adam","2012-03-19")))
             XmlResponse(<results>{u.map(f => f.toXml)}</results>)
          } catch {
            case e: Exception => PlainTextResponse("Bad SQL: " + e.toString)
          }
       }
  }  else PlainTextResponse("Auth fail or wrong db table name") }

  def user_create[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],key:String):LiftResponse  = {  if (RestAPI.authenticate(key)) {
     var u = m.createInstance
     if (m.setFieldsFromParams(u)) XmlResponse(u.toXml) else PlainTextResponse("Forgot a non_null field or did not set any fields")
  }  else PlainTextResponse("Auth fail or wrong db table name") }

  def user_update[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],id:Long,key:String):LiftResponse = {  if (RestAPI.authenticate(key)) {
    try {
       var ubox = m.find(BySql("id="+id,IHaveValidatedThisSQL("adam",""))) //dangerous
       if (!ubox.isEmpty) {
         val  u = ubox.open_!
         if (m.setFieldsFromParams(u)) XmlResponse(u.toXml) else PlainTextResponse("Forgot a non_null field or did not set any fields")
       } else PlainTextResponse("Not found")
    } catch {
      case e: Exception => PlainTextResponse("Bad SQL: " + e.toString)
    }
  }  else PlainTextResponse("Auth fail or wrong db table name") }

  def user_delete[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],id:Long,key:String):LiftResponse = {  if (RestAPI.authenticate(key)) {
      try { if ((!S.param("auth").isEmpty) && (id >= 0))
        if (S.param("auth").open_! == "true") {
          val toDelete = m.find(BySql("id="+id,IHaveValidatedThisSQL("adam","")))
          if (!toDelete.isEmpty)
            if (toDelete.open_!.delete_!)
              PlainTextResponse("Delete successful")
            else PlainTextResponse("Delete unsuccessful")
          else PlainTextResponse("Item not found")
        } else PlainTextResponse("Not authorized to delete")
      else PlainTextResponse("Not authorized or ID wrong") } catch { case e:Exception => PlainTextResponse(e.toString)}
   }  else PlainTextResponse("Auth fail or wrong db table name")  }

   def user_get[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],id:Long,key:String):LiftResponse = {  if (RestAPI.authenticate(key)) {
        try {
           var ubox = m.find(BySql("id="+id,IHaveValidatedThisSQL("adam",""))) //dangerous
           if (!ubox.isEmpty) {
              XmlResponse(ubox.open_!.toXml)
           } else PlainTextResponse("Not found")
        } catch { case e: Exception => PlainTextResponse(e.toString) }
  } else PlainTextResponse("Forgot a non_null field or did not set any fields") }

    def user_metaData[M <: MegaProtoUser[M]](m: MetaMegaProtoUser[M] with RESTify[Long,  M],key:String):LiftResponse = {  if (RestAPI.authenticate(key)) { XmlResponse(
   <model dbTableName={m.dbTableName.toString}>
        <fields>
         {m.mappedFields.map(f => <field dbColumnName={f.dbColumnName.toString} type={f.targetSQLType.toString} not_null={f.dbNotNull_?.toString} /> )}
          </fields>
     </model>
  )  } else PlainTextResponse("Forgot a non_null field or did not set any fields") }

}

trait RESTify[KeyType, RESTType <: KeyedMapper[KeyType, RESTType]]  {
  self: RESTType with KeyedMetaMapper[KeyType, RESTType] =>
  val tn:String = dbTableName
  def generateQuery:List[QueryParam[RESTType]] = {
      var result:List[QueryParam[RESTType]] = Nil
      mappedFields.foreach(z => {
        val f = z.asInstanceOf[MappedField[KeyType,RESTType]] //cast our BaseMappedField to the correct type of MappedField
        if (!S.param(f.dbColumnName.toString()).isEmpty) {  //if the url query string with a param name of f's database column name is set
          var param = S.param(f.dbColumnName.toString()).open_! //get that param value
          if (f.get.isInstanceOf[Long]) { //to handle Ids. Might need more here for some other stuff.
            val value = java.lang.Long.parseLong(param).asInstanceOf[f.ValueType] //cast the param value for use by Mapper QueryParam
            val r =  List(By[RESTType, KeyType, f.ValueType](f, value)) //Create the QueryParam
            result = result ::: r //add it to the list
          } else { //strings
              val value = param.asInstanceOf[f.ValueType]  //cast the param value for use by Mapper QueryParam
              val r =  List(By[RESTType, KeyType, f.ValueType](f, value)) //Create the QueryParam
              result = result ::: r  //add it to the list
          }
      }})
      result //return
  }
  def setFieldsFromParams(item:RESTType):Boolean = {
     var success = true
     var i = 0
      item.getSingleton.mappedFields.foreach(z => {
         val f = item.fieldByName(z.name).open_!
         if (success) {
           if ((f.dbNotNull_?) && (S.param(f.dbColumnName.toString()).isEmpty) && !f.dbColumnName.equals("id")) {
              success = false
           } else if (!(S.param(f.dbColumnName.toString).isEmpty)) {
              i = i + 1
              var param = S.param(f.dbColumnName.toString()).open_! //get that param value

              if (f.get.isInstanceOf[Long]) { //to handle Ids. Might need more here for some other stuff.
                  val value = java.lang.Long.parseLong(param).asInstanceOf[z.ValueType] //cast the param value for use by Mapper QueryParam
                  f.setFromAny(value)
              } else { //strings
                  val value = param.asInstanceOf[z.ValueType]  //cast the param value for use by Mapper QueryParam
                  f.setFromAny(value)
              }
           }
         }
      })

     item.getSingleton.mappedFields.foreach(f => println(item.fieldByName(f.name).open_!.dbColumnName + "=" + f.get))
     if (i > 0) item.save() else false
  }
}


--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

Reply all
Reply to author
Forward
0 new messages