in Practice Scala patric.fornasier@thoughtworks.com - - PowerPoint PPT Presentation

in practice scala
SMART_READER_LITE
LIVE PREVIEW

in Practice Scala patric.fornasier@thoughtworks.com - - PowerPoint PPT Presentation

in Practice Scala patric.fornasier@thoughtworks.com marc.hofer@thoughtworks.com e c n e i r e p x E Optimising IT n g i s e D Organisations Consulting Build & Release Management technology esting T Offices 2 Warning


slide-1
SLIDE 1

inPractice

Scala

patric.fornasier@thoughtworks.com marc.hofer@thoughtworks.com

slide-2
SLIDE 2
slide-3
SLIDE 3

Optimising IT Organisations technology

Consulting

T esting Build & Release Management E x p e r i e n c e D e s i g n

slide-4
SLIDE 4

Offices

2

slide-5
SLIDE 5

presentation Contains code examples

Warning

slide-6
SLIDE 6

Context

A bit of about the project

slide-7
SLIDE 7
slide-8
SLIDE 8
slide-9
SLIDE 9
slide-10
SLIDE 10

?

Why Scala

slide-11
SLIDE 11
slide-12
SLIDE 12

What is Scala ?

slide-13
SLIDE 13

Scala

Functional Object Oriented Statically Typed Scriptable JVM- based

slide-14
SLIDE 14

JVM Byte Code Compile Scala

slide-15
SLIDE 15

Benefits

slide-16
SLIDE 16

“Functional Programming”

slide-17
SLIDE 17

List<String> features = Arrays.asList("noFeature", "featureC", "featureB", "featureA"); for (String feature : features) { System.out.println(feature); }

slide-18
SLIDE 18

List<String> javaFeatures = Arrays.asList("noFeature", "featureC", "featureB", "featureA"); for (String feature : javaFeatures) { System.out.println(feature); } val scalaFeatures = List( "noFeature", "featureC", "featureB", "featureA") scalaFeatures foreach { println _ }

slide-19
SLIDE 19

val currentFeatures = List("noFeature", "featureC", "featureB", "featureA") Result: "featureA_toggle, featureB_toggle" currentFeatures.filter { _.startsWith("feature") } .mkString(", ") .map { _ + "_toggle" } .sorted .take(2)

?

slide-20
SLIDE 20

String result = ""; List<String> currentFeatures = Arrays.asList("noFeature", "featureC", "featureB", "featureA"); Collections.sort(currentFeatures); int i = 0, j = 0, take = 2; while(i < take && j < currentFeatures.size()) { String feature = currentFeatures.get(j++); if(feature.startsWith("feature")) { result += feature + "_toggle" + ((i++ != take) ? ", " : ""); } } currentFeatures.filter { _.startsWith("feature") } .mkString(", ") .map { _ + "_toggle" } .sorted .take(2)

slide-21
SLIDE 21

“Less Boilerplate”

slide-22
SLIDE 22

public class Property {}

slide-23
SLIDE 23

class Property {}

slide-24
SLIDE 24

public class Property { private final String key; private final String value; private final String defaultValue; public Property(String key, String value, String defaultValue) { this.key = key; this.value = value; this.defaultValue = defaultValue; } }

slide-25
SLIDE 25

class Property(key: String, value: String, default: String) {}

slide-26
SLIDE 26

public class Property { // ... public String getKey() { return key; } public String getValue() { return value; } public String getDefaultValue() { return defaultValue; } }

slide-27
SLIDE 27

class Property(val key: String, val value: String, val default: String) {}

slide-28
SLIDE 28

public class Property { // ... public Property changeTo(String newValue) { return new Property(key, value, newValue); } public Property reset() { return new Property(key, value); } @Override public String toString() { return String.format("%s=%s", key, value); } }

slide-29
SLIDE 29

class Property(val key: String, val value: String, val default: String) { def changeTo(newValue: String) = new Property(key, newValue, default) def reset = new Property(key, default)

