SlideShare a Scribd company logo
MODULE MAGIC
 and my trip to RubyKaigi2009
JAMES EDWARD
GRAY II
JAMES EDWARD
GRAY II
Created the Ruby Quiz and wrote that book
JAMES EDWARD
GRAY II
Created the Ruby Quiz and wrote that book

Built FasterCSV (now CSV), HighLine (with Greg), Elif, and
some other scarier experiments
JAMES EDWARD
GRAY II
Created the Ruby Quiz and wrote that book

Built FasterCSV (now CSV), HighLine (with Greg), Elif, and
some other scarier experiments

Documented some of Ruby
JAMES EDWARD
GRAY II
Created the Ruby Quiz and wrote that book

Built FasterCSV (now CSV), HighLine (with Greg), Elif, and
some other scarier experiments

Documented some of Ruby

  http://blog.grayproductions.net/
JAMES EDWARD
GRAY II
Created the Ruby Quiz and wrote that book

Built FasterCSV (now CSV), HighLine (with Greg), Elif, and
some other scarier experiments

Documented some of Ruby

  http://blog.grayproductions.net/

  http://twitter.com/JEG2
LSRC SPEECHES
 TV shows geeks should know!
LSRC SPEECHES
 TV shows geeks should know!
RUBYKAIGI2009
RUBYKAIGI2009
RUBYKAIGI2009

See how the Japanese do
conferences
RUBYKAIGI2009

See how the Japanese do
conferences

  The translation time
  allows you to think more
RUBYKAIGI2009

See how the Japanese do
conferences

  The translation time
  allows you to think more

Meet nice Rubyists from
Japan and other places
RUBYKAIGI2009

See how the Japanese do
conferences

  The translation time
  allows you to think more

Meet nice Rubyists from
Japan and other places

See Japan!
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
MIXIN MODULES
A TRIVIAL MIXIN
 I’m sure most of us know this
module Mixin
  def shared_method
    puts "Called!"
  end
end

class Whatever
  include Mixin
end

Whatever.new.shared_method




   A TRIVIAL MIXIN
        I’m sure most of us know this
module Mixin
  def shared_method
    puts "Called!"
  end
end

class Whatever
                              Called!
  include Mixin
end

Whatever.new.shared_method




   A TRIVIAL MIXIN
        I’m sure most of us know this
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
class A
  def call
    puts "A"
  end
end

%w[B C D].each do |name|
  eval <<-END_RUBY
  module #{name}
    def call
      puts "#{name}"
      super
    end
  end
  END_RUBY
end

class E < A
  include B
  include C
  include D
  def call
    puts "E"
    super
  end
end

E.new.call
E
class A
  def call
    puts "A"
  end
end




                           D
%w[B C D].each do |name|
  eval <<-END_RUBY
  module #{name}
    def call
      puts "#{name}"
      super



                           C
    end
  end
  END_RUBY
end




                           B
class E < A
  include B
  include C
  include D
  def call
    puts "E"



                           A
    super
  end
end

E.new.call
INHERITANCE
   p E.ancestors
[E, D, C, B, A, Object, Kernel, BasicObject]




       INHERITANCE
                p E.ancestors
Module Magic
Module Magic
Module Magic
Module Magic
Module Magic
class A
  def call
    puts "A"
  end
end

%w[B C D].each do |name|
  eval <<-END_RUBY
  module #{name}
    def call
      puts "#{name}"
      super
    end
  end
  END_RUBY
end

a = A.new
a.extend(B)
a.extend(C)
a.extend(D)
a.call
D
class A
  def call
    puts "A"
  end
end




                           C
%w[B C D].each do |name|
  eval <<-END_RUBY
  module #{name}
    def call
      puts "#{name}"
      super



                           B
    end
  end
  END_RUBY
end




                           A
a = A.new
a.extend(B)
a.extend(C)
a.extend(D)
a.call
INVISIBLE CLASS
  class << a; p ancestors end
[D, C, B, A, Object, Kernel, BasicObject]




    INVISIBLE CLASS
          class << a; p ancestors end
The (invisible) “singleton class” is here


[D, C, B, A, Object, Kernel, BasicObject]




    INVISIBLE CLASS
           class << a; p ancestors end
Module Magic
class A
  def call
    puts "A"
  end
end

