Your SlideShare is downloading. ×
  • Like
Module Magic
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Module Magic

  • 891 views
Published

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

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

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
891
On SlideShare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
10
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    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

Transcript

  • 1. MODULE MAGIC and my trip to RubyKaigi2009
  • 2. JAMES EDWARDGRAY II
  • 3. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that book
  • 4. JAMES EDWARDGRAY IICreated the Ruby Quiz and wrote that bookBuilt FasterCSV (now CSV), HighLine (with Greg), Elif, andsome other scarier experiments
  • 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. 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. 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. LSRC SPEECHES TV shows geeks should know!
  • 9. LSRC SPEECHES TV shows geeks should know!
  • 10. RUBYKAIGI2009
  • 11. RUBYKAIGI2009
  • 12. RUBYKAIGI2009See how the Japanese doconferences
  • 13. RUBYKAIGI2009See how the Japanese doconferences The translation time allows you to think more
  • 14. RUBYKAIGI2009See how the Japanese doconferences The translation time allows you to think moreMeet nice Rubyists fromJapan and other places
  • 15. RUBYKAIGI2009See how the Japanese doconferences The translation time allows you to think moreMeet nice Rubyists fromJapan and other placesSee Japan!
  • 16. MIXIN MODULES
  • 17. A TRIVIAL MIXIN I’m sure most of us know this
  • 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. 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. 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. 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. INHERITANCE p E.ancestors
  • 23. [E, D, C, B, A, Object, Kernel, BasicObject] INHERITANCE p E.ancestors
  • 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. 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. INVISIBLE CLASS class << a; p ancestors end
  • 27. [D, C, B, A, Object, Kernel, BasicObject] INVISIBLE CLASS class << a; p ancestors end
  • 28. The (invisible) “singleton class” is here[D, C, B, A, Object, Kernel, BasicObject] INVISIBLE CLASS class << a; p ancestors end
  • 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. 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. JUST A SHORTCUTWe now know what extend() rea#y is
  • 32. obj.extend(Mod) JUST A SHORTCUT We now know what extend() rea#y is
  • 33. class << objobj.extend(Mod) include Mod end JUST A SHORTCUT We now know what extend() rea#y is
  • 34. NAMESPACE MODULES
  • 35. GROUP CONSTANTSGroup constants/classes and even mix them in
  • 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. 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. DUAL INTERFACESome modules are both a namespace and a mixin
  • 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. 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. MIXIN YOURSELFBetter than Ruby’s module_function()
  • 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. 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. LIMITED MAGICSummon new error types as needed
  • 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. SOME EXAMPLESBridging the theory to implementation gap
  • 47. SOME EXAMPLESBridging the theory to implementation gap
  • 48. A BIT TOO CLEVERIf RDoc can’t read it, it’s probably too clever
  • 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. 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. LESS MAGICRDoc and I can both read it now
  • 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. WITH CLASS METHODS A classic pattern made popular by Rails
  • 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. LABELING OBJECTSYou can use a do-nothing module as a type
  • 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. OBJECT EDITING Using hooks to edit objects
  • 58. p "ruby".strip!. capitalize!# NoMethodError:# undefined method# `capitalize! for# nil:NilClass OBJECT EDITING Using hooks to edit objects
  • 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. SUMMARY
  • 61. SUMMARY
  • 62. SUMMARYMaster Ruby’s methodlookup; it’s worth the effort
  • 63. SUMMARYMaster Ruby’s methodlookup; it’s worth the effortModules are a terrific atlimiting the scope of magic
  • 64. SUMMARYMaster Ruby’s methodlookup; it’s worth the effortModules are a terrific atlimiting the scope of magicRemember, modules canmodify individual objects
  • 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. QUESTIONS?