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

Make an Event of It! (RubyNation 2013)

Make an Event of It! (RubyNation 2013)

The upsurge in asynchronous programming has brought event-driven patterns to the forefront. But even if you aren't re-writing your app in Node.js, evented patterns can improve your application. This talk demonstrates concrete examples of how events can benefit the various layers of your application. It shows how events can pitch in the fight against fat controllers (and fat models too!) It applies eventing to simplify testing and decouple our app from external dependencies. It even reveals how eventing can help shape data to provide flexibility and auditing.

92e7389893670a1920a4fd98aec0d246?s=128

Jason R Clark

June 14, 2013
Tweet

More Decks by Jason R Clark

Other Decks in Technology

Transcript

  1. Jason Clark @jasonrclark Ruby Agent Engineer Make an Event of

    It! Evented Patterns in Ruby Friday, June 14, 13
  2. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events as Data 2 Friday, June 14, 13
  3. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events asEvents as Data Data 3 Friday, June 14, 13
  4. The Pattern 4 Friday, June 14, 13

  5. 5 Friday, June 14, 13

  6. 6 Friday, June 14, 13

  7. 6 Friday, June 14, 13

  8. 6 Friday, June 14, 13

  9. Events Aren't (Necessarily) 7 • Asynchronous • IO related •

    Distributed • Complicated Friday, June 14, 13
  10. 8 Friday, June 14, 13

  11. 8 Notifier Friday, June 14, 13

  12. 8 Notifier Subscriber Subscriber Subscriber Friday, June 14, 13

  13. 8 Notifier Subscriber Subscriber Subscriber Eventing System Friday, June 14,

    13
  14. 8 Notifier Subscriber Subscriber Subscriber Eventing System Friday, June 14,

    13
  15. 8 Notifier Subscriber Subscriber Subscriber Eventing System Friday, June 14,

    13
  16. 9 Why Not Methods? Friday, June 14, 13

  17. 10 Friday, June 14, 13

  18. 10 Notifier Subscriber Subscriber Subscriber Eventing System Friday, June 14,

    13
  19. 10 Notifier Subscriber Subscriber Subscriber Eventing System Friday, June 14,

    13
  20. Why Not Callbacks? 11 Friday, June 14, 13

  21. 12 Friday, June 14, 13

  22. 12 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup

    self.signed_up_on = Date.today end end Friday, June 14, 13
  23. 12 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup

    self.signed_up_on = Date.today end end Friday, June 14, 13
  24. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events as Data 13 Friday, June 14, 13
  25. 14 A Tale of Two Configurations gem install newrelic_rpm Friday,

    June 14, 13
  26. 15 Friday, June 14, 13

  27. 15 app start Friday, June 14, 13

  28. 15 app start connect New Relic Server Friday, June 14,

    13
  29. {server config} 15 app start connect New Relic Server Friday,

    June 14, 13
  30. {server config} 15 app start connect New Relic Server ?

    Friday, June 14, 13
  31. 16 def connect() # ... setup connection config = config_from_server()

    finish_setup(config) end Friday, June 14, 13
  32. 17 def finish_setup(config) # ... configure the agent add_naming_rule(config['txn_names']) @beacon

    = Beacon.new end Friday, June 14, 13
  33. 18 def test_finish_setup_naming_rules @agent.naming_rules = RulesEngine.new config = { 'rules'

    => [...] } finish_setup(config) rules = @agent.transaction_rules assert_equal 2, rules.size end Friday, June 14, 13
  34. 19 class Beacon def initialize Net::HTTP.get("http://...") end end Friday, June

    14, 13
  35. 20 def finish_setup(config) # ... configure the agent add_naming_rule(config['txn_names']) @beacon

    = Beacon.new end Friday, June 14, 13
  36. 21 def finish_setup(config) # ... configure the agent events.notify(:configured) end

    Friday, June 14, 13
  37. 22 class Beacon def initialize events.subscribe(:configured) do Net::HTTP.get("http://...") end end

    end Friday, June 14, 13
  38. 23 Friday, June 14, 13

  39. 23 def test_finish_setup called = false @events.subscribe(:configured) do called =

    true end finish_setup({}) assert called end Friday, June 14, 13
  40. + More Focused Tests 24 Friday, June 14, 13

  41. + More Independent Classes 25 Friday, June 14, 13

  42. - Implicit Ordering 26 Friday, June 14, 13

  43. - Debugging 27 Friday, June 14, 13

  44. - Performance 28 Friday, June 14, 13

  45. What Happens in Your App? 29 Friday, June 14, 13

  46. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events as Data 30 Friday, June 14, 13
  47. 31 #call me maybe gem install newrelic_rpm Friday, June 14,

    13
  48. 32 Friday, June 14, 13

  49. 32 Javascript insertion Friday, June 14, 13

  50. 32 Javascript insertion Error collection Friday, June 14, 13

  51. 32 Javascript insertion Cross application tracing Error collection Friday, June

    14, 13
  52. 33 No Setup Please! Friday, June 14, 13

  53. 34 module NewRelic::Rack class AgentHooks def call(env) notify(:before_call, env) result

    = @app.call(env) notify(:after_call, env, result) result end end end Friday, June 14, 13
  54. 35 class CrossAppMonitor def initialize events.subscribe(:before_call) do |e| process_request(e) end

    ... end end Friday, June 14, 13
  55. Ever Heard of Composition? 36 Friday, June 14, 13

  56. + Looser Coupling 37 Friday, June 14, 13

  57. - Compatibilty Layer 38 Friday, June 14, 13

  58. What Do You Depend On? 39 Friday, June 14, 13

  59. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events as Data 40 Friday, June 14, 13
  60. 41 How Do I Event? gem install simple_events Friday, June

    14, 13
  61. 42 class SimpleEvents::Notifier def initialize @events = {} ... end

    end Friday, June 14, 13
  62. 43 class SimpleEvents::Notifier ... def subscribe(event, &handler) @events[event] ||= []

    @events[event] << handler check_for_runaway_subscriptions(event) end ... end Friday, June 14, 13
  63. 44 class SimpleEvents::Notifier ... def notify(event, *args) return unless @events.has_key?(event)

    @events[event].each do |handler| begin handler.call(*args) rescue => err logger.debug("Fail #{@event}", err) end end end end Friday, June 14, 13
  64. 45 class SimpleEvents::Notifier ... def notify(event, *args) return unless @events.has_key?(event)

    @events[event].each do |handler| begin handler.call(*args) rescue => err logger.debug("Fail #{@event}", err) end end end end Friday, June 14, 13
  65. 46 class SimpleEvents::Notifier ... def notify(event, *args) return unless @events.has_key?(event)

    @events[event].each do |handler| begin handler.call(*args) rescue => err logger.debug("Fail #{@event}", err) end end end end Friday, June 14, 13
  66. 47 class SimpleEvents::Notifier ... def notify(event, *args) return unless @events.has_key?(event)

    @events[event].each do |handler| begin handler.call(*args) rescue => err logger.debug("Fail #{@event}", err) end end end end Friday, June 14, 13
  67. 48 It's In the Box Already! gem install rails Friday,

    June 14, 13
  68. 49 AS::Notifications = ActiveSupport::Notifications Friday, June 14, 13

  69. Rails Request 50 •action_dispatch.request •start_processing.action_controller •process_action.action_controller •sql.active_record •render_template.action_view • ...

    and more! Friday, June 14, 13
  70. Subscribing 51 events = [] AS::Notifications.subscribe("event name") \ do |*args|

    events << *args end Friday, June 14, 13
  71. Subscribing 52 events = [] AS::Notifications.subscribe("event") \ do |*args| events

    << *args end ["render_template.action_view", "e2a1e92a3c613576c2b0" {:identifier=>"/file/path/here"}] Friday, June 14, 13
  72. Easier Access 53 e = AS::Notifications::Event.new(*args) e.name # => "render_template..."

    e.duration # => 10 (in milliseconds) e.payload # => { :identifier => ... } Friday, June 14, 13
  73. Notifying 54 AS::Notifications.instrument( "event", :my => :data) Friday, June 14,

    13
  74. Notifying 55 AS::Notifications.instrument( "event", :my => :data) do ... your

    timed code here end Friday, June 14, 13
  75. Other Functionality • Regexp on Notifications.subscribe • Event#parent_of? • Temporary

    subscription/Unsubscribe • LogSubscriber 56 Friday, June 14, 13
  76. What Could You Notify On? 57 Friday, June 14, 13

  77. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events as Data 58 Friday, June 14, 13
  78. 59 Save the Stream! Friday, June 14, 13

  79. 60 Opened Account $100 Check 0001 -$15 Deposit $20 Check

    0002 -$21 Total $84 Bank Transactions Friday, June 14, 13
  80. Customer Pricing 61 Friday, June 14, 13

  81. 62 Friday, June 14, 13

  82. 62 customer_id date plan 1 1/1/2013 basic 1 1/2/2013 premium

    1 2/1/2013 basic 1 2/2/2013 premium Friday, June 14, 13
  83. 62 customer_id date plan 1 1/1/2013 basic 1 1/2/2013 premium

    1 2/1/2013 basic 1 2/2/2013 premium Friday, June 14, 13
  84. Metric Data 63 Friday, June 14, 13

  85. 64 Metric Data 10:00AM CPU/Utilization 0.85 Friday, June 14, 13

  86. 65 Metric Data 10:01AM CPU/Utilization 0.85 Friday, June 14, 13

  87. 66 Metric Data Friday, June 14, 13

  88. 67 Metric Data Friday, June 14, 13

  89. 68 Metric Data Friday, June 14, 13

  90. 69 Metric Data Friday, June 14, 13

  91. 69 Metric Data Friday, June 14, 13

  92. 69 Metric Data Friday, June 14, 13

  93. Other Examples 70 • Database replication • Log files •

    PubSub • Immutable data structures Friday, June 14, 13
  94. Upsides 71 ★ No Updates ★ Automatic Audits ★ Can

    Summarize/Transform/Cache Friday, June 14, 13
  95. Downsides - So Much Data! - Hard to Query Efficiently

    - More Complex Code 72 Friday, June 14, 13
  96. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Mechanics • Events as Data 73 Friday, June 14, 13
  97. 74 ? http://github.com/jasonrclark/simple_events Friday, June 14, 13