Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Accelerating Titanium Development with CoffeeScript, Compass, and Sass

Accelerating Titanium Development with CoffeeScript, Compass, and Sass

7e19cd5486b5d6dc1ef90e671ba52ae0?s=128

Wynn Netherland

September 20, 2011
Tweet

Transcript

  1. TITANIUM DEVELOPMENT WITH COFFEESCRIPT, COMPASS, AND SASS Accelerated WYNNNETHERLAND

  2. $ whoami

  3. NETHERLAND

  4. None
  5. Mobile? Web? Both?

  6. Nice to meet ya.

  7. PIXELS I'VE PUSHED With some very talented folks.

  8. None
  9. None
  10. Play golf? Find this dude and get in on the

    beta.!
  11. None
  12. None
  13. None
  14. Titanium powered kiosk!

  15. None
  16. None
  17. TITANIUM STACK A polyglot's

  18. COFFEESCRIPT

  19. It's still just JavaScript.

  20. var foo = function () { } foo = ()

    -> I’d rather write this. JAVASCRIPT COFFEESCRIPT
  21. 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
  22. button = Titanium.UI.createButton title: 'I am a Button' height: 40

    width: 200 top: 10 button.addEventListener 'click', (e) -> alert "Oooh, that tickles!" COFFEESCRIPT
  23. It's about more than aesthetics ;{ } For me,

  24. Some of my favorite features...

  25. 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
  26. for own key, value of query uri += "#{ key

    }=#{ escape(value) }&" COFFEESCRIPT Comprehensions
  27. for own key, value of query uri += "#{ key

    }=#{ escape(value) }&" COFFEESCRIPT Interpolation
  28. courseButtonSubhead = Ti.UI.createLabel className: 'optRowSubhead' text: "#{GolfStatus.App.currentGame?.green?.name}" COFFEESCRIPT The Existential

    Operator
  29. 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 # elsewhere game = new GolfStatus.Models.Game(...) COFFEESCRIPT Simple inheritance pattern
  30. 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 @
  31. 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
  32. 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
  33. 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
  34. noticeHTML =''' <html> <head></head> <body> ''' noticeHTML += "<div>#{noticeText}</div>" noticeHTML

    += ''' </body> </html> ''' COFFEESCRIPT Heredocs
  35. Because string building sucks.

  36. And so much more.

  37. http://wynn.fm/2Y

  38. GET THE BOOK. http://wynn.fm/g8

  39. STYLESHEETS

  40. Are you using JSS?

  41. JSS works like CSS.

  42. Why do we have CSS?

  43. 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.
  44. A example

  45. 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
  46. 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
  47. #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
  48. #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 hooks JAVASCRIPT JSS
  49. There's a better way to write CSS. JSS

  50. & CSS extensions & compiler Patterns & plugins

  51. gem install compass

  52. #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
  53. #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
  54. Which do you prefer?

  55. #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
  56. #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
  57. Pick one. Or not. Mix and match.

  58. Organize with partials.

  59. 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 ...
  60. 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 sass if you're so inclined.
  61. Don't Repeat Yourself

  62. .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
  63. .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
  64. #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
  65. =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
  66. =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
  67. #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
  68. .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
  69. .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 .js JSS
  70. Craft themes with color functions.

  71. $button-base: #a7a7a7 #buttonOne color: $button-base title: "Button 1" #buttonTwo color:

    $button-base title: "Button 2" variables SASS
  72. $button-base: #a7a7a7 #buttonOne color: $button-base title: "Button 1" #buttonTwo color:

    $button-base title: "Button 2" variables SASS
  73. $button-base: #a7a7a7 #buttonOne color: $button-base title: "Button 1" #buttonTwo color:

    darken($button-base, 20%) title: "Button 2" color functions SASS
  74. $button-base: #a7a7a7 #buttonOne color: $button-base title: "Button 1" #buttonTwo color:

    darken($button-base, 20%) title: "Button 2" color functions SASS
  75. hue(#cc3) # => 60deg saturation(#cc3) # => 60% lightness(#cc3) #

    => 50% adjust-hue(#cc3, 20deg) # => #9c3 saturate(#cc3, 10%) # => #d9d926 desaturate(#cc3, 10%) # => #bfbf40 lighten(#cc3, 10%) # => #d6d65c darken(#cc3, 10%) # => #a3a329 grayscale(#cc3) # => desaturate(#cc3, 100%) = #808080 complement(#cc3) # => adjust-hue(#cc3, 180deg) = #33c mix(#cc3, #00f) # => #e56619 mix(#cc3, #00f, 10%) # => #f91405 mix(#cc3, #00f, 90%) # => #d1b72d more color functions SASS
  76. 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.75 opacity(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!
  77. Building a hybrid native+web app?

  78. Share your stylesheet variables!

  79. No more vendor namespaces. Selector inheritance. URL helpers. So much

    more.
  80. I could write a book.

  81. Oh wait. We did!

  82. Isn’t she Sassy, folks? GET THE BOOK. http://wynn.fm/ti-sass

  83. sass40 Save 40% and get early access! Sadly, sass100 is

    not a valid code. http://wynn.fm/ti-sass
  84. None
  85. Patterns

  86. Web languages. Native apps.

  87. Stateful.

  88. Event driven.

  89. MVC?

  90. Views...

  91. ... are really ViewControllers.

  92. So we create View factories...

  93. ... and Model factories ...

  94. ...and factories that make miniature models of factories.

  95. The Titanium WayTM

  96. 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?
  97. So how do you manufacture your own views?

  98. Compose from other views.

  99. 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
  100. 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
  101. 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 from Titanium types or others of your own COFFEESCRIPT
  102. 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
  103. 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
  104. 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
  105. Models

  106. CoffeeScript classes

  107. class GolfStatus.Models.Game constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> serialize: ->

    deserialize: (data) -> save: -> resume: -> dataForSubmit: () -> submit: (error) -> ... COFFEESCRIPT
  108. API Wrappers

  109. class GolfStatus.API # Initialize with login and password constructor: (@login,

    @password) -> COFFEESCRIPT CoffeeScript class
  110. ... # 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
  111. # 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
  112. # 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
  113. # ### 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
  114. Folder structure

  115. . ├── Resources # Titanium root │ └── vendor #

    JavaScript frameworks ├── src # CoffeeScript root │ └── golf_status # App root │ ├── models │ └── views │ ├── account # App domains │ ├── activity │ ├── courses │ ├── leaderboard │ └── play └── stylesheets # Sass
  116. Stitching it all together

  117. app.js

  118. 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
  119. 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
  120. 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
  121. 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
  122. 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
  123. Lean app.js makes for flexibility

  124. Tapping through to test deep screens bites!

  125. # GolfStatus.App.init() window = GolfStatus.Views.Play.createGameWindow() window.open() COFFEESCRIPT Comment out init

    and fire up the deepest view.
  126. make

  127. 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/g9 guilhermechapiewski (Guilherme Chapiewski)
  128. rake I'm a Rubyist, so I speak

  129. Compile

  130. 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
  131. 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 compilation end RAKEFILE
  132. 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 compilation end RAKEFILE Compile App namespaces to single file
  133. 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 compilation end RAKEFILE Compile app.js which includes the app library
  134. Build

  135. 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
  136. 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
  137. 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
  138. None
  139. Choose what works for you.

  140. JavaScript Frameworks

  141. Underscore.js https://github.com/documentcloud/underscore From Jeremy Ashkenas, the creator of CoffeeScript.

  142. Don't Repeat Yourself Not Repeating Yourself

  143. Ti GEM Automating these patterns. A work in progress.

  144. gem install ti

  145. Generate.

  146. ti new <name> <id> <platform>

  147. ti new codestrong-app com.codestrong.app iphone

  148. ├── 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
  149. ti generate <model/controller/view> <name>

  150. Golf.Views.GamePlay.createScoreCardView = (options) -> view = Ti.UI.createView (options) view

  151. ti scaffold <window/tabgroup/view> <domain> <name>

  152. Compile.

  153. ti compile <all/coffee/sass>

  154. Build.

  155. ti build <all/iphone/android/ipad/desktop/>

  156. Ti GEM @revans @baldrailers @rupakg

  157. Get involved!

  158. We've got BIG ideas.

  159. Testing.

  160. Jasmine

  161. Jasmine https://github.com/akahigeg/jasmine-titanium

  162. XIB

  163. xib2js https://github.com/daoki2/xib2js

  164. js2coffee http://ricostacruz.com/js2coffee/

  165. http://spkr8.com/t/8342 QUESTIONS?