Skip to content

Instantly share code, notes, and snippets.

@DarrenBishop
Last active January 11, 2016 19:31
Show Gist options
  • Save DarrenBishop/a84f8d40a1e800d37675 to your computer and use it in GitHub Desktop.
Save DarrenBishop/a84f8d40a1e800d37675 to your computer and use it in GitHub Desktop.
NullSafeCoalescer

Null Safe Coalescer Operator and more for Scala

package com.darrenbishop
/**
* Created by Darren on 02/04/2015.
*/
object Fixture {
import XorSet._
sealed trait Country { self:Country =>
sealed trait City
}
case object UK extends Country {
case object London extends City
case object Manchester extends City
case object Liverpool extends City
}
case object USA extends Country {
case object Washington extends City
case object NewYork extends City
case object MiamiBeach extends City
}
case object HK extends Country {
case object Central extends City
case object Kowloon extends City
}
case class Address(country: Country, city: Country#City=null)
case class Person(firstname: String, lastname: String, address: Address=null)
case class Department(name: String)
case class Employee(person: Person, department: Department)
case class Company(name: String, departments:Set[Department] = Set.empty, employees:Set[Employee] = Set.empty) {
def expand(department:String) = {
departments.find { _.name == department } match {
case None => Company(name, departments + Department(department), employees)
case Some(d) => this
}
}
def contract(department:String):Company = {
departments.find { _.name == department } match {
case None => this
case Some(d) => Company(name, departments - d, employees &~ employees.filter(_.department == d))
}
}
def fire(person: Person) = employees find {_.person == person} match {
case None => this
case Some(e) => Company(name, departments, employees - e)
}
def fire(people: Person*) = employees filterNot( e => people contains e.person) match {
case `employees` => this
case s => Company(name, departments, s)
}
def employ(person:Person, department:String):Company = {
(departments.find { _.name == department }, employees find {_.person == person }) match {
case (None, _) => this
case (Some(d), Some(e)) => Company(name, departments, employees &! Set(e, Employee(person, d)))
case (Some(d), None) => Company(name, departments, employees + Employee(person, d))
}
}
}
}
/**
* Created by Darren on 02/04/2015.
*/
package com.darrenbishop
object NullSafeCoalescer {
case class NSCOps[E >: Nothing](oe: Option[E]) {
def ?>[R >: Nothing](f: E => R): Option[R] = oe match {
//oe map f
// At the time of writing, map uses Some(...) instead of Option(...) which can lead to Some(null) being returned
case None => None
case Some(e) => Option(f(e))
}
def ?~(f: E => Boolean): Option[E] = {
oe filter f
}
def ??[P >: Nothing](p: => P): Option[E] = {
oe filter { _ == p }
}
def ?![P >: Nothing](p: => P): Option[E] = {
oe filterNot { _ == p }
}
}
@inline implicit def anyToNSCOps[E >: Nothing](oe: Option[E]): NSCOps[E] = NSCOps(oe)
@inline implicit def anyToNSCOps[E >: Nothing](e: E): NSCOps[E] = NSCOps(Option(e))
}
package com.darrenbishop
import org.scalatest.FlatSpec
import util.UnitSpec
class NullSafeCoalescerSpec extends FlatSpec with UnitSpec {
import NullSafeCoalescer._
import Fixture._
val jd = Person("John", "Doe", address = null)
val js = Person("John", "Smith", Address(UK, UK.Manchester))
val co = Company("ACME International", Set(Department("hr"), Department("accounting"))).employ(jd, "hr").employ(js, "accounting")
"NullSafeCoalescer" should "extract Mr Doe from ACME Int" in {
co ?> { _.employees.head } ?> { _.person } should be(Some(Person("John", "Doe", null)))
co ?> { _.employees.head } ?> { _.person } should be(Some(jd))
}
it should "not extract anything on a null company" in {
def employee(co: Company) = co.employees.head
def person(emp: Employee) = emp.person
def address(p: Person) = p.address
val nullCo: Company = null
nullCo ?> employee ?> person ?> address should be(None)
nullCo ?> { _.employees.head } ?> { _.person } ?> { _.address } should be(None)
}
it should "not extract Mr Doe's address" in {
co ?> { _.employees.head } ?> { _.person } ?> { _.address } should be(None)
}
it should "not extract Mr Doe's city" in {
co ?> { _.employees.head } ?> { _.person } ?> { _.address } ?> { _.city } should be(None)
}
it should "not extract and stringify Mr Doe's city" in {
co ?> { _.employees.head } ?> { _.person } ?> { _.address } ?> { _.city } ?> { _.toString } should be(None)
}
it should "still not extract Mr Doe's address, even if he is Mr Doe" in {
co ?> { _.employees.head } ?> { _.person } ?~ { _.lastname == "Doe" } ?> { _.address } ?> { _.city } ?> { _.toString } should be(None)
}
"NullSafeCoalescer" should "extract Mr Smith from ACME Int" in {
co ?> { _.employees.tail.head } ?> { _.person } should be(Some(js))
}
it should "extract Mr Smith's address" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } should be(Some(Address(UK, UK.Manchester)))
}
it should "extract Mr Smith's City" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?> { _.city } should be(Some(UK.Manchester))
}
it should "extract and stringfy Mr Smith's City" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?> { _.city } ?> { _.toString } should be(Some("Manchester"))
}
it should "extract and stringify Mr Smith's City if he's from Manchester, UK" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?? { Address(UK, UK.Manchester) } ?> { _.city } ?> { _.toString } should be(Some("Manchester"))
}
it should "not extract and stringify Mr Smith's City if he's not from London, UK" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?? { Address(UK, UK.London) } ?> { _.city } ?> { _.toString } should be(None)
}
it should "not extract and stringify Mr Smith's City if he is from Manchester, UK" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?! { Address(UK, UK.Manchester) } ?> { _.city } ?> { _.toString } should be(None)
}
it should "not extract and stringify Mr Smith's City if he's not Mr Doe" in {
co ?> { _.employees.tail.head } ?> { _.person } ?~ { _.lastname == "Doe" } ?> { _.address } ?> { _.city } ?> { _.toString } should be(None)
}
it should "comment on the weather if Mr Smith is from Manchester, UK" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?? { Address(UK, UK.Manchester) } match {
case None => fail
case Some(_) => println("It's normally rainy")
}
}
it should "comment on the night-sky if Mr Smith is not from London, UK" in {
co ?> { _.employees.tail.head } ?> { _.person } ?> { _.address } ?? { Address(UK, UK.London) } match {
case None => println("You can see so many stars when not in the big city")
case Some(_) => fail
}
}
}
object NullSafeCoalescer {
...
@inline implicit def anyToOption[E >: Nothing](e: E): Option[E] = Option(e)
...
}
if (root != null && root.level1 != null && root.level1.level2 != null && root.level1.level2.level3 != null && root.level1.level2.level3.finalPropertyMightYetBeNull != null)
println("Finally we are safe: " + root.level1.level2.level3.finalPropertyMightYetBeNull)
}
root ?> _.level1 ?> _.level2 ?> _.level3 ?> _.finalPropertyMightYetBeNull match {
case Some(butItAintNull) => println("Safe Safe Safe: " + butItAintNull)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment