Poptocat

I am poptocat

So, my drama w/ UI stacks continues… Kind of a soap opera. We have been evaluating all kinds of stacks for our Vnext Portal. We landed on AngularJS. This was quite an adjustment (at least for me, personally). A full stack / client side MVC framework is pretty slick, but leaves a lot of questions open on integration w/ your back-end systems… as it turns out, it’s not really that big of an issue. Due to the contributors singular focus on releasing cool tech the documentation and examples of integrating more than one of these systems is just hard to find.

Anyhow… that’s what this post is about. We’re going to attempt to drive a fully working web UI from the experimental yeomEn (the E is for ExpressJS) branch of the Yeoman project.

Read up first:

Setup and Installation

If you have already been using Yeoman and Grunt projects in the past, you can probably breeze through this next section. For this posting, I cleared out my global NPM installations to make sure we touch on the prerequisites correctly.

1) Install Yeoman and Grunt Globally

Run the following commands (may require sudo depending on npm setup)

npm install -g yeoman
npm install -g grunt

2) Clone the ExpressJS branch of the Yeoman project

This branch was made specifically to test using yeoman to manage and AngularJS / Express end-to-end stack. It’s still an experimental branch (at the time of this posting)

Open a terminal window somewhere you would typically store utilities (we’ll be referencing this location in our $PATH later)

git clone git://github.com/yeoman/yeoman.git
cd yeoman
git checkout express-stack
npm install
grunt install

The grunt install command will output a full path to your new binary (yeomen). Add that to your $PATH.

Create a Project

You should be able to run yeoman --version and yeomen --version from the terminal and get the same output. We have a few options for setting up a project. We’re going to create our Angular / Express app in a few steps, so we’ll skip using the generators for this series. After we build up from scratch, we’ll circle back and review what is provided for you in this preview version of yeoman.

Open a terminal and run the following series of commands: (Name your project)

mkdir [project name]
cd [project name]
yeomen init angularcrud

You should get output similar to the following:

Running "init:yeoman" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.

"yeoman" template notes:

   invoke  angularcrud
Please answer the following:

Would you like to include Twitter Bootstrap? (Y/n) y

If so, would you like to use Twitter Bootstrap for Compass (as opposed to vanilla CSS)? (Y/n) n

Do you need to make any changes to the above before continuing? (y/N) n

Writing compiled Bootstrap
   create    app/styles/bootstrap.css
   invoke      angularcrud:common:angularcrud
   create        .gitattributes
   create        .gitignore
   create        app/.buildignore
   create        app/.htaccess
   create        app/404.html
   create        app/favicon.ico
   create        app/robots.txt
   create        app/scripts/vendor/angular.js
   create        app/scripts/vendor/angular.min.js
   create        app/scripts/vendor/es5-shim.min.js
   create        app/scripts/vendor/json3.min.js
   create        app/styles/main.css
   create        app/views/main.html
   create        Gruntfile.js
   create        package.json
   create        test/vendor/angular-mocks.js
   invoke      angularcrud:app:angularcrud
   create        app/scripts/app.js
   create        app/index.html
   invoke      angularcrud:controller:angularcrud
   create        app/scripts/controllers/main.js
   create        test/spec/controllers/main.js
   invoke      testacular:app:angularcrud
   create        testacular.conf.js

Our new app has been stubbed out w/ Angular so far. Let’s add Express.

yeomen init express

You should see output similar to the following:

Running "init:yeoman" (init) task
This task will create one or more files in the current directory, based on the
environment and the answers to a few questions. Note that answering "?" to any
question will show question-specific help and answering "none" to most questions
will leave its value blank.

"yeoman" template notes:

   invoke  express
   create    server/index.js
   invoke      express:crud:express
   create        server/Routes.js

We not have a fully integrated seed project for Express and Angular, and this was all built w/ yeoman (well, yeomEn actually). Most of the yeoman CLI will function w/ the new binary (yeomen). Although, keep in mind… this is still preview.

Next steps:

