Upload
appcelerator-inc
View
2.285
Download
3
Embed Size (px)
DESCRIPTION
JavaScript and Titanium's JavaScript Stylesheets would be the quickest way to develop native mobile applications if it weren't for CoffeeScript and Sass. This talk will show you how to speed up your dev cycles and use CoffeeScript and Sass not only to write code faster, but better organize your JavaScript and JSS.
Citation preview
TITANIUM DEVELOPMENT WITHCOFFEESCRIPT, COMPASS, AND SASS
Accelerated
WYNNNETHERLAND
$ whoami
NETHERLAND
Mobile?Web?Both?
Nice to meet ya.
PIXELS I'VE PUSHEDWith some very talented folks.
Play golf?Find this dude andget in on the beta.!
Titanium powered kiosk!
TITANIUM STACKA polyglot's
COFFEESCRIPT
It's still just JavaScript.
var foo = function () {
}foo = () ->
I’d rather write this.JAVASCRIPT COFFEESCRIPT
var button = Titanium.UI.createButton({ title: 'I am a Button', height: 40, width: 200, top: 10});
button.addEventListener('click', function(e) { alert("Oooh, that tickles!");});
JAVASCRIPT
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10
button.addEventListener 'click', (e) -> alert "Oooh, that tickles!"
COFFEESCRIPT
It's about more than aesthetics
; { }For me,
Some of my favorite features...
for own key, value of query uri += "#{ key }=#{ escape(value) }&"
COFFEESCRIPT
var key, value;var __hasProp = Object.prototype.hasOwnProperty;for (key in query) { if (!__hasProp.call(query, key)) continue; value = query[key]; uri += "" + key + "=" + (escape(value)) + "&";}
JAVASCRIPT
for own key, value of query uri += "#{ key }=#{ escape(value) }&"
COFFEESCRIPT
Comprehensions
for own key, value of query uri += "#{ key }=#{ escape(value) }&"
COFFEESCRIPT
Interpolation
courseButtonSubhead = Ti.UI.createLabel className: 'optRowSubhead' text: "#{GolfStatus.App.currentGame?.green?.name}"
COFFEESCRIPT
The Existential Operator
class GolfStatus.Models.Game
constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players = {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1
# elsewheregame = new GolfStatus.Models.Game(...)
COFFEESCRIPT
Simple inheritance pattern
class GolfStatus.Models.Game
constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players = {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1
COFFEESCRIPT
@
class GolfStatus.Models.Game
constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players = {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1
COFFEESCRIPT
Default values
class GolfStatus.Models.Game
constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players = {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1
COFFEESCRIPT
More human conditionals
GolfStatus.Models.Game::PlayingForTypes = brag: 'Bragging Rights' cash: 'Status Cash'
GolfStatus.Models.Game::ScoringFormats = low_net: 'Low Net' low_grows: 'Low Gross'
COFFEESCRIPT
Easy object.prototype
noticeHTML =''' <html> <head></head> <body> '''noticeHTML += "<div>#{noticeText}</div>"noticeHTML += ''' </body> </html> '''
COFFEESCRIPT
Heredocs
Because string building sucks.
And so much more.
http://wynn.fm/2Y
GETTHEBOOK.
http://wynn.fm/g8
STYLESHEETS
Are you using JSS?
JSS works like CSS.
Why do we have CSS?
content
presentation
<ul class='wynning'> <li class='logo-mark'> <a href='/about'>Wynn Netherland</a> on design, development, and general geekery. </li> <li> <a class='gowalla' href='http://gowalla.com/pengwynn'>Gowalla</a> </li> <li> <a class='facebook' href='http://facebook.com/pengwynn'>Facebook</a> </li> <li> <a class='dribbble' href='http://dribbble.com/pengwynn'>Dribbble</a> </li> <li> <a class='linked-in' href='http://linkedin.com/in/netherland'>LinkedIn</a> </li> <li> <a class='github' href='http://github.com/pengwynn'>GitHub</a> </li> <li> <a class='twitter' href='http://twitter.com/pengwynn'>Twitter</a> </li>
...
✂You gotta keep 'em separated.
A example
var buttonOne = Titanium.UI.createButton({ title:'I am a Button', height:40, width:200, top:10});
var buttonTwo = Titanium.UI.createButton({ title:'I am also a Button', image:'../images/chat.png', width:200, height:40, top:60});
JAVASCRIPT
var buttonOne = Titanium.UI.createButton({ title:'I am a Button', height:40, width:200, top:10});
var buttonTwo = Titanium.UI.createButton({ title:'I am also a Button', image:'../images/chat.png', width:200, height:40, top:60});
Presentation
JAVASCRIPT
#buttonOne { title:'I am a Button'; width:200; height:40; top:10}#buttonTwo { title:'I am also a Button'; image:'../images/chat.png'; width:200; height:40; top:60}.button { height: 40; width: 200;}
var buttonOne = Titanium.UI.createButton({ id: "buttonOne", className: "button"});
var buttonTwo = Titanium.UI.createButton({ id: "buttonTwo", className: "button"});
Behavior and composition in
Presentation in
.js
.jss
JSS
JAVASCRIPT
#buttonOne { title:'I am a Button'; width:200; height:40; top:10}#buttonTwo { title:'I am also a Button'; image:'../images/chat.png'; width:200; height:40; top:60}.button { height: 40; width: 200;}
var buttonOne = Titanium.UI.createButton({ id: "buttonOne", className: "button"});
var buttonTwo = Titanium.UI.createButton({ id: "buttonTwo", className: "button"});
Style hooksJAVASCRIPT
JSS
There's a better way to write CSS.JSS
&CSS extensions &
compiler
Patterns &plugins
gem install compass
#buttonOne { title: 'I am a Button'; width: 200; height: 40; top: 10}#buttonTwo { title: 'I am also a Button'; image: '../images/chat.png'; width: 200; height: 40; top: 60}.button { height: 40; width: 200;}
Is it JSS or Sassy CSS?
Yes?JSS / SCSS
#buttonOne title: 'I am a Button' width: 200 height: 40 top: 10
#buttonTwo title: 'I am also a Button' image: '../images/chat.png' width: 200 height: 40 top: 60
.button height: 40 width: 200
I prefer Sass' original indented, whitespace aware syntax.
SASS
Which do you prefer?
#buttonOne { title: 'I am a Button'; width: 200; height: 40; top: 10}#buttonTwo { title: 'I am also a Button'; image: '../images/chat.png'; width: 200; height: 40; top: 60}.button { height: 40; width: 200;}
SCSS
#buttonOne title: 'I am a Button' width: 200 height: 40 top: 10
#buttonTwo title: 'I am also a Button' image: '../images/chat.png' width: 200 height: 40 top: 60
.button height: 40 width: 200
SASS
Pick one. Or not. Mix and match.
Organize with partials.
stylesheets├── _activity.sass├── _base.sass├── _confirmation.sass├── _course.scss├── _courses.sass├── _friends.scss├── _gameplay.sass├── _leaderboard.sass├── _leaders.sass├── _login.sass├── _requests.sass├── _tourcard.sass└── app.sass
@import 'base'@import 'login'@import 'activity'@import 'course'@import 'courses'@import 'friends'@import 'leaderboard'@import 'leaders'@import 'requests'@import 'tourcard'@import 'confirmation'@import 'gameplay'
Resources├── app.js├── app.jss...
stylesheets├── _activity.sass├── _base.sass├── _confirmation.sass├── _course.scss├── _courses.sass├── _friends.scss├── _gameplay.sass├── _leaderboard.sass├── _leaders.sass├── _login.sass├── _requests.sass├── _tourcard.sass└── app.sass
@import 'base'@import 'login'@import 'activity'@import 'course'@import 'courses'@import 'friends'@import 'leaderboard'@import 'leaders'@import 'requests'@import 'tourcard'@import 'confirmation'@import 'gameplay'
Resources├── app.js├── app.jss...
Mix scss with sassif you're so inclined.
Don't Repeat Yourself
.user-info bottom: 1 color: #333 font-size: 11 font-weight: bold height: auto left: 0 shadowColor: #fff shadowOffset-x: 0 shadowOffset-y: -1 text-align: center width: 92
SASS
.user-info bottom: 1 color: #333 font: size: 11 weight: bold height: auto left: 0 shadowColor: #fff shadowOffset: x: 0 y: '-1' text-align: center width: 92
DRY it up. Nesting
SASS
#buttonOne title: 'I am a Button' width: 200 height: 40 top: 10
#buttonTwo title: 'I am also a Button' image: '../images/chat.png' width: 200 height: 40 top: 60
.button height: 40 width: 200
SASS
=button height: 40 width: 200
#buttonOne +button title: 'I am a Button' top: 10
#buttonTwo +button title: 'I am also a Button' image: '../images/chat.png' top: 60
DRY it up. Mixins
SASS
=bottom-right($height: 40, $width: 200) height: $size width: $size right: 0 bottom: 0
#buttonOne +bottom-right title: 'I am a Button'
#buttonTwo +bottom-right(50, 300) title: 'I am also a Button' image: '../images/chat.png'
DRY it up. Mixins with params
SASS
#buttonOne title: 'I am a Button' width: 200 height: 40 top: 10
#buttonTwo title: 'I am also a Button' image: '../images/chat.png' width: 200 height: 40 top: 60
.button height: 40 width: 200
SASS
.button height: 40 width: 200
#buttonOne @extend .button title: 'I am a Button' top: 10
#buttonTwo @extend .button title: 'I am also a Button' image: '../images/chat.png' top: 60
DRY it up. @extend
SASS
.button, #buttonOne, #buttonTwo { height: 40; width: 200;}
#buttonOne { title: 'I am a Button'; width: 200;}#buttonTwo { title: 'I am also a Button'; image: '../images/chat.png'; top: 60}
DRY it up. @extend
One less class in our .jsJSS
Craft themes with color functions.
$button-base: #a7a7a7
#buttonOne color: $button-base title: "Button 1"
#buttonTwo color: $button-base title: "Button 2"
variables
SASS
$button-base: #a7a7a7
#buttonOne color: $button-base title: "Button 1"
#buttonTwo color: $button-base title: "Button 2"
variables
SASS
$button-base: #a7a7a7
#buttonOne color: $button-base title: "Button 1"
#buttonTwo color: darken($button-base, 20%) title: "Button 2"
color functions
SASS
$button-base: #a7a7a7
#buttonOne color: $button-base title: "Button 1"
#buttonTwo color: darken($button-base, 20%) title: "Button 2"
color functions
SASS
hue(#cc3) # => 60degsaturation(#cc3) # => 60%lightness(#cc3) # => 50%
adjust-hue(#cc3, 20deg) # => #9c3saturate(#cc3, 10%) # => #d9d926desaturate(#cc3, 10%) # => #bfbf40lighten(#cc3, 10%) # => #d6d65cdarken(#cc3, 10%) # => #a3a329
grayscale(#cc3) # => desaturate(#cc3, 100%) = #808080complement(#cc3) # => adjust-hue(#cc3, 180deg) = #33c
mix(#cc3, #00f) # => #e56619mix(#cc3, #00f, 10%) # => #f91405mix(#cc3, #00f, 90%) # => #d1b72d
more color functions
SASS
mix(rgba(51, 255, 51, 0.75), #f00) # => rgba(178, 95, 19, 0.875)mix(rgba(51, 255, 51, 0.90), #f00) # => rgba(163, 114, 22, 0.95)
alpha(rgba(51, 255, 51, 0.75)) # => 0.75opacity(rgba(51, 255, 51, 0.75)) # => 0.75
opacify(rgba(51, 255, 51, 0.75), 0.1) # => rgba(51, 255, 51, 0.85)fade-in(rgba(51, 255, 51, 0.75), 0.1) # => rgba(51, 255, 51, 0.85)
transparentize(rgba(51, 255, 51, 0.75), 0.1) # => rgba(51, 255, 51, 0.65)fade-out(rgba(51, 255, 51, 0.75), 0.1) # => rgba(51, 255, 51, 0.65)
even more color functions
SASS
with alpha support!
Building a hybrid native+web app?
Share your stylesheet variables!
No more vendor namespaces.Selector inheritance.URL helpers.So much more.
I could write a book.
Oh wait. We did!
Isn’t she Sassy, folks?
GETTHEBOOK.
http://wynn.fm/ti-sass
sass40Save 40% and get early access!
Sadly, sass100 is not a valid code.
http://wynn.fm/ti-sass
Patterns
Web languages. Native apps.
Stateful.
Event driven.
MVC?
Views...
... are really ViewControllers.
So we create View factories...
... and Model factories ...
...and factories that make miniature models of factories.
The Titanium WayTM
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10
button.addEventListener 'click', (e) -> alert "Oooh, that tickles!"
COFFEESCRIPT
Look familiar?
So how do you manufacture your own views?
Compose from other views.
MyApp.Views.createLoginWindow = (opts={}) -> window = Ti.UI.createWindow(opts)
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10 window.add button
# methods say = (msg) -> alert(msg)
# event handlers button.addEventListener 'click', -> say('hello')
window
COFFEESCRIPT
MyApp.Views.createLoginWindow = (opts={}) -> window = Ti.UI.createWindow(opts)
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10 window.add button
# methods say = (msg) -> alert(msg)
# event handlers button.addEventListener 'click', -> say('hello')
window
Create the factory method in the appropriate namespace.
COFFEESCRIPT
MyApp.Views.createLoginWindow = (opts={}) -> window = Ti.UI.createWindow(opts)
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10 window.add button
# methods say = (msg) -> alert(msg)
# event handlers button.addEventListener 'click', -> say('hello')
window
Compose the view fromTitanium types or others ofyour own
COFFEESCRIPT
MyApp.Views.createLoginWindow = (opts={}) -> window = Ti.UI.createWindow(opts)
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10 window.add button
# methods say = (msg) -> alert(msg)
# event handlers button.addEventListener 'click', -> say('hello')
window
Methods in familiar place
COFFEESCRIPT
MyApp.Views.createLoginWindow = (opts={}) -> window = Ti.UI.createWindow(opts)
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10 window.add button
# methods say = (msg) -> alert(msg)
# event handlers button.addEventListener 'click', -> say('hello')
window
And event handlers...
COFFEESCRIPT
MyApp.Views.createLoginWindow = (opts={}) -> window = Ti.UI.createWindow(opts)
button = Titanium.UI.createButton title: 'I am a Button' height: 40 width: 200 top: 10 window.add button
# methods say = (msg) -> alert(msg)
# event handlers button.addEventListener 'click', -> say('hello')
window
Return your view
COFFEESCRIPT
Models
CoffeeScript classes
class GolfStatus.Models.Game
constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') ->
serialize: ->
deserialize: (data) ->
save: ->
resume: ->
dataForSubmit: () ->
submit: (error) ->
...
COFFEESCRIPT
API Wrappers
class GolfStatus.API
# Initialize with login and password constructor: (@login, @password) ->
COFFEESCRIPT
CoffeeScript class
...
# Build the full API URI for a request requestURI: (path, query={}) ->
uri = "#{GolfStatus.API_ENDPOINT}#{path}.json?" for own key, value of query uri += "#{ key }=#{ escape(value) }&"
uri
COFFEESCRIPT
URI building
# Common request handling across all verbs request: (path, options, authenticated=true) -> # Default to GET options.method ?= 'GET' options.query ?= {} options.success ?= -> Ti.API.info options.error ?= -> Ti.API.error
xhr = Ti.Network.createHTTPClient() xhr.onreadystatechange = (e) -> ... # Default event handlers # Basic auth # other common stuff
...
if options.body data = JSON.stringify(options.body) Ti.API.debug data xhr.send(data) else xhr.send()
COFFEESCRIPT
HTTP Request building
# High level method for GET requests get: (path, options, authenticated=true) -> options.method = 'GET' @request path, options, authenticated
# High level method for POST requests post: (path, options, authenticated=true) -> options.method = 'POST' @request path, options, authenticated
# High level method for DELETE requests delete: (path, options, authenticated=true) -> options.method = 'DELETE' @request path, options, authenticated
COFFEESCRIPT
High level methods for HTTP verbs
# ### Authenticate the user ### authenticate: (options) -> Ti.API.debug "GolfStatus.API.authenticate" @get '/me', options
# ### Logout the user ### logout: (options) -> Ti.API.debug "GolfStatus.API.logout" @delete '/logout', options
# ### Forgot password forgotPassword: (email, options) -> Ti.API.debug "GolfStatus.API.forgotPassword" options.query = {} options.query.email = email @post '/passwords', options, false
# ### Convenience method to get current user info ### me: (options) -> Ti.API.debug "GolfStatus.API.me" @authenticate options
COFFEESCRIPT
Higher level API methods
Folder structure
.├── Resources # Titanium root│ └── vendor # JavaScript frameworks├── src # CoffeeScript root│ └── golf_status # App root│ ├── models│ └── views│ ├── account # App domains│ ├── activity│ ├── courses│ ├── leaderboard│ └── play└── stylesheets # Sass
Stitching it all together
app.js
GolfStatus = Models: {} Views: Account: {} Activity: {} Courses: {} Leaderboard: {} Play: {}
Ti.include('vendor/date.js')Ti.include('vendor/underscore.js')Ti.include('golf_status.js')
GolfStatus.App.init()
COFFEESCRIPT
GolfStatus = Models: {} Views: Account: {} Activity: {} Courses: {} Leaderboard: {} Play: {}
Ti.include('vendor/date.js')Ti.include('vendor/underscore.js')Ti.include('golf_status.js')
GolfStatus.App.init()
COFFEESCRIPT
Set up your namespaces
GolfStatus = Models: {} Views: Account: {} Activity: {} Courses: {} Leaderboard: {} Play: {}
Ti.include('vendor/date.js')Ti.include('vendor/underscore.js')Ti.include('golf_status.js')
GolfStatus.App.init()
COFFEESCRIPT
third party frameworks
GolfStatus = Models: {} Views: Account: {} Activity: {} Courses: {} Leaderboard: {} Play: {}
Ti.include('vendor/date.js')Ti.include('vendor/underscore.js')Ti.include('golf_status.js')
GolfStatus.App.init()
COFFEESCRIPT
All of our app in just one file
GolfStatus = Models: {} Views: Account: {} Activity: {} Courses: {} Leaderboard: {} Play: {}
Ti.include('vendor/date.js')Ti.include('vendor/underscore.js')Ti.include('golf_status.js')
GolfStatus.App.init()
COFFEESCRIPT
Fire up the app and first window
Lean app.js makes for flexibility
Tapping through to test deep screens bites!
# GolfStatus.App.init()
window = GolfStatus.Views.Play.createGameWindow()window.open()
COFFEESCRIPT
Comment out init andfire up the deepest view.
make
run-iphone:& @DEVICE_TYPE=iphone make run
test-iphone:& @DEVICE_TYPE=iphone make test
run-ipad:& @DEVICE_TYPE=ipad make run
test-ipad:& @DEVICE_TYPE=ipad make test
run:& @if [ "${DEVICE_TYPE}" == "" ]; then\& & echo "Please run \"make run-[iphone|ipad]\" instead.";\& & exit 1;\& fi& @mkdir -p ${PROJECT_ROOT}/${PROJECT_NAME}/Resources/test/& @echo "" > ${PROJECT_ROOT}/${PROJECT_NAME}/Resources/test/enabled.js& @make launch-titanium
http://wynn.fm/g9guilhermechapiewski (Guilherme Chapiewski)
rakeI'm a Rubyist, so I speak
Compile
def compile_sass puts "Compiling stylesheets".blue input = "stylesheets/app.sass" output = "Resources/app.jss" system "sass --compass -C -t expanded #{input} > #{output}"end
RAKEFILE
def compile_coffee paths = `find src/golf_status -name '*.coffee'`.split("\n") compilation = ( puts "Compiling CoffeeScript (golf_status.js)".blue output = "Resources/golf_status.js" system "coffee --join #{output} -b -c #{paths.join(' ')}" and puts "Compiling CoffeeScript (app.js)".blue system "coffee -p --bare src/app.coffee > Resources/app.js" )
if compilation puts "Successfully compiled CoffeeScript".green else puts "Error compiling CoffeeScript".red end compilationend
RAKEFILE
def compile_coffee paths = `find src/golf_status -name '*.coffee'`.split("\n") compilation = ( puts "Compiling CoffeeScript (golf_status.js)".blue output = "Resources/golf_status.js" system "coffee --join #{output} -b -c #{paths.join(' ')}" puts "Compiling CoffeeScript (app.js)".blue system "coffee -p --bare src/app.coffee > Resources/app.js" )
if compilation puts "Successfully compiled CoffeeScript".green else puts "Error compiling CoffeeScript".red end compilationend
RAKEFILE Compile App namespaces to single file
def compile_coffee paths = `find src/golf_status -name '*.coffee'`.split("\n") compilation = ( puts "Compiling CoffeeScript (golf_status.js)".blue output = "Resources/golf_status.js" system "coffee --join #{output} -b -c #{paths.join(' ')}" puts "Compiling CoffeeScript (app.js)".blue system "coffee -p --bare src/app.coffee > Resources/app.js" )
if compilation puts "Successfully compiled CoffeeScript".green else puts "Error compiling CoffeeScript".red end compilationend
RAKEFILE Compile app.js which includes the app library
Build
def build(options={}) return unless compile options[:device] ||= 'iphone' puts "Building with Titanium... (DEVICE_TYPE:#{options[:device]})".blue sh %Q{bash -c "#{TI_BUILD} run #{PROJECT_ROOT}/ #{IPHONE_SDK_VERSION} #{APP_ID} #{APP_NAME} #{APP_DEVICE}" \| perl -pe 's/^\\[DEBUG\\].*$/\\e[35m$&\\e[0m/g;s/^\\[INFO\\].*$/\\e[36m$&\\e[0m/g;s/^\\[WARN\\].*$/\\e[33m$&\\e[0m/g;s/^\\[ERROR\\].*$/\\e[31m$&\\e[0m/g;'}
end
RAKEFILE
def build(options={}) return unless compile options[:device] ||= 'iphone' puts "Building with Titanium... (DEVICE_TYPE:#{options[:device]})".blue sh %Q{bash -c "#{TI_BUILD} run #{PROJECT_ROOT}/ #{IPHONE_SDK_VERSION} #{APP_ID} #{APP_NAME} #{APP_DEVICE}" \| perl -pe 's/^\\[DEBUG\\].*$/\\e[35m$&\\e[0m/g;s/^\\[INFO\\].*$/\\e[36m$&\\e[0m/g;s/^\\[WARN\\].*$/\\e[33m$&\\e[0m/g;s/^\\[ERROR\\].*$/\\e[31m$&\\e[0m/g;'}
end
RAKEFILE Build with Titanium Python command line
def build(options={}) return unless compile options[:device] ||= 'iphone' puts "Building with Titanium... (DEVICE_TYPE:#{options[:device]})".blue sh %Q{bash -c "#{TI_BUILD} run #{PROJECT_ROOT}/ #{IPHONE_SDK_VERSION} #{APP_ID} #{APP_NAME} #{APP_DEVICE}" \| perl -pe 's/^\\[DEBUG\\].*$/\\e[35m$&\\e[0m/g;s/^\\[INFO\\].*$/\\e[36m$&\\e[0m/g;s/^\\[WARN\\].*$/\\e[33m$&\\e[0m/g;s/^\\[ERROR\\].*$/\\e[31m$&\\e[0m/g;'}
end
RAKEFILE
Pipe to PERL for some colored terminal goodness
Choose what works for you.
JavaScript Frameworks
Underscore.jshttps://github.com/documentcloud/underscore
From Jeremy Ashkenas,the creator of CoffeeScript.
Don't Repeat Yourself Not Repeating Yourself
Ti GEM
Automating these patterns. A work in progress.
gem install ti
Generate.
ti new <name> <id> <platform>
ti new codestrong-app com.codestrong.app iphone
├── Coffeefile├── Guardfile├── LICENSE├── Rakefile├── Readme.mkd├── Resources│ ├── app.js│ ├── app.jss│ ├── images│ │ ├── KS_nav_ui.png│ │ └── KS_nav_views.png│ ├── lsrc.js│ └── vendor├── app│ ├── app.coffee│ └── lsrc│ ├── api.coffee│ ├── app.coffee│ ├── helpers│ │ └── application.coffee│ ├── models│ ├── stylesheets│ │ ├── app.sass│ │ └── partials│ └── views├── build├── config│ └── config.rb├── docs├── spec│ ├── app_spec.coffee│ ├── helpers│ ├── models│ └── views├── tiapp.xml└── tmp
ti generate <model/controller/view> <name>
Golf.Views.GamePlay.createScoreCardView = (options) -> view = Ti.UI.createView (options) view
ti scaffold <window/tabgroup/view> <domain> <name>
Compile.
ti compile <all/coffee/sass>
Build.
ti build <all/iphone/android/ipad/desktop/>
Ti GEM
@revans @baldrailers
@rupakg
Get involved!
We've got BIG ideas.
Testing.
Jasmine
Jasminehttps://github.com/akahigeg/jasmine-titanium
XIB
xib2jshttps://github.com/daoki2/xib2js
js2coffeehttp://ricostacruz.com/js2coffee/
http://spkr8.com/t/8342
QUESTIONS?