Slide 1

Slide 1 text

Jason Clark @jasonrclark Ruby Agent Engineer Make an Event of It! Evented Patterns in Ruby Tuesday, April 22, 14

Slide 2

Slide 2 text

Storytime! Tuesday, April 22, 14

Slide 3

Slide 3 text

Tuesday, April 22, 14

Slide 4

Slide 4 text

Tuesday, April 22, 14

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

https://flic.kr/p/6qdTC1 Tuesday, April 22, 14

Slide 12

Slide 12 text

Where Are We Going? The Pattern Coupling Mechanics Responsibilities 12 Tuesday, April 22, 14

Slide 13

Slide 13 text

The Pattern http://flic.kr/p/6K9jC4 Tuesday, April 22, 14

Slide 14

Slide 14 text

14 Tuesday, April 22, 14

Slide 15

Slide 15 text

15 Tuesday, April 22, 14

Slide 16

Slide 16 text

15 Tuesday, April 22, 14

Slide 17

Slide 17 text

15 Tuesday, April 22, 14

Slide 18

Slide 18 text

Events Aren't (Necessarily) 16 Asynchronous IO related Distributed Complicated Tuesday, April 22, 14

Slide 19

Slide 19 text

17 Tuesday, April 22, 14

Slide 20

Slide 20 text

17 Notifier Tuesday, April 22, 14

Slide 21

Slide 21 text

17 Notifier Subscriber Subscriber Subscriber Tuesday, April 22, 14

Slide 22

Slide 22 text

17 Notifier Subscriber Subscriber Subscriber Eventing System Tuesday, April 22, 14

Slide 23

Slide 23 text

17 Notifier Subscriber Subscriber Subscriber Eventing System Tuesday, April 22, 14

Slide 24

Slide 24 text

17 Notifier Subscriber Subscriber Subscriber Eventing System Tuesday, April 22, 14

Slide 25

Slide 25 text

18 Why Not Methods? Tuesday, April 22, 14

Slide 26

Slide 26 text

19 Notifier Subscriber Subscriber Subscriber Eventing System Tuesday, April 22, 14

Slide 27

Slide 27 text

Why Not Callbacks? 20 Tuesday, April 22, 14

Slide 28

Slide 28 text

21 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup self.signed_up_on = Date.today end end Tuesday, April 22, 14

Slide 29

Slide 29 text

21 class Subscription < ActiveRecord::Base before_create :record_signup private def record_signup self.signed_up_on = Date.today end end Tuesday, April 22, 14

Slide 30

Slide 30 text

Where Are We Going? The Pattern Coupling Mechanics Responsibilities 22 Tuesday, April 22, 14

Slide 31

Slide 31 text

23 Internal Coupling http://flic.kr/p/KikA9 Tuesday, April 22, 14

Slide 32

Slide 32 text

newrelic_rpm 24 Tuesday, April 22, 14

Slide 33

Slide 33 text

newrelic_rpm 24 app start Tuesday, April 22, 14

Slide 34

Slide 34 text

newrelic_rpm 24 app start connect() New Relic Server Tuesday, April 22, 14

Slide 35

Slide 35 text

newrelic_rpm {server config} 24 app start connect() New Relic Server Tuesday, April 22, 14

Slide 36

Slide 36 text

newrelic_rpm {server config} 24 app start connect() New Relic Server ? Tuesday, April 22, 14

Slide 37

Slide 37 text

newrelic_rpm 25 def connect() config = config_from_server() finish_setup(config) end Tuesday, April 22, 14

Slide 38

Slide 38 text

newrelic_rpm 26 def finish_setup(config) add_naming_rule(config['rules']) @cross_app_tracing.set_key(config) @js_instrumentor.log_config(config) @beacon = Beacon.new end Tuesday, April 22, 14

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

29 def finish_setup(config) add_naming_rule(config['rules']) @cross_app_tracing.set_key(config) @js_instrumentor.log_config(config) @beacon = Beacon.new end newrelic_rpm Tuesday, April 22, 14

Slide 42

Slide 42 text

30 def finish_setup(config) add_naming_rule(config['rules']) @cross_app_tracing.set_key(config) @js_instrumentor.log_config(config) @beacon = Beacon.new end newrelic_rpm Tuesday, April 22, 14

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

33 def finish_setup(config) add_naming_rule(config['rules']) @cross_app_tracing.set_key(config) @js_instrumentor.log_config(config) @beacon = Beacon.new end newrelic_rpm Tuesday, April 22, 14

Slide 46

Slide 46 text

34 def finish_setup(config) events.notify(:configured) end newrelic_rpm Tuesday, April 22, 14

Slide 47

Slide 47 text

