Module Magic

1,094 views
1,045 views

Published on

I gave this speech at LSRC 2009 to explain the fundamentals of Ruby's method look-up system.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,094
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
10
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Module Magic

    1. 1. MODULE MAGIC and my trip to RubyKaigi2009
    2. 2. JAMES EDWARDGRAY II
    3. 3. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that book
    4. 4. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that bookBuilt FasterCSV (now CSV), HighLine (with Greg), Elif, andsome other scarier experiments
    5. 5. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that bookBuilt FasterCSV (now CSV), HighLine (with Greg), Elif, andsome other scarier experimentsDocumented some of Ruby
    6. 6. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that bookBuilt FasterCSV (now CSV), HighLine (with Greg), Elif, andsome other scarier experimentsDocumented some of Ruby http://blog.grayproductions.net/
    7. 7. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that bookBuilt FasterCSV (now CSV), HighLine (with Greg), Elif, andsome other scarier experimentsDocumented some of Ruby http://blog.grayproductions.net/ http://twitter.com/JEG2
    8. 8. LSRC SPEECHES TV shows geeks should know!
    9. 9. LSRC SPEECHES TV shows geeks should know!
    10. 10. RUBYKAIGI2009
    11. 11. RUBYKAIGI2009
    12. 12. RUBYKAIGI2009See how the Japanese doconferences
    13. 13. RUBYKAIGI2009See how the Japanese doconferences The translation time allows you to think more
    14. 14. RUBYKAIGI2009See how the Japanese doconferences The translation time allows you to think moreMeet nice Rubyists fromJapan and other places
    15. 15. RUBYKAIGI2009See how the Japanese doconferences The translation time allows you to think moreMeet nice Rubyists fromJapan and other placesSee Japan!
    16. 16. MIXIN MODULES
    17. 17. A TRIVIAL MIXIN I’m sure most of us know this
    18. 18. module Mixin def shared_method puts "Called!" endendclass Whatever include MixinendWhatever.new.shared_method A TRIVIAL MIXIN I’m sure most of us know this
    19. 19. module Mixin def shared_method puts "Called!" endendclass Whatever Called! include MixinendWhatever.new.shared_method A TRIVIAL MIXIN I’m sure most of us know this
    20. 20. class A def call puts "A" endend%w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super end end END_RUBYendclass E < A include B include C include D def call puts "E" super endendE.new.call
    21. 21. Eclass A def call puts "A" endend D%w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super C end end END_RUBYend Bclass E < A include B include C include D def call puts "E" A super endendE.new.call
    22. 22. INHERITANCE p E.ancestors
    23. 23. [E, D, C, B, A, Object, Kernel, BasicObject] INHERITANCE p E.ancestors
    24. 24. class A def call puts "A" endend%w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super end end END_RUBYenda = A.newa.extend(B)a.extend(C)a.extend(D)a.call
    25. 25. Dclass A def call puts "A" endend C%w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super B end end END_RUBYend Aa = A.newa.extend(B)a.extend(C)a.extend(D)a.call
    26. 26. INVISIBLE CLASS class << a; p ancestors end
    27. 27. [D, C, B, A, Object, Kernel, BasicObject] INVISIBLE CLASS class << a; p ancestors end
    28. 28. The (invisible) “singleton class” is here[D, C, B, A, Object, Kernel, BasicObject] INVISIBLE CLASS class << a; p ancestors end
    29. 29. class A def call puts "A" endend%w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super end end END_RUBYenda = A.newa.extend(B)a.extend(C)a.extend(D)class << a def call puts "Invisible" super endenda.call
    30. 30. class A def call puts "A" end Invisibleend%w[B C D].each do |name| eval <<-END_RUBY D module #{name} def call puts "#{name}" super C end end END_RUBYend Ba = A.newa.extend(B)a.extend(C)a.extend(D) Aclass << a def call puts "Invisible" super endenda.call
    31. 31. JUST A SHORTCUTWe now know what extend() rea#y is
    32. 32. obj.extend(Mod) JUST A SHORTCUT We now know what extend() rea#y is
    33. 33. class << objobj.extend(Mod) include Mod end JUST A SHORTCUT We now know what extend() rea#y is
    34. 34. NAMESPACE MODULES
    35. 35. GROUP CONSTANTSGroup constants/classes and even mix them in
    36. 36. class Logger # ... # Logging severity. module Severity DEBUG = 0 INFO = 1 WARN = 2 ERROR = 3 FATAL = 4 UNKNOWN = 5 end include Severity # ...end GROUP CONSTANTS Group constants/classes and even mix them in
    37. 37. class Logger module JSON # ... # Logging severity. class Array module Severity DEBUG = 0 # ... INFO = 1 end WARN = 2 ERROR = 3 class Object FATAL = 4 UNKNOWN = 5 # ... end end include Severity # ... # ...end end GROUP CONSTANTS Group constants/classes and even mix them in
    38. 38. DUAL INTERFACESome modules are both a namespace and a mixin
    39. 39. module MoreMath def self.dist(x1, y1, x2, y2) Math.sqrt( (x2 - x1) ** 2 + (y2 - y1) ** 2 ) endend DUAL INTERFACESome modules are both a namespace and a mixin
    40. 40. module MoreMath module MoreMath def self.dist(x1, y1, x2, y2) extend Math def self.dist( x1, y1, Math.sqrt( (x2 - x1) ** 2 + x2, y2 ) (y2 - y1) ** 2 ) sqrt( (x2 - x1) ** 2 + (y2 - y1) ** 2 ) end end endend DUAL INTERFACESome modules are both a namespace and a mixin
    41. 41. MIXIN YOURSELFBetter than Ruby’s module_function()
    42. 42. module MiniLogger extend self def logger $stdout end def log(message) logger.puts "%s: %s" % [ Time.now.strftime("%D %H:%M:%S"), message ] endendif __FILE__ == $PROGRAM_NAME MiniLogger.log "Called as a module method and " + "written to $stdout."end MIXIN YOURSELF Better than Ruby’s module_function()
    43. 43. module MiniLogger require "mini_logger" extend self def logger class Whatever $stdout include MiniLogger end def logger def log(message) @logger ||= open("whatever.log", "w") logger.puts "%s: %s" % end [ Time.now.strftime("%D %H:%M:%S"), def initialize message ] log "Called as an " + end "instance method " +end "and written to " + "a file."if __FILE__ == $PROGRAM_NAME end MiniLogger.log "Called as a module method and " + end "written to $stdout."end Whatever.new MIXIN YOURSELF Better than Ruby’s module_function()
    44. 44. LIMITED MAGICSummon new error types as needed
    45. 45. module Errors class BaseError < RuntimeError; end def self.const_missing(error_name) if error_name.to_s =~ /wErrorz/ const_set(error_name, Class.new(BaseError)) else super end endendp Errors::SaveError LIMITED MAGIC Summon new error types as needed
    46. 46. SOME EXAMPLESBridging the theory to implementation gap
    47. 47. SOME EXAMPLESBridging the theory to implementation gap
    48. 48. A BIT TOO CLEVERIf RDoc can’t read it, it’s probably too clever
    49. 49. require "ostruct"class << Config = OpenStruct.new def update_from_config_file(path = config_file) eval <<-END_UPDATE config = self #{File.read(path)} config END_UPDATE end # ...endConfig.config_file = "config.rb"Config.update_from_config_file A BIT TOO CLEVER If RDoc can’t read it, it’s probably too clever
    50. 50. require "ostruct"class << Config = OpenStruct.new def update_from_config_file(path = config_file) eval <<-END_UPDATE config = self #{File.read(path)} config.command = "ls" config config.retries = 42 END_UPDATE # ... end # ...endConfig.config_file = "config.rb"Config.update_from_config_file A BIT TOO CLEVER If RDoc can’t read it, it’s probably too clever
    51. 51. LESS MAGICRDoc and I can both read it now
    52. 52. require "ostruct"# These extra methods are mixed into the OpenStruct stored in Config.module Configured # This method loads configuration settings from a plain Ruby file. def update_from_config_file(path = config_file) eval <<-END_UPDATE config = self #{File.read(path)} config END_UPDATE end # ...end# This constant holds all global configuration, see Configured for details.Config = OpenStruct.new.extend(Configured) LESS MAGIC RDoc and I can both read it now
    53. 53. WITH CLASS METHODS A classic pattern made popular by Rails
    54. 54. module DoubleMixin module ClassMethods # ... end module InstanceMethods # ... end def self.included(receiver) receiver.extend(ClassMethods) receiver.send(:include, InstanceMethods) end endWITH CLASS METHODS A classic pattern made popular by Rails
    55. 55. LABELING OBJECTSYou can use a do-nothing module as a type
    56. 56. module DRb # ... module DRbUndumped def _dump(dummy) # :nodoc: raise TypeError, cant dump end end # ... class DRbMessage # ... def dump(obj, error=false) # :nodoc: obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped # ... end # ... end # ... endLABELING OBJECTSYou can use a do-nothing module as a type
    57. 57. OBJECT EDITING Using hooks to edit objects
    58. 58. p "ruby".strip!. capitalize!# NoMethodError:# undefined method# `capitalize! for# nil:NilClass OBJECT EDITING Using hooks to edit objects
    59. 59. module SafelyChainable def self.extended(singleton) singleton.methods.grep(/w!z/). each do |bang|p "ruby".strip!. singleton.instance_eval <<-END_RUBY def #{bang} capitalize! super self# NoMethodError: end# undefined method END_RUBY# `capitalize! for end# nil:NilClass end end p "ruby".extend(SafelyChainable). strip!.capitalize! OBJECT EDITING Using hooks to edit objects
    60. 60. SUMMARY
    61. 61. SUMMARY
    62. 62. SUMMARYMaster Ruby’s methodlookup; it’s worth the effort
    63. 63. SUMMARYMaster Ruby’s methodlookup; it’s worth the effortModules are a terrific atlimiting the scope of magic
    64. 64. SUMMARYMaster Ruby’s methodlookup; it’s worth the effortModules are a terrific atlimiting the scope of magicRemember, modules canmodify individual objects
    65. 65. SUMMARYMaster Ruby’s methodlookup; it’s worth the effortModules are a terrific atlimiting the scope of magicRemember, modules canmodify individual objects Try replacing some inheritance with extend()
    66. 66. QUESTIONS?

    ×