-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0406ae1
Showing
76 changed files
with
11,963 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.bundle | ||
.DS_Store | ||
.idea | ||
.sass-cache/ | ||
closure-library | ||
jasmine | ||
bin | ||
Gemfile.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
source 'http://rubygems.org' | ||
|
||
# google closure compiler and templates compiler | ||
gem 'closure' | ||
|
||
# sass/compass | ||
gem 'compass' | ||
gem 'sassy-buttons' | ||
|
||
# so that jasmine tests are automatically compiled to js | ||
gem 'guard-coffeescript' | ||
gem 'rb-fsevent' | ||
|
||
# cucumber and capybara, and a drb server for quicker cucumber execution | ||
gem 'cucumber' | ||
gem 'capybara' | ||
gem 'capybara-webkit' | ||
gem 'spork', '~> 0.9.0.rc' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# A sample Guardfile | ||
# More info at https://github.com/guard/guard#readme | ||
|
||
guard 'coffeescript', :input => 'app' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Closure-Script Boilerplate | ||
|
||
Google Closure development with the [Closure-Script](https://github.com/dturnbull/closure-script) gem, [Jasmine](https://github.com/pivotal/jasmine) for BDD style unit testing, and [Cucumber](http://cukes.info/) with [Capybara](https://github.com/jnicklas/capybara) for functional/integration/acceptance testing. Also included are Sass/Compass with the HTML5 Boilerplate all sassed up, and Guard for Coffeescript compilation, so that Jasmine tests can be written with Coffeescript. | ||
|
||
I use this on OSX Lion. Prerequisites: Ruby 1.9.2 (haven't tested 1.8 or JRuby) via `rvm` and `homebrew`. | ||
|
||
## Installation | ||
|
||
Clone me: | ||
|
||
git clone https://github.com/robert-stuttaford/Closure-Script-Boilerplate.git | ||
cd Closure-Script-Boilerplate | ||
|
||
Get the Closure Library: | ||
|
||
svn checkout http://closure-library.googlecode.com/svn/trunk closure-library | ||
|
||
Get Jasmine: | ||
|
||
git clone https://github.com/pivotal/jasmine.git | ||
|
||
I actually have both of these in a separate frameworks folder and I've simply symlinked these into each project. | ||
|
||
Install the QT library from Nokia, capybara-webkit requires this (see <https://github.com/thoughtbot/capybara-webkit/wiki/Installing-QT>): | ||
|
||
brew install qt | ||
|
||
Install gems: | ||
|
||
bundle install --binstubs | ||
|
||
Start the Rack server: | ||
|
||
./serve | ||
|
||
Visit `localhost:3000` to see the development dashboard. | ||
|
||
If you're writing Jasmine tests, be sure to start guard: | ||
|
||
guard | ||
|
||
And if you're writing any CSS, be sure to start compass: | ||
|
||
compass watch | ||
|
||
## Development dashboard | ||
|
||
This is a two column page with useful links down the left and an iframe for your app and Jasmine tests on the right. | ||
|
||
The left column has a couple sections: | ||
|
||
* A big refresh button, mapped to keyboard shortcut 'r'. This refreshes the iframe. | ||
* Links to Jasmine specs: | ||
* A link to run all the _spec.js files found inside `app/` (regardless of depth) at the same time. | ||
* A dynamic list of all those _spec.js files, nicely formatted for readability. My own project uses short filenames, so I chose to allow more than one spec per line for compactness. | ||
* Links to view the app itself: | ||
* Development version (uncompiled) | ||
* Compiled debug version, and the compile-on-demand debug version, which produces the app.debug.js used by the compiled version. | ||
* Compiled production version, and the compile-on-demand production version, which produces the app.js used by the compiled version. | ||
* Tools and reference: | ||
* Externs generator: load up a 3rd-party javascript file, enter which objects you want externs for, and it'll produce the externs for you. Drop the contents into a file named (file).externs.js into the externs folder, and the compiler will use it. Credit goes to Guido Tapa on the Closure-Library Google Group list for this. | ||
* Links to the local Closure demos, and the Closure Library and Templates API documentation on the web. These open in a new tab. | ||
|
||
## Cucumber/Capybara testing | ||
|
||
Start the `spark` daemon: | ||
|
||
spork | ||
|
||
Then run `cucumber --drb` to run your cucumber integration tests. See `features/app.feature` for a sample test. See <http://cheat.errtheblog.com/s/capybara> for a quick reference and <https://github.com/jnicklas/capybara> for the full story. | ||
|
||
## Deployment | ||
|
||
To take advantage of the image compression, install `optipng` and `jpegtrans`: | ||
|
||
brew install optipng jpeg | ||
|
||
Alter the deploy script to suit your own requirements. I target this script in my Jenkins CI build configuration. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
goog.provide('app.App'); | ||
|
||
// require this so that the jasmine tests work | ||
goog.require('app.model.util'); | ||
goog.require('app.services.ConfigService'); | ||
goog.require('app.ui.common.templates'); | ||
goog.require('app.ui.Widget'); | ||
goog.require('app.ui.Main'); | ||
goog.require('goog.events'); | ||
goog.require('goog.events.EventTarget'); | ||
goog.require('goog.style'); | ||
|
||
/** | ||
* The App application. | ||
* @constructor | ||
* @extends {goog.events.EventTarget} | ||
*/ | ||
app.App = function() { | ||
goog.events.EventTarget.call(this); | ||
|
||
var div = document.createElement('div'); | ||
div.style.cssText = 'height:100%'; | ||
div.innerHTML = app.ui.common.templates.app(); | ||
document.body.appendChild(div); | ||
|
||
/** @type {app.services.ConfigService} */ | ||
var configService = new app.services.ConfigService(); | ||
goog.events.listenOnce(configService, app.services.ConfigService.EventType.CONFIG_LOADED, this.startUp_, false, this); | ||
configService.loadConfig(); | ||
|
||
/** | ||
* The Main view | ||
* @type {app.ui.Main} | ||
* @private | ||
*/ | ||
this.main_ = new app.ui.Main(); | ||
this.main_.decorate(goog.dom.getElement('main')); | ||
}; | ||
goog.inherits(app.App, goog.events.EventTarget); | ||
|
||
/** | ||
* Starts App after loading the config | ||
* @param {goog.events.Event=} opt_event Event. | ||
* @private | ||
*/ | ||
app.App.prototype.startUp_ = function(opt_event) { | ||
/** | ||
* The Widget | ||
* @type {app.ui.Widget} | ||
* @private | ||
*/ | ||
this.widget_ = new app.ui.Widget(); | ||
this.main_.addChild(this.widget_, true); | ||
}; | ||
|
||
/** Start the app */ | ||
app.App.start = function() { | ||
app.App.app = new app.App(); | ||
}; | ||
|
||
goog.exportSymbol('start', app.App.start); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<% | ||
args = %w{ | ||
--summary_detail_level 3 | ||
--ns app.App | ||
} | ||
compiled = %w{ | ||
--compilation_level ADVANCED_OPTIMIZATIONS | ||
--warning_level VERBOSE | ||
--language_in ECMASCRIPT5_STRICT | ||
} | ||
compiled += Dir.glob( expand_path('../externs/*.externs.js') ).map { |x| ['--externs',x] } | ||
compiled.flatten! | ||
args += case query_string | ||
when 'build' then compiled + %w{ | ||
--js_output_file ../public/js/app.js | ||
--create_source_map ../public/js/app.map | ||
} | ||
when 'debug' then compiled + %w{ | ||
--js_output_file ../public/js/app.debug.js | ||
--debug true | ||
--formatting PRETTY_PRINT | ||
} | ||
else;[];end | ||
goog.soy_to_js %w{ | ||
--cssHandlingScheme goog | ||
--shouldGenerateJsdoc | ||
--shouldProvideRequireSoyNamespaces | ||
--outputPathFormat {INPUT_DIRECTORY}{INPUT_FILE_NAME}.js | ||
**/*.soy | ||
} | ||
@response = goog.compile(args).to_response | ||
%> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
goog.provide('app.Config'); | ||
|
||
/** | ||
* Public Configuration Store | ||
* @type {Object} | ||
*/ | ||
app.Config = { | ||
CONFIG_VALUE: '' | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
goog.provide('app.model.util'); | ||
|
||
/** @enum {string} */ | ||
app.model.ID_RANGE = { | ||
ALL: 'all', | ||
NONE: 'none' | ||
}; | ||
|
||
/** | ||
* Expand a set of numerical ID ranges to full lists of distinct IDs | ||
* @param {string} range | ||
* @return {?string} | ||
*/ | ||
app.model.util.expandIDs = function (range) { | ||
if ( range == null || range.search( /^(all|none|[0-9,-]+)$/i ) == -1 ) { | ||
return null; | ||
} | ||
if ( range.indexOf( '-' ) == -1 ) { | ||
return range.toLowerCase(); | ||
} | ||
/** @type {Array.<string>} */ | ||
var items = range.replace( / /g, '' ).split( ',' ); | ||
|
||
/** @type {Array} */ | ||
var newItems = goog.array.map(items,function (/** @type {string} */item) { | ||
if ( item.indexOf( '-' ) === -1 ) { | ||
return item; | ||
} | ||
/** @type {Array.<string>} */ | ||
var bounds = item.split( '-' ); | ||
/** @type {number} */ | ||
var lower = parseInt(bounds[ 0 ], 10); | ||
/** @type {number} */ | ||
var upper = parseInt(bounds[ 1 ], 10); | ||
if ( lower > upper ) { | ||
return null; | ||
} | ||
lower--; | ||
upper++; | ||
/** @type {Array.<string>} */ | ||
var newSet = []; | ||
while ( ++lower < upper ) { | ||
newSet.push( lower ); | ||
} | ||
return newSet.join( ',' ); | ||
}); | ||
return newItems.join( ',' ); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
describe 'Model Util', -> | ||
|
||
describe 'expand ids', -> | ||
|
||
expandIDs = null | ||
|
||
beforeEach -> | ||
expandIDs = app.model.util.expandIDs | ||
|
||
it 'returns null if given null', -> | ||
expect( expandIDs() ).toBe null | ||
|
||
it 'returns null if not given "all", "none", or any combination of comma-delimited numbers and/or ranges of numbers', -> | ||
expect( expandIDs('bad data') ).toBe null | ||
expect( expandIDs('all,none') ).toBe null | ||
expect( expandIDs('all,1,2,3') ).toBe null | ||
|
||
it 'returns what it was given if there are no ranges present', -> | ||
expect( expandIDs( '1,2,3' ) ).toBe '1,2,3' | ||
expect( expandIDs( 'all' ) ).toBe 'all' | ||
expect( expandIDs( 'none' ) ).toBe 'none' | ||
|
||
it 'returns expanded list if given a range', -> | ||
expect( expandIDs('1-4') ).toBe '1,2,3,4' | ||
expect( expandIDs('10-14') ).toBe '10,11,12,13,14' | ||
|
||
it 'returns all numbers, ranges expanded, if given any combination of comma-delimited numbers and/or ranges of numbers', -> | ||
expect( expandIDs('1,2,3-6') ).toBe '1,2,3,4,5,6' | ||
expect( expandIDs('1-3,4,5,6') ).toBe '1,2,3,4,5,6' | ||
expect( expandIDs('1-3,4-6') ).toBe '1,2,3,4,5,6' | ||
|
||
describe 'filter items by ids', -> | ||
|
||
filterItemsByIDs = null | ||
|
||
beforeEach -> | ||
filterItemsByIDs = app.model.util.filterItemsByIDs | ||
|
||
it 'returns empty collection if given nulls', -> | ||
expect( goog.object.getCount filterItemsByIDs(null,null) ).toBe 0 | ||
|
||
it 'returns empty collection if given NONE', -> | ||
expect( goog.object.getCount filterItemsByIDs(app.model.ID_RANGE.NONE,{}) ).toBe 0 | ||
|
||
it 'returns clone of passed in collection if given ALL', -> | ||
expect( goog.object.getCount filterItemsByIDs(app.model.ID_RANGE.ALL,{1:'foo'}) ).toBe 1 | ||
|
||
it 'returns correct items in collection when passed ids', -> | ||
items = {1:'foo',2:'bar'} | ||
filteredItems = filterItemsByIDs('1',items) | ||
expect( goog.object.getCount filteredItems ).toBe 1 | ||
expect( filteredItems[1] ).toBe items[1] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
(function() { | ||
|
||
describe('Model Util', function() { | ||
describe('expand ids', function() { | ||
var expandIDs; | ||
expandIDs = null; | ||
beforeEach(function() { | ||
return expandIDs = app.model.util.expandIDs; | ||
}); | ||
it('returns null if given null', function() { | ||
return expect(expandIDs()).toBe(null); | ||
}); | ||
it('returns null if not given "all", "none", or any combination of comma-delimited numbers and/or ranges of numbers', function() { | ||
expect(expandIDs('bad data')).toBe(null); | ||
expect(expandIDs('all,none')).toBe(null); | ||
return expect(expandIDs('all,1,2,3')).toBe(null); | ||
}); | ||
it('returns what it was given if there are no ranges present', function() { | ||
expect(expandIDs('1,2,3')).toBe('1,2,3'); | ||
expect(expandIDs('all')).toBe('all'); | ||
return expect(expandIDs('none')).toBe('none'); | ||
}); | ||
it('returns expanded list if given a range', function() { | ||
expect(expandIDs('1-4')).toBe('1,2,3,4'); | ||
return expect(expandIDs('10-14')).toBe('10,11,12,13,14'); | ||
}); | ||
return it('returns all numbers, ranges expanded, if given any combination of comma-delimited numbers and/or ranges of numbers', function() { | ||
expect(expandIDs('1,2,3-6')).toBe('1,2,3,4,5,6'); | ||
expect(expandIDs('1-3,4,5,6')).toBe('1,2,3,4,5,6'); | ||
return expect(expandIDs('1-3,4-6')).toBe('1,2,3,4,5,6'); | ||
}); | ||
}); | ||
return describe('filter items by ids', function() { | ||
var filterItemsByIDs; | ||
filterItemsByIDs = null; | ||
beforeEach(function() { | ||
return filterItemsByIDs = app.model.util.filterItemsByIDs; | ||
}); | ||
it('returns empty collection if given nulls', function() { | ||
return expect(goog.object.getCount(filterItemsByIDs(null, null))).toBe(0); | ||
}); | ||
it('returns empty collection if given NONE', function() { | ||
return expect(goog.object.getCount(filterItemsByIDs(app.model.ID_RANGE.NONE, {}))).toBe(0); | ||
}); | ||
it('returns clone of passed in collection if given ALL', function() { | ||
return expect(goog.object.getCount(filterItemsByIDs(app.model.ID_RANGE.ALL, { | ||
1: 'foo' | ||
}))).toBe(1); | ||
}); | ||
return it('returns correct items in collection when passed ids', function() { | ||
var filteredItems, items; | ||
items = { | ||
1: 'foo', | ||
2: 'bar' | ||
}; | ||
filteredItems = filterItemsByIDs('1', items); | ||
expect(goog.object.getCount(filteredItems)).toBe(1); | ||
return expect(filteredItems[1]).toBe(items[1]); | ||
}); | ||
}); | ||
}); | ||
|
||
}).call(this); |
Oops, something went wrong.