Currently, here’s where I plan to take this series… if you have suggestions on other posting topics in-line w/ this subject, please let me know.

  • Extending with Login/Password control (adding Node passport-local to the mix)
  • Deployment to Heroku
  • Adding integration and e2e testing
  • Language and localization with il8n

References

YeomEn, @addyosmani, @blai


Part 1: Exploring Scalatra

Github Project: kyleroche/scalatra-example

In Part 1 of this series we looked at setting up and configuring Scalatra. Instead of progressing immediately to complex configurations (CoffeeScript / Backbone.js / Jade / etc), I’m going to continue looking at configuration items for a bit. Mostly, we’ll be focusing on the ones I couldn’t find documentation for on the site (b/c they are busy building cool stuff).

In this posting, we’ll be looking at Internationalization. Coming from play! this was something we always took for granted. It’s super easy w/ play! and after 2.1 Scalatra handles it well also.

Directory Structure of Scalatra

Like the old days of JSP, you’ll find resources, WEB-INF and some other familar folders in your project. For Internationalization, we need to focus on src/main/resources, which is where we’ll be putting our properties files.

Under resources create a directory called i18n. For reference, here are the supported nationality codes for language translation from ISO: Country Codes. For this posting, we’re going to select just two (US for United States and ES for Spain) of them.

For each (I really wanted to type foreach there… took a few tries to get the space) of the codes we need a corresponding properties file. Create these in the src/main/resources/i18n directory. Since US is my default, I’m going to leave that alone… if you wanted to specifically add any language you just use the format messages_[language code].properties. Scalatra will pick up the language if it’s in the i18n directory.

messages.properties

greeting=hi there

messages_ES.properties

greeting=hola

NOTE: Possible bug w/ Scalatra

This could be a 2.2 RC bug or a bug in general. But, after adding language files you’ll get a startup error like the following:

> container:start
[info] Updating {file:/Users/kyleroche/Documents/Development/workspace/example-app/}default-f56729...
[info] Resolving org.eclipse.jetty#jetty-io;8.1.7.v20120910 ...
[info] Done updating.
[info] Compiling 3 Scala sources to /Users/kyleroche/Documents/Development/workspace/example-app/target/scala-2.9.2/classes...
[trace] Stack trace suppressed: run last container:start for the full output.
[error] (container:start) java.lang.IllegalArgumentException: file:/Users/kyleroche/Documents/Development/workspace/example-app/target/scala-2.9.2/resource_managed/main/webapp is not an existing directory.
[error] Total time: 6 s, completed Dec 3, 2012 10:47:47 AM

This persists until something exists in the src/main/coffee directory. This is where coffeescript files are placed to get compiled into your application. We’ll talk about this in a later article, but for now add a file called example.coffee to this directory. The contents of the file can be any valid coffeescript. Something like alert "hello" will work just fine.

Using the Current Translation

By default, our Scalatra project comes w/ a Scalate template and a Servlet called ExampleServlet.scala. (That might vary if you chose other settings on project creation). To use the i18n classes, we need to add those libraries to our Servlet. Add the following import statment to ExampleServlet.scala.

import org.scalatra.scalate.ScalateI18nSupport

Next, we have to extend our Servlet with some required traits for i18n. Change the class statement for the Servlet w/ the following.

