Slide 1

Slide 1 text

Dissecting Ruby with Ruby @schneems

Slide 2

Slide 2 text

@schneems

Slide 3

Slide 3 text

Schnauser

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

That man is married to Ruby, so to speak

Slide 6

Slide 6 text

Ruby Me

Slide 7

Slide 7 text

Python <3 ?

Slide 8

Slide 8 text

Hans Peter Von Wolfe (the 5th)

Slide 9

Slide 9 text

Sextant Gem

Slide 10

Slide 10 text

Wicked ‘ ‘ Gem

Slide 11

Slide 11 text

Triage Code codetriage.com

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Adjunct Professor

Slide 14

Slide 14 text

Good News Everyone! schneems.com/ut-rails

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

We optimize developer happiness

Slide 17

Slide 17 text

Git Push: Deploy

Slide 18

Slide 18 text

Ruby Task Force

Slide 19

Slide 19 text

Ruby Task Force Member

Slide 20

Slide 20 text

Co-worker

Slide 21

Slide 21 text

Eat Naan Talk Ruby

Slide 22

Slide 22 text

Eat Naan Talk Ruby

Slide 23

Slide 23 text

Warning: I have Naan Jokes

Slide 24

Slide 24 text

Some of them fall a bit “flat”

Slide 25

Slide 25 text

Close your Laptops

Slide 26

Slide 26 text

Unless you’re commenting on rails/rails issues

Slide 27

Slide 27 text

We’ve all been there

Slide 28

Slide 28 text

That comment is no longer valid “

Slide 29

Slide 29 text

I have no Idea how it works, it just does. “

Slide 30

Slide 30 text

Docs? Just look at the source code “

Slide 31

Slide 31 text

Get in Get Info Get Around

Slide 32

Slide 32 text

Get in

Slide 33

Slide 33 text

$ bundle open wicked

Slide 34

Slide 34 text

$ bundle open wicked

Slide 35

Slide 35 text

Set Editor

Slide 36

Slide 36 text

~/.bashrc export EDITOR="mvim"

Slide 37

Slide 37 text

~/.bashrc export EDITOR="subl -w"

Slide 38

Slide 38 text

$ echo $EDITOR subl -w

Slide 39

Slide 39 text

Get Info Out

Slide 40

Slide 40 text

Forget Fancy Debuggers

Slide 41

Slide 41 text

All you need is “puts”

Slide 42

Slide 42 text

Tracer Round

Slide 43

Slide 43 text

puts “=================” Tracer Round

Slide 44

Slide 44 text

Find the output

Slide 45

Slide 45 text

puts “=================” Tracer Round

Slide 46

Slide 46 text

puts “=================” Tracer Round Do you see it?

Slide 47

Slide 47 text

How about now?

Slide 48

Slide 48 text

A note on Notation

Slide 49

Slide 49 text

String.new Class/Module

Slide 50

Slide 50 text

String.new Method

Slide 51

Slide 51 text

String.new class method

Slide 52

Slide 52 text

Kernel#puts Not class method

Slide 53

Slide 53 text

Caution!

Slide 54

Slide 54 text

Nil

Slide 55

Slide 55 text

Nil Null

Slide 56

Slide 56 text

Nil Null Naan

Slide 57

Slide 57 text

Obi-Naan Kenobi

Slide 58

Slide 58 text

Who uses Ruby 2.0?

Slide 59

Slide 59 text

Who uses Ruby 1.9.3?

Slide 60

Slide 60 text

Who uses Ruby 1.8.7?

Slide 61

Slide 61 text

Ruby 1.8.7 is End of Life-d in days

Slide 62

Slide 62 text

try 2.0.0 or JRuby on heroku

Slide 63

Slide 63 text

Get Around

Slide 64

Slide 64 text

Let’s look at Ruby

Slide 65

Slide 65 text

Where is this method used? Problem:

Slide 66

Slide 66 text

Solution: Kernel#caller

Slide 67

Slide 67 text

www. ruby-doc .org

Slide 68

Slide 68 text

Wat?

Slide 69

Slide 69 text

Kernel#caller gives you the back trace

Slide 70

Slide 70 text

def self.order_by_issue_count puts caller.inspect self.order("issues_count DESC") end

Slide 71

Slide 71 text

