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

Make an Event of It! (CodeMash 2014)

Make an Event of It! (CodeMash 2014)

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.

Jason R Clark

January 09, 2014
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 Thursday, January 9, 14
  2. Where Are We Going? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 2 Thursday, January 9, 14
  3. Events Aren't (Necessarily) 6 • Asynchronous • IO related •

    Distributed • Complicated Thursday, January 9, 14
  4. 11 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup

    self.signed_up_on = Date.today end end Thursday, January 9, 14
  5. 11 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup

    self.signed_up_on = Date.today end end Thursday, January 9, 14
  6. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 12 Thursday, January 9, 14
  7. {server config} 14 app start connect New Relic Server ?

    newrelic_rpm Thursday, January 9, 14
  8. 17 def test_finish_setup_naming_rules config = { 'rules' => [...] }

    @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Thursday, January 9, 14
  9. 18 def test_finish_setup_naming_rules @agent.cross_app_tracing.stub(:set_key) @agent.js_instrumentor.stub(:log_config) config = { 'rules' =>

    [...] } @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Thursday, January 9, 14
  10. 20 class Beacon def initialize # Your code is bad

    and you should feel bad Net::HTTP.get("http://...") end end newrelic_rpm Thursday, January 9, 14
  11. 21 def test_finish_setup_naming_rules @agent.cross_app_tracing.stub(:set_key) @agent.js_instrumentor.stub(:log_config) Beacon.stub(:new).return(stub("fake beacon")) config = {

    'rules' => [...] } @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Thursday, January 9, 14
  12. 25 def test_finish_setup_notifies called = false @events.subscribe(:configured) do called =

    true end @agent.finish_setup({}) assert called end newrelic_rpm Thursday, January 9, 14
  13. 26 def test_add_naming_rules config = { 'rules' => [...] }

    @agent.add_naming_rules(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Thursday, January 9, 14
  14. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 32 Thursday, January 9, 14
  15. 36 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 newrelic_rpm Thursday, January 9, 14
  16. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 41 Thursday, January 9, 14
  17. 46 class SimpleEvents::Notifier ... def subscribe(event, &handler) @events[event] ||= []

    @events[event] << handler check_for_runaway_subscriptions(event) end ... end simple_events Thursday, January 9, 14
  18. 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 simple_events Thursday, January 9, 14
  19. 48 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 simple_events Thursday, January 9, 14
  20. 49 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 simple_events Thursday, January 9, 14
  21. 50 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 simple_events Thursday, January 9, 14
  22. 51 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 simple_events Thursday, January 9, 14
  23. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 52 Thursday, January 9, 14
  24. 58 e = AS::Notifications::Event.new(*args) e.name # => "render_template..." e.duration #

    => 10 (in milliseconds) e.payload # => { :identifier => ... } activesupport Thursday, January 9, 14
  25. • Regexp on Notifications.subscribe • Event#parent_of? • Temporary subscription •

    Unsubscribe • LogSubscriber 61 activesupport Thursday, January 9, 14
  26. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Events • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 62 Thursday, January 9, 14
  27. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Events • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 68 Thursday, January 9, 14
  28. 73 class UserSignup < Mutations::Command def execute user = User.create!(inputs)

    NewsletterSubscriptions.create(user.id) UserMailer.async(:deliver_welcome, user.id) user end end Thursday, January 9, 14
  29. Where Have We Been? • The Pattern • Internal Coupling

    • External Coupling • Adding Events • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data 76 Thursday, January 9, 14
  30. 78 Opened Account $100 Check 0001 -$15 Deposit $20 Check

    0002 -$21 Total $84 Bank Transactions Thursday, January 9, 14
  31. 79 customer_id date plan 1 1/1/2013 basic 1 1/2/2013 premium

    1 2/1/2013 basic 1 2/2/2013 premium Customer Pricing Thursday, January 9, 14
  32. 79 customer_id date plan 1 1/1/2013 basic 1 1/2/2013 premium

    1 2/1/2013 basic 1 2/2/2013 premium Customer Pricing Thursday, January 9, 14
  33. Other Examples 88 • Database replication • Log files •

    PubSub • Immutable data structures Thursday, January 9, 14
  34. Jason Clark @jasonrclark Ruby Agent Engineer • The Pattern •

    Internal Coupling • External Coupling • Adding Eventing • ActiveSupport::Notifications • Notifiers • Subscribers • Events as Data ? Thursday, January 9, 14