How would you implement a self relation for the MegaProtoUser? - scala

I'm trying to implement a a relation for the user class modelling the "friends" concept with the Lift Mapper framework. My attempt goes something like this
object User extends User with MetaMegaProtoUser[User] {
override def dbTableName = "users" // define the DB table name
override def screenWrap = Full(<lift:surround with="default" at="content">
<lift:bind /></lift:surround>)
// define the order fields will appear in forms and output
override def fieldOrder = List(id, firstName, lastName, email,
locale, timezone, password)
// comment this line out to require email validations
override def skipEmailValidation = true
}
class User extends MegaProtoUser[User] with OneToMany[Long,User]
{
def getSingleton = User // what's the "meta" server
object friends extends MappedOneToMany(User, User.id)
}
but it fails to compile because of a type mismatch
[error] src/main/scala/code/model/User.scala:30: type mismatch;
[error] found : net.liftweb.mapper.MappedLongIndex[code.model.User]
[error] required: net.liftweb.mapper.MappedForeignKey[Long,?,code.model.User]
[error] object friends extends MappedOneToMany(User, User.id)
[error] ^
What would be the way to go about this?
Regards

If you look at the error message you see that the OneToMany helper needs a foreign key in the "many" end of the relation pointing back to this "one". User.id is the primary key and therefore doesn't fit.
If you think about it what you want is really a ManyToMany-relationship.
That you can create like this:
object User extends User with MetaMegaProtoUser[User] with MappedManyToMany {
...
object friends extends MappedManyToMany(Friends, Friends.friend, Friends.friendee, User)
where you have a join table:
class Friends extends Mapper[Friends] {
object friend extends MappedLongForeignKey(this, User) {
override def dbIndexed_? = true
}
object friendee extends MappedLongForeignKey(this, User) {
override def dbIndexed_? = true
}
def getSingleton = Friends
}
object Friends extends Friends with MetaMapper[Friends]
If you want the friend relation to be reflexive, you'll have to do some further work.
This is an extremely late answer I guess. I would suggest posting to the lift mailing list for extremely fast replies: https://groups.google.com/forum/?fromgroups#!forum/liftweb

Related

Play-Slick: Is it possible to improve this design (pattern) ... and how to call it?

I'm using Play-Slick versions 2.5.x and 3.1.x respectively. I use Slick's code generator and produce the Slick model from an existing database. Actually I'm shy to admit that I'm DB-design driven and not class-design driven.
This is the initial setup:
Generated Slick model under generated.Tables._
Generic Slick dao implementation
Service layer that builds on top of the Generic Slick dao
These are the forces behind the pattern which I temporary called "Pluggable Service" because it allows plugging in the service layer functionality to the model:
Play's controllers and views must only see the Service layer (and not the Dao's) e.g. UserService
Generated model e.g. UserRow is expected to comply to business layer interfaces e.g. Deadbolt-2's Subject but not implement it directly. To be able to implement it one needs "too much" e.g. the UserRow model type, the UserDao and potentially some business context.
Some of the UserService methods naturally apply to the model UserRow instance e.g. loggedUser.roles or loggedUser.changePassword
Therefore I have:
generated.Tables.scala Slick model classes:
case class UserRow(id: Long, username: String, firstName: String,
lastName : String, ...) extends EntityAutoInc[Long, UserRow]
dao.UserDao.scala Dao extensions and customizations specific to the User model:
#Singleton
class UserDao #Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
extends GenericDaoAutoIncImpl[User, UserRow, Long] (dbConfigProvider, User) {
//------------------------------------------------------------------------
def roles(user: UserRow) : Future[Seq[Role]] = {
val action = (for {
role <- SecurityRole
userRole <- UserSecurityRole if role.id === userRole.securityRoleId
user <- User if userRole.userId === user.id
} yield role).result
db.run(action)
}
}
services.UserService.scala service that facades all user operations to the rest of the Play application:
#Singleton
class UserService #Inject()(auth : PlayAuthenticate, userDao: UserDao) {
// implicitly executes a DBIO and waits indefinitely for
// the Future to complete
import utils.DbExecutionUtils._
//------------------------------------------------------------------------
// Deadbolt-2 Subject implementation expects a List[Role] type
def roles(user: UserRow) : List[Role] = {
val roles = userDao.roles(user)
roles.toList
}
}
services.PluggableUserService.scala finally the actual "Pluggable" pattern that dynamically attaches service implementations to the model type:
trait PluggableUserService extends be.objectify.deadbolt.scala.models.Subject {
override def roles: List[Role]
}
object PluggableUserService {
implicit class toPluggable(user: UserRow)(implicit userService: UserService)
extends PluggableUserService {
//------------------------------------------------------------------------
override def roles: List[Role] = {
userService.roles(user)
}
}
Finally one can do in the controllers:
#Singleton
class Application #Inject() (implicit
val messagesApi: MessagesApi,
session: Session,
deadbolt: DeadboltActions,
userService: UserService) extends Controller with I18nSupport {
import services.PluggableUserService._
def index = deadbolt.WithAuthRequest()() { implicit request =>
Future {
val user: UserRow = userService.findUserInSession(session)
// auto-magically plugs the service to the model
val roles = user.roles
// ...
Ok(views.html.index)
}
}
Is there any Scala way that could help not having to write the boilerplate code in the Pluggable Service object? does the Pluggable Service name makes sense?
One of the common variant may be a parent trait for your controllers that has something along these lines:
def MyAction[A](bodyParser: BodyParser[A] = parse.anyContent)
(block: (UserWithRoles) => (AuthenticatedRequest[A]) => Future[Result]): Action[A] = {
deadbolt.WithAuthRequest()(bodyParser) { request =>
val user: UserRow = userService.findUserInSession(session)
// this may be as you had it originally
// but I don't see a reason not to
// simply pull it explicitly from db or
// to have it in the session together with roles in the first place (as below UserWithRoles class)
val roles = user.roles
block(UserWithRoles(user, roles))(request)
}
The elephant in the room here is how you get userService instance. Well you would need to explicitly require it in your controller constructor (in the same way you do with DeadboltActions). Alternatively you may bundle DeadboltActions, UserService and what else into one class (e.g. ControllerContext?) and inject this single instance as one constructor parameter (but that's probably another discussion...).
After that your controller code would be like this:
def index = MyAction() { implicit user => implicit request =>
Future {
// ...
Ok(views.html.index)
}
}
both user and request is implicit which helps to pass into into inner parts of your application (which is often the case - you bring user object to perform some business logic).
It doesn't get rid of your PluggableUserService per se (logic is still there) but it may help you to easier reuse same logic everywhere in your controllers (as in my experience, you need to have both User together with Roles more often than not in any real application).
EDIT: I got a feeling I didn't quite get your question. You want to avoid boilerplate in PluggableUserService or you want to avoid scattering this conversion with use of PluggableUserService everywhere, in every controller (IMHO 2nd option is something to be avoided)?

phantom cassandra multiple tables throw exceptions

I'm using phantom to connect cassandra in play framework. Created the first class following the tutorial. Everything works fine.
case class User(id: String, page: Map[String,String])
sealed class Users extends CassandraTable[Users, User] {
object id extends StringColumn(this) with PartitionKey[String]
object page extends MapColumn[String,String](this)
def fromRow(row: Row): User = {
User(
id(row),
page(row)
)
}
}
abstract class ConcreteUsers extends Users with RootConnector {
def getById(page: String): Future[Option[User]] = {
select.where(_.id eqs id).one()
}
def create(id:String, kv:(String,String)): Future[ResultSet] = {
insert.value(_.id, id).value(_.page, Map(kv)).consistencyLevel_=(ConsistencyLevel.QUORUM).future()
}
}
class UserDB(val keyspace: KeySpaceDef) extends Database(keyspace) {
object users extends ConcreteUsers with keyspace.Connector
}
object UserDB extends ResourceAuthDB(conn) {
def createTable() {
Await.ready(users.create.ifNotExists().future(), 3.seconds)
}
}
However, when I try to create another table following the exact same way, play throws the exception when compile:
overriding method session in trait RootConnector of type => com.datastax.driver.core.Session;
How could I build create another table? Also can someone explain what causes the exception? Thanks.
EDIT
I moved the connection part together in one class:
class UserDB(val keyspace: KeySpaceDef) extends Database(keyspace) {
object users extends ConcreteUsers with keyspace.Connector
object auth extends ConcreteAuthInfo with keyspace.Connector
}
This time the error message is:
overriding object session in class AuthInfo; lazy value session in trait Connector of
type com.datastax.driver.core.Session cannot override final member
Hope the message helps identify the problem.
The only problem I see here is not to do with connectors, it's here:
def getById(page: String): Future[Option[User]] = {
select.where(_.id eqs id).one()
}
This should be:
def getById(page: String): Future[Option[User]] = {
select.where(_.id eqs page).one()
}
Try this, I was able to compile. Is RootConnector the default one or do you define another yourself?
It took me 6 hours to figure out the problem. It is because there is a column named "session" in the other table. It turns out that you need to be careful when selecting column names. "session" obviously gives the above exception. Cassandra also has a long list of reserved keywords. If you accidentally use one of them as your column name, phantom will not throw any exceptions (maybe it should?). I don't know if any other keywords are reserved in phantom. A list of them will be really helpful.

Convenient way of inserting values to Cassandra with Phantom

Does anyone know of a convenient way of inserting values to Cassandra via phatom-dsl? Currently I'm doing this:
case class Entry(id: UUID, firstName: String, lastName: String)
sealed class EntryTable extends CassandraTable[EntryTable, Entry] {
override val tableName = "entries"
object id extends UUIDColumn(this) with Index[UUID]
object firstName extends StringColumn(this)
object lastName extends StringColumn(this)
override def fromRow(r: dsl.Row): Entry = {
Entry(id(r), firstName(r), firstName(r))
}
}
object EntryTable extends EntryTable {
private val connector = CassandraConnector.apply(Set(InetAddress.getByName("localhost")))
implicit val keySpace = KeySpace("keyspace")
def insert(e: Entry) = {
connector.withSessionDo(implicit session => {
insert().value(_.id, e.id)).value(_.firstName, e.firstName).value(_.lastName, e.lastName).future()
}
}
}
But I would like to do:
def insert(e: Entry) = {
connector.withSessionDo(implicit session => {
insert().value(e).future()
}
}
Which would be way more convenient when the case class has many fields. Any ideas?
You are using the API slightly wrong and we are in the process of publishing multiple tutorials to make the "new" way public. In the mean time, a basic version of it is available here and this branch in the activator template is also describing everything you need to know.
Specifically, the way to insert records is described here.

What are the correct apply and unapply methods to avoid this java.lang.ClassCastException error in a Scala Play app?

I'm building a Scala Play app where events and data are persisted in Json format, and I'm trying to model users and the roles they're assigned. My thinking has been to model Roles as case objects, as each standard role only needs defining once for all users in the application, and I'd like to pattern match on the type of role a particular user has been assigned. So far, I have this;
package models
abstract class User {
def displayName: String
def role: Role
}
case class GuestUser(displayName: String, role: Role) extends User {
}
case class RegisteredUser(displayName: String, role: Role) extends User {
}
trait Role { // have tried abstract class too - but like idea of developing stackable traits for role permissions
}
object Role {
implicit val RoleTypeFormat: Format[Role] = Json.format[Role]
def apply(className: String): Role = Class.forName(className: String).asInstanceOf[Role]
def unapply(role: Role): Option[String] = Option(this.getClass.getName) // have also tried .getSimpleName
}
case object GuestUserRole extends Role {
}
case object RegisteredUserRole extends Role {
}
If I don't define an apply and unapply method in object Role, and rely only on the implicit value that uses Json.format[Role], I get a 'no apply function found' or 'no unapply function found' error - so I added them, to try and get rid of this error.
I couldn't get it to compile without adding .asInstanceOf[Role] to the Role apply method. It now compiles, but when I try to set the role: Role parameter of a new RegisteredUser instance using,
val role: Role = RegisteredUserRole
a new RegisteredUser instance is created, where the role property gets serialized to Json as;
"role":{"className":"models.Role$”}
But when I try to deserialize it, I get Exception in thread "pool-4868-thread-1" java.lang.ClassCastException: java.lang.Class cannot be cast to models.Role
My aim is to end up with the same RegisteredUser instance (or GuestUser instance), so I can do pattern matching in the view controllers, along the lines of;
def isAuthorized: Boolean = {
role match {
case RegisteredUserRole => true
case GuestUserRole => false
// etc
}
}
Any help and advice on this would be very much appreciated. I'm not yet skilled and knowledgeable enough in Scala and Play to know whether I'm even on the right track with modelling Users and Roles.
As #lmm suggested, it would be better to provide a custom Format[Role] rather than trying to create instances in a weird way.
Something like this:
implicit val fmt = new Format[Role] {
def reads(js: JsValue): JsResult[Role] = {
js.validate[String] fold (
error => JsError(error),
role => role match {
case "GuestUserRole" => JsSuccess(GuestUserRole)
case "RegisteredUserRole" => JsSuccess(RegisteredUserRole)
case _ => JsError(Nil) // Should probably contain some sort of `ValidationError`
}
)
}
def writes(r: Role): JsValue = JsString(r.toString)
}

Storing case object with squeryl

How do I store user case objects with squeryl? I have an Account object with a permission field of type Permission (defined as a sealed trait). I also have 2 case objects (Administrator and NormalUser) extending from Permission. How can I persist the Account class using Squeryl. Example code below:
sealed trait Permission
case object Administrator extends Permission
case object NormalUser extends Permission
case class Account(
id: Long,
email: String,
permission: Permission
) extends KeyedEntity[Long]
Expanding on my comment, if you use a custom type to retrieve the permission type, such that it persists to the database as an integer (in the example below 1 and 0), you can override the unapply method to lookup the case object and pattern matching should work fine. I imagine something like the following should work:
class Permission(identifier:Int) extends org.squeryl.customtypes.IntField(identifier) {
self: CustomType[Int] =>
private lazy val permissions =
List(Administrator, NormalUser).
map(p => p.value -> p).
toMap
def unapply = permissions.get(value)
}
case object Administrator extends Permission(1)
case object NormalUser extends Permission(0)
Then you should be able to store the permission directly in your code, using your entity definition:
case class Account(
id: Long,
email: String,
permission: Permission
) extends KeyedEntity[Long]
you can set the permission field directly as Administrator or NormalUser and you should also be able to pattern match like:
account.permission match {
case Administrator => ..
case NormalUser => ..
}
You need to define schema:
object Library extends Schema {
val authors = table[Author]("AUTHORS")
}
and then insert entity:
authors.insert(new Author("Herby Hancock"))
http://squeryl.org/schema-definition.html
http://squeryl.org/inserts-updates-delete.html
In order to store Permission it must inherit one of the subtypes of CustomType in the package
org.squeryl.customtypes, and import the org.squeryl.customtypes.CustomTypesMode._
into the scope where statements are defined. For details read section Custom Types here: http://squeryl.org/schema-definition.html