%w[B C D].each do |name|
  eval <<-END_RUBY
  module #{name}
    def call
      puts "#{name}"
      super
    end
  end
  END_RUBY
end

a = A.new
a.extend(B)
a.extend(C)
a.extend(D)
class << a
  def call
    puts "Invisible"
    super
  end
end
a.call
class A
  def call
    puts "A"
  end


                           Invisible
end

%w[B C D].each do |name|
  eval <<-END_RUBY


                               D
  module #{name}
    def call
      puts "#{name}"
      super


                               C
    end
  end
  END_RUBY
end



                               B
a = A.new
a.extend(B)
a.extend(C)
a.extend(D)


                               A
class << a
  def call
    puts "Invisible"
    super
  end
end
a.call
JUST A SHORTCUT
We now know what extend() rea#y is
obj.extend(Mod)




   JUST A SHORTCUT
     We now know what extend() rea#y is
class << obj
obj.extend(Mod)     include Mod
                  end


   JUST A SHORTCUT
     We now know what extend() rea#y is
Module Magic
NAMESPACE
 MODULES
GROUP CONSTANTS
Group constants/classes and even mix them in
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
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
Module Magic
Module Magic
Module Magic
Module Magic
DUAL INTERFACE
Some modules are both a namespace and a mixin
module MoreMath
  def self.dist(x1, y1, x2, y2)
    Math.sqrt( (x2 - x1) ** 2 +
               (y2 - y1) ** 2 )
  end
end




    DUAL INTERFACE
Some modules are both a namespace and a mixin
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
                                  end
end




    DUAL INTERFACE
Some modules are both a namespace and a mixin
MIXIN YOURSELF
Better than Ruby’s module_function()
module MiniLogger
  extend self
  def logger
    $stdout
  end
  def log(message)
    logger.puts "%s: %s" %
                [ Time.now.strftime("%D %H:%M:%S"),
                  message ]
  end
end

if __FILE__ == $PROGRAM_NAME
  MiniLogger.log "Called as a module method and " +
                 "written to $stdout."
end




       MIXIN YOURSELF
          Better than Ruby’s module_function()
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()
Module Magic
Module Magic
Module Magic
Module Magic
LIMITED MAGIC
Summon new error types as needed
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
  end
end

p Errors::SaveError




    LIMITED MAGIC
     Summon new error types as needed
SOME EXAMPLES
Bridging the theory to implementation gap
SOME EXAMPLES
Bridging the theory to implementation gap
A BIT TOO CLEVER
If RDoc can’t read it, it’s probably too clever
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
  # ...
end

Config.config_file = "config.rb"
Config.update_from_config_file




     A BIT TOO CLEVER
    If RDoc can’t read it, it’s probably too clever
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
  # ...
end

Config.config_file = "config.rb"
Config.update_from_config_file




     A BIT TOO CLEVER
    If RDoc can’t read it, it’s probably too clever
LESS MAGIC
RDoc and I can both read it now
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
Module Magic
Module Magic
Module Magic
Module Magic
WITH CLASS METHODS
  A classic pattern made popular by Rails
module DoubleMixin
    module ClassMethods
      # ...
    end
    module InstanceMethods
      # ...
    end

    def self.included(receiver)
      receiver.extend(ClassMethods)
      receiver.send(:include, InstanceMethods)
    end
  end




WITH CLASS METHODS
  A classic pattern made popular by Rails
Module Magic
Module Magic
Module Magic
Module Magic
LABELING OBJECTS
You can use a do-nothing module as a type
module DRb
   # ...
   module DRbUndumped
     def _dump(dummy) # :nodoc:
       raise TypeError, 'can't dump'
     end
   end
   # ...
   class DRbMessage
     # ...
     def dump(obj, error=false) # :nodoc:
       obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
       # ...
     end
     # ...
   end
   # ...
 end




LABELING OBJECTS
You can use a do-nothing module as a type
OBJECT EDITING
  Using hooks to edit objects
p "ruby".strip!.
         capitalize!

# NoMethodError:
#   undefined method
#   `capitalize!' for
#   nil:NilClass




      OBJECT EDITING
               Using hooks to edit objects
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
SUMMARY
SUMMARY
SUMMARY

Master Ruby’s method
lookup; it’s worth the effort
SUMMARY

Master Ruby’s method
lookup; it’s worth the effort

Modules are a terrific at
limiting the scope of magic
SUMMARY

