Help, Ruby 1.8.6-p287 broke my "use" library and I don't know why

D

Daniel Berger

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
 
N

Nobuyoshi Nakada

Hi,

At Sun, 28 Sep 2008 05:42:59 +0900,
Daniel Berger wrote in [ruby-talk:316199]:
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.

Reproduced with:

module X
XXX = 1
def xxx
XXX
end
end

class Foo
use X
end

Foo.new.xxx

And worked fine with the SVN 1.8 branch head.
 
D

David A. Black

Hi Dan --

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.

Do you have a non-Windows example where it does this?


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL *
* Co-taught with Patrick Ewing!
See http://www.rubypal.com for details and updates!
 
D

Daniel Berger

Hi Dan --






Do you have a non-Windows example where it does this?

Nobu's example summarizes the problem:

require 'use'

module X
XXX = 1
def xxx
XXX
end
end

class Foo
use X
end

Foo.new.xxx

Regards,

Dan
 
D

Daniel Berger

David said:
Hi --



For what it's worth, here's an even simpler example, not involving
your "use" library:

class Class
def myuse(mod)
include mod.clone
end
end

module X
XXX = 1
def xxx
XXX
end
end

class Foo
myuse X
end

Foo.new.xxx

This works with p230 but not p287. I guess it has to do with module
cloning and constants, but I haven't tracked it down beyond that.

Thanks for that David. I was wondering what the underlying issue was.

Regards,

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,690
Latest member
MacGyver

Latest Threads

Top