Scoped_require 0.0

… and from the substratum, it arises …

One-liner: scoped_require provides an optional parameter to
Kernel#require to allow you to shove the created modules/classes into
some sandboxy container module. I use it to prevent namespace collision
with an external library. It’s a hack. Heed the version number.

Example usage:
require ‘rubygems’
require_gem ‘rubyful_soup’ # luckily this doesn’t have autorequire
set
require ‘rubyful_soup’, :module => :Soup # here’s the magic
assert_equal ‘constant’, defined? Soup::BeautifulSoup

Browse or get at:

http://opensvn.csie.org/twifkak/trunk/scoped_require/scoped_require.rb

See the unit tests for usage. If you wanna run the unit tests, export
the whole scoped_require directory. And have the rubyful_soup gem
installed. Weirdo.

Several-liner:

So, I was writing a Rails app, and I was using RubyfulSoup to parse in
some documents and feed some metadata into the database. Hoorah. (Thanks
for the library, by the way!)

Then, I decided, “I should implement folksonomy!” As any standard human
would do, I created a model object called Tag, and went on creating my
tagging functionality.

I decide to run all of my tests again. Woo! Big honkin’ error in the
feeder code. See, it seems that RubyfulSoup defines a class called Tag
and shoves it in the top-level space – or, at least, tries to, before
getting a “base class mismatch” error.

Well, I could contact the author and tell him to move his class out of
my way, but I’m antisocial, and besides, I want it to work now! I
could put my class in a module, but I WILL NOT SUCCUMB. No, no, I will
put his classes in a module. And I’ll do it without touching his code.
(Look, ma, no CM issues!)

Well, being the naïve and lazy fellow I am, I try the simplest approach:
module Soup
require ‘rubyful_soup’
end

whimper It doesn’t work. I eventually settle on this can of ugly:
module Soup
eval IO.read(‘sgml_parser.rb’)
$" << ‘sgml_parser.rb’
eval IO.read(‘rubyful_soup.rb’)
end

Yeah. Cry. So I packaged up that can of ugly into a pretty little
#require override, so that I never have to think about it again.

Judging by http://www.rcrchive.net/rcr/show/289, I’m not the only person
who had the desire for this capability. Is this “library” useful to
other people? Would it be, if I got rid of the need for a require_gem
first? Or is this just some weird thing that only helps me? Do you have
suggestions for making it suck less? Is it sad that my test code is
prettier than my real code? Is there a library out there that already
does this, but better? Will I become famous and wealthy thanks to this
library (please oh please oh please)?

Thanks,
Devin
Yeah, I don’t mean to pick on RubyfulSoup so much… 's just on the
brain…

On Fri, Dec 16, 2005 at 04:08:05PM +0900, Devin M. wrote:

Example usage:
require ‘rubygems’
require_gem ‘rubyful_soup’ # luckily this doesn’t have autorequire set
require ‘rubyful_soup’, :module => :Soup # here’s the magic
assert_equal ‘constant’, defined? Soup::BeautifulSoup
[…]
I eventually settle on this can of ugly:
module Soup
eval IO.read(‘sgml_parser.rb’)
$" << ‘sgml_parser.rb’
eval IO.read(‘rubyful_soup.rb’)
end

Yeah. Cry. So I packaged up that can of ugly into a pretty little
#require override, so that I never have to think about it again.

You also dislike alias_method, don’t you? :wink:
class Object
old_require = method :require
define_method :require do |*args|

As for the actual low-level mechanism:
eval <<-SCOPED
module #{mod.name}
#{IO.read("#{file_path}")}
end
SCOPED

wouldn’t that break code like

foo.rb

class String

instead of class ::String

def foo; “foo #{self}” end
end

Foo = Struct.new(:bar){ def doit; bar.foo end }
#=================================================================

in myapp.rb

require ‘foo’, :module => :Foo

Foo::Foo.bar

Foo.new(“foobar”).bar # => “foobar”

There’s another tricky issue regarding the transitivity of
scoped_require:

a.rb

require ‘b’, :module => :B

#=================================================================

b.rb

require ‘something’

#=================================================================

app.rb

require ‘a’, :module => :A

