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

Extending Gems - Patterns and Anti-Patterns of Making Your Gem Pluggable

Jason R Clark
November 08, 2013

Extending Gems - Patterns and Anti-Patterns of Making Your Gem Pluggable

One strength of the Ruby community is the simplicity of sharing code via gems. When a gem is popular enough, it can even develop an ecosystem of additional gems that build on it.

But extending a gem that wasn't built with that flexibility in mind isn't always easy. This talk highlights tips and techniques for making your gem simpler to plug into, and avoid mistakes that will have devs cursing under their breath.

We'll cover the highs and lows of interacting with others gems, from configuration to documentation and everywhere in between.

http://rubyconf13.multifaceted.io/talks/extending-gems.html

Jason R Clark

November 08, 2013
Tweet

More Decks by Jason R Clark

Other Decks in Technology

Transcript

  1. Extending Gems Patterns and Anti-Patterns of Making Your Gem Pluggable

    Jason Clark @jasonrclark Ruby Agent Engineer Monday, November 11, 13
  2. 3 newrelic_rpm module NewRelic::Agent::BrowserMonitoring def browser_monitoring_queue_time queue_time = current_transaction.queue_time millis

    = queue_time.to_f * 1000.0 clamp_to_positive(millis.round) end def footer_js_string(config) "... #{browser_monitoring_queue_time}" end end Monday, November 11, 13
  3. 4 module NewRelic::Agent::BrowserMonitoring def current_timings TransactionTimings.new end def footer_js_string(config) "...

    #{current_timings.queue_time_in_millis}" end end newrelic_rpm Monday, November 11, 13
  4. Where We're Going 11 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  5. excon 19 class SimpleInstrumentor class << self attr_accessor :events def

    instrument(name, params = {}, &blk) puts "#{name} just happened." yield if block_given? end end end Monday, November 11, 13
  6. Resque 27 class SendMessageJob def self.perform(id, text) user = User.find(id)

    user.send_message(text) end end Resque.enqueue(SendMessageJob, 42, "Yo") Monday, November 11, 13
  7. Where We've Been 29 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  8. Where We've Been 39 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  9. 42 # [status, headers, response] def call(env) # ... before

    result = @app.call(env) # ... after result end Monday, November 11, 13
  10. 43 # [status, headers, response] def call(env) # ... before

    result = @app.call(env) # ... after result end Monday, November 11, 13
  11. http://flic.kr/p/SSGmF ActionDispatch::Static Rack::Lock <ActiveSupport::Cache::Strategy> Rack::Runtime Rack::MethodOverride ActionDispatch::RequestId Rails::Rack::Logger ActionDispatch::ShowExceptions ActionDispatch::DebugExceptions

    ActionDispatch::RemoteIp ActionDispatch::Reloader ActionDispatch::Callbacks ActiveRecord::ConnectionAdapters ActiveRecord::QueryCache ActionDispatch::Cookies ActionDispatch::Session::CookieStore ActionDispatch::Flash ActionDispatch::ParamsParser Rack::Head Rack::ConditionalGet Rack::ETag AgentSnoop::Middleware RpmTestApp::Application.routes Monday, November 11, 13
  12. 46 # [status, headers, response] def call(env) # ... before

    result = @app.call(env) # ... after result end Monday, November 11, 13
  13. 47 class MyMiddleware def initialize(options=nil) # options == { :foo

    => 1 } end def call(worker, msg, queue) yield end end Monday, November 11, 13
  14. Where We've Been 49 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  15. unicorn 61 ♥ὑὑ~/myapp:unicorn_rails -c ./unicorn.rb unicorn_rails master unicorn_rails worker[0] unicorn_rails

    worker[1] unicorn_rails worker[2] unicorn_rails worker[3] Monday, November 11, 13
  16. 62 loads gems forks after_fork starts loop forks after_fork starts

    loop loads gems preload_app true preload_app false unicorn Monday, November 11, 13
  17. 63 loads gems forks after_fork starts loop forks after_fork starts

    loop loads gems preload_app true preload_app false unicorn Monday, November 11, 13
  18. Where We've Been 64 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  19. 69 ~/source/newrelic/ruby_agent/lib: ls -la drwxr-xr-x Sep 19 14:46 . drwxr-xr-x

    Oct 10 11:19 .. drwxr-xr-x Oct 10 11:20 new_relic -rw-r--r-- Sep 19 14:46 newrelic_rpm.rb drwxr-xr-x Sep 19 14:46 sequel drwxr-xr-x Oct 7 08:53 tasks Monday, November 11, 13
  20. 70 ~/source/newrelic/ruby_agent/lib: ls -la drwxr-xr-x Sep 19 14:46 . drwxr-xr-x

    Oct 10 11:19 .. drwxr-xr-x Oct 10 11:20 new_relic -rw-r--r-- Sep 19 14:46 newrelic_rpm.rb drwxr-xr-x Sep 19 14:46 sequel drwxr-xr-x Oct 7 08:53 tasks Monday, November 11, 13
  21. 78 newrelic_rpm module NewRelic module Agent class AgentLogger LOG_LEVELS =

    { "debug" => Logger::DEBUG, ... } end end end Monday, November 11, 13
  22. 79 newrelic_rpm module NewRelic module Agent class AgentLogger LOG_LEVELS =

    { "debug" => Logger::DEBUG, ... } end end end Monday, November 11, 13
  23. 80 newrelic_rpm module NewRelic module Agent class AgentLogger LOG_LEVELS =

    { "debug" => ::Logger::DEBUG, ... } end end end Monday, November 11, 13
  24. Where We've Been 81 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  25. yml + ERB 85 file = File.read("~/config/awesome_gem.yml") # Locals available

    in ERB default_awesome_key = "YEAH!" erb = ERB.new(file).result(binding) config = YAML.load(erb) Monday, November 11, 13
  26. yml + ERB 86 ~/config/awesome_gem.yml development: name: <%= default_awesome_key %>

    license_key: <%= ENV["LICENSE_KEY"] %> Monday, November 11, 13
  27. 88 unicorn worker_processes 5 preload_app true timeout 30 after_fork do

    |server, worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end Monday, November 11, 13
  28. Where We've Been 93 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  29. Where We've Been 107 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  30. Where We've Been 108 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Monday, November 11, 13
  31. Where We've Been 108 • Pass It On • Events

    • Middleware • Lifecycle • Names and Paths • Config • Docs Jason Clark @jasonrclark Ruby Agent Engineer Monday, November 11, 13