Thursday, November 15, 2012

@Delegate vs. Extends

// "favor composition over inheritance" ~Effective Java

class Parent {
    def getAll() {
        String resp = ""
        2.times {
            resp += get(it)
        }
        return resp
    }
    
    def get(def i) {
        "get PARENT $i\n"
    }
}

class Child1 extends Parent {
    def get(def i) {
        "get CHILD $i\n" // is used by parent's getAll
    }
}

class Child2 {
    @Delegate Parent p = new Parent() // decorated Parent instance
    
    def get(def i) {
        "get CHILD $i\n" // not used by parent's getAll
    }
}

Parent c1 = new Child1()
assert c1.getAll() == "get CHILD 0\nget CHILD 1\n" // calls child's get()
assert c1.get(0) == "get CHILD 0\n"

//Parent c2 = new Child2() // Child2 is not a Parent!
Child2 c2 = new Child2()
assert c2.getAll() == "get PARENT 0\nget PARENT 1\n" // calls parent's get()
assert c2.get(0) == "get CHILD 0\n"

Monday, November 5, 2012

DNS lookup - timed

int maxTimeout = 30000
int minTimeout = 15000

List domainList = [
    'gmail.com', 'google.co.uk', 'google.com', 'bbc.co.uk', 'cnn.com', 'java.oracle.com',
    'facebook.com', 'twitter.com', 'oracle.com', 'zen.co.uk', 'java.net', 'www.scala-lang.org',
    'plus.google.com', 'guardian.co.uk', 'linkedin.com', 'www.typesafe.com', 'www.yahoo.com',
    'www.ibm.com', 'www.apache.org', 'www.adobe.com', 'www.microsoft.com', 'www.stackoverflow.com',
    'www.apple.com', 'groovy.codehaus.org', 'java.oracle.com', 'www.telegraph.co.uk', 'www.jroller.com',
    'www.dell.com', 'www.samsung.com', 'www.amazon.co.uk', 'docs.oracle.com', 'www.infoq.com',
    'www.devoxx.com', 'www.qconlondon.com', 'www.smashingmagazine.com', 'en.wikipedia.com' ]

int count = 1
while (true) {
    int idx = (int)( Math.random() * domainList.size )    
    println "[$count]  nslookup ${domainList[idx]}"
    def process = "nslookup ${domainList[idx]}".execute()
    println "Found text ${process.text}"
    
    int sleepTime = (int)( minTimeout + Math.random() * ( maxTimeout  - minTimeout))
    Thread.sleep( sleepTime );
    
    ++count
}

Monday, October 29, 2012

Resolve an HTTP Redirect

// source: https://gist.github.com/3899754

def findRealUrl(url) {
    HttpURLConnection conn = url.openConnection()
    conn.followRedirects = false
    conn.requestMethod = 'HEAD'
    if(conn.responseCode in [301,302]) {
        if (conn.headerFields.'Location') {
          return findRealUrl(conn.headerFields.Location.first().toURL())
        } else {
            throw new RuntimeException('Failed to follow redirect')
        }
    }
    return url
}

Wednesday, August 1, 2012

match/when implemented with Groovy's GEP-3

//WOW! https://gist.github.com/3002724

// No commas
def a = 'tim'
def nocom = match( a ) {
  when 'dave' 'Hi Dave'
  when 'tim'  'Hi Tim'
  otherwise 'none of the above'
}
assert nocom == 'Hi Tim'

// Commas
a = 'William'
def com = match( a ) {
  when 'dave', 'Hi Dave'
  when 'tim',  'Hi Tim'
  otherwise 'none of the above'
}
assert com == 'none of the above'

// Lists (can't use commas)
a = [1,3]
def list = match( a ) {
  when [1,2] 'One and two'
  when [1,3] 'One and three'
  when [2,1] 'Two and one'
}
assert list == 'One and three'

