From: Daniel Berger Date: 2008-09-28T05:42:59+09:00 Subject: Help, Ruby 1.8.6-p287 broke my "use" library and I don't know why Hi everyone, I need help! I've got a library called "use" that let's you mixin methods from modules in a fine-grained fashion. Unfortunately, something broke it between Ruby 1.8.6-p111 and Ruby 1.8.6-p287. It may have something to do with the way I autogenerate some methods in the windows-api library, as simple cases still work fine, but I'm not sure how or why. Here's some sample code: # test.rb require 'use' require 'windows/path' require 'windows/msvcrt/io' class Foo use Windows::Path use Windows::MSVCRT::IO, :tmpfile def initialize p PathIsRoot("C:\\") p tmpfile() end end Foo.new Using Ruby 1.8.6-p111 I get: true 2009464032 Using Ruby 1.8.6-p287 I get: (eval):3:in `PathIsRoot': uninitialized constant Class::PathIsRoot (NameError) from test.rb:10:in `initialize' from test.rb:15:in `new' from test.rb:15 What the heck happened between p 111 and p 287? Below is the full source for use.rb. Regards, Dan class Class # The version of the 'use' library USE_VERSION = '1.2.2' # Allows you to include mixins in a fine grained manner. Instead of # including all methods from a given module, you can instead mixin # only those methods you want through a combination of the :include, # :exclude, and :alias options. # # Examples: # # # Defines a 'bar' and 'baz' method # module Foo # def bar # "hello" # end # def baz # "world" # end # end # # # Defines a 'bar', 'blah', and 'zap' methods # module Test # def bar # "goodbye" # end # def blah # "new york" # end # def zap # "zap" # end # end # # # From the Foo module, only mixin the 'bar' method. From the Test # # module exclude the 'bar' and 'zap' methods. # class Zap # use Foo, :bar # use Test, :exclude => [:bar, :zap] # end # # z = Zap.new # # z.bar # => "hello" # z.baz # => NoMethodError - wasn't mixed in # z.zap # => NoMethodError - wasn't mixed in # z.blah # =>"new york" # # # Alias a method on the fly # class MyKlass # use Foo :alias => {:bar, :test} # end # # m = MyKlass.new # m.test # => "hello" # m.bar # => NoMethodError - was aliased to 'test' # # If no options follow the module name this method is identical # to a standard include. # def use(*args) valid_keys = %w/include exclude alias/ excluded = [] included = [] aliased = [] mod = args.shift.clone # If no arguments follow the mod name, treat as a standard include if args.empty? included.concat(mod.instance_methods) end m = Module.new args.each{ |arg| if arg.kind_of?(Hash) arg.each{ |key, val| case key.to_s when "include" if val.respond_to?(:each) val.each{ |arg| included.push(arg.to_s) } else included.push(val.to_s) end when "exclude" if val.respond_to?(:each) val.each{ |arg| excluded.push(arg.to_s) } else excluded.push(val.to_s) end when "alias" aliased.push(val) else raise "invalid key '#{key}'" end } else included.push(arg.to_s) end } unless included.empty? || excluded.empty? err = "you cannot include and exclude in the same statement" raise ArgumentError, err end imethods = mod.instance_methods # Remove excluded methods unless excluded.empty? (imethods & excluded).each{ |meth| mod.module_eval{ remove_method(meth) } } end # Alias methods aliased.each{ |pair| pair.each{ |old, new| included.push(old) # Aliased methods automatically included mod.module_eval{ alias_method(new, old) remove_method(old) } } } # Remove methods not specifically included. The rescue was needed # for cases where a module included another module. Also, don't # remove methods that already exist unless specifically included. unless included.empty? (imethods - included).each{ |meth| if superclass.instance_methods.include?(meth) if included.include?(meth) mod.module_eval{ undef_method(meth) rescue nil } else mod.module_eval{ remove_method(meth) rescue nil } end else mod.module_eval{ undef_method(meth) rescue nil } end } end m.module_eval{ include mod } # Raise a warning if methods are shadowed (in $VERBOSE mode) if $VERBOSE imethods = instance_methods(true) m.instance_methods.each{ |meth| next unless imethods.include?(meth) warn "method '#{meth}' aliased, shadows old '#{meth}'" } end include m end end