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!

F0b14b7dbae1e90259eb946d1c841a17?s=128

Ken Collins

October 11, 2013
Tweet

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