$30 off During Our Annual Pro Sale. View Details »

Breaking It Down!

Ken Collins
October 11, 2013

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!

    View Slide

  2. Great Developer Skills
    2
    * Really understanding the goal.
    * Start at the end!!!
    * Creative and technical possibilities.
    * Abstraction can yield yak shaving moments.

    View Slide

  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.
    -

    View Slide

  4. 4
    My Personal Project

    View Slide

  5. HomeMarks
    5

    View Slide

  6. HomeMarks
    6

    View Slide

  7. HomeMarks
    7
    FYI, I already have the full Rails/Ruby app (the API) all setup for this.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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

    View Slide

  11. 11
    HomeMarks
    These tools/reflection is what allowed this page to be rendered.

    View Slide

  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.

    View Slide

  13. 13
    TDD With Mocha.js & Konacha
    +S
    These tools/reflection is what allowed this page to be rendered.

    View Slide

  14. 14
    TDD With Mocha.js & Konacha
    $ rake konacha:serve
    $ open http://localhost:3500/homemarks/models/bookmark_spec

    View Slide

  15. 15
    TDD With Mocha.js & Konacha

    View Slide

  16. Lets TDD This...
    16
    * Decided I wanted to go in CRUD order.
    * Let’s create first.

    View Slide

  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!

    View Slide

  18. ActsAsList
    18
    • Low Level Implementation (Private)
    –inList()
    –removeFromList()
    –incrementPositionsOnAllItems()
    –decrementPositionsOnLowerItems()
    –incrementPositionsOnLowerItem(newPosition)
    Used existing ruby implementation.

    View Slide

  19. ActsAsList
    19
    • Lifecycle Hooks (Public)
    –addToListTop()
    –removeFromList()
    –insertAt(newPosition)
    –insertAtNewScopeAndPosition(newScope,
    newPosition)
    Used existing ruby implementation.
    - Create
    - Delete
    - Different Updates

    View Slide

  20. Lets TDD This...
    20
    Decided to build HomeMarks.ListBehavior

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  24. 24
    addToListTop: ->
    @position = 1
    @incrementPositionsOnAllItems()
    @one 'save', @addToListTopEl
    addToListTopEl: ->
    item = @listReflection().itemView model: this
    items = @scopeParentContainer()
    items.prepend item
    AAL Lifecycle Hooks

    View Slide

  25. Lets TDD This...
    25
    Decided to build HomeMarks.ListBehavior

    View Slide

  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

    View Slide

  27. Ken Collins, October 10th 2013
    Thank You!
    27
    BREAK

    View Slide