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

hanami Minsk

hanami Minsk

Anton Davydov

April 23, 2016
Tweet

More Decks by Anton Davydov

Other Decks in Programming

Transcript

  1. View Slide

  2. English transcription
    https://goo.gl/morJTT

    View Slide

  3. Hello Minsk!

    View Slide

  4. View Slide

  5. Anton Davydov
    github.com/davydovanton

    twitter.com/anton_davydov
    davydovanton.com

    View Slide

  6. OpenSource

    View Slide

  7. Sidekiq
    Hanami

    View Slide

  8. ruby-doc
    rails
    crystal
    etc

    View Slide

  9. moscow.rb
    rubyunderhood

    View Slide

  10. hanami (ᜰ憎)
    Hanami (ᜰ憎, lit. "flower viewing") is the
    Japanese traditional custom of enjoying
    the transient beauty of flowers, flowers
    ("hana") in this case almost always
    referring to those of the cherry ("sakura")
    or, less frequently, plum ("ume") trees.

    View Slide

  11. View Slide

  12. View Slide

  13. Luca Guidi
    github.com/jodosha

    View Slide

  14. Alfonso Uceda
    github.com/AlfonsoUceda
    Trung Lê
    github.com/joneslee85

    View Slide

  15. View Slide

  16. The Base Ideas

    View Slide

  17. Modularity
    Forget about fat models

    View Slide

  18. Simplicity
    Framework is only tool

    View Slide

  19. Less DSLs
    No magic

    View Slide

  20. Few Conventions
    Come with us or against us

    View Slide

  21. Pure Objects
    If you know ruby you know hanami

    View Slide

  22. Zero Monkey-Patching
    Don’t think framework or language

    View Slide

  23. Threadsafe

    View Slide

  24. View Slide

  25. hanami != rails


    View Slide

  26. Typical parts of
    web app

    View Slide

  27. Web App
    Business

    logic
    Data
    flow

    View Slide

  28. Application Architecture
    Monolith First

    View Slide

  29. app/
    ʮʒʒ application.rb
    ʮʒʒ assets
    ʔ ʦʒʒ ...
    ʮʒʒ config
    ʔ ʦʒʒ ...
    ʮʒʒ controllers
    ʔ ʦʒʒ ...
    ʮʒʒ templates
    ʔ ʦʒʒ ...
    ʦʒʒ views
    ʦʒʒ ...

    View Slide

  30. app/
    ʮʒʒ application.rb
    ʮʒʒ assets
    ʔ ʦʒʒ ...
    ʮʒʒ config
    ʔ ʦʒʒ ...
    ʮʒʒ controllers
    ʔ ʦʒʒ ...
    ʮʒʒ templates
    ʔ ʦʒʒ ...
    ʦʒʒ views
    ʦʒʒ ...

    View Slide

  31. app/
    ʮʒʒ application.rb
    ʮʒʒ assets
    ʔ ʦʒʒ ...
    ʮʒʒ config
    ʔ ʦʒʒ ...
    ʮʒʒ controllers
    ʔ ʦʒʒ ...
    ʮʒʒ templates
    ʔ ʦʒʒ ...
    ʦʒʒ views
    ʦʒʒ ...

    View Slide

  32. app/
    ʮʒʒ application.rb
    ʮʒʒ assets
    ʔ ʦʒʒ ...
    ʮʒʒ config
    ʔ ʦʒʒ ...
    ʮʒʒ controllers
    ʔ ʦʒʒ ...
    ʮʒʒ templates
    ʔ ʦʒʒ ...
    ʦʒʒ views
    ʦʒʒ ...

    View Slide

  33. Container Architecture
    Clean Architecture

    View Slide

  34. apps/
    ʮʒʒ admin
    ʔ ʮʒʒ application.rb
    ʔ ʮʒʒ assets
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ config
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ controllers
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ templates
    ʔ ʔ ʦʒʒ ...
    ʔ ʦʒʒ views
    ʔ ʦʒʒ ...
    ʦʒʒ web
    ʮʒʒ ...

    View Slide

  35. apps/
    ʮʒʒ admin
    ʔ ʮʒʒ application.rb
    ʔ ʮʒʒ assets
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ config
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ controllers
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ templates
    ʔ ʔ ʦʒʒ ...
    ʔ ʦʒʒ views
    ʔ ʦʒʒ ...
    ʦʒʒ web
    ʮʒʒ ...

    View Slide

  36. apps/
    ʮʒʒ admin
    ʔ ʮʒʒ application.rb
    ʔ ʮʒʒ assets
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ config
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ controllers
    ʔ ʔ ʦʒʒ ...
    ʔ ʮʒʒ templates
    ʔ ʔ ʦʒʒ ...
    ʔ ʦʒʒ views
    ʔ ʦʒʒ ...
    ʦʒʒ web
    ʮʒʒ ...

    View Slide

  37. lib/
    ʮʒʒ config
    ʔ ʦʒʒ mapping.rb
    ʮʒʒ project_name
    ʔ ʮʒʒ entities
    ʔ ʔ ʦʒʒ user.rb
    ʔ ʮʒʒ mailers
    ʔ ʔ ʦʒʒ templates
    ʔ ʦʒʒ repositories
    ʔ ʦʒʒ user_repository.rb
    ʦʒʒ project_name.rb

    View Slide

  38. lib/
    ʮʒʒ config
    ʔ ʦʒʒ mapping.rb
    ʮʒʒ project_name
    ʔ ʮʒʒ entities
    ʔ ʔ ʦʒʒ user.rb
    ʔ ʮʒʒ mailers
    ʔ ʔ ʦʒʒ templates
    ʔ ʦʒʒ repositories
    ʔ ʦʒʒ user_repository.rb
    ʦʒʒ project_name.rb

    View Slide

  39. lib/
    ʮʒʒ config
    ʔ ʦʒʒ mapping.rb
    ʮʒʒ project_name
    ʔ ʮʒʒ entities
    ʔ ʔ ʦʒʒ user.rb
    ʔ ʮʒʒ mailers
    ʔ ʔ ʦʒʒ templates
    ʔ ʦʒʒ repositories
    ʔ ʦʒʒ user_repository.rb
    ʦʒʒ project_name.rb

    View Slide

  40. lib/
    ʮʒʒ config
    ʔ ʦʒʒ mapping.rb
    ʮʒʒ project_name
    ʔ ʮʒʒ entities
    ʔ ʔ ʦʒʒ user.rb
    ʔ ʮʒʒ mailers
    ʔ ʔ ʦʒʒ templates
    ʔ ʦʒʒ repositories
    ʔ ʦʒʒ user_repository.rb
    ʦʒʒ project_name.rb

    View Slide

  41. General Parts

    View Slide

  42. hanami - base repository, CLI
    router - Rack compatible HTTP router for Ruby
    controller - Full featured and fast actions for Rack
    utils - Ruby core extensions and class utilities
    model - Persistence with entities and repositories

    View Slide

  43. validations - Validations mixin for Ruby objects
    helpers - View helpers for Ruby applications
    view - Presentation with a separation between views and templates
    assets - Assets management for Ruby
    mailer - Mail for Ruby applications

    View Slide

  44. Differences

    View Slide

  45. – Linus Torvalds
    “Talk is cheap. Show me the code.”

    View Slide

  46. # rack
    class HelloApp
    def call(env)
    [200, {}, ['Hello!']]
    end
    end

    View Slide

  47. # hanami
    class HelloApp
    def call(env)
    [200, {}, ['Hello!']]
    end
    end
    router = Hanami::Router.new
    router.get '/', to: 'hello_app'

    View Slide

  48. # sinatra
    class Hello < Sinatra
    get '/' do
    'Hello!'
    end
    end

    View Slide

  49. # hanami
    Hanami::Router.new do
    get '/' do
    [200, {}, ['Hello!']]
    end
    end

    View Slide

  50. class InstitutionsController < ApplicationController
    load_and_authorize_resource :only => [:destroy,:edit,:new,:create,:update]
    before_filter :authenticate_user!, :except =>
    [:student_registration, :show, :validate_registration_pin, :result, :admission, :buy_registration_pin,:paygate_callback_fai
    lure, :paygate_cancel, :paygate_pending, :paygate_callback_success, :pin_transaction_info_print]
    before_filter :find_institution, :except =>
    [:show,:index, :new, :create, :semesters_for_institute_type, :start_end_date_for_assessment_period, :courses_for_batch, :pa
    ygate_callback_failure, :paygate_cancel, :paygate_pending, :paygate_callback_success, :pin_transaction_info_print]
    before_filter :add_bread_crumb,:except => [:show]
    def paygate_callback_success
    @pay_gate_config = YAML::load(File.open("#{Rails.root}/config/pay_gate_config.yml"))[Rails.env]
    @payment = TransactionRecord.find_by_order_number(params[:OrderID])
    uri = URI("https://fidelitypaygate.fidelitybankplc.com/cipg/MerchantServices/UpayTransactionStatus.ashx")
    parameters = {:MERCHANT_ID => "#{@pay_gate_config['merchant_id']}", :ORDER_ID => "#{@payment.order_number}"}
    uri.query = URI.encode_www_form(parameters)
    result =open(uri).read
    result_hash = Hash.from_xml(result)
    record_payment_details(result_hash)
    if result_hash["CIPG"]["StatusCode"] == PaymentRecord::PAYMENT_SUCCESS_CODE
    if @payment.transactionable_type.eql?("PaymentRecord")
    redirect_to institution_fees_path(@payment.transactionable_type.fee.institution), :notice => "Payment transaction
    has been #{result_hash['CIPG']['Status']}"
    elsif @payment.transactionable_type.eql?("PinBuyerInfo")
    unless @payment.transactionable.pin_id.present?
    @registration = @payment.transactionable.registration
    @valid_registration_pin_groups = @registration.valid_registration_pin_groups
    @online_valid_registration_pin_groups = @valid_registration_pin_groups.where(:pin_available_type => 'Online')
    @offline_valid_registration_pin_groups = @valid_registration_pin_groups.where(:pin_available_type => 'Offline')
    @available_pin = nil
    @online_valid_registration_pin_groups.each do |vpg|
    if vpg.available_pins.present?
    @available_pin = vpg.available_pins.first
    break
    else
    next
    end
    end
    if !@available_pin.present?
    @offline_valid_registration_pin_groups.each do |vpg|
    if vpg.available_pins.present?
    @available_pin = vpg.available_pins.first
    break
    else
    next

    View Slide

  51. class InstitutionsController < ApplicationController
    load_and_authorize_resource :only => [:destroy,:edit,:new,:create,:update]
    before_filter :authenticate_user!, :except =>
    [:student_registration, :show, :validate_registration_pin, :result, :admission, :buy_registration_pin,:paygate_callback_fai
    lure, :paygate_cancel, :paygate_pending, :paygate_callback_success, :pin_transaction_info_print]
    before_filter :find_institution, :except =>
    [:show,:index, :new, :create, :semesters_for_institute_type, :start_end_date_for_assessment_period, :courses_for_batch, :pa
    ygate_callback_failure, :paygate_cancel, :paygate_pending, :paygate_callback_success, :pin_transaction_info_print]
    before_filter :add_bread_crumb,:except => [:show]
    def paygate_callback_success
    @pay_gate_config = YAML::load(File.open("#{Rails.root}/config/pay_gate_config.yml"))[Rails.env]
    @payment = TransactionRecord.find_by_order_number(params[:OrderID])
    uri = URI("https://fidelitypaygate.fidelitybankplc.com/cipg/MerchantServices/UpayTransactionStatus.ashx")
    parameters = {:MERCHANT_ID => "#{@pay_gate_config['merchant_id']}", :ORDER_ID => "#{@payment.order_number}"}
    uri.query = URI.encode_www_form(parameters)
    result =open(uri).read
    result_hash = Hash.from_xml(result)
    record_payment_details(result_hash)
    if result_hash["CIPG"]["StatusCode"] == PaymentRecord::PAYMENT_SUCCESS_CODE
    if @payment.transactionable_type.eql?("PaymentRecord")
    redirect_to institution_fees_path(@payment.transactionable_type.fee.institution), :notice => "Payment transaction
    has been #{result_hash['CIPG']['Status']}"
    elsif @payment.transactionable_type.eql?("PinBuyerInfo")
    unless @payment.transactionable.pin_id.present?
    @registration = @payment.transactionable.registration
    @valid_registration_pin_groups = @registration.valid_registration_pin_groups
    @online_valid_registration_pin_groups = @valid_registration_pin_groups.where(:pin_available_type => 'Online')
    @offline_valid_registration_pin_groups = @valid_registration_pin_groups.where(:pin_available_type => 'Offline')
    @available_pin = nil
    @online_valid_registration_pin_groups.each do |vpg|
    if vpg.available_pins.present?
    @available_pin = vpg.available_pins.first
    break
    else
    next
    end
    end
    if !@available_pin.present?
    @offline_valid_registration_pin_groups.each do |vpg|
    if vpg.available_pins.present?
    @available_pin = vpg.available_pins.first
    break
    else
    next

    View Slide

  52. Rails and Hanami

    View Slide

  53. Rails and Hanami

    View Slide

  54. Controllers

    View Slide

  55. Controllers: Rails
    class UsersController < AC
    def new
    end
    def send_sms
    end
    end

    View Slide

  56. class Cats::CruelsController < AC
    def index
    end
    def show
    end
    end
    Controllers: DHH style

    View Slide

  57. module Web::Controllers::Board
    class Index
    include Web::Action
    def call(params)
    end
    end
    end
    Controllers: hanami

    View Slide

  58. Model

    View Slide

  59. class User < ActiveRecord::Base
    validates :name, presence: true
    # ...
    end
    Model: Rails

    View Slide

  60. Model: ROM

    View Slide

  61. Model: hanami entity
    class User
    include Hanami::Entity
    attributes :name
    # ...
    end

    View Slide

  62. Model: hanami repository
    class UserRepository
    include Hanami::Repository
    def find_by_name(name)
    query do
    # ...
    end
    end
    end

    View Slide

  63. Utils

    View Slide

  64. # rails
    class String
    def pluralize
    # ...
    end
    end
    # hanami
    module Hanami
    module Utils
    class String
    def pluralize
    # ...
    end
    end
    end
    end
    Utils

    View Slide

  65. View

    View Slide

  66. View: Rails
    rails view (partials?) + rails helper

    View Slide

  67. View: Hanami
    hanami view (ruby class) + partial

    View Slide

  68. Assets

    View Slide

  69. Pros and Cons

    View Slide

  70. No magic

    View Slide

  71. Controller test
    describe Web::Controllers::Board::Index do
    let(:action){ Board::Index.new }
    let(:params){ Hash[] }
    it 'is successful' do
    response = action.call(params)
    response[0].must_equal 200
    end
    end

    View Slide

  72. Controller test
    describe Web::Controllers::Board::Index do
    let(:action){ Board::Index.new }
    let(:params){ Hash[] }
    it 'is successful' do
    response = action.call(params)
    response[0].must_equal 200
    end
    end

    View Slide

  73. Controller test
    describe Web::Controllers::Board::Index do
    let(:action){ Board::Index.new }
    let(:params){ Hash[] }
    it 'is successful' do
    response = action.call(params)
    response[0].must_equal 200
    end
    end

    View Slide

  74. No monkey-patching

    View Slide

  75. Best practices

    View Slide

  76. Modularity

    View Slide

  77. The logic separation

    View Slide

  78. TDD

    View Slide

  79. View Slide

  80. TDD

    View Slide

  81. The same type of
    code in the controllers

    View Slide

  82. Not stable version

    View Slide

  83. Missing Gems

    View Slide

  84. WebSockets
    WebPack
    React

    View Slide

  85. * overcome hardships

    View Slide

  86. Benchmarks

    View Slide

  87. wrk --connections 4 \
    --duration 30 \
    --threads 4 localhost:9292
    davydovanton/hanami-bench
    puma *.ru
    +

    View Slide

  88. rack vs hanami
    0
    3000
    6000
    9000
    12000
    req/s
    9 873
    11 937
    rack hanami

    View Slide

  89. sinatra vs grape vs hanami (JSON)
    0
    1250
    2500
    3750
    5000
    req/s
    4 127
    1 703
    2 739
    sinatra grape hanami

    View Slide

  90. rails vs hanami
    0
    200
    400
    600
    800
    req/s
    735
    384
    653
    54
    rails(view) hanami(view) rails(json) hanami(json)

    View Slide

  91. Experience

    View Slide

  92. Types of apps
    • Pure API
    • link shortener (davydovanton/link-shortener)
    • Full app with admin and web parts

    View Slide

  93. Types of apps
    • Pure API
    • link shortener (davydovanton/link-shortener)
    • Full app with admin and web parts

    View Slide

  94. Types of apps
    • Pure API
    • link shortener (davydovanton/link-shortener)
    • Full app with admin and web parts

    View Slide

  95. Types of apps
    • Pure API
    • link shortener (davydovanton/link-shortener)
    • Full app with admin and web parts

    View Slide

  96. Third-party gems

    View Slide

  97. Rack middleware

    View Slide

  98. Contacts
    hanamirb.org
    gitter.im/hanami/chat
    discuss.hanamirb.org

    View Slide

  99. Thank you ❤
    Anton Davydov


    github:davydovanton

    View Slide