Dissecting Ruby with Ruby

Dissecting Ruby with Ruby

Dig into massive libraries with ease when you learn how to dissect ruby with ruby.

India Ruby Conf Edition.

Db953d125f5cc49756edb6149f1b813e?s=128

Richard Schneeman

March 23, 2013
Tweet

Transcript

  1. Dissecting Ruby with Ruby @schneems

  2. @schneems

  3. Schnauser

  4. None
  5. That man is married to Ruby, so to speak

  6. Ruby Me

  7. Python <3 ?

  8. Hans Peter Von Wolfe (the 5th)

  9. Sextant Gem

  10. Wicked ‘ ‘ Gem

  11. Triage Code codetriage.com

  12. None
  13. Adjunct Professor

  14. Good News Everyone! schneems.com/ut-rails

  15. None
  16. We optimize developer happiness

  17. Git Push: Deploy

  18. Ruby Task Force

  19. Ruby Task Force Member

  20. Co-worker

  21. Eat Naan Talk Ruby

  22. Eat Naan Talk Ruby

  23. Warning: I have Naan Jokes

  24. Some of them fall a bit “flat”

  25. Close your Laptops

  26. Unless you’re commenting on rails/rails issues

  27. We’ve all been there

  28. That comment is no longer valid “

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

  30. Docs? Just look at the source code “

  31. Get in Get Info Get Around

  32. Get in

  33. $ bundle open wicked

  34. $ bundle open wicked

  35. Set Editor

  36. ~/.bashrc export EDITOR="mvim"

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

  38. $ echo $EDITOR subl -w

  39. Get Info Out

  40. Forget Fancy Debuggers

  41. All you need is “puts”

  42. Tracer Round

  43. puts “=================” Tracer Round

  44. Find the output

  45. puts “=================” Tracer Round

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

  47. How about now?

  48. A note on Notation

  49. String.new Class/Module

  50. String.new Method

  51. String.new class method

  52. Kernel#puts Not class method

  53. Caution!

  54. Nil

  55. Nil Null

  56. Nil Null Naan

  57. Obi-Naan Kenobi

  58. Who uses Ruby 2.0?

  59. Who uses Ruby 1.9.3?

  60. Who uses Ruby 1.8.7?

  61. Ruby 1.8.7 is End of Life-d in days

  62. try 2.0.0 or JRuby on heroku

  63. Get Around

  64. Let’s look at Ruby

  65. Where is this method used? Problem:

  66. Solution: Kernel#caller

  67. www. ruby-doc .org

  68. Wat?

  69. Kernel#caller gives you the back trace

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

  71. [ "~/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
  72. [ "~/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
  73. [ "~/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
  74. Use Kernel#caller

  75. Where is this method defined? Problem:

  76. Introducing My favorite super secret class

  77. Method

  78. Object#method

  79. > m = "hello world".method(:upcase) => #<Method: String#upcase>

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

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

    => Method > m.call => "HELLO WORLD"
  82. Where is this method defined? Problem: (recap)

  83. Solution: Method#source_location

  84. $ 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 #...
  85. $ rails c User.last.method(:github_url).source_location

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

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

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

  89. Let’s see something real

  90. link_to in (rails) email templates needs a host set, but

    fails if you include http:// Problem:
  91. Fails config.action_mailer.default_url_options = { host: 'http://example.com' } http://http://example.com

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

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

  94. How does that option get used?

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

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

  97. $ bundle open $ bundle open actionpack

  98. 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 "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escape(name || url)}</a end end {
  99. Enhance

  100. 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)}\"" "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escap
  101. 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)}\"" "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escap
  102. 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)}\"" "<a #{href_attr}#{tag_options}>#{ERB::Util.html_escap
  103. Which url_for?

  104. None
  105. None
  106. 13 matches 12 Files

  107. Method# source_location to the rescue

  108. 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)
  109. puts “==============================” puts method(:url_for).source_location => [“actionpack-3.2.12/ lib/ action_view/helpers/url_helper.rb”, 100]

  110. 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
  111. 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
  112. Module#ancestors

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

  114. 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
  115. puts "===========================" puts self.class.ancestors [#<Class:0x007f8d0bc26ad8>, #<Module:0x007f8d0d65aaa8 #<Module:0x007f8d0a33d4e0>, ActionMailer::MailHelper ActionDispatch::Routing::RouteSet::MountedHelpers, #<Module:0x007f8d0d6bcb90>,

    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
  116. None
  117. Too much Ruby?

  118. Solution: Moar Ruby

  119. Module#method_defined?

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

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

  122. Module#instance_method

  123. 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

  124. What were we doing?

  125. 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
  126. Right

  127. 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)
  128. 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)
  129. 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)
  130. 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)
  131. 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
  132. ====================== 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
  133. ====================== 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

  134. ====================== 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

  135. ====================== 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

  136. ====================== 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!

  137. Almost Done (kinda)

  138. Quick Break:

  139. None
  140. None
  141. Back to your regularly scheduled broadcast

  142. 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
  143. It checks out ============================ [“actionpack-3.2.12/ lib/ action_view/ helpers/ url_helper.rb:112:in `url_for'",

    # ...
  144. 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
  145. 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
  146. Success!

  147. Now What???

  148. Reproduce the problem

  149. Attempt a fix

  150. Raise Awareness (open an issue)

  151. rails/ rails#9794

  152. None