Should ‘something’ be scoped to B, A, A::B, or not at all?
And if it is loaded under some module, should it be added to $"?

So it seems scoped_require would work best with 1-file libs that don’t
mess with code classes. In other cases, it could still be useful:
there’s
nothing wrong with encouraging lecture :wink:

Mauricio F. wrote:

You also dislike alias_method, don’t you? :wink:

I tried to pollute the namespace as little as possible. (And then I
refactored and created Module#find_or_create_module, but whatever.) I
also tried to be as ‘cute’ as possible (but that was a tertiary
concern).

wouldn’t that break code like

foo.rb

class String

instead of class ::String

def foo; “foo #{self}” end
end

Good point, yes… I suppose I could inject #clones of the toplevel
namespace’s constants into the container module… 'twould be a little
performance-heavy, so maybe I’d make that an option, à la:
require ‘foo’, :module => :Foo, :import => [String, Hash]
Would that work? Do you have a better idea?

app.rb

require ‘a’, :module => :A

Should ‘something’ be scoped to B, A, A::B, or not at all?
And if it is loaded under some module, should it be added to $"?

I should be deadling with that, now. Without running the above code, I’m
pretty sure 'something’ll get loaded under A::B. And my require override
uses @__require_feature rather than $", so it’s specific to each
container module.

Although, admittedly, I probably could use a couple of unit tests to
test the interaction between scoped requires and non-scoped requires
(and $").

So it seems scoped_require would work best with 1-file libs that don’t
mess with code classes. In other cases, it could still be useful: there’s
nothing wrong with encouraging lecture :wink:

Well, definitely more than one file – it was in my requirements (using
RubyfulSoup) that nested requires work.* But yeah, I couldn’t imagine
that require ‘rails’, :module => :Rails would work. :stuck_out_tongue:

Thanks for the feedback. I may add the import thing later.

Devin

  • Well, sort of. It’s actually the main file, rubyful_soup.rb, that
    defines the Tag class, not its dependency, sgml_parser.rb, so I could’ve
    gotten away with not handling nested requires. But I was having fun…
    :slight_smile:

Devin M. wrote:

Mauricio F. wrote:

You also dislike alias_method, don’t you? :wink:

I tried to pollute the namespace as little as possible. (And then I
refactored and created Module#find_or_create_module, but whatever.) I
also tried to be as ‘cute’ as possible (but that was a tertiary
concern).

wouldn’t that break code like

foo.rb

class String

instead of class ::String

def foo; “foo #{self}” end
end

Good point, yes… I suppose I could inject #clones of the toplevel
namespace’s constants into the container module… 'twould be a little
performance-heavy, so maybe I’d make that an option, à ¬a:
require ‘foo’, :module => :Foo, :import => [String, Hash]
Would that work? Do you have a better idea?

I recall talking about this some time ago, either here
or on #ruby-lang, and someone (Austin Z., maybe?)
came up with a workable solution. I will see if I can
locate that code. In the meanwhile, see if you can
search the archives for it.

app.rb

require ‘a’, :module => :A

Should ‘something’ be scoped to B, A, A::B, or not at all?
And if it is loaded under some module, should it be added to $"?

I should be deadling with that, now. Without running the above code, I’m
pretty sure 'something’ll get loaded under A::B. And my require override
uses @__require_feature rather than $", so it’s specific to each
container module.

Although, admittedly, I probably could use a couple of unit tests to
test the interaction between scoped requires and non-scoped requires
(and $").

So it seems scoped_require would work best with 1-file libs that don’t
mess with code classes. In other cases, it could still be useful: there’s
nothing wrong with encouraging lecture :wink:

Well, definitely more than one file – it was in my requirements (using
RubyfulSoup) that nested requires work.* But yeah, I couldn’t imagine
that require ‘rails’, :module => :Rails would work. :stuck_out_tongue:

Thanks for the feedback. I may add the import thing later.

Devin

  • Well, sort of. It’s actually the main file, rubyful_soup.rb, that
    defines the Tag class, not its dependency, sgml_parser.rb, so I could’ve
    gotten away with not handling nested requires. But I was having fun…
    :slight_smile:

E