class ExampleServlet extends ScalatraServlet with ScalateSupport with ScalateI18nSupport with CookieSupport{

We added ScalateI18nSupport and CookieSupport to the class. CookieSupport is a dependency of ScalateI18nSupport, so we need both.

In ExampleServlet we will find the following method already created for us.

get("/") {
    <html>
      <body>
        <h1>Hello, world!</h1>
        Say <a href="hello-scalate">hello to Scalate</a>.
      </body>
    </html>
  }

We need to make one slight change to test out our language changes. Replace Hello, with {messages.get("greeting").getOrElse("salutations")} and reload the container. You should see hi there rendered in the place of Hello,. Change your browser’s default language to test out Spanish.

Using i18n messages in a template

As long as a Request is in scope you can also use messages in your Scalate templates. For example, in a SCAML template you can add:

%h1= messages.get("greeting").getOrElse("not found")

Or, in Jade you could do something like:

p=messages.get("greeting").getOrElse("not found")

Next, I’ll be exploring the compiled assets functionality of Scalatra like CoffeeScript, LESS, etc.


We are huge fans of the play! framework. We are a Scala shop and play! runs our REST API and DAO layer. However, we build custom portals for most of our customers and also provide a common adminstrative portal solution for your m2m device needs / licensing / user provisioning, etc. Play! is pretty complex to setup as a portal solution so we started exploring other options. If we were going to lose the full stack advantage of something like play!, we wanted to get as barebones as possible.

Scalatra, although not totally barebones, is a great lightweight server (micro web-framework) for Scala applications. It borrows quite a bit from Sinatra and has a almost not learning curve to get going… Like Scalatra, the project’s documentation is also lightweight (grin). Hopefully, this guide will help some of you get going.

NOTE: I’m using 2.2 for this guide due to some FW features we required

Getting Setup

We found installation to be pretty straight forward. It’s already documented on the 2.2. docs site. I didn’t actually follow this… we instead installed parts of the TypeSafe stack. If the standard installation doesn’t work for your system, you can use homebrew to install the requirements:

