Building Gems that Don't Suck

4dea430d31b993abaf41cd9b54f8128d?s=47 avdi
May 23, 2012

Building Gems that Don't Suck

A Lunch & Learn talk I gave at Hashrocket going over some tips for architecting and packaging quality Ruby libraries

4dea430d31b993abaf41cd9b54f8128d?s=128

avdi

May 23, 2012
Tweet

Transcript

  1. Hashrocket Lunch & Learn #2 Avdi Grimm 2012-04-19

  2. Building Libraries that Don’t Suck Distribution Coding API Design Error

    API Design
  3. Distribution

  4. Limit dependencies Yes, ActiveSupport is the bee’s knees . .

    . but it may break someone’s codebase
  5. Semantic Versioning Patch versions: fix defects. Minor versions: add features;

    never change existing (correct) behavior. Major version: break backwards compatibility Make the tiddly-waka (~>) count for something!
  6. Don’t name it after a pattern That’s just confusing.

  7. Coding

  8. No monkey patching! (Unless that’s the purpose of the library)

  9. No classes named “Base” Don’t make grepping any harder than

    it needs to be. Identify the concept, and name it. (ActiveRecord::Record, anyone?) Exceptions: Libraries involving baseball Libraries about base jumping Libraries concerned with Swedish 90s pop group Ace of Base
  10. Don’t eat client exceptions module SWS def with_sandwich # ...

    yield sandwich rescue => e # OM NOM NOM EXCEPTIONS! nil end end
  11. Don’t eat client exceptions # Meanwhile, in client code SWS.with_sandwich

    do # ... some_method_that_raises_a_runtime_error # ... end
  12. API design

  13. Block-style configuration SWS.configure do |config| conf.selleck_shirtless = true conf.sandwich_type =

    :baloney conf.waterfall_height *= 2 end
  14. Block-style configuration Code-as-config for ultimate flexibility Discoverable: default values can

    be queried. A convenient point to add validations Can be called more than once
  15. Top-level Facade # Instead of: SWS::Sandwich.new(...) # Try: SWS.new_sandwich(...)

  16. Top-level Facade Top-level API acts as a reference Doesn’t tie

    client code to the structure of your library Gives you more leeway to move things around internally Or proliferate classes
  17. Error API

  18. Exception Classes Logic Error: Library has a defect. Client Error:

    Client mis-used the library. Transient Failure: Some external dependency isn’t working right now.
  19. Exception Tagging with Modules module SWS::Error; end # ... def

    upload_selleck_to_waterfall # ... rescue HTTPError => e e.extend(SWS::Error) raise end
  20. Exception Tagging with Modules # Client code interested in HTTP

    errors def foo upload_selleck_to_waterfall rescue HTTPError => e log_http_error(e) end
  21. Exception Tagging with Modules # Client code concerned with SWS

    errors def foo upload_selleck_to_waterfall rescue SWS::Error # ignore SWS errors end
  22. No-raise API request.on_complete do |response| if response.success? # hell yeah

    elsif response.timed_out? # aw hell no log( "got a time out" ) elsif response.code == 0 # Could not get an http response log(response.curl_error_message) else # Received a non-successful http response. log( "request failed: " + response.code.to_s) end end
  23. Tiered Architecture For wrapping services / libraries Layer 1: Thinnest

    possible wrapper Typically procedural Stateless Minimal “magic” Layer 2: Object model Stateful
  24. Tiered Architecture Evolution Layer 1: Thinnest possible wrapper Layer 2:

    Glue Layer 3: Object model
  25. Your Turn