CoffeeScript is a new programming language built on top of JavaScript. CoffeeScript offers a clean syntax that should appeal to anyone who likes Python or Ruby, and it also provides many functional programming features inspired by languages such as Haskell and Lisp.
In Part 1 of this series, you learned about the benefits of using CoffeeScript instead of using just JavaScript. You set up the development environment and ran scripts. In Part 2, you explored the CoffeeScript programming language by trying many of the features while solving mathematical problems.
In this article, you create a sample CoffeeScript application. Both the client-side and server-side code will be written completely in CoffeeScript. You can also download the source code used in this article.
Search powered by CoffeeScript
The sample application that you'll create lets the user enter a search term, searching both Google and Twitter, and then display the combined results. The client side of the application gets the data the user entered and sends it to the server for results. When the results are returned from the server, the client side creates UI elements for the results and displays them on the screen. For now, don't worry about how the server side works.
Start by defining the data model for the application. The application will display
search results, so define a SearchResult class. Listing 1 shows the definition.
Listing 1. Base
SearchResult class
class SearchResult
constructor: (data) ->
@title = data.title
@link = data.link
@extras = data
toHtml: -> "<a href='#{@link}'>#{@title}</a>"
toJson: -> JSON.stringify @extras
|
The SearchResult class is rather simple. It:
- Starts with a constructor that will define two member variables:
titleandlink. - Looks for these two values in the data object that is passed to the constructor.
- Stores the rest of the data passed to a member variable called
extras.This will be convenient because you know you'll have two different types of search results in the application (those from Google and those from Twitter).
- Defines two methods for the
SearchResultclass:-
toHtml, which creates a simple HTML string out of theSearchResultinstance, taking advantage of CoffeeScript's string interpolation. -
toJson, for turning theSearchResultobject into a JSON string.
-
The class in Listing 1 provides the basic features of a search result. You'll be getting much more data from search results from both Google and Twitter. Model this by creating subclasses for each type of search result. Listing 2 shows the type of search result from Google.
Listing 2.
GoogleSearchResult class
class GoogleSearchResult extends SearchResult
constructor: (data) ->
super data
@content = @extras.content
toHtml: ->
"#{super} <div class='snippet'>#{@content}</div>"
|
Listing 2 shows just how easy object-oriented programming
is in CoffeeScript. GoogleSearchResult extends the base
SearchResult class from Listing 1.
Its constructor calls the constructor of the superclass. If you have ever done
class-style inheritance in JavaScript, then you know how tricky this can be. Listing 3 shows the generated JavaScript.
Listing 3.
GoogleSearchResult JavaScript
GoogleSearchResult = (function() {
__extends(GoogleSearchResult, SearchResult);
function GoogleSearchResult(data) {
GoogleSearchResult.__super__.constructor.call(this, data);
this.content = this.extras.content;
}
GoogleSearchResult.prototype.toHtml = function() {
return "" + GoogleSearchResult.__super__.toHtml.apply(this, arguments)
+ " <div class='snippet'>" + this.content + "</div>";
};
return GoogleSearchResult;
})();
|
To call the constructor of the superclass, you must keep an instance of the
superclass in the __super__ variable (the name could be
anything), and then explicitly call the constructor function. Going back to Listing 2, you can see how much easier this is in CoffeeScript.
Notice that the example defined a new instance variable called content in the GoogleSearchResult class.
This is basically a snippet of HTML from the web page that the search result points
to. It should not be surprising that the GoogleSearchResult
has this but the base class does not. Finally, notice the override of the toHtml method. The example uses the superclass's toHtml method but also appends an extra div with the content
snippet. Look at Listing 3 again to see how this call to the superclass's toHtml method is accomplished. Because you have a GoogleSearchResult subclass, you need a TwitterSearchResult subclass as well, as shown in Listing 4.
Listing 4.
TwitterSearchResult class
class TwitterSearchResult extends SearchResult
constructor: (data) ->
super data
@source = @extras.from_user
@link = "http://twitter.com/#{@source}/status/#{@extras.id_str}"
@title = @extras.text
toHtml: ->
"<a href='http://twitter.com/#{@source}'>@#{@source}</a>: #{super}"
|
The TwitterSearchResult class follows the same pattern as
the GoogleSearchResult class in Listing
2. Its constructor leverages the constructor of its superclass. It also:
- Defines its own member variable called
source. - Constructs the
linkmember variable using a string template and its member variables. - Resets the
titlemember variable to a different field from the input data. - Overrides the
toHtmlmethod of the superclass, appending a link to the user who created the tweet.
Once again, CoffeeScript's string interpolation makes it easy to use the
superclass's toHtml method when creating a new one. To
call the superclass's toHtml method, you simply call
super. You might be tempted to call super.toHtml instead, but there is no need and doing so would
actually cause an error. CoffeeScript implies that you want to call the same method on the superclass, making your life just a bit easier.
You now have the data structures that the application will need, and you can begin writing some of the client-side logic. It would be much easier to test the code with a working back end. Because there's not one, let's use the next best thing: mock data.
When building a client-server application like a modern web application, it often makes sense to create the shared interface where the two parts of the application meet and then create mock data. This lets you develop the client and server portions of the application in parallel. The approach works especially well with CoffeeScript because you can use the same programming language on both the client and server. Listing 5 shows mock search results from Google.
Listing 5. Mock Google search results
mockGoogleData = [
GsearchResultClass:"GwebSearch",
link:"http://jashkenas.github.com/coffee-script/",
url:"http://jashkenas.github.com/coffee-script/",
visibleUrl:"jashkenas.github.com",
cacheUrl:"http://www.google.com/search?q\u003dcache:nuWrlCK4-v4J:jashkenas
.github.com",
title:"\u003cb\u003eCoffeeScript\u003c/b\u003e",
titleNoFormatting:"CoffeeScript",
content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a little language that
compiles into JavaScript. Underneath all of those embarrassing braces and
semicolons, JavaScript has always had a \u003cb\u003e...\u003c/b\u003e"
,
GsearchResultClass:"GwebSearch",
link:"http://en.wikipedia.org/wiki/CoffeeScript",
url:"http://en.wikipedia.org/wiki/CoffeeScript",
visibleUrl:"en.wikipedia.org",
cacheUrl:"http://www.google.com/search?q\u003dcache:wshlXQEIrhIJ
:en.wikipedia.org",
title:"\u003cb\u003eCoffeeScript\u003c/b\u003e - Wikipedia, the free
encyclopedia",
titleNoFormatting:"CoffeeScript - Wikipedia, the free encyclopedia",
content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a programming language
that transcompiles to JavaScript. The language adds syntactic sugar inspired by
Ruby, Python and Haskell to enhance \u003cb\u003e...\u003c/b\u003e"
,
GsearchResultClass:"GwebSearch",
link:"http://codelikebozo.com/why-im-switching-to-coffeescript",
url:"http://codelikebozo.com/why-im-switching-to-coffeescript",
visibleUrl:"codelikebozo.com",
cacheUrl:"http://www.google.com/search?q\u003dcache:VDKirttkw30J:
codelikebozo.com",
title:"Why I\u0026#39;m (Finally) Switching to \u003cb\u003eCoffeeScript
\u003c/b\u003e - Code Like Bozo",
titleNoFormatting:"Why I\u0026#39;m (Finally) Switching to CoffeeScript -
Code Like Bozo",
content:"Sep 5, 2011 \u003cb\u003e...\u003c/b\u003e You may have already heard
about \u003cb\u003eCoffeeScript\u003c/b\u003e and some of the hype surrounding it
but you still have found several reasons to not make the \u003cb\u003e...
\u003c/b\u003e"
]
|
It's evident that even mock data is easier to create with the simple syntax of CoffeeScript. The example shows object literals in CoffeeScript. Listing 5 is an array. Extra indentation is used to indicate an object, and each property of the object is indented again. This code compares favorably to JSON. The whitespace takes the place of curly braces. They are like JavaScript literals, where the properties are not in quotation marks. With JSON these properties have to be quoted as well.
Listing 6 shows similar mock data for Twitter search results.
Listing 6. Mock Twitter search results
mockTwitterData = [
created_at:"Wed, 09 Nov 2011 04:18:49 +0000",
from_user:"jashkenas",
from_user_id:123323498,
from_user_id_str:"123323498",
geo:null,
id:134122748057370625,
id_str:"134122748057370625",
iso_language_code:"en",
metadata:
recent_retweets:4,
result_type:"popular"
profile_image_url:"http://a3.twimg.com/profile_images/1185870726/gravatar
_normal.jpg",
source:"<a href="http://itunes.apple.com/us/app/twitter/id409789998?mt
=12" rel="nofollow">Twitter for Mac</a>",
text:""CoffeeScript [is] the closest I felt to the power I had twenty
years ago in Smalltalk" - Ward Cunningham (http://t.co/2Wve2V4l) Nice.",
to_user_id:null,
to_user_id_str:null
]
|
The mock data in Listing 6 is similar to the mock data in Listing 5, but with the Twitter-specific fields. Now you just need to create an interface that returns this mock data. Listing 7 shows another class that does just that.
Listing 7. A mock search engine class
class MockSearch
search: (query, callback) ->
results =
google: (new GoogleSearchResult obj for obj in mockGoogleData)
twitter: (new TwitterSearchResult obj for obj in mockTwitterData)
callback results
|
The MockSearch class has a single method called search. It takes two parameters: query to
use for the search, and a callback function. MockSearch returns results quickly, but a real search will need to go over the network to talk to the servers.
To handle this in JavaScript, and to make sure you don't cause the UI to freeze up,
you would typically use a callback function.
The next step creates an object called results. You're
again using the object literal syntax of CoffeeScript. The results object has two fields: google and
twitter. The values of each field are expressed
using an array comprehension. The expression will create an array of the appropriate
type of SearchResult (GoogleSearchResult for Google and TwitterSearchResult for Twitter). Finally, the callback function is invoked with the results object passed to it.
The mock search is working, so you're ready to write the client-side code related to the UI.
Bringing it all together in the browser
Before glueing the UI to the application code, first take a look at the UI you'll be using. Listing 8 shows a very simple web page.
Listing 8. CoffeeSearch web page
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>CoffeeSearch</title>
<script type="text/javascript" src="search.js"></script>
</head>
<body>
<div>
<label for="searchQuery">Keyword:</label>
<input type="text" name="searchQuery" id="searchQuery"></input>
<input type="button" value="Search" onclick="doSearch()"/>
</div>
<div class="goog" id="gr"/>
<div class="twit" id="tr"/>
</body>
</html>
|
The web page above has a basic form for entering a keyword and
passing it to a search engine. It has two sections defined and ready for
search results to be added to them. The web page has no JavaScript defined in
it. Instead, all of it is in a file called search.js, which will be the compiled
version of the CoffeeScript. Notice that when the search button is clicked, a function
called doSearch is invoked. This function must be
in the search.js file; it's the only thing in the search.js file that you haven't seen yet. Listing 9 shows the definition in CoffeeScript.
Listing 9. Web page's
doSearch function
@doSearch = ->
$ = (id) -> document.getElementById(id)
kw = $("searchQuery").value
appender = (id, data) ->
data.forEach (x) ->
$(id).innerHTML += "<p>#{x.toHtml()}</p>"
ms = new MockSearch
ms.search kw, (results) ->
appender("gr", results.google)
appender("tr", results.twitter)
|
You might notice that the function has an @ symbol
prepended to it (a shortcut for this). When defined at the top
level of a script, this becomes the global object. In the case of a script in a web
page, the global object is the window object, which will allow it to be referenced in
the web page you see in Listing 8.
The doSearch function does a lot in just a few lines of
code. The code:
- Defines a local function called
$that's basically a shortcut to the ever usefuldocument.getElementByIdfunction. Use this to get the keyword entered in the search form in Listing 8. - Defines another local function called
appender, which will take the ID of an element in the DOM and an array. It will then iterate over the array, create an HTML string, and append to the element with the given ID. - Creates a
MockSearchobject and invokes itssearchmethod. - Passes the keyword from the form and creates a callback function.
The callback function uses
appenderto append the search results from Google to onedivand the search results from Twitter in the other.
Now you can simply compile all of the code and deploy it. Figure 1 shows the web page with the mock data.
Figure 1. Search page with mock data
The example might not look impressive, but it demonstrates all of the functions that are needed on the client side. Though you don't have the server side coded yet, you can be fairly confident that the application will work as long as the server-side code produces data structured like the mock data.
In this article, I took CoffeeScript out of the laboratory and used it to start building something real. Combined with node.js, CoffeeScript gives you the opportunity to write a complete application, client and server both, using the same elegant programming language. You're well positioned to reuse some of the same code when building the server side of the application in the final part of this series. Stay tuned.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | cs3.zip | 5KB | HTTP |
Information about download methods
Learn
- Check out the previous parts of this series:
-
Node.js: Use this launching point to learn more
about the application.
-
"Just
what is Node.js" (developerWorks, May 2011) explains how Node is a server-side JavaScript interpreter that changes the notion of how a server should work.
-
"Use Node.js as
a full cloud environment development stack" (developerWorks, April 2011) details how Node.js can be combined with cloud technologies.
-
"High-performance
Web development with Google Web Toolkit and Eclipse"
(developerWorks, October 2009): Compiling into JavaScript is not a new idea,
so if you are a fan of the Java™ programming language you should read this article.
-
"All
aboard! An introduction to Rails 3" (developerWorks, March 2010) explains how CoffeeScript is now part of Ruby on Rails. Check out the other new features in Rails in this article.
-
CoffeeScript project on Github: Start here to learn about CoffeeScript.
-
"Create
Ajax applications for the mobile Web" (developerWorks, March 2010) gives you more information about using Ajax in mobile web applications.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- developerWorks on Twitter: Join today to follow developerWorks tweets.
Get products and technologies
-
Node.js: Download Node.js and start easily building scalable network programs.
-
Download IBM product evaluation
versions: Download or explore the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- developerWorks profile: Create your profile today and set up a watchlist.
- The developerWorks community: Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Michael Galpin is a software engineer at Google. He is a co-author of the book Android in iPractice and a frequent contributor to developerWorks. For a sneak peek at what he's up to next, check out his blog or follow him on Twitter @michaelg or Google+ Michael Galpin.




