Modulation

This is an example of modulation in ocaps.

Modulation can involve any kind of intermediary processing involved in handling a capability. Here, the example shows logging of a capability, using before and after hooks.

Modulation is not simply decorative; it can also affect the functionality of operations. For example, modulation can “narrow” a Finder capability by restricting the find operation to a single ID, or replace the result of processing that includes sensitive details. Modulation that interferes with processing is potentially fraught, so it’s best done carefully under specific circumstances.

You can read Managing Capabilities in the guide for more information.

import ocaps.macros._
import org.slf4j.Logger

import scala.util.Try

object Modulation {
  final case class Foo(private val name: String) {
    private object capabilities {
      val doer: Foo.Doer = new Foo.Doer {
        override def doTheThing(): Int = {
          42
        }
      }
    }
  }

  object Foo {
    trait Doer {
      def doTheThing(): Int
    }

    class Access {
      def doer(foo: Foo): Doer = foo.capabilities.doer
    }
  }

  def loggingDoer(doer: Foo.Doer, logger: Logger): Foo.Doer = {
    val before: String => Unit = methodName => {
      logger.info(s"$methodName: before call")
    }
    val after: (String, Any) => Unit = (methodName, result) =>
      logger.info(s"$methodName: after returns $result")
    modulate[Foo.Doer](doer, before, after)
  }

  def main(args: Array[String]): Unit = {
    val access = new Foo.Access()
    val foo = new Foo("foo")
    val doer = access.doer(foo)

    val logger = org.slf4j.LoggerFactory.getLogger("modulation.Foo.Doer")
    val logDoer = loggingDoer(doer, logger)
    val result1 = Try(logDoer.doTheThing())
    println(s"result after first call: $result1")

    val result2 = Try(logDoer.doTheThing())
    println(s"result after second call: $result2")
  }
}
Full source at GitHub