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

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.

Richard Schneeman

March 23, 2013
Tweet

More Decks by Richard Schneeman

Other Decks in Programming

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