  • verride def toString = "%s=%s" format(key, value)

}

slide-30
SLIDE 30

public class Property { // ... @Override public boolean equals(Object obj) { if (!(obj instanceof Property)) return false; Property other = (Property) obj; return other.key.equals(key) && other.value.equals(value) && other.defaultValue.equals(defaultValue) } @Override public int hashCode() { int result = 17; result = 31 * result + key; result = 31 * result + value; result = 31 * result + defaultValue; return result; } }

slide-31
SLIDE 31

case class Property(key: String, value: String, default: String) { def changeTo(newValue: String) = Property(key, newValue, default) def reset = Property(key, default)

  • verride def toString = "%s=%s" format(key, value)

}

slide-32
SLIDE 32 public class Property { private final String key; private final String value; private final String defaultValue; public Property(String key, String value, String defaultValue) { this.key = key; this.value = value; this.defaultValue = defaultValue; } public String getKey() { return key; } public String getValue() { return value; } public String getDefaultValue() { return defaultValue; } public Property changeTo(String newValue) { return new Property(key, value, newValue); } public Property reset() { return new Property(key, value); } @Override public String toString() { return String.format("%s=%s", key, value); } @Override public boolean equals(Object obj) { if (!(obj instanceof Property)) return false; Property other = (Property) obj; return other.key.equals(key) && other.value.equals(value) && other.defaultValue.equals(defaultValue) } @Override public int hashCode() { int result = 17; result = 31 * result + key; result = 31 * result + value; result = 31 * result + defaultValue; return result; } }
slide-33
SLIDE 33

case class Property(key: String, value: String, default: String) { def changeTo(newValue: String) = Property(key, newValue, default) def reset = Property(key, default)

  • verride def toString = "%s=%s" format(key, value)

}

slide-34
SLIDE 34 public class Property { private final String key; private final String value; private final String defaultValue; public Property(String key, String value, String defaultValue) { this.key = key; this.value = value; this.defaultValue = defaultValue; } public String getKey() { return key; } public String getValue() { return value; } public String getDefaultValue() { return defaultValue; } public Property changeTo(String newValue) { return new Property(key, value, newValue); } public Property reset() { return new Property(key, value); } @Override public boolean equals(Object obj) { if (!(obj instanceof Property)) return false; Property other = (Property) obj; return other.key.equals(key) && other.value.equals(value) && other.defaultValue.equals(defaultValue) } @Override public int hashCode() { int result = 17; result = 31 * result + key; result = 31 * result + value; result = 31 * result + defaultValue; return result; } @Override public String toString() { return String.format("%s=%s", key, value); } } case class Property(key: String, value: String, default: String) { def changeTo(newValue: String) = Property(key, newValue, default) def reset = Property(key, default)
  • verride def toString = "%s=%s" format(key, value)
}

45 vs 5

L O C s

* ignoring blank lines
slide-35
SLIDE 35

Another Example: { Blocks }

“Less Boilerplate”

slide-36
SLIDE 36

val content = get("http://foo.com") { toString } val statusCode = get("http://foo.com") { statusCode } get("http://foo.com") int statusCode = httpSupport.get("http://foo.com", new Block<Integer>() { public Integer execute(HttpResponse response) { return response.getStatusLine().getStatusCode()); } }); String content = httpSupport.get("http://foo.com", new Block<String>() { public Integer execute(HttpResponse response) { return EntityUtils.toString(response.getEntity()); } }); httpSupport.get("http://foo.com", new VoidBlock() { public void execute(HttpResponse response) {} });

slide-37
SLIDE 37

“Java Compatibility”

slide-38
SLIDE 38

java -cp "lib/*" com.springer.core.app.Casper

Scala library goes in here!

slide-39
SLIDE 39

val java = new java.util.ArrayList[String] val scala: Seq[String] = foo filter { ... }

slide-40
SLIDE 40

val log = Logger.getLogger("com.foo") log.debug("Hello!")

slide-41
SLIDE 41
slide-42
SLIDE 42

“Gentle Learning Curve”

“Java without ;”

Library re-use

JVM

slide-43
SLIDE 43

<XML-Support/>

slide-44
SLIDE 44

<PdfInfo hasAccess="false">

<DDSId>12345</DDSId> <ContentType>Article</ContentType> </PdfInfo>

val pdfInfo = val contentType = (pdfInfo \ "ContentType").text val hasAccess = (pdfInfo \ "@hasAccess").text.toBoolean

slide-45
SLIDE 45

val pdfInfoList = <PdfInfoList>

<PdfInfo hasAccess="false"> <DDSId>12345</DDSId> <ContentType>Article</ContentType> </PdfInfo> <PdfInfo hasAccess="true"> <DDSId>54321</DDSId> <ContentType>Book</ContentType> </PdfInfo> </PdfInfoList>

case class PdfInfo(xml: NodeSeq) { val ddsId = (xml \ "DDSId").head.text val hasAccess = (xml \ "@hasAccess").head.text val contentType = (xml \ "ContentType").head.text } (pdfInfoList \ "PdfInfo") map { PdfInfo(_) }

slide-46
SLIDE 46

“DSL- Friendly”

slide-47
SLIDE 47

System App2 App3 Functional App1 Unit DB Integration

Levels of Testing

slide-48
SLIDE 48

Unit Test DSL

class PropertyTest extends Spec with ShouldMatchers { it("should render key and value separated with an equals sign") { Property("shark", "fish").toString should equal ("shark=fish") } }

slide-49
SLIDE 49

class ForgottenPasswordPageTests extends FunctionalTestSpec with ForgottenPasswordSteps with Uris { it("should validate user email") { given(iNavigateTo(ForgottenPasswordPage)) when(iSubmitEmail("invalid@email.com")) then(iShouldSeeInvalidEmailErrorMessage) } }

Functional Test DSL

slide-50
SLIDE 50

Document Validation DSL

trait DDSValidationRules extends JournalXPaths with APlusPlusElements { val journalRules = List( No(JournalTitle) is Invalid, MoreThanOneElementIn(JournalArticle) is Invalid, BadLanguageIn(JournalArticleLanguage) is Invalid, JournalIssue With No(JournalIssueCoverYear) is Invalid, Bibliography With No(BibliographyHeading) is Invalid, MarkupIn(JournalTitle) is Unsupported, MarkupIn(JournalAuthor) is Unsupported ) }

slide-51
SLIDE 51

“Active Community”

slide-52
SLIDE 52

“Fun”

slide-53
SLIDE 53

“Fun”

slide-54
SLIDE 54

“Fun”

val foo = (7 to 21)

slide-55
SLIDE 55

“Fun”

List<Integers> foo = new ArrayList<Integers>() for (int i = 7; i <= 21; i++) { foo.add(i); }

slide-56
SLIDE 56

“Fun”

def eat(food: String = "plankton")

slide-57
SLIDE 57

“Fun”

public void eat(String food) {...} public void eat() { eat("plankton"); }

slide-58
SLIDE 58

get("http://foo.com") { statusCode }

“Fun”

slide-59
SLIDE 59

httpSupport.get("http://foo.com", new Block<Integer>() { public Integer execute(HttpResponse response) { return response.getStatusLine().getStatusCode()); } });

“Fun”

slide-60
SLIDE 60

Drawbacks

slide-61
SLIDE 61

“Tool Support”

slide-62
SLIDE 62
slide-63
SLIDE 63
slide-64
SLIDE 64

! ?

slide-65
SLIDE 65

1 : 1

slide-66
SLIDE 66

“More rope...”

slide-67
SLIDE 67 def ++ [B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]):That

Scala Collection Library

slide-68
SLIDE 68

class JournalPageTests extends Spec with FunctionalTestSpec with JournalPageSteps with SearchResultsPageSteps with LanguageSteps with LanguageSupport with JavascriptSupport with Uris with IssuePageSteps with VolumesAndIssuesPageSteps with CoverImageUrlBuilders with FakeEntitlementSteps with FullTextPageSteps with CommonAbstractSteps with GoogleAnalyticsSteps with ActionSectionSteps { ... }

Traits Galore in our codebase...

slide-69
SLIDE 69

xsbt

slide-70
SLIDE 70

scalaz

slide-71
SLIDE 71

Can Should

slide-72
SLIDE 72

Risk Mitigations

slide-73
SLIDE 73
slide-74
SLIDE 74
slide-75
SLIDE 75
slide-76
SLIDE 76
slide-77
SLIDE 77
slide-78
SLIDE 78
slide-79
SLIDE 79

Would we do it again

?

slide-80
SLIDE 80

disclaimer

use scala at your own risk

slide-81
SLIDE 81

Thanks

for

Listening

@patforna | @mhoefi