[ "~/projects/triage/app/controllers/pages_controller.rb:6:in `index'", "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/implicit_render.rb:4:in "ruby/gems/actionpack-3.2.12/lib/abstract_controller/base.rb:167:in `process_act "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/rendering.rb:10:in `proc "ruby/gems/actionpack-3.2.12/lib/abstract_controller/callbacks.rb:18:in `block i "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:414:in `_run__37 "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:405:in `__run_ca "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:385:in `_run_pro "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:81:in `run_callb "ruby/gems/actionpack-3.2.12/lib/abstract_controller/callbacks.rb:17:in `process "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/rescue.rb:29:in `proces "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/instrumentation.rb:30:i "ruby/gems/activesupport-3.2.12/lib/active_support/notifications.rb:123:in `bloc "ruby/gems/activesupport-3.2.12/lib/active_support/notifications/instrumenter.rb "ruby/gems/activesupport-3.2.12/lib/active_support/notifications.rb:123:in `inst "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/instrumentation.rb:29:i File

Slide 72

Slide 72 text

[ "~/projects/triage/app/controllers/pages_controller.rb:6:in `index'", "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/implicit_render.rb:4:in "ruby/gems/actionpack-3.2.12/lib/abstract_controller/base.rb:167:in `process_act "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/rendering.rb:10:in `proc "ruby/gems/actionpack-3.2.12/lib/abstract_controller/callbacks.rb:18:in `block i "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:414:in `_run__37 "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:405:in `__run_ca "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:385:in `_run_pro "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:81:in `run_callb "ruby/gems/actionpack-3.2.12/lib/abstract_controller/callbacks.rb:17:in `process "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/rescue.rb:29:in `proces "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/instrumentation.rb:30:i "ruby/gems/activesupport-3.2.12/lib/active_support/notifications.rb:123:in `bloc "ruby/gems/activesupport-3.2.12/lib/active_support/notifications/instrumenter.rb "ruby/gems/activesupport-3.2.12/lib/active_support/notifications.rb:123:in `inst "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/instrumentation.rb:29:i Line Number

Slide 73

Slide 73 text

[ "~/projects/triage/app/controllers/pages_controller.rb:6:in `index'", "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/implicit_render.rb:4:in "ruby/gems/actionpack-3.2.12/lib/abstract_controller/base.rb:167:in `process_act "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/rendering.rb:10:in `proc "ruby/gems/actionpack-3.2.12/lib/abstract_controller/callbacks.rb:18:in `block i "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:414:in `_run__37 "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:405:in `__run_ca "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:385:in `_run_pro "ruby/gems/activesupport-3.2.12/lib/active_support/callbacks.rb:81:in `run_callb "ruby/gems/actionpack-3.2.12/lib/abstract_controller/callbacks.rb:17:in `process "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/rescue.rb:29:in `proces "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/instrumentation.rb:30:i "ruby/gems/activesupport-3.2.12/lib/active_support/notifications.rb:123:in `bloc "ruby/gems/activesupport-3.2.12/lib/active_support/notifications/instrumenter.rb "ruby/gems/activesupport-3.2.12/lib/active_support/notifications.rb:123:in `inst "ruby/gems/actionpack-3.2.12/lib/action_controller/metal/instrumentation.rb:29:i Method Name

Slide 74

Slide 74 text

Use Kernel#caller

Slide 75

Slide 75 text

Where is this method defined? Problem:

Slide 76

Slide 76 text

Introducing My favorite super secret class

Slide 77

Slide 77 text

Method

Slide 78

Slide 78 text

Object#method

Slide 79

Slide 79 text

> m = "hello world".method(:upcase) => #

Slide 80

Slide 80 text

> m = "hello world".method(:upcase) => # > m.class => Method

Slide 81

Slide 81 text

> m = "hello world".method(:upcase) => # > m.class => Method > m.call => "HELLO WORLD"

Slide 82

Slide 82 text

Where is this method defined? Problem: (recap)

Slide 83

Slide 83 text

Solution: Method#source_location

Slide 84

Slide 84 text

$ cat app/models/user.rb class User < ActiveRecord::Base has_many :repo_subscriptions, dependent: :destroy has_many :repos, :through => :repo_subscriptions scope :public, where("private is not true") alias_attribute :token, :github_access_token def self.random order("RANDOM()") end #...

Slide 85

Slide 85 text

$ rails c User.last.method(:github_url).source_location

Slide 86

Slide 86 text

$ rails c User.last.method(:github_url).source_location =>["~/projects/triage/app/models/user.rb", 93]

Slide 87

Slide 87 text

$ rails c User.last.method(:github_url).source_location =>["~/projects/triage/app/models/user.rb", 93] File

Slide 88

Slide 88 text

$ rails c User.last.method(:github_url).source_location =>["~/projects/triage/app/models/user.rb", 93] Line Number

Slide 89

Slide 89 text

Let’s see something real

Slide 90

Slide 90 text

link_to in (rails) email templates needs a host set, but fails if you include http:// Problem:

Slide 91

Slide 91 text

Fails config.action_mailer.default_url_options = { host: 'http://example.com' } http://http://example.com

Slide 92

Slide 92 text

Works config.action_mailer.default_url_options = { host: 'example.com' } http://example.com

Slide 93

Slide 93 text

let’s remove the http:// automatically, but how? Problem:

Slide 94

Slide 94 text

How does that option get used?

Slide 95

Slide 95 text

<% puts method(:link_to).source_location %>

Slide 96

Slide 96 text

<% puts method(:link_to).source_location %> [“ruby/gems/actionpack-3.2.12/lib/action_view/helpers/ url_helper.rb” 231]

Slide 97

Slide 97 text

$ bundle open $ bundle open actionpack

Slide 98

Slide 98 text

def link_to(*args, &block) if block_given? options = args.first || {} html_options = args.second link_to(capture(&block), options, html_options) else name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(options, html_options url = url_for(options) href = html_options['href'] tag_options = tag_options(html_options) href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" unless href "#{ERB::Util.html_escape(name || url)}

Slide 99

Slide 99 text

Enhance

Slide 100

Slide 100 text

name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(opt url = url_for(options) href = html_options['href'] tag_options = tag_options(html_options) href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" "#{ERB::Util.html_escap

Slide 101

Slide 101 text

name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(opt url = url_for(options) href = html_options['href'] tag_options = tag_options(html_options) href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" "#{ERB::Util.html_escap

Slide 102

Slide 102 text

name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(opt url = url_for(options) href = html_options['href'] tag_options = tag_options(html_options) href_attr = "href=\"#{ERB::Util.html_escape(url)}\"" "#{ERB::Util.html_escap

Slide 103

Slide 103 text

Which url_for?

Slide 104

Slide 104 text

No content

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

13 matches 12 Files

Slide 107

Slide 107 text

Method# source_location to the rescue

Slide 108

Slide 108 text

name = args[0] options = args[1] || {} html_options = args[2] html_options = convert_options_to_data_attributes(opt puts “==============================” puts method(:url_for).source_location url = url_for(options) href = html_options['href'] tag_options = tag_options(html_options)

Slide 109

Slide 109 text

puts “==============================” puts method(:url_for).source_location => [“actionpack-3.2.12/ lib/ action_view/helpers/url_helper.rb”, 100]

Slide 110

Slide 110 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(: super when :back controller.request.env["HTTP_REFERER"] || 'javasc else polymorphic_path(options) end end

Slide 111

Slide 111 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(: puts "===========================" puts self.class.ancestors super when :back controller.request.env["HTTP_REFERER"] || 'javasc else polymorphic_path(options) end end

Slide 112

Slide 112 text

Module#ancestors

Slide 113

Slide 113 text

“foo”.class.ancestors => [String, Comparable, Object, Kernel, BasicObject]

Slide 114

Slide 114 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(: puts "===========================" puts self.class.ancestors super when :back controller.request.env["HTTP_REFERER"] || 'javasc else polymorphic_path(options) end end

Slide 115

Slide 115 text

puts "===========================" puts self.class.ancestors [#, #, ActionMailer::MailHelper ActionDispatch::Routing::RouteSet::MountedHelpers, #, ActionDispatch::Routing: ActionView::Base, Devise::OmniAuth::UrlHelpers, Devi WillPaginate::ActionView, WillPaginate::ViewHelpers, ActionView::Helpers, ActionView::Helpers::Translatio ActionView::Helpers::RecordTagHelper, ActionControll ActionView::Helpers::OutputSafetyHelper, ActionView: ActionView::Helpers::JavaScriptHelper, ActionView::H ActionView::Helpers::FormHelper, ActionView::Helpers ActionView::Helpers::DebugHelper, ActionView::Helper ActionView::Helpers::ControllerHelper, ActionView::H ActionView::Helpers::AtomFeedHelper, ActionView::Hel

Slide 116

Slide 116 text

No content

Slide 117

Slide 117 text

Too much Ruby?

Slide 118

Slide 118 text

Solution: Moar Ruby

Slide 119

Slide 119 text

Module#method_defined?

Slide 120

Slide 120 text

"foo".class.method_defined?(:upcase) => true Module#method_defined?

Slide 121

Slide 121 text

"foo".class.method_defined?(:upcase) => true 99.class.method_defined?(:upcase) => false Module#method_defined?

Slide 122

Slide 122 text

Module#instance_method

Slide 123

Slide 123 text

User.new.method(:github_url).source_location =>["~/projects/triage/app/models/user.rb", 93] User.instance_method(:github_url).source_location =>["~/projects/triage/app/models/user.rb", 93] Module#instance_method

Slide 124

Slide 124 text

What were we doing?

Slide 125

Slide 125 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(: puts "===========================" puts self.class.ancestors super when :back controller.request.env["HTTP_REFERER"] || 'javasc else polymorphic_path(options) end end

Slide 126

Slide 126 text

Right

Slide 127

Slide 127 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(:only puts "===========================" self.class.ancestors.each do |klass| next unless klass.method_defined?(:url_for) puts klass.instance_method(:url_for).source_locatio end super when :back controller.request.env["HTTP_REFERER"] || 'javascript else polymorphic_path(options)

Slide 128

Slide 128 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(:only puts "===========================" self.class.ancestors.each do |klass| next unless klass.method_defined?(:url_for) puts klass.instance_method(:url_for).source_locatio end super when :back controller.request.env["HTTP_REFERER"] || 'javascript else polymorphic_path(options)

Slide 129

Slide 129 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(:only puts "===========================" self.class.ancestors.each do |klass| next unless klass.method_defined?(:url_for) puts klass.instance_method(:url_for).source_locatio end super when :back controller.request.env["HTTP_REFERER"] || 'javascript else polymorphic_path(options)

Slide 130

Slide 130 text

def url_for(options = {}) options ||= {} case options when String options when Hash options = options.symbolize_keys.reverse_merge!(:only puts "===========================" self.class.ancestors.each do |klass| next unless klass.method_defined?(:url_for) puts klass.instance_method(:url_for).source_locatio end super when :back controller.request.env["HTTP_REFERER"] || 'javascript else polymorphic_path(options)

Slide 131

Slide 131 text

self.class.ancestors.each do |klass| next unless klass.method_defined?(:url_for) puts klass.instance_method(:url_for).source_location end ====================== actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_dispatch/routing/url_for.rb 143

Slide 132

Slide 132 text

====================== actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_dispatch/routing/url_for.rb 143 One of these things is not like the others

Slide 133

Slide 133 text

====================== actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_dispatch/routing/url_for.rb 143 Nope

Slide 134

Slide 134 text

====================== actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_dispatch/routing/url_for.rb 143 Nope

Slide 135

Slide 135 text

====================== actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_dispatch/routing/url_for.rb 143 Nope

Slide 136

Slide 136 text

====================== actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_view/helpers/url_helper.rb 100 actionpack-3.2.12/lib/action_dispatch/routing/url_for.rb 143 Yeah!

Slide 137

Slide 137 text

Almost Done (kinda)

Slide 138

Slide 138 text

Quick Break:

Slide 139

Slide 139 text

No content

Slide 140

Slide 140 text

No content

Slide 141

Slide 141 text

Back to your regularly scheduled broadcast

Slide 142

Slide 142 text

Verify Caller def url_for(options = nil) puts "===========================" puts caller.inspect case options when String options when nil, Hash _routes.url_for((options || {}).symbolize_keys.rev else polymorphic_url(options) end end

Slide 143

Slide 143 text

It checks out ============================ [“actionpack-3.2.12/ lib/ action_view/ helpers/ url_helper.rb:112:in `url_for'", # ...

Slide 144

Slide 144 text

Follow the Source def url_for(options = nil) case options when String options when nil, Hash puts "===========================" puts _routes.method(:url_for).source_location _routes.url_for((options || {}).symbolize_keys.rev else polymorphic_url(options) end end

Slide 145

Slide 145 text

action_dispatch/routing/route_set.rb:572 def url_for(options) finalize! options = (options || {}).reverse_merge!(default_url_options) handle_positional_args(options) user, password = extract_authentication(options) path_segments = options.delete(:_path_segments) script_name = options.delete(:script_name) path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/') path_options = options.except(*RESERVED_OPTIONS) path_options = yield(path_options) if block_given? path_addition, params = generate(path_options, path_segments || {}) path << path_addition params.merge!(options[:params] || {}) ActionDispatch::Http::URL.url_for(options.merge!({ :path => path, :params => params, :user => user, :password => password })) end

Slide 146

Slide 146 text

Success!

Slide 147

Slide 147 text

Now What???

Slide 148

Slide 148 text

Reproduce the problem

Slide 149

Slide 149 text

Attempt a fix

Slide 150

Slide 150 text

Raise Awareness (open an issue)

Slide 151

Slide 151 text

rails/ rails#9794

Slide 152

Slide 152 text

No content