// Wildcard Lists (can't use commas)
a = [1,2,3]
def wild = match( a ) {
  when [1,3,2] 'One, three and two'
  when [1,_]   'One and something'
  when [1,_,3] 'One, something and three'
  when [1,_,_] 'One, something and something'
  otherwise    'Something else'
}
assert wild == 'One, something and three'

// Closures as the when/results
String toRoman( int num ) {
  match( num ) {
    when { it >= 100 } { "C"  + toRoman( num - 100 ) }
    when { it >= 90  } { "XC" + toRoman( num - 90  ) }
    when { it >= 50  } { "L"  + toRoman( num - 50  ) }
    when { it >= 40  } { "XL" + toRoman( num - 40  ) }
    when { it >= 10  } { "X"  + toRoman( num - 10  ) }
    when { it >= 9   } { "IX" + toRoman( num - 9   ) }
    when { it >= 5   } { "V"  + toRoman( num - 5   ) }
    when { it >= 4   } { "IV" + toRoman( num - 4   ) }
    when { it >= 1   } { "I"  + toRoman( num - 1   ) }
    otherwise ""
  }
}

assert "I"       == toRoman( 1 )
assert "II"      == toRoman( 2 )
assert "IV"      == toRoman( 4 )
assert "V"       == toRoman( 5 )
assert "VI"      == toRoman( 6 )
assert "IX"      == toRoman( 9 )
assert "X"       == toRoman( 10 )
assert "XVII"    == toRoman( 17 )
assert "XXXVIII" == toRoman( 38 )
assert "CCCXCIX" == toRoman( 399 )
    
///////////////////////////
// Implementation below...

def match( v, c ) {
  new Matcher( var:v, closure:c ).match()
}

class Matcher {
  def var
  Closure closure
  List cases = []
  Otherwise otherwise

  def propertyMissing( name ) {
    if( name == '_' ) {
      new Any()
    }
    else {
      def w = new When()
      cases << w
      w
    }
  }
  def when( condition )         { cases << new When( condition:condition ) }
  def when( condition, result ) { cases << new When( condition:condition, result:result ) }
  def otherwise( result )       { this.otherwise = new Otherwise( result:result ) }
  public match() {
    closure.delegate = this
    closure.resolveStrategy = Closure.DELEGATE_ONLY
    closure()
    def ret = cases.find {
      it.condition instanceof Closure ?
        it.condition( var ) :
        it.condition == var
    }?.result ?: otherwise?.result
    ret instanceof Closure ? ret() : ret
  }
}

class When {
  def condition
  def result
  def getAt( List a )                  { condition = a ; this }
  def call( result )                   { this.result = result }
  def propertyMissing( String result ) { this.result = result }
}

class Otherwise {
  def result
}

class Any {
  boolean equals( other ) { true }
}

println 'DONE'

Sunday, July 15, 2012

GPars DataflowQueues

//source: http://jaxenter.com/tutorial-gpars-making-parallel-systems-groovy-and-java-friendly-43529-2.html
//a sample code build process //

//source: http://jaxenter.com/tutorial-gpars-making-parallel-systems-groovy-and-java-friendly-43529-2.html
//a sample code build process //

import static groovyx.gpars.dataflow.Dataflow.operator
import groovyx.gpars.dataflow.*


def _checkout = { String a ->
    Thread.sleep( (Long)Math.random() * 5 ); // a bit of random delay for fun
    println ">checking out...$a"
    return "checkout-$a"
}
def _compileSources = { a ->
    println ">compiling...$a"
    return "compileSources-$a"
}
def _generateAPIDoc = { a ->
    println ">api Docs...$a"
    return "generateAPIDoc-$a"
}
def _generateUserDocumentation = { a ->
    println ">user Docs...$a"
    return "generateUserDocumentation-$a"
}
def _packageProject = { project, api, doc ->
    println ">packaging...$project $api $doc"
    return "packageProject-$project $api $doc"
}
def _deploy = { a ->
    println ">deploying...$a"
    return (!!Math.round(Math.random()))
}


