Compile-time and Runtime Scott Davis, ThirstyHead.com...
Transcript of Compile-time and Runtime Scott Davis, ThirstyHead.com...
![Page 1: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/1.jpg)
Compile-time and Runtime Metaprogramming with Groovy
Scott Davis, ThirstyHead.com
![Page 2: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/2.jpg)
![Page 3: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/3.jpg)
@scottdavis99Scott Davis
![Page 4: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/4.jpg)
![Page 5: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/5.jpg)
inheritance
![Page 6: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/6.jpg)
![Page 7: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/7.jpg)
metaprogramming
![Page 8: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/8.jpg)
![Page 9: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/9.jpg)
Categories
RuntimeExpandoMetaClassMethodMissing
Compile- timeAST Transformations
![Page 10: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/10.jpg)
Categories(Runtime)
![Page 11: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/11.jpg)
![Page 12: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/12.jpg)
![Page 13: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/13.jpg)
NOTE: Evolutionary dead-end in Java!
![Page 14: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/14.jpg)
![Page 15: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/15.jpg)
![Page 16: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/16.jpg)
Category metaprogramming works with any library (Groovy, Java, or otherwise) as long as:
1. The method is static
2. The first argument of the method is the same data-type as the “delegate” class
java.lang.String (delegate)
StringUtils:static String abbreviate(String str, int maxWidth)
![Page 17: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/17.jpg)
![Page 18: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/18.jpg)
import org.apache.commons.lang.*;
public class TestJava{ public static void main(String[] args){ String sentence = "My name is Inigo Montoya."; System.out.println(sentence); System.out.println(StringUtils.abbreviate(sentence, 15)); }}
$ ./runJava.sh
My name is Inigo Montoya. My name is I...
Java:
![Page 19: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/19.jpg)
import org.apache.commons.lang.*
use(StringUtils){ def sentence = "My name is Inigo Montoya." println sentence //NOTE: look at the cool new method // on java.lang.String!!! println sentence.abbreviate(15)}
$ ./runGroovy.sh
My name is Inigo Montoya. My name is I...
Groovy:
![Page 20: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/20.jpg)
![Page 21: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/21.jpg)
ExpandoMetaClass(Runtime)
![Page 22: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/22.jpg)
![Page 23: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/23.jpg)
def message = "I love groovy"
String.metaClass.shout = { return delegate.toUpperCase()}
println message.shout()
$ groovy shout
I LOVE GROOVY
Groovy (class-level metaprogramming):
![Page 24: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/24.jpg)
def message = "I love groovy"
message.metaClass.shout = { return delegate.toUpperCase()}
println message.shout()
"java is ok, too".shout()
$ groovy shout
I LOVE GROOVYCaught: groovy.lang.MissingMethodException: No signature of method: java.lang.String.shout() is applicable for argument types: () values: []
Groovy (instance-level metaprogramming):
![Page 25: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/25.jpg)
![Page 26: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/26.jpg)
MethodMissing(Runtime)
![Page 27: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/27.jpg)
invokeMethod()
![Page 28: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/28.jpg)
![Page 29: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/29.jpg)
![Page 30: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/30.jpg)
![Page 31: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/31.jpg)
![Page 32: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/32.jpg)
class Person{ String name Map relationships = [:] Object invokeMethod(String what, Object who){ if(relationships.containsKey(what)){ who.each{thisPerson -> relationships.get(what).add(thisPerson) } } else{ relationships.put(what,who as List) } }}
def scott = new Person(name:"Scott") scott.married "Kim" scott.knows "Neal" scott.workedWith "Brian"scott.knows "Ted", "Ben", "David"println scott.relationships
["married" :["Kim" ],"knows":["Neal", "Venkat", "Ted", "Ben", "David"], "workedWith":["Brian", "Jared"]]
![Page 33: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/33.jpg)
methodMissing()
![Page 34: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/34.jpg)
![Page 35: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/35.jpg)
class Ipod { ...
String spacify(String camelCase){ String title = "" camelCase.each{ char c = (char) it title += Character.isUpperCase(c) ? " ${c}" : "${c}" } title.trim() }
def methodMissing(String methodCall, Object arg){ if(methodCall.startsWith("play")){ // "playTwoOfUs" --> "TwoOfUs" return play(spacify(methodCall - "play")) } }}
![Page 36: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/36.jpg)
ipod = new Ipod()ipod << new Song(title:"Two Of Us", duration:217)ipod << new Song(title:"Dig A Pony", duration:235)
println ipod.play("Dig A Pony")println ipod.playTwoOfUs() println ipod.playSomeOtherSong()
![Page 37: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/37.jpg)
Expando
![Page 38: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/38.jpg)
![Page 39: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/39.jpg)
def e = new Expando()e.latitude = 70 e.longitude = 30 println e
e.areWeLost = {-> return (e.longitude != 30) || (e.latitude != 70)}
e.areWeLost()//===> false
![Page 40: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/40.jpg)
![Page 41: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/41.jpg)
AST Transformations(Compile-time)
![Page 42: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/42.jpg)
![Page 43: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/43.jpg)
![Page 44: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/44.jpg)
@Delegate
![Page 45: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/45.jpg)
NOTE: All of the Date field’s methods are “pushed up” to theEvent “parent” class
![Page 46: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/46.jpg)
class AllCapsString{ @Delegate final String body
AllCapsString(String body){ this.body = body.toUpperCase() } String toString(){ body } }
Suppose we wanted to create a new type of String:AllCapsString
We cannot extend java.lang.String because it is final.
But we can use the @Delegate AST transformationto delegate all “String” calls to AllCapsString...
![Page 47: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/47.jpg)
@Category
![Page 48: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/48.jpg)
![Page 49: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/49.jpg)
![Page 50: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/50.jpg)
![Page 51: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/51.jpg)
Categories
RuntimeExpandoMetaClassMethodMissing
Compile- timeAST Transformations
![Page 52: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/52.jpg)
Compile-time and Runtime Metaprogramming with Groovy
Scott Davis, ThirstyHead.com
![Page 54: Compile-time and Runtime Scott Davis, ThirstyHead.com …scottdavis99.github.io/groovyMetaprogramming/groovyMeta... · 2012-04-17 · Metaprogramming with Groovy Scott Davis, ThirstyHead.com.](https://reader034.fdocuments.us/reader034/viewer/2022042112/5e8da680ff55a3197c4a987b/html5/thumbnails/54.jpg)
Image Credits
http://www.flickr.com/photos/timothymorgan/75294154/in/set-1615269/http://www.flickr.com/photos/cwalker71/2175839970/http://www.flickr.com/photos/34517346@N02/3464812992/http://www.flickr.com/photos/chrishedgate/3044800149/http://www.flickr.com/photos/andrewbain/3649614402/