Master Ruby’s method
lookup; it’s worth the effort

Modules are a terrific at
limiting the scope of magic

Remember, modules can
modify individual objects
SUMMARY

Master Ruby’s method
lookup; it’s worth the effort

Modules are a terrific at
limiting the scope of magic

Remember, modules can
modify individual objects

  Try replacing some
  inheritance with extend()
Module Magic
QUESTIONS?

More Related Content

PDF
OOP in PHP
PDF
Functional Programming with Groovy
PPT
Groovy presentation
KEY
Can't Miss Features of PHP 5.3 and 5.4
PDF
PHPID online Learning #6 Migration from procedural to OOP
PDF
DEBUGGING FUZZY XPATH QUERIES
PDF
Lift off with Groovy 2 at JavaOne 2013
PDF
Fantastic DSL in Python
OOP in PHP
Functional Programming with Groovy
Groovy presentation
Can't Miss Features of PHP 5.3 and 5.4
PHPID online Learning #6 Migration from procedural to OOP
DEBUGGING FUZZY XPATH QUERIES
Lift off with Groovy 2 at JavaOne 2013
Fantastic DSL in Python

What's hot (20)

ODP
Groovy intro for OUDL
ZIP
Object Oriented PHP5
PDF
Web 4 | Core JavaScript
PDF
block
PDF
Polyglot JVM
PDF
Clean code
PDF
Design Patterns in PHP5
PPTX
PHP PPT FILE
PPTX
Things about Functional JavaScript
PDF
Web 9 | OOP in PHP
PDF
35 Years of Open Source Software
PDF
Functions in PHP
PDF
Object Oriented Programming with PHP 5 - More OOP
PPTX
Objective-c Runtime
PPTX
PHP Traits
PDF
Web 8 | Introduction to PHP
PPTX
Python advance
PDF
Steady with ruby
PDF
Twig tips and tricks
PDF
Web 11 | AJAX + JSON + PHP
Groovy intro for OUDL
Object Oriented PHP5
Web 4 | Core JavaScript
block
Polyglot JVM
Clean code
Design Patterns in PHP5
PHP PPT FILE
Things about Functional JavaScript
Web 9 | OOP in PHP
35 Years of Open Source Software
Functions in PHP
Object Oriented Programming with PHP 5 - More OOP
Objective-c Runtime
PHP Traits
Web 8 | Introduction to PHP
Python advance
Steady with ruby
Twig tips and tricks
Web 11 | AJAX + JSON + PHP
Ad

Similar to Module Magic (20)

PDF
Designing Ruby APIs
KEY
Dsl
PDF
JRuby e DSL
PDF
Metaprogramovanie #1
PDF
Ruby tricks2
PDF
Metaprogramming 101
PDF
Postobjektové programovanie v Ruby
KEY
Ruby/Rails
PDF
Ruby on Rails at PROMPT ISEL '11
KEY
Why ruby
KEY
Desarrollando aplicaciones web en minutos
KEY
Reusable Ruby • Rt 9 Ruby Group • Jun 2012
PPTX
Groovy to gradle
KEY
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
PDF
Blocks by Lachs Cox
KEY
A tour on ruby and friends
PDF
Metaprogramming Rails
KEY
Rails3ハンズオン資料
KEY
PDF
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Designing Ruby APIs
Dsl
JRuby e DSL
Metaprogramovanie #1
Ruby tricks2
Metaprogramming 101
Postobjektové programovanie v Ruby
Ruby/Rails
Ruby on Rails at PROMPT ISEL '11
Why ruby
Desarrollando aplicaciones web en minutos
Reusable Ruby • Rt 9 Ruby Group • Jun 2012
Groovy to gradle
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Blocks by Lachs Cox
A tour on ruby and friends
Metaprogramming Rails
Rails3ハンズオン資料
Gradle build tool that rocks with DSL JavaOne India 4th May 2012
Ad

More from James Gray (18)