/* We need a Broadcaster and Queues to wire elements together */
def checkedOutProjects_B = new DataflowBroadcast()
def urls_Q = new DataflowQueue()
def compiledProjects_Q = new DataflowQueue()
def apiDocs_Q = new DataflowQueue()
def userDocs_Q = new DataflowQueue()
def packages_Q = new DataflowQueue()
def done_Q = new DataflowQueue()



/* Here's the composition of individual build steps into a process */
operator(inputs: [urls_Q], outputs: [checkedOutProjects_B], maxForks: 3) { url ->
    bindAllOutputs _checkout(url)
}
operator([checkedOutProjects_B.createReadChannel()], [compiledProjects_Q]) { projectRoot ->
    bindOutput _compileSources(projectRoot)
}
operator(checkedOutProjects_B.createReadChannel(), apiDocs_Q) { projectRoot ->
    bindOutput _generateAPIDoc(projectRoot)
}
operator(checkedOutProjects_B.createReadChannel(), userDocs_Q) { projectRoot ->
    bindOutput _generateUserDocumentation(projectRoot)
}
operator([compiledProjects_Q, apiDocs_Q, userDocs_Q], [packages_Q]) { project, api, doc ->
    bindOutput _packageProject(project, api, doc)
}

def deployer = operator(packages_Q, done_Q) { packagedProject ->
    boolean ok = _deploy(packagedProject)
    println "! Deployed? $ok"
    bindOutput (ok)
}


/* add data, start the machine a rollin! */
5.times { urls_Q << "url #$it" }
5.times { urls_Q << "url #${++it*5}" }


/* Now we're set up, and can just wait for the build to finish */
println "==Starting the build process. This line MIGHT NOT be printed out first ...=="

//deployer.join()  //Wait for the last operator in the network to finish??

Thursday, July 12, 2012

GBench - easy code benchmarking

//source: http://nagaimasato.blogspot.jp/2012/07/gbench-031-released.html
//requires Groovy 2.0.0+

@Grab('com.googlecode.gbench:gbench:0.3.1-groovy-2.0') 
import gbench.BenchmarkBuilder
import groovy.transform.CompileStatic
 
int fib(int n) {
    if (n < 2) return n 
    return fib(n - 1) + fib(n - 2)  
} 
 
@CompileStatic 
int fib2(int n) { 
    if (n < 2) return n 
    return fib2(n - 1) + fib2(n - 2)  
} 
 
new BenchmarkBuilder().run { 
    int n = 20  
    "Normal Version" { fib n } 
    "@CompileStatic Version" { fib2 n } 
}.prettyPrint() 

Wednesday, July 11, 2012

Conceptual Map/Reduce Example

//inspiration: http://hamletdarcy.blogspot.ca/2008/01/mapreduce-for-mere-mortals.html

// general purpose function
def mapReduceFunc = { data, mapFunction, reduceFunction ->
  def mappedData = data.collect(mapFunction)
  reduceFunction(mappedData)
}

// the data
Map dictionary = [
  abacus: "a device for making arithmetic calculations", 
  arc: "any unbroken part of the circumference of a circle", 
  beaver: "a large, amphibious rodent of the genus Castor"
]

// the mapping function impl
def startingCharacter = { pair ->
  pair.value = pair.key[0].toLowerCase()
  return pair
}

// the reducing function impl
def countCharacters = { dataMap ->
  Map result = [:]
  ('a'..'z').each{ result[it] = 0 }
  dataMap.each { pair -> result[pair.value]++ }
  return result
}

// put it all together!
def result1 = mapReduceFunc(dictionary, startingCharacter, countCharacters)
println result1

//// TWEAK THE I/O FUNCS!  ////

// the mapping function impl
startingCharacter = { pair ->
  return pair.key[0].toLowerCase()
}

// the reducing function impl
countCharacters = { dataList ->
  Map result = [:]
  ('a'..'z').each{ result[it] = 0 }
  dataList.each { it -> result[it]++ }
  return result
}

// put it all together!
def result2 = mapReduceFunc(dictionary, startingCharacter, countCharacters)
println result2

assert result1 == result2