Forum: Ruby scoped_require 0.0

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Devin M. (Guest)
on 2005-12-16 09:09
(Received via mailing list)
... 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_requi...

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...
Mauricio F. (Guest)
on 2005-12-16 12:25
(Received via mailing list)
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? ;-)
    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 ;-)
Devin M. (Guest)
on 2005-12-16 15:59
(Received via mailing list)
Mauricio F. wrote:

>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, à 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 ;-)
>
>
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...
:)
Eero S. (Guest)
on 2005-12-16 22:57
Devin M. wrote:
> Mauricio F. wrote:
>
>>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 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 ;-)
>>
>>
> 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
This topic is locked and can not be replied to.