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

Extending Gems (OS Bridge 2014)

Extending Gems (OS Bridge 2014)

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 other devs cursing under their breath.

Based on experience working on New Relic’s Ruby agent (aka the newrelic_rpm gem), we’ll cover the highs and lows of interacting with others gems, from configuration to documentation and everywhere in between.

Jason R Clark

June 26, 2014
Tweet

More Decks by Jason R Clark

Other Decks in Technology

Transcript

  1. Extending Gems Jason Clark @jasonrclark Ruby Agent Engineer Patterns and

    Anti-Patterns of Pluggable Gems Thursday, June 26, 14
  2. 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) "<script>...#{browser_monitoring_queue_time}" end end Thursday, June 26, 14
  3. Where We're Going • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  4. excon class SimpleInstrumentor class << self def instrument(name, params =

    {}, &blk) puts "#{name} just happened." yield if block_given? end end end Thursday, June 26, 14
  5. Resque class SendMessageJob def self.perform(id, text) user = User.find(id) user.send_message(text)

    end end Resque.enqueue(SendMessageJob, 42, "Yo") Thursday, June 26, 14
  6. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  7. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  8. # [status, headers, response] def call(env) # ... before result

    = @app.call(env) # ... after result end Thursday, June 26, 14
  9. # [status, headers, response] def call(env) # ... before result

    = @app.call(env) # ... after result end Thursday, June 26, 14
  10. 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 Thursday, June 26, 14
  11. # [status, headers, response] def call(env) # ... before result

    = @app.call(env) # ... after result end Thursday, June 26, 14
  12. class MyMiddleware def initialize(options={}) # options == { :foo =>

    1 } end def call(worker, msg, queue) yield end end Thursday, June 26, 14
  13. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  14. loads gems forks after_fork starts loop forks after_fork starts loop

    loads gems preload_app true preload_app false unicorn Thursday, June 26, 14
  15. loads gems forks after_fork starts loop forks after_fork starts loop

    loads gems preload_app true preload_app false unicorn Thursday, June 26, 14
  16. loads gems forks after_fork starts loop forks after_fork starts loop

    loads gems preload_app true preload_app false unicorn Thursday, June 26, 14
  17. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  18. ~/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 Thursday, June 26, 14
  19. ~/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 Thursday, June 26, 14
  20. newrelic_rpm module NewRelic module Agent class AgentLogger LOG_LEVELS = {

    "debug" => Logger::DEBUG, ... } end end end Thursday, June 26, 14
  21. newrelic_rpm module NewRelic module Agent class AgentLogger LOG_LEVELS = {

    "debug" => Logger::DEBUG, ... } end end end Thursday, June 26, 14
  22. newrelic_rpm module NewRelic module Agent class AgentLogger LOG_LEVELS = {

    "debug" => ::Logger::DEBUG, ... } end end end Thursday, June 26, 14
  23. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  24. yml + ERB 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) Thursday, June 26, 14
  25. unicorn worker_processes 5 preload_app true timeout 30 after_fork do |server,

    worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end Thursday, June 26, 14
  26. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  27. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  28. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Thursday, June 26, 14
  29. Where We've Been • Pass It On • Events •

    Middleware • Lifecycle • Names and Paths • Config • Docs Jason Clark @jasonrclark Ruby Agent Engineer Thursday, June 26, 14