KEY
A Dickens of A Keynote
KEY
I Doubt That!
KEY
Regular expressions
KEY
Counting on God
KEY
In the Back of Your Mind
PDF
Unblocked
KEY
API Design
KEY
Amazon's Simple Storage Service (S3)
KEY
Git and GitHub
KEY
Test Coverage in Rails
KEY
Rails Routing And Rendering
KEY
Sending Email with Rails
KEY
Associations in Rails
KEY
DRYing Up Rails Views and Controllers
KEY
Building a Rails Interface
KEY
Rails Model Basics
KEY
KEY
Wed Development on Rails
A Dickens of A Keynote
I Doubt That!
Regular expressions
Counting on God
In the Back of Your Mind
Unblocked
API Design
Amazon's Simple Storage Service (S3)
Git and GitHub
Test Coverage in Rails
Rails Routing And Rendering
Sending Email with Rails
Associations in Rails
DRYing Up Rails Views and Controllers
Building a Rails Interface
Rails Model Basics
Wed Development on Rails

Recently uploaded (20)

PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Empathic Computing: Creating Shared Understanding
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
KodekX | Application Modernization Development
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Electronic commerce courselecture one. Pdf
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Diabetes mellitus diagnosis method based random forest with bat algorithm
Encapsulation_ Review paper, used for researhc scholars
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
The Rise and Fall of 3GPP – Time for a Sabbatical?
sap open course for s4hana steps from ECC to s4
Empathic Computing: Creating Shared Understanding
The AUB Centre for AI in Media Proposal.docx
KodekX | Application Modernization Development
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Electronic commerce courselecture one. Pdf
MYSQL Presentation for SQL database connectivity
Understanding_Digital_Forensics_Presentation.pptx
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Review of recent advances in non-invasive hemoglobin estimation
Per capita expenditure prediction using model stacking based on satellite ima...
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Digital-Transformation-Roadmap-for-Companies.pptx

