[ANN] scoped_require 0.0

D

Devin Mullins

... and from the substratum, it arises ...

One-liner: scoped_require provides an optional parameter to=20
Kernel#require to allow you to shove the created modules/classes into=20
some sandboxy container module. I use it to prevent namespace collision=20
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 se=
t
require 'rubyful_soup', :module =3D> :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.r=
b

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

Several-liner:

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

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

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

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

Well, being the na=EFve 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=20
#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=20
who had the desire for this capability. Is this "library" useful to=20
other people? Would it be, if I got rid of the need for a require_gem=20
first? Or is this just some weird thing that only helps me? Do you have=20
suggestions for making it suck less? Is it sad that my test code is=20
prettier than my real code? Is there a library out there that already=20
does this, but better? Will I become famous and wealthy thanks to this=20
library (please oh please oh please)?

Thanks,
Devin
Yeah, I don't mean to pick on RubyfulSoup so much... 's just on the brain=
...
 
M

Mauricio Fernandez

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? ;-)
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 ;-)
 
D

Devin Mullins

Mauricio said:
You also dislike alias_method, don't you? ;-)
=20
I tried to pollute the namespace as little as possible. (And then I=20
refactored and created Module#find_or_create_module, but whatever.) I=20
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
=20
Good point, yes... I suppose I could inject #clones of the toplevel=20
namespace's constants into the container module... 'twould be a little=20
performance-heavy, so maybe I'd make that an option, =E0 la:
require 'foo', :module =3D> :Foo, :import =3D> [String, Hash]
Would that work? Do you have a better idea?
There's another tricky issue regarding the transitivity of scoped_requir= e:

# a.rb
require 'b', :module =3D> :B
...
#=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
# b.rb
require 'something'
...
#=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
# app.rb
require 'a', :module =3D> :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 $"?
=20
I should be deadling with that, now. Without running the above code, I'm=20
pretty sure 'something'll get loaded under A::B. And my require override=20
uses @__require_feature rather than $", so it's specific to each=20
container module.

Although, admittedly, I probably could use a couple of unit tests to=20
test the interaction between scoped requires and non-scoped requires=20
(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 ;-)
=20
Well, definitely more than one file -- it was in my requirements (using=20
RubyfulSoup) that nested requires work.* But yeah, I couldn't imagine=20
that require 'rails', :module =3D> :Rails would work. :p

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=20
defines the Tag class, not its dependency, sgml_parser.rb, so I could've=20
gotten away with not handling nested requires. But I was having fun... :)
 
E

Eero Saynatkari

Devin said:
Mauricio said:
You also dislike alias_method, don't you? ;-)
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 Ziegler, 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.
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 $").

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. :p

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...
:)


E
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top