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

Rapid development of enterprise web apps with Netzke

Rapid development of enterprise web apps with Netzke

RubyKaigi 2013, Tokyo: http://rubykaigi.org/2013/talk/S34

Max Gorin

June 01, 2013
Tweet

More Decks by Max Gorin

Other Decks in Technology

Transcript

  1. Rapid development of enterprise
    web apps with Netzke
    Max Gorin (@uptomax)

    View Slide

  2. Max Gorin
    author and maintainer of Netzke
    on Rails since 2007
    nomad since 2008
    Dvorak, vim, geekery, speaking, mountains

    View Slide

  3. Today's talk
    is about building web apps

    View Slide

  4. Not apps that look like this

    View Slide

  5. ... but rather like this

    View Slide

  6. ... or like this

    View Slide

  7. eval
    ?

    View Slide

  8. View Slide

  9. Enterprise apps
    complex data
    complex workflows
    complex GUI

    View Slide

  10. Challenges
    hundreds of data models
    very comlpex views
    monstrous monolitic client side
    decoupled client and server code
    lack of best practices

    View Slide

  11. Few of us deal with those
    but those who do - are in pain

    View Slide

  12. I'm here to ease the pain

    View Slide

  13. Let's have a closer look

    View Slide

  14. Repeated GUI elements

    View Slide

  15. Repeated GUI elements

    View Slide

  16. Repeated GUI elements

    View Slide

  17. ... and most probably more

    View Slide

  18. Devide and conquer!

    View Slide

  19. Modular GUI development
    old proven concept
    used for building complex apps
    does Rails cooperate?
    and Cells
    lack of consistent front-end
    lack of pre-built components

    View Slide

  20. Netzke
    client-server GUI components for web

    View Slide

  21. Netzke component

    View Slide

  22. Sencha Ext JS

    View Slide

  23. Why Ext JS
    consistent, themable look
    tons of feature-rich components
    programmable views

    View Slide

  24. Our arms
    reusability
    extensibility (OOP)
    modular development
    debugging and testing
    composability
    dynamic loading

    View Slide

  25. Our score?
    MAINTAINABLE CODE

    View Slide

  26. Floralogic app

    View Slide

  27. Cargologic

    View Slide

  28. Order flow

    View Slide

  29. Roles

    View Slide

  30. Models and associations

    View Slide

  31. Let's see how Netzke can help

    View Slide

  32. Everest Region, Nepal, ~5300m

    View Slide

  33. Reusability

    View Slide

  34. Models

    View Slide

  35. Contacts
    class Contacts < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "Contact"
    end
    end

    View Slide

  36. Netzke::Basepack::Grid
    CRUD
    pagination
    sorting
    filtering
    association support
    on-the-fly column configuration
    and more

    View Slide

  37. A component is worth nothing
    ...unless it is configurable and extendable

    View Slide

  38. Setting data model
    class Contacts < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "Contact"
    end
    end
    •◦◦

    View Slide

  39. Setting data model
    class Carriers < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "Carrier"
    end
    end
    ◦•◦

    View Slide

  40. Setting data model
    class PackingTypes < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "PackingType"
    end
    end
    ◦◦•

    View Slide

  41. Netzke::Basepack::Grid configuration
    model
    columns
    permissions
    scope
    strong attributes
    and more

    View Slide

  42. Our arms
    reusability ✓
    extensibility (OOP)
    modular development
    composability
    dynamic loading

    View Slide

  43. Extensibility

    View Slide

  44. Netzke component is Ruby class

    View Slide

  45. Duplication
    class Contacts < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "Contact"
    end
    end
    class Carriers < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "Carrier"
    end
    end
    class PackingTypes < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = "PackingType"
    end
    end

    View Slide

  46. Better
    class GridBase < Netzke::Basepack::Grid
    def configure(c)
    super
    c.model = self.class.name.singularize
    end
    end
    # Now:
    class Contacts < GridBase
    end
    class Carriers < GridBase
    end
    class PackingTypes < GridBase
    end
    # etc

    View Slide

  47. Shared changes
    class GridBase < Netzke::Basepack::Grid
    # hide the created_at and updated_at columns
    column :created_at do |c|
    c.excluded = true
    end
    column :updated_at do |c|
    c.excluded = true
    end
    def configure(c)
    super
    c.model = self.class.name.singularize
    end
    end

    View Slide

  48. Nothing new to you
    you are Ruby developers!
    what about the client side?

    View Slide

  49. Shipments

    View Slide

  50. Shipments

    View Slide

  51. Code structure
    floralogic/
    app/
    components/
    shipments/
    grid.rb  

    View Slide

  52. Code structure
    floralogic/
    app/
    components/
    shipments/
    grid.rb grid/
    javascripts/
    grid.js

    View Slide

  53. grid.js
    {
    initComponent: function(){
    this.features = this.features || [];
    this.features.push({ ftype: 'grouping' });
    this.callParent();
    }
    }

    View Slide

  54. What goes to JS
    overrides
    event handlers
    any client-side logic
    more on this in a few minutes

    View Slide

  55. Extending with Ruby mixins
    attachment management for orders and shipments

    View Slide

  56. class Shipments::Grid < GridBase
    include Floralogic::Netzke::Attachments
    # ...
    end

    View Slide

  57. class Shipments::Grid < GridBase
    include Floralogic::Netzke::Attachments
    # ...
    end

    View Slide

  58. class Shipments::Grid < GridBase
    include Floralogic::Netzke::Attachments
    # ...
    end

    View Slide

  59. class Shipments::Grid < GridBase
    include Floralogic::Netzke::Attachments
    # ...
    end

    View Slide

  60. Our arms
    reusability ✓
    extensibility (OOP) ✓
    modular development
    composability
    dynamic loading

    View Slide

  61. View Slide

  62. View Slide

  63. Modular development

    View Slide

  64. Floralogic app

    View Slide

  65. Floralogic app

    View Slide

  66. Filters in Orders::Grid

    View Slide

  67. Tests first!

    View Slide

  68. Testing code structure
    floralogic/
    spec/
    mocha/
    fixtures/
    orders/
    grid.rb orders/
    grid.js.coffee support/
    ... mocha_spec.rb

    View Slide

  69. Fixtures
    seller1 = FactoryGirl.create :seller, short_name: 'One'
    seller2 = FactoryGirl.create :seller, short_name: 'Two'
    cargo1 = FactoryGirl.create :cargo_type, name: 'CARGO ONE'
    cargo2 = FactoryGirl.create :cargo_type, name: 'CARGO TWO'
    FactoryGirl.create :order,
    seller: seller1, cargo_type: cargo1
    FactoryGirl.create :order,
    seller: seller1, cargo_type: cargo2
    FactoryGirl.create :order,
    seller: seller2, cargo_type: cargo1
    # ...

    View Slide

  70. Mocha specs
    describe 'Orders::Grid', ->
    it 'searches by seller', (done) ->
    wait ->
    expect(gridCount('Orders')).to.eql 5
    select 'One', from: combo('seller_id')
    wait ->
    expect(gridCount('Orders')).to.eql 3
    select 'Two', from: combo('seller_id')
    wait ->
    expect(gridCount('Orders')).to.eql 2
    done()

    View Slide

  71. Running specs

    View Slide

  72. Our arms
    reusability ✓
    extensibility (OOP) ✓
    modular development ✓
    composability
    dynamic loading

    View Slide

  73. Composability

    View Slide

  74. We've seen how to split things apart

    View Slide

  75. Now let's see how to put them together

    View Slide

  76. Composability in Floralogic

    View Slide

  77. Orders::Grid nested in...

    View Slide

  78. ShipmentsAndOrders nested in...

    View Slide

  79. Workspace nested in...

    View Slide

  80. Application

    View Slide

  81. ShipmentsAndOrders

    View Slide

  82. ShipmentsAndOrders component
    one-to-many relationship

    View Slide

  83. ShipmentsAndOrders component

    View Slide

  84. ShipmentsAndOrders: nesting
    class ShipmentsAndOrders < Netzke::Base
    js_configure do |c|
    c.layout = :border
    end
    component :shipments do |c|
    c.klass = Shipments::Grid
    c.region = :center
    end
    component :orders do |c|
    c.klass = Orders::Grid
    c.region = :south
    c.height = 300
    end
    def configure(c)
    super
    c.items = [:shipments, :orders]
    end
    end

    View Slide

  85. Client-side code
    initComponent: function(){
    this.callParent();
    var shipments = this.netzkeGetComponent('shipments'),
    orders = this.netzkeGetComponent('orders');
    shipments.on('itemclick', function(view, record){
    // call endpoint
    this.serverSelectShipment(record.get('id'));
    orders.getStore().load();
    }, this);
    }

    View Slide

  86. Select shipment at server
    class ShipmentsAndOrders < Netzke::Base
    endpoint :server_select_shipment do |id, this|
    # dept: authorization
    component_session[:selected_shipment_id] = id
    end
    component :orders do |c|
    c.klass = Orders::Grid
    c.shipment_id = component_session[:selected_shipment_id]
    c.region = :south
    c.height = 300
    end
    # ...
    end

    View Slide

  87. Orders::Grid
    module Orders
    class Grid < GridBase
    def configure(c)
    super
    c.model = "Order"
    if c.shipment_id
    c.scope = {
    shipment_id: c.shipment_id
    }
    c.strong_default_attrs = {
    shipment_id: c.shipment_id
    }
    end
    end
    # ...
    end
    end

    View Slide

  88. Notes on composability
    nesting components is easy
    by doing it we get new components
    nested doesn't always mean visible
    we can show components on demand
    however...

    View Slide

  89. "Complex" app

    View Slide

  90. An enterprise app can have hundreds of models
    and even a bigger number of components

    View Slide

  91. Loading them all in one go can be costly

    View Slide

  92. Our arms
    reusability ✓
    extensibility (OOP) ✓
    modular development ✓
    composability ✓
    dynamic loading

    View Slide

  93. Dynamic loading

    View Slide

  94. class Shipments::Grid < GridBase
    include Floralogic::Netzke::Attachments
    # ...
    end

    View Slide

  95. Dynamic loading
    server side
    class Shipments::Grid < GridBase
    component :attachment_window do |c|
    c.klass = Attachment::Window
    end
    # ...
    end

    View Slide

  96. Dynamic loading
    client side
    onManageAttachments: function(){
    var row = this.getSelectionModel().selected.first();
    if (row) {
    this.netzkeLoadComponent('attachment_window', {
    clientConfig: { id: row.get('id') },
    callback: function(w){ w.show(); }
    });
    }
    }

    View Slide

  97. Conclusion

    View Slide

  98. Powerful techniques
    reusability ✓
    extensibility (OOP) ✓
    modular development ✓
    composability ✓
    dynamic loading ✓
    JS encapsulation

    View Slide

  99. Final notes
    Netzke component != data grid
    use Netkze Core to build any reusable GUI element
    that needs to talk to the server
    forms, trees, dynamic layouts with persistence, photo
    galeries, charts, etc

    View Slide

  100. I love building things with Netzke

    View Slide

  101. "If I had to develop a webapp like that,
    I would try your framework."
    -- Matz

    View Slide

  102. Thanks!
    @uptomax @netzke

    View Slide

  103. View Slide