35 class Beacon def initialize events.subscribe(:configured) do Net::HTTP.get("http://...") end end end newrelic_rpm Tuesday, April 22, 14

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

More Focused Tests 38 Tuesday, April 22, 14

Slide 51

Slide 51 text

More Independent Classes 39 Tuesday, April 22, 14

Slide 52

Slide 52 text

Domain Language 40 Tuesday, April 22, 14

Slide 53

Slide 53 text

Implicit Ordering 41 Tuesday, April 22, 14

Slide 54

Slide 54 text

Debugging 42 Tuesday, April 22, 14

Slide 55

Slide 55 text

Performance 43 Tuesday, April 22, 14

Slide 56

Slide 56 text

http://flic.kr/p/euQpT External Coupling Tuesday, April 22, 14

Slide 57

Slide 57 text

45 newrelic_rpm Tuesday, April 22, 14

Slide 58

Slide 58 text

45 Javascript insertion newrelic_rpm Tuesday, April 22, 14

Slide 59

Slide 59 text

45 Javascript insertion Error collection newrelic_rpm Tuesday, April 22, 14

Slide 60

Slide 60 text

45 Javascript insertion Cross application tracing Error collection newrelic_rpm Tuesday, April 22, 14

Slide 61

Slide 61 text

46 No Setup Please! newrelic_rpm Tuesday, April 22, 14

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

48 class CrossAppTracing def initialize events.subscribe(:before_call) do |e| process_request(e) end ... end end newrelic_rpm Tuesday, April 22, 14

Slide 64

Slide 64 text

Ever Heard of Composition? 49 Tuesday, April 22, 14

Slide 65

Slide 65 text

Looser Coupling 50 Tuesday, April 22, 14

Slide 66

Slide 66 text

Compatibilty Layer 51 Tuesday, April 22, 14

Slide 67

Slide 67 text

Where Are We Going? The Pattern Coupling Mechanics Responsibilities 52 Tuesday, April 22, 14

Slide 68

Slide 68 text

http://flic.kr/p/dYor95 Adding Eventing Tuesday, April 22, 14

Slide 69

Slide 69 text

54 gem install simple_events http://github.com/jasonrclark/simple_events Tuesday, April 22, 14

Slide 70

Slide 70 text

55 class SimpleEvents::Notifier def initialize @events = {} ... end end simple_events Tuesday, April 22, 14

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

ActiveSupport:: Notifications http://flic.kr/p/aWNwU2 Tuesday, April 22, 14

Slide 78

Slide 78 text

63 AS::Notifications = ActiveSupport::Notifications activesupport Tuesday, April 22, 14

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

66 e = AS::Notifications::Event.new(*args) e.name # => "render_template..." e.duration # => 10 (in milliseconds) e.payload # => { :identifier => ... } activesupport Tuesday, April 22, 14

Slide 82

Slide 82 text

67 AS::Notifications.instrument("evt", :data => 1) activesupport Tuesday, April 22, 14

Slide 83

Slide 83 text

68 AS::Notifications.instrument("evt", :data => 1) do # your timed code here end activesupport Tuesday, April 22, 14

Slide 84

Slide 84 text

• Regexp on Notifications.subscribe • Event#parent_of? • Temporary subscription • Unsubscribe • LogSubscriber 69 activesupport Tuesday, April 22, 14

Slide 85

Slide 85 text

Where Are We Going? The Pattern Coupling Mechanics Responsibilities 70 Tuesday, April 22, 14

Slide 86

Slide 86 text

http://flic.kr/p/a1dEec Notifiers Tuesday, April 22, 14

Slide 87

Slide 87 text

Naming 72 Tuesday, April 22, 14

Slide 88

Slide 88 text

Flexible Payload 73 Tuesday, April 22, 14

Slide 89

Slide 89 text

Primitives at Boundaries 74 Tuesday, April 22, 14

Slide 90

Slide 90 text

75 http://flic.kr/p/8w9PyF Subscribers Tuesday, April 22, 14

Slide 91

Slide 91 text

Who subscribes? 76 Tuesday, April 22, 14

Slide 92

Slide 92 text

77 Tuesday, April 22, 14

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

79 Nesting Tuesday, April 22, 14

Slide 95

Slide 95 text

Synchronous 80 Tuesday, April 22, 14

Slide 96

Slide 96 text

81 class SomeSubscriber def initialize events.subscribe(:configured) do log_config end end def log_config ... end end Leaks Tuesday, April 22, 14

Slide 97

Slide 97 text

Where Are We Going? The Pattern Coupling Mechanics Responsibilities 82 Tuesday, April 22, 14

Slide 98

Slide 98 text

The Pattern Coupling Mechanics Responsibilities ? Jason Clark @jasonrclark Ruby Agent Engineer Tuesday, April 22, 14