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
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!
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 include Mixin end ! Whatever.new.shared_method Called!
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 D C B A 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
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
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 C B A
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 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 Invisible D C B A
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 # ... # Logging severity. module Severity DEBUG = 0 INFO = 1 WARN = 2 ERROR = 3 FATAL = 4 UNKNOWN = 5 end include Severity # ... end module JSON class Array # ... end class Object # ... end # ... end
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
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() require "mini_logger" ! class Whatever include MiniLogger def logger @logger ||= open("whatever.log", "w") end def initialize log "Called as an " + "instance method " + "and written to " + "a file." end end ! Whatever.new 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
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 =~ /\wError\z/ const_set(error_name, Class.new(BaseError)) else super end end end ! p Errors::SaveError
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)
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
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
OBJECT EDITING Using hooks to edit objects module SafelyChainable def self.extended(singleton) singleton.methods.grep(/\w!\z/). each do |bang| singleton.instance_eval <<-END_RUBY def #{bang} super self end END_RUBY end end end ! p "ruby".extend(SafelyChainable). strip!.capitalize! p "ruby".strip!. capitalize! ! # NoMethodError: # undefined method # `capitalize!' for # nil:NilClass
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()