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

Make an Event of It!

Make an Event of It!

Are your controllers jumbled with seemingly unrelated steps? Does testing any bit of application logic require fixtures and setup helpers a mile long?

Evented patterns create a vocabulary of what happens in your system, and a way to separate code triggering events from code that responds to them. That helps tame the sprawl by setting clean boundaries, simplifying tests, and keeping your dependencies isolated.

This talk reveals the power of events and what's already in Rails to help you.

Jason R Clark

April 22, 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 Tuesday, April 22, 14
  2. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save redirect_to @user else render 'new' end end end Tuesday, April 22, 14
  3. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save UserMailer.welcome_email(@user).deliver redirect_to @user ... end end end Tuesday, April 22, 14
  4. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save UserMailer.welcome_email(@user).deliver Analytics.notify_user_creation(@user) redirect_to @user ... end end end Tuesday, April 22, 14
  5. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save UserMailer.welcome_email(@user).deliver Analytics.notify_user_creation(@user) Crm::Gateway.new(@user.id, @user.name, ...) redirect_to @user ... end end end Tuesday, April 22, 14
  6. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save UserMailer.welcome_email(@user).deliver Analytics.notify_user_creation(@user) Crm::Gateway.new(@user.id, @user.name, ...) Resque.enqueue(AlertTheSocials, @user.id) redirect_to @user ... end end end Tuesday, April 22, 14
  7. class UsersController < ApplicationController def create @user = User.new(user_params) if

    @user.save UserMailer.welcome_email(@user).deliver Analytics.notify_user_creation(@user) Crm::Gateway.new(@user.id, @user.name, ...) Resque.enqueue(AlertTheSocials, @user.id) redirect_to @user ... end end end Tuesday, April 22, 14
  8. newrelic_rpm 27 def test_finish_setup_naming_rules config = { 'rules' => [...]

    } @agent.finish_setup(config) assert_equal 2, @agent.transaction_rules.size end Tuesday, April 22, 14
  9. 28 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 Tuesday, April 22, 14
  10. 31 class Beacon def initialize # Your code is bad

    and you should feel bad Net::HTTP.get("http://somewhere.com") end end newrelic_rpm Tuesday, April 22, 14
  11. 32 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 Tuesday, April 22, 14
  12. 36 def test_finish_setup_notifies called = false @events.subscribe(:configured) do called =

    true end @agent.finish_setup({}) assert called end newrelic_rpm Tuesday, April 22, 14
  13. 37 def test_add_naming_rules config = { 'rules' => [...] }

    @agent.add_naming_rules(config) assert_equal 2, @agent.transaction_rules.size end newrelic_rpm Tuesday, April 22, 14
  14. 47 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 Tuesday, April 22, 14
  15. 56 class SimpleEvents::Notifier ... def subscribe(event, &handler) @events[event] ||= []

    @events[event] << handler check_for_runaway_subscriptions(event) end ... end simple_events Tuesday, April 22, 14
  16. 57 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 Tuesday, April 22, 14
  17. 58 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 Tuesday, April 22, 14
  18. 59 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 Tuesday, April 22, 14
  19. 60 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 Tuesday, April 22, 14
  20. 61 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 Tuesday, April 22, 14
  21. Typical Rails Request 64 • action_dispatch.request • start_processing.action_controller • process_action.action_controller

    • sql.active_record • render_template.action_view • ... and more! Tuesday, April 22, 14
  22. 65 events = [] AS::Notifications.subscribe("event") do |*args| events << *args

    end ["render_template.action_view", "e2a1e92a3c613576c2b0" {:identifier=>"/file/path/here"}] activesupport Tuesday, April 22, 14
  23. 66 e = AS::Notifications::Event.new(*args) e.name # => "render_template..." e.duration #

    => 10 (in milliseconds) e.payload # => { :identifier => ... } activesupport Tuesday, April 22, 14
  24. • Regexp on Notifications.subscribe • Event#parent_of? • Temporary subscription •

    Unsubscribe • LogSubscriber 69 activesupport Tuesday, April 22, 14
  25. 78 class UserSignup < Mutations::Command def execute user = User.create!(inputs)

    NewsletterSubscriptions.create(user.id) UserMailer.async(:deliver_welcome, user.id) user end end Tuesday, April 22, 14