   > brew install scala sbt maven giter8

Technically, this is a bit more than is required, but I’ll be using these components throughout this series.

Setup the IDE (Eclipse)

Like every other developer, the command line makes me feel smarter and more hackeriffic. However, sooner or later, I need the project in some sort of IDE. If you’re coming from another Scala web framework you may already be setup. If not, you need to get the Scala-IDE for Eclipse Plugin so you can edit your project in Eclipse.

Project Templates

Scalatra has helper templates for getting started. We’ll use the g8 utility to pull down our first template. It’ll ask you a few standard questions to customize the template, then it’ll create the project in a directory off .

> g8 scalatra/scalatra-sbt
organization [com.example]: com.kyleroche
package [com.example.myapp]: com.kyleroche.example
name [My Scalatra Web App]: Example App
servlet_name [MyServlet]: ExampleServlet
version [0.1.0-SNAPSHOT]: 

Applied scalatra/scalatra-sbt.g8 in example-app

Adding Eclipse Support

Let’s just get this over with… edit project/plugins.sbt using VI or whatever command line editor you feel most comfortable using. Here’s my plugins.sbt file. I also added support for LESS and CoffeeScript, which we’ll get to in later posts.

libraryDependencies <+= sbtVersion(v => v match {
    case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8"
    case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10"
    case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.11"
    case "0.11.3" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.3-0.2.11.1"
    case x if (x.startsWith("0.12")) => "com.github.siasia" %% "xsbt-web-plugin" % "0.12.0-0.2.11.1"
})

addSbtPlugin("me.lessis" % "coffeescripted-sbt" % "0.2.3")

addSbtPlugin("me.lessis" % "less-sbt" % "0.1.10")

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0")

Note: version numbers could differ over time.

To test, let’s open the sbt utility and execute the eclipse command, which preps our project for the IDE. You should see something similar to the following:

Kyles-MacBook-Pro:example-app kyleroche$ sbt
[info] Loading project definition from /Users/kyleroche/Documents/Development/workspace/example-app/project
[info] Updating {file:/Users/kyleroche/Documents/Development/workspace/example-app/project/}default-d10865...
[info] Resolving org.scala-sbt#precompiled-2_10_0-m7;0.12.1 ...
[info] Done updating.
[info] Set current project to Example App (in build file:/Users/kyleroche/Documents/Development/workspace/example-app/)
[info] Updating {file:/Users/kyleroche/Documents/Development/workspace/example-app/}default-f56729...
[info] Resolving org.eclipse.jetty#jetty-io;8.1.7.v20120910 ...
[info] Done updating.
> eclipse
[info] About to create Eclipse project files for your project(s).
[info] Successfully created Eclipse project files for project(s):
[info] Example App
>

The eclipse command would fail w/out the change to our plugins.sbt file.

First run

Okay, let’s run the application from sbt and make sure everything looks okay in the browser. To do so, we’re going to run container:start from the sbt utility.

> container:start
[info] Compiling 3 Scala sources to /Users/kyleroche/Documents/Development/workspace/example-app/target/scala-2.9.2/classes...
[info] jetty-8.1.7.v20120910
[info] NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
[info] started o.e.j.w.WebAppContext{/,[file:/Users/kyleroche/Documents/Development/workspace/example-app/src/main/webapp/]}
[info] started o.e.j.w.WebAppContext{/,[file:/Users/kyleroche/Documents/Development/workspace/example-app/src/main/webapp/]}
14:13:44.998 [pool-11-thread-5] INFO  o.scalatra.servlet.ScalatraListener - Initializing life cycle class: Scalatra
[info] started o.e.j.w.WebAppContext{/,[file:/Users/kyleroche/Documents/Development/workspace/example-app/src/main/webapp/]}
14:13:45.131 [pool-11-thread-5] INFO  o.f.s.servlet.ServletTemplateEngine - Scalate template engine using working directory: /var/folders/q2/y5m1j_q90mn5l_khl_s5ydth0000gn/T/scalate-9194221376497415326-workdir
[info] Started SelectChannelConnector@0.0.0.0:8080
[success] Total time: 3 s, completed Dec 1, 2012 2:13:45 PM
> 

So, Scalatra is running in a dedicated Jetty container. You can access the application from http://localhost:8080.

Update to V2.2 RC1

Here’s how I swapped to the latest version. The features in the subsequent posts might depend on newer versions. This is optional if you just wanted to get started with Scalatra.

Open build.sbt (off project root) in Eclipse. Edit it as follows: (Don’t just cut/paste, verify your project specific settings)

organization := "com.kyleroche"

name := "Example App"

version := "0.1.0-SNAPSHOT"

scalaVersion := "2.9.2"

seq(webSettings :_*)

classpathTypes ~= (_ + "orbit")

libraryDependencies ++= Seq(
  //"org.scalatra" % "scalatra" % "2.1.1",
  //"org.scalatra" % "scalatra-scalate" % "2.1.1",
  //"org.scalatra" % "scalatra-specs2" % "2.1.1" % "test",
  "org.scalatra" % "scalatra" % "2.2.0-SNAPSHOT",
  "org.scalatra" % "scalatra-scalate" % "2.2.0-SNAPSHOT",
  "org.scalatra" % "scalatra-json" % "2.2.0-SNAPSHOT",
  "org.scalatra" % "scalatra-data-binding" % "2.2.0-SNAPSHOT",
  "com.typesafe.akka" % "akka" % "2.0.4",
  "org.scalatra" % "scalatra-akka" % "2.2.0-SNAPSHOT",
  "org.scalatra" % "scalatra-lift-json" % "2.2.0-SNAPSHOT",
  "org.json4s" %% "json4s-jackson" % "3.0.0",
  "org.scalatra" % "scalatra-specs2" % "2.2.0-SNAPSHOT" % "test",
  "ch.qos.logback" % "logback-classic" % "1.0.6" % "runtime",
  "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "container",
   "org.eclipse.jetty" % "test-jetty-servlet" % "8.1.5.v20120716" % "test",
  "org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" artifacts     (Artifact("javax.servlet", "jar", "jar"))
)

resolvers ++= Seq(
 "Sonatype Nexus Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
 "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
) 

seq(coffeeSettings: _*)

(resourceManaged in (Compile, CoffeeKeys.coffee)) <<= (resourceManaged in Compile)(_ / "webapp" / "js")

com.github.siasia.PluginKeys.webappResources in Compile <+= (resourceManaged in Compile)(_ / "webapp" )

That should do it. Run sbt clean update compile to make sure you don’t have any issues.

REFERENCES

Getting Started with Scalatra (4 part series) http://www.smartjava.org/content/tutorial-getting-started-scala-and-scalatra-part-i

Frank LoVecchio (my fellow m2m hacker extraordinaire) https://github.com/franklovecchio/scalatraback