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

Breaking It Down!

Breaking It Down!

Dive into my personal thought process on how to develop large feature sets. Along the way, we will explore componentizing a user interface deliverable in JavaScript (really CoffeeScript) while testing it along the way via Mocha.js. Lets iterate to success!

Ken Collins

October 11, 2013
Tweet

More Decks by Ken Collins

Other Decks in Technology

Transcript

  1. Ken Collins, October 10th 2013 Breaking It... 1 Down!

  2. Great Developer Skills 2 * Really understanding the goal. *

    Start at the end!!! * Creative and technical possibilities. * Abstraction can yield yak shaving moments.
  3. def show sound = Sound.find(params[:id]) @xml_path = File.dirname(sound.file.path) s3 =

    AWS::S3.new(:access_key_id => 'XXX', :secret_access_key => 'XXX') @url = s3.buckets['dev'].objects[sound.file.path[1..-1]].url_for(:read, :expires => 10*60) if sound.id_job != 0 && sound.transcript_progress != 100 @response = Savon.client("http://srap.php?wsdl").request(:avance) do soap.body = {:Jeton => "abcdef", :ID_job => sound.id_job,} end @response = @response.to_hash @progress = @response[:avance][:avancement].to_s.split("#")[1]keep "123" if @progress == "Termine" sound.transcript_progress = 100 elsif @progress == "ERROR" flash.now[:alert] = "Oups, il semblerait que le fichier soit illisible, ou qu'il n'y ait rien a ecouter !" elsif @progress != "Queued" sound.transcript_progress = @response[:avance_response][:avancement].to_s.split("#")[2].split("%") [0].to_i end sound.save end if sound.transcript_progress == 100 # If transcription finished # Get XML File URL on the FTP @xml_path = Savon.client("http://srap.php?wsdl").request(:donneResultat) do soap.body = {:Jeton => "XXX", :FichierSon => sound.id_job} end # Parse XML Path URL on Kimsufi @xml_path = @xml_path.to_hash[:donne_resultat_transposition_response][:chemin_fichier].to_s.split("#") [2].to_s.split("/")[5] # Create local directory (/tmp/sounds) for XML Temp Save if ! File.directory?(Rails.root.to_s + '/tmp/sounds') Dir.mkdir(Rails.root.to_s + '/tmp/sounds') end # Get XML from FTP ftp=Net::FTP.new ftp.connect("ftp.com", 21) ftp.login("XXX", "XXX") if ftp.closed? flash.now[:alert] = "Oups, il semblerait qu'il y ait eu un problème ! Merci d'actualiser la page" else ftp.passive = true ftp.chdir('results') ftp.getbinaryfile(@xml_path, Rails.root.to_s + '/tmp/sounds/' + @xml_path) ftp.close end Self Reflection 3 Poor Soul On Stack Overflow - http://bit.ly/NoLo5A What went wrong? - Using SOAP - No. -
  4. 4 My Personal Project

  5. HomeMarks 5

  6. HomeMarks 6

  7. HomeMarks 7 FYI, I already have the full Rails/Ruby app

    (the API) all setup for this.
  8. Lets TDD This... 8 Using Mocha.js, Chai.js assertions all under

    Konacha. Will give a link to holy_grail_harness if this interests you.
  9. class ListReflection constructor: (@_klass, @_name, options = {}) -> scopeName:

    -> scopeClass: -> scopeAttribute: -> listName: -> listNamePlural: -> listNameTitleized: -> listNameLower: -> listModelClass: -> listViewClass: -> listView: (params={}) -> itemViewClass: -> itemView: (params={}) -> itemSelector: -> subListPresent: -> subListNameTitleized: -> subListNameLower: -> subListNamePlural: -> subListName: -> subListModelClass: -> subListControllerClass: -> subListSelector: -> subListModels: (item) -> What I Currently Had 9 ListReflectionExtend = configureList: (name, opts = {}) -> @listReflection = new ListReflection this, name, opts ListReflectionInclude = listReflection: -> @constructor.listReflection @HomeMarks.ListReflection = extended: -> @extend ListReflectionExtend @include ListReflectionInclude DSL for declaring lists. List view class and rendering. Item view class and rendering. Sub list names for DOM and Model reflection.
  10. # Controllers class @HomeMarks.App.Columns extends HomeMarks.Controllers.List @configureList 'Columns', subListName: 'Boxes'

    class @HomeMarks.App.Boxes extends HomeMarks.Controllers.List @configureList 'Boxes', subListName: 'Bookmarks' class @HomeMarks.App.Bookmarks extends HomeMarks.Controllers.List @configureList 'Bookmarks' # Model class @HomeMarks.App.Column extends HomeMarks.Models.List @configureList 'Columns', listScope: 'User', subListName: 'Boxes' class @HomeMarks.App.Box extends HomeMarks.Models.List @configureList 'Boxes', listScope: 'Column', subListName: 'Bookmarks' class @HomeMarks.App.Bookmark extends HomeMarks.Models.List @configureList 'Bookmarks', listScope: 'Box' What I Currently Had 10
  11. 11 HomeMarks These tools/reflection is what allowed this page to

    be rendered.
  12. Lets TDD This... 12 * Just like super controller code,

    tests (EVEN SETUP) should be really small! * If your test setup is long winded, you need to go build something.
  13. 13 TDD With Mocha.js & Konacha +S These tools/reflection is

    what allowed this page to be rendered.
  14. 14 TDD With Mocha.js & Konacha $ rake konacha:serve $

    open http://localhost:3500/homemarks/models/bookmark_spec
  15. 15 TDD With Mocha.js & Konacha

  16. Lets TDD This... 16 * Decided I wanted to go

    in CRUD order. * Let’s create first.
  17. box = boxes 'JavaScript' box.bookmarks().create name: 'Mocha.js' url: 'http://visionmedia.github.io/mocha/' What

    I Wanted 17 * Simple command to create. * Models would do what I wanted! * Views would react!
  18. ActsAsList 18 • Low Level Implementation (Private) –inList() –removeFromList() –incrementPositionsOnAllItems()

    –decrementPositionsOnLowerItems() –incrementPositionsOnLowerItem(newPosition) Used existing ruby implementation.
  19. ActsAsList 19 • Lifecycle Hooks (Public) –addToListTop() –removeFromList() –insertAt(newPosition) –insertAtNewScopeAndPosition(newScope,

    newPosition) Used existing ruby implementation. - Create - Delete - Different Updates
  20. Lets TDD This... 20 Decided to build HomeMarks.ListBehavior

  21. 21 #= require spec_helper describe 'HomeMarks.Models.List', -> beforeEach -> @column1

    = columns(0) @column2 = columns(1) @column3 = columns(2) @bm1 = bookmarks('Ruby On Rails') @bm2 = bookmarks('Rubyflow') @bm3 = bookmarks('Rubygems.org') @bm4 = bookmarks('Ruby Language') it.only 'inList', -> expect(@column1.inList()).to.be.true @column1.position = null expect(@column1.inList()).to.be.false @column1.position = undefined expect(@column1.inList()).to.be.false it 'removeFromList', -> it 'incrementPositionsOnAllItems', -> it 'incrementPositionsOnLowerItems', -> it 'decrementPositionsOnLowerItems', -> AAL Implementation
  22. 22 inList: -> @position? removeFromList: -> @position = null Spine.Ajax.disable

    => @save() incrementPositionsOnAllItems: -> return unless @inList() items = @listItems() Spine.Ajax.disable -> for item in items item.position = item.position + 1 item.save() decrementPositionsOnLowerItems: -> return unless @inList() position = @position items = (item for item in @listItems() when item.position > position) Spine.Ajax.disable -> for item in items item.position = item.position - 1 item.save() incrementPositionsOnLowerItems: (newPosition) -> items = (item for item in @listItems() when item.position >= newPosition) Spine.Ajax.disable -> for item in items item.position = item.position + 1 item.save() AAL Implementation
  23. 23 class @HomeMarks.Models.List extends HomeMarks.Models.Base @extend HomeMarks.ListBehavior constructor: (atts) ->

    super @bind 'beforeCreate', @addToListTop @bind 'beforeDestroy', @removeFromList AAL Lifecycle Hooks
  24. 24 addToListTop: -> @position = 1 @incrementPositionsOnAllItems() @one 'save', @addToListTopEl

    addToListTopEl: -> item = @listReflection().itemView model: this items = @scopeParentContainer() items.prepend item AAL Lifecycle Hooks
  25. Lets TDD This... 25 Decided to build HomeMarks.ListBehavior

  26. 26 # spec_helper/fixtures.js.coffee @hmCreateBookmark = (done) -> box = boxes('JavaScript')

    d = -> done() f = -> console.log "The hmCreateBookmark() request failed." ; done() $.mockjax url: "#{box.hmURL()}/bookmarks/", responseText: JSON.stringify(id: 323) box.bookmarks().create {name: 'Mocha.js', url: 'http://visionmedia.github.io/mocha/'}, {done: d, fail: f} # bookmark_spec.js.coffee it 'create', (done) -> box = boxes('JavaScript') hmCreateBookmark => bm = box.bookmarks().allSorted()[0] expect(bm.isNew()).to.be.false expect(bm.id).to.equal 323 expect(bm.position).to.equal 1 expect(@bm1.position).to.equal 2 expect(@bm2.position).to.equal 3 # Builds the element. bmEl = box.el.find '.bookmark:first' link = bmEl.find '.bookmark-link' expect(link).to.have.text 'Mocha.js' expect(link).to.have.attr 'href', 'http://visionmedia.github.io/mocha/' expect(bmEl.hmModel()).to.deep.equal bm done() AAL Lifecycle Hooks
  27. Ken Collins, October 10th 2013 Thank You! 27 BREAK