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

Building maintainable command-line tools with MRuby

58479f76374a3ba3c69b9804163f39f4?s=47 Eric Hodel
September 09, 2016

Building maintainable command-line tools with MRuby

MRuby and mruby-cli makes it possible to ship single binary command-line tools that can be used without setup effort. How can we make these easy to write too?

58479f76374a3ba3c69b9804163f39f4?s=128

Eric Hodel

September 09, 2016
Tweet

Transcript

  1. Building maintainable command-line tools with MRuby

  2. MRuby & mruby-cli Eric Hodel — @drbrain

  3. About Me • Ruby commi?er • RubyGems commi?er • Rake

    commi?er • Author of many gems • On vacaEon
  4. None
  5. None
  6. Fastly + Ruby www.ruby-lang.org rubygems.org

  7. @FastlyJapan

  8. Part Ⅰ

  9. Web API Wrapper

  10. Why not Ruby? • Install Ruby • Installing gems •

    Expensive support
  11. MRuby =~ Ruby 1. Prototype in CRuby 2. Port to

    MRuby 3. Maintain in MRuby
  12. MRuby

  13. MRuby • Lightweight ruby • Embeddable • Hardware • SoWware

  14. mrbgems • CRuby stdlib parts • mruby libraries • C

    extensions
  15. Other mruby Talks 2016: Welcome to haconiwa 2016: Recent Advances

    in HTTP 2016: System calls hijacking with (m)ruby 2015: Making robot with mruby 2015: Building CLI Apps for Everyone 2014: MRuby as Development Plaborm for Payments 2014: MRuby on LEGO Mindstorms EV3 ® 2014: Resource Control Architecture scripEng with mruby
  16. Using MRuby

  17. build_config.rb • Which mrbgems to use • Which compiler to

    use • Library & include path • Can have many builds
  18. mrbgem.rake • Gem metadata • Gem dependencies • Gem binaries

  19. rake

  20. rake Results • bin/your-cli • bin/mruby • bin/mirb • bin/mrbtest

  21. mruby-cli

  22. Building CLI Apps for Everyone

  23. Write Ruby release on Linux, OS X, Windows

  24. Using mruby-cli 1. Write MRuby 2. Compile 3. GOTO 1

    unless tests pass 4. Release executables
  25. MRuby Challenges

  26. MRuby != CRuby

  27. Modular stdlib • Minimal core library • mruby-array-ext • Easy

    to forget for mrbgems
  28. Language Issues • No pre-defined globals • Some syntax bugs

    • No keyword arguments for def • Regexp replacements
  29. ExcepEons • Inaccurate backtrace • More C exploraEon

  30. PorEng Libraries • Easy overall • Beware restricted syntax •

    PorEng tests is hard
  31. mrbgems • Like Ruby 1.6.x • Libraries have narrow focus

    • Expect to submit patches
  32. Patches mruby-onig-regexp mruby-curl mruby-io mruby-h?psclient mruby-cli

  33. Build InformaEon • Needed for cross-compile • Hard to read

    without rake
  34. mruby-cli Challenges

  35. Docker

  36. Docker • Ready to cross-compile • Clumsy to use •

    Best opEon available
  37. docker-compose run compile • Too long to type • run

    shell to debug • Unfamiliar environment
  38. Build System

  39. Task OrganizaEon • MRuby and mruby-cli mixed • Hard to

    add new tasks
  40. Improvements • Build MRuby separately • Run docker from a

    task
  41. Docker cd mruby; rake rake compile rake -f in-docker Outer

    Inner
  42. "Outer" Tasks • Download MRuby • Global test setup •

    Release tasks
  43. "Inner" Tasks • Cross-compile libraries • Compile MRuby • Run

    tests
  44. AddiEonal Benefits • Hooks for custom tasks • Faster rake

    test
  45. C Libraries

  46. StaEc Linking • Single file • Re-release on vulnerabiliEes •

    Larger
  47. Dynamic Linking • Easy vulnerability management • Adds a dependency

  48. Cross Compiling

  49. Cross Compiling • Download release • Cross-compile • MRuby compiler

    configuraEon
  50. CrossLibrary CrossLibrary.new 'curl' do |cross| cross.release_name = 'curl-7.28.0' cross.url =

    "https://curl.haxx.se/…" # TODO: cross-compile OpenSSL too. cross.configure_flags << '--without-ssl' end
  51. CrossLibrary CrossLibrary.new 'curl' do |cross| cross.release_name = 'curl-7.28.0' cross.url =

    "https://curl.haxx.se/…" # TODO: cross-compile OpenSSL too. cross.configure_flags << '--without-ssl' end
  52. CrossLibrary CrossLibrary.new 'curl' do |cross| cross.release_name = 'curl-7.28.0' cross.url =

    "https://curl.haxx.se/…" # TODO: cross-compile OpenSSL too. cross.configure_flags << '--without-ssl' end
  53. CrossLibrary CrossLibrary.new 'curl' do |cross| cross.release_name = 'curl-7.28.0' cross.url =

    "https://curl.haxx.se/…" # TODO: cross-compile OpenSSL too. cross.configure_flags << '--without-ssl' end
  54. CrossLibrary CrossLibrary.new 'curl' do |cross| cross.release_name = 'curl-7.28.0' cross.url =

    "https://curl.haxx.se/…" # TODO: cross-compile OpenSSL too. cross.configure_flags << '--without-ssl' end
  55. Part Ⅱ

  56. Customer Support Tool

  57. Problem • API-only to start • Complicated setup • Support

    burden
  58. SoluEon • Automate • Validate inputs • Reduce errors

  59. Prototypes

  60. Single File Script • Easy to write • Inflexible

  61. MulEple Scripts • One purpose per script • More flexible

    • Well documented
  62. Extract classes • JSON-API • Argument parsing

  63. Lessons

  64. Document • No API docs • 50% comments • Users

    can suggest changes
  65. Simple Scripts • Minimum dependencies • Minimum opEons • One

    script per task • Very few large scripts
  66. Hard to Maintain • CRuby version • Clone and edit

    by support
  67. Switch to mruby-cli

  68. RubyGems Pa?ern • Subclass per subcommand • mruby-optparse • setup

    for opEons • execute to run
  69. Example Command class ConditionList < Command def setup option_service_id required:

    true option_version required: true end
  70. Example Command class ConditionList < Command def execute results =

    api.send_request … results.each do |result| puts "…" end end
  71. mruby-optparse • Familiar • Type checking • Argument compleEon •

    Shell compleEon
  72. mruby-curl • Most mature HTTP library • HTTP + HTTPS

    • Persistent connecEons • libcurl dependency
  73. TesEng • mruby-mtest for asserEons • mruby-test as a runner

    • Test case setup is painful
  74. TesEng

  75. mruby-mtest • Like minitest • Test subclass • test_* methods

    • setup / teardown • Remember: MTest::Unit.new.run
  76. mruby-test • Test runner • One MRuby VM per test

    file • Complete isolaEon
  77. ComplicaEons • Global setup hard • docker-compose documentaEon • Slow

    startup
  78. Workarounds • Setup before all tests • Pass data out-of-band

    • Compile only host for tesEng
  79. rake bintest • IntegraEon tests • Assert output from input

  80. Part Ⅲ

  81. Future Work

  82. mruby-cli • Improved build system • DocumentaEon • Easier upgrades

  83. MRuby • Accurate backtraces • build_config.rb library • Keyword args

    in methods
  84. CRuby • mrb_get_args() • AutomaEc type check • More expressive

    than rb_scan_args()
  85. mruby-test • AutomaEc test_preload • --name opEon

  86. Thank You QuesEons?