воскресенье, 20 марта 2011 г.

Dependency injection with Lift

While migrating my web app to Lift 2.2 and CSS selectors i've noticed the dependency injection section in Simply Lift. It looked interesting and i decided to try it. Here is what i've finally got:

The Factory:
object DependencyFactory extends Factory {
  // The following trait has become necessary to avoid name clashes 
  // between a FactoryMaker and its contents during implicit conversion
  // (e.g. FactoryMaker has method 'find' so if contained type has a 
  // 'find' too it will not trigger the implicit conversion and you'll
  // get error instead)
  trait VendorMarker[T] {
    this: FactoryMaker[T] =>
    // since our fields will be visible as Vendor (to avoid name clashes),
    // we need a method to get back to FactoryMaker (e.g. to use its 
    // 'doWith' method)
    def asFactoryMaker: FactoryMaker[T] = this
  }
  // factory method to make FactoryMakers visible as Vendors with a 
  // marker trait
  private def newVendor[T : Manifest](v: T): Vendor[T] with VendorMarker[T] =
    new FactoryMaker[T](v) with VendorMarker[T]

  implicit def vendorToValue[T](vendor: Vendor[T]): T = vendor.vend

  val props = newVendor[AppProperties](new AppPropertiesImpl)
  val db = newVendor[DbAccess](Model)
  val roomStore = newVendor(new RoomStore)
  val quizFactory = newVendor(new QuizFactory)
  // etc
}

Usage examples:
class RoomStore {
  // Dependency requirements expressed with imports
  import lib.DependencyFactory.db

  // now we are using db as DbAccess via the implicit conversion
  def updateRoom(room: Room): Room = db.merge(room)
  def getRoom(id: Long): Option[Room] = db.find(classOf[Room], id)
}

// This one has a lot of dependencies
import lib.DependencyFactory.{chatFactory, currentTime, roomStore, persistentMessageStore, questionStore}
class Quiz(_room: Room) extends AbstractQuiz(chatFactory.newChat(), currentTime) {
// ...
}

class QuizFactory {
  def newQuiz(room: Room) = new Quiz(room)
  // note the asFactoryMaker.doWith usage to temporarily replace a dependency
  def newQuiz(room: Room, msgs: List[Message]) =
    lib.DependencyFactory.chatFactory.asFactoryMaker.doWith(new ChatFactory {
        override def newChat(msgs: List[Message], capacity: Int) = new Chat(msgs, 100)
      }) { new Quiz(room) }
}


Quite cool feature to use!

вторник, 15 июня 2010 г.

Type-safe authorization

While experimenting with Scala and implementing a simple web application i decided to see if i can design type-safe authorization interfaces. I like static typing, if you can do it right (without too much boilerplate) it makes code more transparent and easy to maintain.

Simple authorization system (like the one in Apache Shiro) works like this:
  • user has a set of permissions
  • permissions can imply other permissions
  • different actions can require different combinations of permissions

So we need to express somehow that a method requires a set of permissions and we need to express that certain permissions imply other permissions.

After trying different language constructs (like type parameters, variance etc) and asking for help at scala-user mailing list, i came to the following solution:

object Permissions {
  trait UserPermissions
  trait AnyUserPermissions extends UserPermissions
  trait RegisteredUserPermissions extends AnyUserPermissions
  trait AdminPermissions extends RegisteredUserPermissions

  trait DomainAPermissions extends RegisteredUserPermissions
  trait DomainBPermissions extends RegisteredUserPermissions
  implicit def adm2doma(x: AdminPermissions): DomainAPermissions = new DomainAPermissions{}
  implicit def adm2domb(x: AdminPermissions): DomainBPermissions = new DomainBPermissions{}
}

object Test {
  import Permissions._

  def sampleRegisteredUserFunction[T <% RegisteredUserPermissions](p: T) = println("registered user")
  def sampleCombinedPermissionsFunction[T <% DomainAPermissions <% DomainBPermissions](p: T) = println("combined")

  def main(args: Array[String]) {
    val anyUserPermissions = new UserPermissions{}
    val combinedPermissions = new DomainAPermissions with DomainBPermissions
    val adminPermissions: AdminPermissions = new AdminPermissions{}

    sampleRegisteredUserFunction(combinedPermissions)
    sampleCombinedPermissionsFunction(combinedPermissions)

    sampleRegisteredUserFunction(adminPermissions)
    sampleCombinedPermissionsFunction(adminPermissions)
    sampleRegisteredUserFunction(anyUserPermissions) // compile error - as expected
  }
}
The idea is quite obvious: permissions are traits that you can inherit and mix when implementing your own permissions. If inheritance is not enough (for example, we want AdminPermissions to imply any other permissions including those added by users of our library) we can use implicit conversions.

To require certain sets of permissions in a method we declare it using view bounded type parameters (T <% DomainAPermissions <% DomainBPermissions means that T should be implicitly convertable to both DomainAPermissions and DomainBPermissions).

This way you can mark some parts of your code to require statically checked permissions. I think it's better than explicit runtime checks or annotations with AOP.