Commit 59bb061e by O'Reilly Media, Inc.

Initial commit

parents
9780596155964
\ No newline at end of file
<html>
<head>
<title>"Programming Scala": README for Code Examples</title>
</head>
<body>
<h1 id="programming_scala_1st_edition">Programming Scala, 1st Edition</h1>
<h2 id="readme_for_the_code_examples">README for the Code Examples</h2>
<p>Dean Wampler and Alex Payne <br>
September 20, 2009</p>
<p>This archive contains all the code examples found in <a href="http://programming-scala.labs.oreilly.com/">Programming Scala</a>, with the exception of several code snippets in the text. In the book, all the examples for which there are corresponding files here begin with a path comment like this:</p>
<pre><code>// code-examples/chapter/../filename
</code></pre>
<p>Use these comments to find the corresponding file in this distribution. In addition, this archive contains Scala <em>Specs</em> to test the code, most of which are not reproduced in the text of the book. <em>Specs</em> is a Behavior-Driven Development - BDD - tool like RSpec for Ruby. It is discussed in the <em>Tools and Libraries</em> chapter of the book.</p>
<h2 id="naming_conventions">Naming Conventions</h2>
<p>The examples include scripts that are run with the <code>scala</code> command, source files that are compiled with <code>scalac</code>, and source files that deliberately fail to compile. To keep then straight and to support the build process, the following naming conventions are used for the files.</p>
<ul>
<li><code>sake.scala</code> - build scripts for the <em>sake</em> build tool (described below).</li>
<li><code>*-script.scala</code> - Scripts that you can run using, for example, <code>scala foo-script.scala</code>. These script are executed as part of the normal compile and test process, but currently their outputs aren&#8217;t checked automatically for correctness. (If they fail outright, they cause the build to fail.)</li>
<li><code>*-wont-compile.scala</code> - Scala files that are used in the book, but deliberately don&#8217;t compile. They are also excluded from the build.</li>
<li><code>*-v28-*.scala</code> - Scala version 2.8 only. These files demonstrate new 2.8 features (see note below).</li>
<li>All other <code>*.scala</code> files - Compilable Scala files that are built and tested by the <em>sake</em> build process.</li>
</ul>
<blockquote>
<p>NOTE: While we have tried to ensure that all files work with Scala version 2.8, that release has not matured as fast as we expected while writing the book. For example, some libraries we use do not yet support v2.8. Therefore, the build process will not work, as is, with the v2.8 tool chain.</p>
</blockquote>
<h2 id="building_the_code_examples">Building the Code Examples</h2>
<p>To build and run the examples, the following tools must be installed with the appropriate environment variables set (e.g., <code>PATH</code>), so that the tools can be used from the command line.</p>
<ul>
<li>Scala version 2.7.5 or later (excluding version 2.8).</li>
<li>Java 1.5 or later.</li>
</ul>
<p>To build the examples and run the tests, change to the <code>code-examples</code> directory and use the following shell command</p>
<pre><code>bin/sake
</code></pre>
<p>There will be a <em>lot</em> of output. (The <em>sake</em> build tool is described below.) You will find both a <em>bash</em> shell and a Windows command shell version of this script in <code>bin</code>. However, the Windows version does not support the options supported by the <em>bash</em> version at this time.</p>
<p>This <code>sake</code> script reads the build targets and rules from the <code>sake.scala</code> file in the current directory (analogous to a <em>make</em> file). The top-level build file simple iterates through all the chapter directories and builds the <code>all</code> target in each of them.</p>
<p>The <code>all</code> target in the <code>saka.scala</code> file for each chapter begins by cleaning previous build artifacts. The source files (i.e., non-scripts, as described above) are compiled and the tests are run, most of which are written using <em>Specs</em>. Finally, most of the &#8220;scripts&#8221; are executed, although their output is not automatically checked for the correct results, at this time.</p>
<p>In some directories, some compilable files and scripts are excluded for various reasons. These exceptions are documented in the corresponding <code>sake.scala</code> build files. </p>
<p>Each directory under <code>code-examples</code> corresponds to a chapter in the book. If you want to build in those individual directories, run the following command:</p>
<pre><code>../bin/sake [target]
</code></pre>
<p>The targets supported include the following:</p>
<pre><code>all Default target
clean Remove build artifacts
compile Compile the non-script Scala files
spec Run the tests (Specs) files
scripts Run the script files
</code></pre>
<p>Some chapters have only script files. For them, the <code>clean</code>, <code>compile</code>, and <code>spec</code> targets do nothing.</p>
<p>Many of the script files depend on other code compiled for the same chapter (if any). All compiled class files are written to the <code>build</code> directory corresponding to each chapter. In some cases, scripts rely on code in other chapters or on some of the jars in the top-level <code>lib</code> directory. Hence, to run an individual script file, <em>e.g.,</em> <code>foo-script.scala</code>, try the following command first.</p>
<pre><code>scala foo-script.scala
</code></pre>
<p>If it requires code in the build directory, run the <code>../bin/sake</code> command first to build the code, then try the following:</p>
<pre><code>scala -classpath build foo-script.scala
</code></pre>
<p>Finally, if the the script appears to require one or more jars from <code>lib</code>, add those jars to the <code>classpath</code> argument, <em>e.g.,</em></p>
<pre><code>scala -classpath build:../lib/junit4-5.jar foo-script.scala
</code></pre>
<h3 id="optional_build_targets">Optional Build Targets</h3>
<p>There are a few examples in the book that use <em>Ant</em> with <em>Ivy</em>, <em>AspectJ</em>, or <em>Spring</em> (but never at the same time ;). These are not built by default, so we don&#8217;t assume you have those tools installed. If you want to build some or all of them, you&#8217;ll need one or more of the following tools installed.</p>
<pre><code>- Ant 1.7+ and Ivy 2
- AspectJ 1.6 or later
- Spring 2.5 or later
</code></pre>
<p><em>Ant</em> and <em>Ivy</em> are required to build the SMTPD example in the <code>Concurrency</code> chapter. If you want to build it, go to the <code>Concurrency/smtpd</code> directory and run the command <code>ant</code>.</p>
<p><em>AspectJ</em> and <em>Spring</em> are only required if you build the examples in the <code>ToolsLibs</code> directory that demonstrate integration between these tools and Scala. In the <code>ToolsLibs</code> directory, run either or both of the following commands:</p>
<pre><code>../bin/sake spring
../bin/sake aspectj
</code></pre>
<h2 id="about_sake">About Sake</h2>
<p>As an exercise, Dean built a build tool, named <em>sake</em> (as in the Japanese alcoholic beverage) that we used for the code examples. It&#8217;s inspired by Ruby <em>rake</em> and Unix <em>make</em>. It&#8217;s quite incomplete in terms of features and it has a number of warts. Nevertheless, it provides a nontrivial example of implementing an <em>internal</em> DSL in Scala. (There is a chapter on DSLs in the book.)</p>
<p>The <code>sake</code> driver script for *nix-like systems is in the <code>bin</code> directory. There is also a <code>sake.bat</code> for Windows systems (but it is largely untested and it supports fewer invocation options). The corresponding jar file and a jar of the source code are in the <code>lib</code> directory. Sake is maintained at <a href="http://github.com/deanwampler/SakeScalaBuildTool">http://github.com/deanwampler/SakeScalaBuildTool</a>.</p>
<p>The <em>Scala Tools, Libraries, and IDE Support</em> chapter in the book discuss build tool options. If you are looking for &#8220;production quality&#8221; alternatives to <em>ant</em> and <em>maven</em>, we recommend that you consider the <em>Simple Build Tool</em> (sbt) or <em>Buildr</em>.</p>
<h2 id="feedback">Feedback</h2>
<p>We welcome feedback on the examples. For the code that actually appears in the book, you can post comments, corrections, etc. at the public &#8220;labs&#8221; site, <a href="http://programming-scala.labs.oreilly.com/">http://programming-scala.labs.oreilly.com/</a>. Or, you can post feedback on the <a href="http://forums.oreilly.com/">O&#8217;Reilly forum</a>. We always appreciate your willingness to provide feedback.</p>
</body>
</html>
\ No newline at end of file
# Programming Scala, 1st Edition#
## README for the Code Examples ##
Dean Wampler and Alex Payne
September 20, 2009
This archive contains all the code examples found in [Programming Scala](http://programming-scala.labs.oreilly.com/), with the exception of several code snippets in the text. In the book, all the examples for which there are corresponding files here begin with a path comment like this:
// code-examples/chapter/../filename
Use these comments to find the corresponding file in this distribution. In addition, this archive contains Scala *Specs* to test the code, most of which are not reproduced in the text of the book. *Specs* is a Behavior-Driven Development - BDD - tool like RSpec for Ruby. It is discussed in the _Tools and Libraries_ chapter of the book.
## Naming Conventions
The examples include scripts that are run with the `scala` command, source files that are compiled with `scalac`, and source files that deliberately fail to compile. To keep then straight and to support the build process, the following naming conventions are used for the files.
- `sake.scala` - build scripts for the *sake* build tool (described below).
- `*-script.scala` - Scripts that you can run using, for example, `scala foo-script.scala`. These script are executed as part of the normal compile and test process, but currently their outputs aren't checked automatically for correctness. (If they fail outright, they cause the build to fail.)
- `*-wont-compile.scala` - Scala files that are used in the book, but deliberately don't compile. They are also excluded from the build.
- `*-v28-*.scala` - Scala version 2.8 only. These files demonstrate new 2.8 features (see note below).
- All other `*.scala` files - Compilable Scala files that are built and tested by the *sake* build process.
> NOTE: While we have tried to ensure that all files work with Scala version 2.8, that release has not matured as fast as we expected while writing the book. For example, some libraries we use do not yet support v2.8. Therefore, the build process will not work, as is, with the v2.8 tool chain.
## Building the Code Examples
To build and run the examples, the following tools must be installed with the appropriate environment variables set (e.g., `PATH`), so that the tools can be used from the command line.
- Scala version 2.7.5 or later (excluding version 2.8).
- Java 1.5 or later.
To build the examples and run the tests, change to the `code-examples` directory and use the following shell command
bin/sake
There will be a *lot* of output. (The *sake* build tool is described below.) You will find both a *bash* shell and a Windows command shell version of this script in `bin`. However, the Windows version does not support the options supported by the *bash* version at this time.
This `sake` script reads the build targets and rules from the `sake.scala` file in the current directory (analogous to a *make* file). The top-level build file simple iterates through all the chapter directories and builds the `all` target in each of them.
The `all` target in the `saka.scala` file for each chapter begins by cleaning previous build artifacts. The source files (i.e., non-scripts, as described above) are compiled and the tests are run, most of which are written using *Specs*. Finally, most of the "scripts" are executed, although their output is not automatically checked for the correct results, at this time.
In some directories, some compilable files and scripts are excluded for various reasons. These exceptions are documented in the corresponding `sake.scala` build files.
Each directory under `code-examples` corresponds to a chapter in the book. If you want to build in those individual directories, run the following command:
../bin/sake [target]
The targets supported include the following:
all Default target
clean Remove build artifacts
compile Compile the non-script Scala files
spec Run the tests (Specs) files
scripts Run the script files
Some chapters have only script files. For them, the `clean`, `compile`, and `spec` targets do nothing.
Many of the script files depend on other code compiled for the same chapter (if any). All compiled class files are written to the `build` directory corresponding to each chapter. In some cases, scripts rely on code in other chapters or on some of the jars in the top-level `lib` directory. Hence, to run an individual script file, *e.g.,* `foo-script.scala`, try the following command first.
scala foo-script.scala
If it requires code in the build directory, run the `../bin/sake` command first to build the code, then try the following:
scala -classpath build foo-script.scala
Finally, if the the script appears to require one or more jars from `lib`, add those jars to the `classpath` argument, *e.g.,*
scala -classpath build:../lib/junit4-5.jar foo-script.scala
### Optional Build Targets ###
There are a few examples in the book that use *Ant* with *Ivy*, *AspectJ*, or *Spring* (but never at the same time ;). These are not built by default, so we don't assume you have those tools installed. If you want to build some or all of them, you'll need one or more of the following tools installed.
- Ant 1.7+ and Ivy 2
- AspectJ 1.6 or later
- Spring 2.5 or later
*Ant* and *Ivy* are required to build the SMTPD example in the `Concurrency` chapter. If you want to build it, go to the `Concurrency/smtpd` directory and run the command `ant`.
*AspectJ* and *Spring* are only required if you build the examples in the `ToolsLibs` directory that demonstrate integration between these tools and Scala. In the `ToolsLibs` directory, run either or both of the following commands:
../bin/sake spring
../bin/sake aspectj
## About Sake ##
As an exercise, Dean built a build tool, named *sake* (as in the Japanese alcoholic beverage) that we used for the code examples. It's inspired by Ruby *rake* and Unix *make*. It's quite incomplete in terms of features and it has a number of warts. Nevertheless, it provides a nontrivial example of implementing an *internal* DSL in Scala. (There is a chapter on DSLs in the book.)
The `sake` driver script for *nix-like systems is in the `bin` directory. There is also a `sake.bat` for Windows systems (but it is largely untested and it supports fewer invocation options). The corresponding jar file and a jar of the source code are in the `lib` directory. Sake is maintained at [http://github.com/deanwampler/SakeScalaBuildTool](http://github.com/deanwampler/SakeScalaBuildTool).
The *Scala Tools, Libraries, and IDE Support* chapter in the book discuss build tool options. If you are looking for "production quality" alternatives to *ant* and *maven*, we recommend that you consider the *Simple Build Tool* (sbt) or *Buildr*.
## Feedback ##
We welcome feedback on the examples. For the code that actually appears in the book, you can post comments, corrections, etc. at the public "labs" site, [http://programming-scala.labs.oreilly.com/](http://programming-scala.labs.oreilly.com/). Or, you can post feedback on the [O'Reilly forum](http://forums.oreilly.com/). We always appreciate your willingness to provide feedback.
## Example files for the title:
# Programming Scala, by Dean Wampler
[![Programming Scala, by Dean Wampler](http://akamaicovers.oreilly.com/images/9780596155957/cat.gif)](https://www.safaribooksonline.com/library/view/title/9780596801908//)
The following applies to example files from material published by O’Reilly Media, Inc. Content from other publishers may include different rules of usage. Please refer to any additional usage rights explained in the actual example files or refer to the publisher’s website.
O'Reilly books are here to help you get your job done. In general, you may use the code in O'Reilly books in your programs and documentation. You do not need to contact us for permission unless you're reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from our books does not require permission. Answering a question by citing our books and quoting example code does not require permission. On the other hand, selling or distributing a CD-ROM of examples from O'Reilly books does require permission. Incorporating a significant amount of example code from our books into your product's documentation does require permission.
We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN.
If you think your use of code examples falls outside fair use or the permission given here, feel free to contact us at <permissions@oreilly.com>.
Please note that the examples are not production code and have not been carefully testing. They are provided "as-is" and come with no warranty of any kind.
// code-examples/AdvOOP/objects/PersonUser.java
package objects;
public class PersonUser {
public static void main(String[] args) {
// The following line won't compile.
// Person buck = Person.apply("Buck Trends", 100);
Person buck = PersonFactory.make("Buck Trends", 100);
System.out.println(buck);
}
}
\ No newline at end of file
// code-examples/AdvOOP/objects/PersonUserWontCompile.java
// WON'T COMPILE
package objects;
public class PersonUserWontCompile {
public static void main(String[] args) {
Person buck = Person.apply("Buck Trends", 100); // ERROR
System.out.println(buck);
}
}
\ No newline at end of file
// code-examples/AdvOOP/objects/button-unapply-spec.scala
package objects
import org.specs._
object ButtonUnapplySpec extends Specification {
"Button.unapply" should {
"match a Button object" in {
val b = new Button("click me")
b match {
case Button(label) => label mustEqual "click me"
case _ => fail()
}
}
"match a RadioButton object" in {
val b = new RadioButton(false, "click me")
b match {
case Button(label) => label mustEqual "click me"
case _ => fail()
}
}
"not match a non-Button object" in {
val tf = new TextField("hello world!")
tf match {
case Button(label) => fail()
case x => x must notBeNull // hack to make Specs not ignore this test.
}
}
"extract the Button's label" in {
val b = new Button("click me")
b match {
case Button(label) => label mustEqual "click me"
case _ => fail()
}
}
"extract the RadioButton's label" in {
val rb = new RadioButton(false, "click me, too")
rb match {
case Button(label) => label mustEqual "click me, too"
case _ => fail()
}
}
}
}
\ No newline at end of file
// code-examples/AdvOOP/objects/button.scala
package objects
import ui3.Clickable
class Button(val label: String) extends Widget with Clickable {
def click() = {
// Logic to give the appearance of clicking a button...
}
def draw() = {
// Logic to draw the button on the display, web page, etc.
}
override def toString() = "(button: label="+label+", "+super.toString()+")"
}
object Button {
def unapply(button: Button) = Some(button.label)
}
\ No newline at end of file
// code-examples/AdvOOP/objects/list-apply-example-script.scala
val list1 = List()
val list2 = List(1, 2.2, "three", 'four)
val list3 = List("1", "2.2", "three", "four")
println("1: "+list1)
println("2: "+list2)
println("3: "+list3)
// code-examples/AdvOOP/objects/list-unapply-example-script.scala
val list = List(1, 2.2, "three", 'four)
list match {
case List(x, y, _*) => println("x = "+x+", y = "+y)
case _ => throw new Exception("No match! "+list)
}
// code-examples/AdvOOP/objects/person-factory.scala
package objects
object PersonFactory {
def make(name: String, age: Int) = new Person(name, age)
}
\ No newline at end of file
// code-examples/AdvOOP/objects/person.scala
package objects
class Person(val name: String, val age: Int) {
override def toString = "name: " + name + ", age: " + age
}
object Person {
def apply(name: String, age: Int) = new Person(name, age)
def unapply(person: Person) = Some((person.name, person.age))
def main(args: Array[String]) = {
// Test the constructor...
val person = new Person("Buck Trends", 18)
assert(person.name == "Buck Trends")
assert(person.age == 21)
}
}
object PersonTest {
def main(args: Array[String]) = Person.main(args)
}
// code-examples/AdvOOP/objects/radio-button-unapply-spec.scala
package objects
import org.specs._
object RadioButtonUnapplySpec extends Specification {
"RadioButton.unapply" should {
"should match a RadioButton object" in {
val b = new RadioButton(true, "click me")
b match {
case RadioButton(on, label) => label mustEqual "click me"
case _ => fail()
}
}
"not match a Button (parent class) object" in {
val b = new Button("click me")
b match {
case RadioButton(on, label) => fail()
case x => x must notBeNull
}
}
"not match a non-RadioButton object" in {
val tf = new TextField("hello world!")
tf match {
case RadioButton(on, label) => fail()
case x => x must notBeNull
}
}
"extract the RadioButton's on/off state and label" in {
val b = new RadioButton(true, "click me")
b match {
case RadioButton(on, label) => {
label mustEqual "click me"
on mustEqual true
}
case _ => fail()
}
}
}
}
\ No newline at end of file
// code-examples/AdvOOP/objects/radio-button.scala
package objects
/**
* Button with two states, on or off, like an old-style,
* channel-selection botton on a radio.
*/
class RadioButton(val on: Boolean, label: String) extends Button(label)
object RadioButton {
def unapply(button: RadioButton) = Some((button.on, button.label))
// equivalent to: = Some(Pair(button.on, button.label))
}
// code-examples/AdvOOP/objects/text-field-unapply-spec.scala
package objects
import org.specs._
object TextFieldUnapplySpec extends Specification {
"TextField.unapply" should {
"should match a TextField object" in {
val tf = new TextField("hello world!")
tf match {
case TextField(text) => text mustEqual "hello world!"
case _ => fail()
}
}
"not match a non-TextField object" in {
val b = new Button("click me")
b match {
case TextField(text) => fail()
case x => x must notBeNull
}
}
"extract the text field's text" in {
val tf = new TextField("hello world!")
tf match {
case TextField(text) => text mustEqual "hello world!"
case _ => fail()
}
}
}
}
\ No newline at end of file
// code-examples/AdvOOP/objects/text-field.scala
package objects
import ui3.Clickable
class TextField(var text: String) extends Widget with Clickable {
def click() = {
// ... logic to select the appropriate point in the text.
}
def draw() = {
// ... logic to draw the text field on the display, web page, etc.
}
override def toString() = "(textfield: text="+text+", "+super.toString()+")"
}
object TextField {
def unapply(textField: TextField) = Some(textField.text)
}
\ No newline at end of file
// code-examples/AdvOOP/objects/widget-apply-spec.scala
package objects
import org.specs._
object WidgetApplySpec extends Specification {
"Widget.apply with a valid widget specification string" should {
"return a widget instance with the correct fields set" in {
Widget("(button: label=click me, (Widget))") match {
case Some(w) => w match {
case b:Button => b.label mustEqual "click me"
case x => fail(x.toString())
}
case None => fail("None returned.")
}
Widget("(textfield: text=This is text, (Widget))") match {
case Some(w) => w match {
case tf:TextField => tf.text mustEqual "This is text"
case x => fail(x.toString())
}
case None => fail("None returned.")
}
}
}
"Widget.apply with an invalid specification string" should {
"return None" in {
Widget("(button: , (Widget)") mustEqual None
}
}
}
\ No newline at end of file
// code-examples/AdvOOP/objects/widget.scala
package objects
abstract class Widget {
def draw(): Unit
override def toString() = "(widget)"
}
object Widget {
val ButtonExtractorRE = """\(button: label=([^,]+),\s+\(Widget\)\)""".r
val TextFieldExtractorRE = """\(textfield: text=([^,]+),\s+\(Widget\)\)""".r
def apply(specification: String): Option[Widget] = specification match {
case ButtonExtractorRE(label) => new Some(new Button(label))
case TextFieldExtractorRE(text) => new Some(new TextField(text))
case _ => None
}
}
// code-examples/AdvOOP/observer/button-observer2-spec.scala
package ui
import org.specs._
import observer._
object ButtonObserver2Spec extends Specification {
"An Observer watching a SubjectForReceiveUpdateObservers button" should {
"observe button clicks" in {
val observableButton =
new Button(name) with SubjectForReceiveUpdateObservers {
override def click() = {