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

repost-oss

 repost-oss

Hiroki Zenigami

September 28, 2017
Tweet

More Decks by Hiroki Zenigami

Other Decks in Programming

Transcript

  1. 3BJMTͰ೔ใڞ༗ΞϓϦέʔγϣϯΛ
    044ͱͯ͠։ൃ͍ͯ͠Δ࿩

    3BJMT%FWFMPQFST.FFUVQ
    મਆ༟ٓʢ!LBNJ@[Iʣ

    View Slide

  2. ൃදΛͱ͓ͯ͠ͷ໨ඪ
    w 3FQPTUͷ։ൃΛͱ͓ͨ͠஌ݟΛ΋ͱʹ

    3FEVY 8FCQBDLFS FUD
    w 3BJMTΞϓϦͷ։ൃܦݧ͸͋Δ͚Ͳ41"͸ະܦݧͷํΛର৅ʹ
    w 3BJMTº41"ͷ։ൃࣄྫʹ৮Εͯ΋Β͏͜ͱ
    2

    View Slide

  3. ࣗݾ঺հ
    w મਆ༟ٓʢͥʹ͕ΈͻΖ͖ʣ
    w !LBNJ[I!LBNJ@[I
    w גࣜձࣾສ༿ٕज़։ൃ෦
    w ۽ຊग़਎ʢߴઐɾઐ߈Պ·Ͱʣ‎େֶӃ͔Βઍ༿ʙ౦ژ
    w ৽ଔ͔Β਺೥ؒ͸8FCαʔϏεͷ6*ઃܭ΍اըͳͲΛ୲౰
    w ʙ೥લ͔ΒΤϯδχΞʹΫϥενΣϯδ
    3

    View Slide

  4. View Slide

  5. ͞·͟·ͳऔΓ૊Έʢൈਮʣ
    w 3VCZίϛϡχςΟ΁ͷεϙϯαʔυ

    3VCZ,BJHJ 38$ 3BJMT(JSMT FUD
    w ΧϯϑΝϨϯε΁ͷࢀՃඅࢧԉ

    3VCZ,BJHJ!޿ౡ΋ෛ୲ͳ͘ࢀՃͰ͖ͨ
    w ։ൃίϯςετ

    ۀ຿࣌ؒ΍ࣾ಺ϦιʔεΛ͔ͭͬͯ։ൃͰ͖ͨ
    5

    View Slide

  6. ໨࣍
    3FQPTUͱ͸
    3FQPTUͷσʔλϑϩʔ
    3BJMTͱ3FQPTU
    ޻෉ͨ͜͠ͱ
    ൓ল఺
    6

    View Slide

  7. όʔδϣϯ৘ใʢݱࡏʣ
    w 3FQPTUW

    w 3VCZW

    w 3VCZPO3BJMTW

    w 8FCQBDLFSW

    w 3FEVYW

    7

    View Slide

  8. 3FQPTUͱ͸

    View Slide

  9. 3FQPTU
    IUUQTHJUIVCDPNLBNJ[ISFQPTU

    View Slide

  10. 3FQPTUͱ͸
    w Φʔϓϯιʔεͷ೔ใڞ༗ΞϓϦέʔγϣϯʢWʣ
    w .BSLEPXOͰॻ͚ΔʢϦΞϧλΠϜϓϨϏϡʔʣ
    w ֆจࣈʹΑΔϦΞΫγϣϯػೳ
    w νϟϯωϧػೳ

    ͱ͋ΔνʔϜͷϝϯόʔ͚ͩͷ౤ߘΛදࣔͰ͖Δ
    w Α͋͘Δػೳ

    هࣄͷετοΫ 4MBDL࿈ܞ ݖݶ؅ཧ FUD
    10

    View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. ࣮૷͍ͨ͠ػೳҰཡ
    w ίϝϯτػೳ
    w ௨஌ػೳ
    w هࣄதͷֆจࣈ΍ϦϓϥΠɺը૾ʹରԠ

    %SBGU+41MVHJOT
    w ଟݴޠରԠ
    w ݕࡧػೳ
    w %FQMPZUP)FSPLV
    14

    View Slide

  15. 3FQPTUͷσʔλϑϩʔ
    3FEVY3BJMTͰ41"Λઃܭ͢ΔࡍͷࢀߟʹMMMMMMMM

    View Slide

  16. ᶃ 7JFX͕มԽ͢Δ‎"DUJPO͕࣮ߦ͞ΕΔ
    ᶄ ඞཁʹԠͯ͡"1*ʹϦΫΤετΛૹΔ
    ᶅ 3FEVDFSͷఆٛʹ͕ͨͬͯ͠4UPSF಺ͷ4UBUF͕มԽ͢Δ
    ᶆ ͜ͷ4UBUFΛߪಡ͍ͯ͠Δ7JFX͕ߋ৽͞ΕΔ
    16
    7JFX "DUJPOT %JTQBUDIFS

    .JEEMFXBSFT
    "1*
    4UPSF

    3FEVDFST
    3VCZPO3BJMT
    3FEVY




    View Slide

  17. ۩ମྫ
    w ྫɿ֎෦αʔϏε࿈ܞʢ࣮૷͕࠷΋γϯϓϧͩͬͨʣ
    w આ໌͠΍͍͢Α͏࣮ࡍͷίʔυΛมߋɾলུ͍ͯ͠·͢
    17
    app/
    controllers/
    api/
    └─ services_controller.rb
    javascript/
    actions/
    └─ servicesActions.js
    features/
    └─ Services.jsx
    reducers/
    └─ services.js

    View Slide

  18. View Slide

  19. 7JFX "DUJPOT %JTQBUDIFS

    .JEEMFXBSFT
    "1*
    4UPSF

    3FEVDFST
    3VCZPO3BJMT
    3FEVY

    View Slide

  20. w ࿈ܞதͷ֎෦αʔϏεҰཡΛදࣔ͢Δ$POUBJOFS$PNQPOFOU

    هࣄ౤ߘ࣌ͷ௨஌ઌ4MBDLͳͲ
    w componentDidMount()͔ΒΞΫγϣϯΛ࣮ߦ͍ͯ͠Δ

    3FBDUͷϥΠϑαΠΫϧ
    20
    import * as servicesActions from '../actions/servicesActions'
    class Services extends Component {
    componentDidMount() {
    this.props.fetchServices()
    }
    }
    function mapDispatchToProps(dispatch) {
    return bindActionCreators(servicesActions, dispatch)
    }
    BQQKBWBTDSJQUGFBUVSFT4FSWJDFTKTY

    View Slide

  21. 7JFX "DUJPOT %JTQBUDIFS

    .JEEMFXBSFT
    "1*
    4UPSF

    3FEVDFST
    3VCZPO3BJMT
    3FEVY

    View Slide

  22. w 7JFX͔Βݺ͹ΕΔ"DUJPO
    w ࣍ͷΑ͏ͳܗͰ"1*ʹର͢ΔϦΫΤετΛఆٛ
    w )551ΫϥΠΞϯτ༻ͷϛυϧ΢ΣΞΛಋೖ͍ͯ͠Δ

    SFEVYBYJPTNJEEMFXBSF
    22
    export function fetchServices() {
    return {
    type: 'FETCH_SERVICES',
    payload: {
    request: {
    url: '/api/services.json'
    }
    }
    }
    }
    BQQKBWBTDSJQUBDUJPOTTFSWJDFT"DUJPOTKT

    View Slide

  23. 7JFX "DUJPOT %JTQBUDIFS

    .JEEMFXBSFT
    "1*
    4UPSF

    3FEVDFST
    3VCZPO3BJMT
    3FEVY

    View Slide

  24. w "1*͸3BJMTͰ࣮૷͢Δ
    w ϞσϧΛͱ͓ͯ͠࿈ܞதͷ֎෦αʔϏεҰཡΛऔಘ

    ࿈ܞ਺͸ଟ͘ͳΒͳ͍͜ͱΛ૝ఆͯ͠.allͰऔಘ
    w +CVJMEFSʹΑΓ7JFX͕ඞཁͱ͢Δ৘ใΛ+40/ܗࣜͰฦ͢
    24
    class Api::ServicesController < Api::ApplicationController
    def index
    @services = Service.all # JbuilderͰJSONΛฦ͢
    end
    end
    BQQDPOUSPMMFSTBQJTFSWJDFT@DPOUSPMMFSSC

    View Slide

  25. 7JFX "DUJPOT %JTQBUDIFS

    .JEEMFXBSFT
    "1*
    4UPSF

    3FEVDFST
    3VCZPO3BJMT
    3FEVY

    View Slide

  26. w 3FEVDFS͸4UPSFͷ4UBUFͷมԽΛఆٛͨؔ͠਺
    w ϦΫΤετ͕੒ޭͨ͠৔߹_SUCCESSͱ͍͏઀ඌ͕͍ࣙͭͨ

    λΠϓ͕ฦ٫͞ΕΔ

    SFEVYBYJPTNJEEMFXBSFͷ;Δ·͍
    w "1*ͷϨεϙϯε͕4UBUFʹอ࣋͞ΕΔ

    ͜ͷ৔߹࿈ܞதͷ֎෦αʔϏεҰཡ
    26
    function services(state = initialState, action) {
    switch (action.type) {
    case 'FETCH_SERVICES_SUCCESS':
    return action.payload.data // ࿈ܞதͷαʔϏεҰཡ
    default:
    return state
    }
    }
    BQQKBWBTDSJQUSFEVDFSTTFSWJDFTKT

    View Slide

  27. 7JFX "DUJPOT %JTQBUDIFS

    .JEEMFXBSFT
    "1*
    4UPSF

    3FEVDFST
    3VCZPO3BJMT
    3FEVY

    View Slide

  28. w 4UBUFΛ1SPQTʹϚοϓ
    w 4UBUF͕มԽ͢Δͱίϯϙʔωϯτʹ౉͢ύϥϝʔλ΋มԽ͢Δ

    7JFX͕ߋ৽͞ΕΔ
    28
    class Services extends Component {
    render() {
    return (



    )
    }
    }
    function mapStateToProps(state) {
    return { services: state.services }
    }
    BQQKBWBTDSJQUGFBUVSFT4FSWJDFTKTY

    View Slide

  29. ͜͜·Ͱͷ·ͱΊ
    w 7JFX‎"DUJPO‎"1*‎3FEVDFS‎7JFX
    w 'MVYΞʔΩςΫνϟͷ֓೦Λཧղ͢Δඞཁ͕͋Δ

    3FQPTUͰ͍͏ͱ3FEVYͷ࢓૊Έ
    w σʔλϑϩʔ͕୯ํ޲ͳͷͰઃܭ͠΍͍͢
    w "1*͸3BJMTͷࢿ࢈Λ࢖͑Δ
    29

    View Slide

  30. ʢࢀߟʣ.BTUPEPO
    IUUQTHJUIVCDPNUPPUTVJUFNBTUPEPO
    w 3BJMTΞϓϦͰ3FEVY3FBDUΛ࠾༻
    w app/javascriptԼͷߏ੒΍package.json͕ࢀߟʹͳΔ
    ˞ 41"Ͱ͸ͳ͍
    30

    View Slide

  31. 3BJMTͱ3FQPTU

    View Slide

  32. 8FCQBDLFS
    w +BWB4DSJQUΞϓϦέʔγϣϯΛ3BJMTͰ؅ཧ͢ΔͨΊͷ

    8FCQBDLΛ༻͍ͨπʔϧ
    w 3FQPTU͸"1*Λ3BJMTɺϑϩϯτΤϯυΛ3FEVY3FBDU
    w Ͳ͏΍ͬͯ͜ͷ؀ڥΛߏங͢Δ͔
    ‎3BJMTͰಋೖ͞Εͨ8FCQBDLFS΍:BSOʹΑΓγϯϓϧʹ࿈ܞͰ͖Δ
    32

    View Slide

  33. w rails new͢Δͱ͖ʹ--webpack=reactΦϓγϣϯΛ͚ͭΔ

    webpacker:install΍yarn install͕૸Δ
    w ͜ͷ࣌఺Ͱ3FBDUͷͻͳܗ͕Ͱ͖͍ͯΔ
    w ඞཁʹԠͯ͡reduxͳͲͷϥΠϒϥϦΛΠϯετʔϧ͢Δ

    yarn addͰΠϯετʔϧ
    w ։ൃ࣌͸webpack-dev-serverΛ࣮ߦ͢Δ

    ίʔυมߋ࣌ʹ)PU.PEVMF3FQMBDFNFOUͯ͘͠ΕΔ
    33
    $ rails new foo --webpack=react
    $ bin/yarn add redux react-redux react-router-redux ...
    $ bin/webpack-dev-server

    View Slide

  34. w &4ΛτϥϯεύΠϧ͢ΔͨΊͷ#BCFMͷઃఆ΋͋Δ
    w ܥ͸ଟ͘ͷઃఆ͕ϥΠϒϥϦଆʹٵऩ͞Εͨ
    ‎ΞϓϦέʔγϣϯଆͷઃఆΛγϯϓϧʹอͯΔ
    w 3FQPTUͷ8FCQBDLFS͸·ͩܥ

    ΧελϚΠζΛ͍ͯ͠Δ͜ͱ΋͋ΓઃఆϑΝΠϧ͕ଟ͍
    34

    View Slide

  35. w 8FCQBDLFSͰϏϧυͨ͠ϑΝΠϧΛ

    8FCQBDLFSͷjavascript_pack_tagΛͱ͓ͯ͠ಡΉ
    w ݟͨ໨͸͢΂ͯ+BWB4DSJQU
    35



    Repost
    <%= stylesheet_pack_tag 'application' %>


    <%= javascript_pack_tag 'application' %>


    BQQWJFXTMBZPVUTBQQMJDBUJPOIUNMFSC

    View Slide

  36. :BSO
    w +BWB4DSJQUͷύοέʔδ؅ཧπʔϧ
    w 3BJMT͔Β:BSOΛαϙʔτ
    w ύοέʔδͷґଘ؅ཧɺΠϯετʔϧͷߴ଎ԽɺηΩϡΞԽͳͲ

    OQNͱͷൺֱ
    w ௚઀ؔΘΔΠϯλϑΣʔε͸installaddremove͘Β͍
    ‎8FCQBDLFSͱ:BSOʹΑΓ+BWB4DSJQUͷΤίγεςϜΛ

    ಋೖ͢Δͷ͕ͱͯ΋؆୯ʹͳͬͨʢ3FEVYͳͲʣ
    36

    View Slide

  37. "DUJPO%JTQBUDI4ZTUFN5FTU$BTF
    w 3BJMT͔Βఏڙ͞Εͨɺ&&ςετΛ࣮ߦ͢ΔͨΊͷΫϥε
    w ಺෦Ͱ$BQZCBSB%4-ͳͲΛΠϯΫϧʔυ
    w τϥϯβΫγϣϯ؅ཧΛͯ͘͠ΕΔ
    ‎σʔλϕʔεͷΫϦΞͳͲͷઃఆ͕ෆཁ
    w 3BJMTͷϨʔϧʹ৐ͬͨ··

    +BWB4DSJQUΛؚΜͩϖʔδͷ&&ςετ·Ͱॻ͚Δ
    ‎3BJMTͰͷ41"։ൃ͕ΑΓεϜʔζʹ
    37

    View Slide

  38. ʢࢀߟʣ.JOJUFTU3FUSZ
    IUUQTHJUIVCDPNZZBHJNJOJUFTUSFUSZ
    w &&ςετ͕$*্Ͱͨ·ʹམͪΔʢ3FCVJME͢Δͱύε͢Δʜʜʣ
    ‎ೝূʹ༻͍Δ৘ใΛϩʔΧϧετϨʔδʹ֨ೲ͍ͯ͠Δ
    w ςετέʔε͕ࣦഊͨ͠ͱ͖ʹࢦఆճ਺ϦτϥΠͯ͘͠ΕΔ
    w 3FQPTUͷςετ͸.JOJUFTU
    w ͜ͷϥΠϒϥϦͷ͓͔͛Ͱ҆ఆͨ͠
    ‎ࠜຊతͳղܾΛ͍ͨ͠
    38

    View Slide

  39. ޻෉ͨ͜͠ͱ

    View Slide

  40. ςετΛॻ͘ର৅
    w ࣍ͷςετΛ͔ͬ͠Γॻ͍ͨ
    w 3BJMTͷϢχοτςετʢϞσϧ"1*ʣ
    w &&ςετ
    w +BWB4DSJQUͷϢχοτςετ͸ॻ͔ͳ͔ͬͨ
    ‎3FEVY͸4UBUFΛ؅ཧ͢ΔίϯςφҎ֎͸७ؔ਺ςετ͠΍͍͢
    ‎Ћ൛ͳͷͰ6*Λࢼߦࡨޡ͍ͨ͠‎&&ςετͰ໢ཏ
    40

    View Slide

  41. w ςετΛॻ͘ͱେ෯ͳϦϑΝΫλϦϯάΛ҆৺ͯ͠Ͱ͖Δ
    ‎+BWB4DSJQUͷߏ੒ɺ3FEVDFSͷઃܭͳͲͷ஌ݟ͕ͳ͔ͬͨ
    ‎ࢼߦࡨޡ͕ඞཁͳͱ͜Ζ͸ϦϑΝΫλϦϯά͕ଟ͘ͳΔ
    ‎ςετΛ͔ͬ͠Γॻ͍͓ͯ͘ͱΑͦ͞͏ʁͱߟ͑ͨ
    w ͨͱ͑͹͜ΜͳϦϑΝΫλϦϯάΛͯ͠΋;Δ·͍͕อূ͞ΕΔ




    41

    View Slide

  42. 4QSPDLFUT͸࡟আͨ͠
    w ͦΕͧΕͷ୲౰ൣғ
    w 8FCQBDLFS+BWB4DSJQUΞϓϦέʔγϣϯΛ؅ཧ
    w 4QSPDLFUT3BJMTͷίϯτϩʔϥϏϡʔ͝ͱͷΞηοτΛ؅ཧ

    ‎"TTFU1JQFMJOFͰ࿈݁
    w 3FQPTU͸41"ͳͷͰ4QSPDLFUT͸ඞཁͳ͔ͬͨ
    w ΞϓϦέʔγϣϯͷੑ࣭ʹԠͯ͡બ୒͋Δ͍͸૊Έ߹ΘͤΔ

    ྫɿ3FEVYͱ3BJMTͷϏϡʔ͕ڞଘ͢ΔՄೳੑ΋͋Δ
    42

    View Slide

  43. ηοτΞοϓΛָʹ͢Δ
    w bin/setup bin/updateΛϝϯςφϯε͢Δ
    w େ஥͞Μʢ!POLʣͷεϥΠυͰ஌ͬͨ
    w ;ͭ͏ͷ3BJMTΞϓϦέʔγϣϯ։ൃ

    IUUQTXXXTMJEFTIBSFOFUUBLBGVNJPOBLBSBJMT
    w git clone/pullͨ͠ޙʹ͜ΕΛୟ͚ͩ͘Ͱ͢΂ͯͷ४උ͕੔͏
    43

    View Slide

  44. w 3FQPTU͸044
    w ίϯτϦϏϡʔτͷҙࢥΛ΋ͬͨਓ͕͙͢ʹ։ൃΛ͸͡ΊΒΕΔ

    ؀ڥΛఏڙ͢Δ͜ͱ͕ॏཁͩͱߟ͑ͨ
    44

    View Slide

  45. w ؔ࿈͢Δ13Λཱͯͨ

    IUUQTHJUIVCDPNSBJMTSBJMTQVMM
    w BTTJHOFF͕!EIIͳͷͰݫͦ͠͏ʜʜ







    45

    View Slide

  46. ൓ল఺

    View Slide

  47. Ϟʔμϧ͸৻ॏʹಋೖ͢Δ
    w 4UBUFͷ؅ཧͰ࣮૷͕ෳࡶʹͳΔ

    ྫɿϑΥʔϜɺλϒ
    w &&ςετ͕ॻ͖ͮΒ͍ɾ஗͘ͳΔ

    ྫɿϞʔμϧΛ։͘ɺͱ͍͏ΞΫγϣϯ͕ඞཁʹͳΔ
    w Ϟʔμϧͩͱ࢖͍ͮΒ͍έʔε΋͋Δ

    ྫɿด͡Δɺͱ͍͏ΞΫγϣϯ͕ඞཁ
    47

    View Slide

  48. w ࠷ॳ͸ઃఆؔ࿈Λ͢΂ͯϞʔμϧͰ࣮૷͍ͯͨ͠
    ‎్தͰ͢΂ͯॻ͖௚ͨ͠
    ‎ςετΛ͔ͬ͠Γॻ͍͓ͯ͘ͱॻ͖௚͠΋҆৺ͯ͠Ͱ͖Δ
    w ͦͷػೳ͕ຊ౰ʹϞʔμϧͰͳ͚Ε͹ͳΒͳ͍͔ߟ͑Δ
    48

    View Slide

  49. 3BJMTͷࢿ࢈Λ׆༻͢Δ
    w ࢿ࢈ɿ5VSCPMJOLT΍'PSN)FMQFSͳͲ
    w 3FQPTUͷߏ੒͸3FEVY3FBDUͰ41"ʢษڧͷͨΊʣ
    w ઃఆը໘ͳͲɺ࣮૷޻਺͸͔͔Δ͕࢖༻ස౓͕௿͍ػೳ΋ଟ͍

    ΋ͪΖΜ։ൃऀͷεΩϧʹΑΔ
    49

    View Slide

  50. w 5VSCPMJOLT΍'PSN)FMQFSͳͲͷ׆༻Λݕ౼͢Δ
    ‎هࣄͷҰཡ΍දࣔɺ౤ߘίϯϙʔωϯτ͸3FEVYͰΑͦ͞͏
    ‎.BTUPEPO͸ϋΠϒϦουܕʢઃఆͳͲΛ3BJMTଆͰ΍͍ͬͯΔʣ
    50

    View Slide

  51. ༨ஊ

    View Slide

  52. Ϟνϕʔγϣϯͷอͪํ

    View Slide

  53. w தଜ͞Μʢ!SLBNVSBʣͷεϥΠυ
    w ݸਓ։ൃͷ΍͍͖ͬͯํ

    IUUQTTQFBLFSEFDLDPNSLBNVSBHFSFOLBJGBGBMTFZBUVUFJLJGBOH
    w ΤλΒͳ͍ͨΊʹ

    ΤλΒͳ͍ϞνϕʔγϣϯΛอͪଓ͚Δ
    w ࣗ෼ʹͱͬͯͳʹ͕։ൃͷϞνϕʔγϣϯͳͷ͔Λ

    ݟग़͢͜ͱ͕ॏཁ
    53

    View Slide

  54. w ࠷৽ͷ3BJMTͷػೳΛࢼ͢৔ͱͯ͠࢖͏
    w స৬׆ಈͷͱ͖ʹ໊ࢗ୅ΘΓʹ࢖͏
    54

    View Slide

  55. w ࢲͷ৔߹͸஌తཉٻۦಈ։ൃ
    w ࣗ෼ֶ͕ͼ͍ٕͨज़Λ࠾༻͢Δ‎͍Βͳ͘ͳͬͨΒফ͢
    w ීٴͨ͠ΒελοΫΛબఆ͢Δඞཁ͕͋Δ͕ɺͦΕ·Ͱ͸

    ͭ͘Γ͍ͨΑ͏ʹͭ͘Δ
    55

    View Slide

  56. View Slide

  57. w 8FCαʔϏεͱͯ͠ެ։ͨ͠΋ͷ΋͍͔ͭ͋͘Δ
    w ๏຿΍ܦཧͳͲ͕໘౗‎ٛ຿ײ͕ੜ·Εͨ
    w 044ͩͱ͕͠ΒΈ͕ͳ͍
    57

    View Slide

  58. w Ϟνϕʔγϣϯ͸ਓͦΕͧΕҧ͏
    w ࣗ෼ͷϞνϕʔγϣϯ͕ͳʹ͔Λߟ͑ɺͦΕΛ࣠ʹ

    ։ൃ͢ΔͱΑͦ͞͏
    w ࠓ΍Γ͍ͨͷ͸5ZQF4DSJQUɺ"DUJWF4UPSBHFͳͲ
    58

    View Slide

  59. ·ͱΊ

    View Slide

  60. w 3BJMTͰͷ41"͸3BJMTͰ։ൃ͠΍͘͢ͳͬͨ

    8FCQBDLFS :BSO
    w "DUJPO%JTQBUDI4ZTUFN5FTU$BTFʹΑΓ41"ͷςετ΋

    Ϩʔϧʹ৐ͬͨ··ॻ͚Δ
    w +BWB4DSJQUͰॻ͔͘ɺ3BJMTͷࢿ࢈Λ׆༻͢Δ͔͸

    ͔ͬ͠Γͱݕ౼͢Δ
    60

    View Slide

  61. 3FQPTU
    IUUQTHJUIVCDPNLBNJ[ISFQPTU

    View Slide