Module Magic

  • 1. MODULE MAGIC and my trip to RubyKaigi2009
  • 3. JAMES EDWARD GRAY II Created the Ruby Quiz and wrote that book
  • 4. JAMES EDWARD GRAY II Created the Ruby Quiz and wrote that book Built FasterCSV (now CSV), HighLine (with Greg), Elif, and some other scarier experiments
  • 5. JAMES EDWARD GRAY II Created the Ruby Quiz and wrote that book Built FasterCSV (now CSV), HighLine (with Greg), Elif, and some other scarier experiments Documented some of Ruby
  • 6. JAMES EDWARD GRAY II Created the Ruby Quiz and wrote that book Built FasterCSV (now CSV), HighLine (with Greg), Elif, and some other scarier experiments Documented some of Ruby http://blog.grayproductions.net/
  • 7. JAMES EDWARD GRAY II Created the Ruby Quiz and wrote that book Built FasterCSV (now CSV), HighLine (with Greg), Elif, and some other scarier experiments Documented 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!
  • 12. RUBYKAIGI2009 See how the Japanese do conferences
  • 13. RUBYKAIGI2009 See how the Japanese do conferences The translation time allows you to think more
  • 14. RUBYKAIGI2009 See how the Japanese do conferences The translation time allows you to think more Meet nice Rubyists from Japan and other places
  • 15. RUBYKAIGI2009 See how the Japanese do conferences The translation time allows you to think more Meet nice Rubyists from Japan and other places See Japan!
  • 30. A TRIVIAL MIXIN I’m sure most of us know this
  • 31. module Mixin def shared_method puts "Called!" end end class Whatever include Mixin end Whatever.new.shared_method A TRIVIAL MIXIN I’m sure most of us know this
  • 32. module Mixin def shared_method puts "Called!" end end class Whatever Called! include Mixin end Whatever.new.shared_method A TRIVIAL MIXIN I’m sure most of us know this
  • 38. class A def call puts "A" end end %w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super end end END_RUBY end class E < A include B include C include D def call puts "E" super end end E.new.call
  • 39. E class A def call puts "A" end end D %w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super C end end END_RUBY end B class E < A include B include C include D def call puts "E" A super end end E.new.call
  • 40. INHERITANCE p E.ancestors
  • 41. [E, D, C, B, A, Object, Kernel, BasicObject] INHERITANCE p E.ancestors
  • 47. class A def call puts "A" end end %w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super end end END_RUBY end a = A.new a.extend(B) a.extend(C) a.extend(D) a.call
  • 48. D class A def call puts "A" end end C %w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super B end end END_RUBY end A a = A.new a.extend(B) a.extend(C) a.extend(D) a.call
  • 49. INVISIBLE CLASS class << a; p ancestors end
  • 50. [D, C, B, A, Object, Kernel, BasicObject] INVISIBLE CLASS class << a; p ancestors end
  • 51. The (invisible) “singleton class” is here [D, C, B, A, Object, Kernel, BasicObject] INVISIBLE CLASS class << a; p ancestors end
  • 53. class A def call puts "A" end end %w[B C D].each do |name| eval <<-END_RUBY module #{name} def call puts "#{name}" super end end END_RUBY end a = A.new a.extend(B) a.extend(C) a.extend(D) class << a def call puts "Invisible" super end end a.call
  • 54. class A def call puts "A" end Invisible end %w[B C D].each do |name| eval <<-END_RUBY D module #{name} def call puts "#{name}" super C end end END_RUBY end B a = A.new a.extend(B) a.extend(C) a.extend(D) A class << a def call puts "Invisible" super end end a.call
  • 55. JUST A SHORTCUT We now know what extend() rea#y is
  • 56. obj.extend(Mod) JUST A SHORTCUT We now know what extend() rea#y is
  • 57. class << obj obj.extend(Mod) include Mod end JUST A SHORTCUT We now know what extend() rea#y is
  • 61. 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
  • 62. 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
  • 67. DUAL INTERFACE Some modules are both a namespace and a mixin
  • 68. module MoreMath def self.dist(x1, y1, x2, y2) Math.sqrt( (x2 - x1) ** 2 + (y2 - y1) ** 2 ) end end DUAL INTERFACE Some modules are both a namespace and a mixin
  • 69. 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 end end DUAL INTERFACE Some modules are both a namespace and a mixin
  • 70. MIXIN YOURSELF Better than Ruby’s module_function()
  • 71. module MiniLogger extend self def logger $stdout end def log(message) logger.puts "%s: %s" % [ Time.now.strftime("%D %H:%M:%S"), message ] end end if __FILE__ == $PROGRAM_NAME MiniLogger.log "Called as a module method and " + "written to $stdout." end MIXIN YOURSELF Better than Ruby’s module_function()
  • 72. 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()
  • 77. LIMITED MAGIC Summon new error types as needed
  • 78. 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 end end p Errors::SaveError LIMITED MAGIC Summon new error types as needed
  • 79. SOME EXAMPLES Bridging the theory to implementation gap
  • 80. SOME EXAMPLES Bridging the theory to implementation gap
  • 81. A BIT TOO CLEVER If RDoc can’t read it, it’s probably too clever
  • 82. 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 # ... end Config.config_file = "config.rb" Config.update_from_config_file A BIT TOO CLEVER If RDoc can’t read it, it’s probably too clever
  • 83. 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 # ... end Config.config_file = "config.rb" Config.update_from_config_file A BIT TOO CLEVER If RDoc can’t read it, it’s probably too clever
  • 84. LESS MAGIC RDoc and I can both read it now
  • 85. 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
  • 90. WITH CLASS METHODS A classic pattern made popular by Rails
  • 91. module DoubleMixin module ClassMethods # ... end module InstanceMethods # ... end def self.included(receiver) receiver.extend(ClassMethods) receiver.send(:include, InstanceMethods) end end WITH CLASS METHODS A classic pattern made popular by Rails
  • 96. LABELING OBJECTS You can use a do-nothing module as a type
  • 97. module DRb # ... module DRbUndumped def _dump(dummy) # :nodoc: raise TypeError, 'can't dump' end end # ... class DRbMessage # ... def dump(obj, error=false) # :nodoc: obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped # ... end # ... end # ... end LABELING OBJECTS You can use a do-nothing module as a type
  • 98. OBJECT EDITING Using hooks to edit objects
  • 99. p "ruby".strip!. capitalize! # NoMethodError: # undefined method # `capitalize!' for # nil:NilClass OBJECT EDITING Using hooks to edit objects
  • 100. 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
  • 103. SUMMARY Master Ruby’s method lookup; it’s worth the effort
  • 104. SUMMARY Master Ruby’s method lookup; it’s worth the effort Modules are a terrific at limiting the scope of magic
  • 105. SUMMARY Master Ruby’s method lookup; it’s worth the effort Modules are a terrific at limiting the scope of magic Remember, modules can modify individual objects
  • 106. SUMMARY Master Ruby’s method lookup; it’s worth the effort Modules are a terrific at limiting the scope of magic Remember, modules can modify individual objects Try replacing some inheritance with extend()

Editor's Notes