I have a namespace collision problem between a Rubygem (htmltools 1.09)
and a Rails framework class (in ActionPack 1.12.1).
In order to use both, I will need to modify the namespace of one of
them.
However, I would really like to not have to modify the existing gems and
I was thinking that maybe there might be some clever way to use the
Module class to “wrap” the offending module redefine it’s namespace.
Has anyone ever done anything like this?
Otherwise, I will be looking at “taking ownership” of the gem code and
moving it to a locally - controlled location essentially freezing the
version of that Gem for my application
Is there a way to unload (“un-require”) a module at runtime? Is there a
way to load (“re-require”) a module at runtime?
Thanks,
Wes
Wes G. wrote:
All,
I have a namespace collision problem between a Rubygem (htmltools 1.09)
and a Rails framework class (in ActionPack 1.12.1).
In order to use both, I will need to modify the namespace of one of
them.
However, I would really like to not have to modify the existing gems and
I was thinking that maybe there might be some clever way to use the
Module class to “wrap” the offending module redefine it’s namespace.
Has anyone ever done anything like this?
Otherwise, I will be looking at “taking ownership” of the gem code and
moving it to a locally - controlled location essentially freezing the
version of that Gem for my application
Is there a way to unload (“un-require”) a module at runtime? Is
there a
way to load (“re-require”) a module at runtime?
Thanks,
Wes
I think you may be over-estimating the relationship between modules
and files.
One does not require modules, one requires files. One file may have
many modules in it, or none. Regardless one can still ‘require’ that
file.
One way to hack something so it gets stuck in a module is:
module Protect
eval(File.read(“path/to/file.rb”))
end
Then you access all the classes defined in file.rb like:
On Wed, May 17, 2006 at 09:34:34AM +0900, Logan C. wrote:
[…]
} One does not require modules, one requires files. One file may have
} many modules in it, or none. Regardless one can still ‘require’ that
} file.
}
} One way to hack something so it gets stuck in a module is:
}
} module Protect
} eval(File.read(“path/to/file.rb”))
} end
}
} Then you access all the classes defined in file.rb like:
}
} Protect::Node.new # etc.
This is a great idiom, but it needs to be able to function as a (nearly)
drop-in replacement for require. This means that I need to be able to
find
the file in the search path that require would normally use. Consider
the
following:
def find_require_file(filename)
NEED IMPLEMENTATION HERE
end
def namespace_get(str)
str.to_s.split("::").inject(Object) { |ns,name| ns.const_get(name) }
end
def require_within(module_name, filename)
file = find_require_file(file)
mod = module_name
case module_name
when Module,Class #noop
else
mod = namespace_get(mod)
end
mod.instance_eval { eval File.read(file) }
end
This is untested, and I don’t know how to implement that
find_require_file,
but the idea is that the following should work:
module Foo
module Bar
end
end
require_within Foo::Bar, “date”
x = Foo::Bar::Date.today
Anyone want to implement find_require_file and test the idea?
It takes a string on the form “A::C” and splits it into an array;
[“A”, “B”, “C”]. When calling #inject on that array, you iterate over
each element, yielding them to the block (the second parameter). The
first parameter is the result of the last call to the block, or, if it’s
the first element, the default value, here Object.
%w(A B C).inject(Object) {|parent, child| parent.const_get(child) }
On Wed, May 17, 2006 at 09:34:34AM +0900, Logan C. wrote:
[…]
} One does not require modules, one requires files. One file may have
} many modules in it, or none. Regardless one can still ‘require’ that
} file.
}
} One way to hack something so it gets stuck in a module is:
}
} module Protect
} eval(File.read(“path/to/file.rb”))
} end
}
} Then you access all the classes defined in file.rb like:
}
} Protect::Node.new # etc.
This is a great idiom, but it needs to be able to function as a (nearly)
drop-in replacement for require. This means that I need to be able to
find
the file in the search path that require would normally use. Consider
the
following:
def find_require_file(filename)
NEED IMPLEMENTATION HERE
end
def namespace_get(str)
str.to_s.split("::").inject(Object) { |ns,name| ns.const_get(name) }
end
def require_within(module_name, filename)
file = find_require_file(file)
mod = module_name
case module_name
when Module,Class #noop
else
mod = namespace_get(mod)
end
mod.instance_eval { eval File.read(file) }
end
This is untested, and I don’t know how to implement that
find_require_file,
but the idea is that the following should work:
module Foo
module Bar
end
end
require_within Foo::Bar, “date”
x = Foo::Bar::Date.today
Anyone want to implement find_require_file and test the idea?
def find_require_file(filename)
$LOAD_PATH.each do |directory|
[ “.rb”, “.so”, “.dll” ].each do |ext|
if File.exists?(f = File.join(directory, filename + ext))
return f
end
end
end
otherwise we didn’t find it
raise ‘Could not find #{filename} in $LOAD_PATH.’
end
if File.exists?(f = File.join(directory, filename + ext))
return f
end
end
end
otherwise we didn’t find it
raise ‘Could not find #{filename} in $LOAD_PATH.’
end
Actually in retrospect, I should have used double quotes in the raise
and if you know you’re always gonna be eval-ing these files, you
probably want to get rid of the .dll and .so extensions.
On Thu, May 18, 2006 at 02:20:17AM +0900, Wes G. wrote:
} Greg,
}
} How does this
}
} str.to_s.split("::").inject(Object) { |ns,name| ns.const_get(name) }
}
} work?
Magic
So suppose you have a string “Foo::Bar” and you want to retrieve the
module
(or class, or other constant) Bar from the module (or class) Foo. (For
simplicity we will assume that both are modules.) First, we make sure
it’s
a string and split it on “::” which results in the array [“Foo”, “Bar”
].
Now we use inject with its optional starting object.
For the first iteration of inject, ns = Object and name = “Foo” so the
value of ns.const_get(name) is Foo (the module). On the next iteration,
ns = Foo and name = “Bar” so the value of ns.const_get(name) is Bar (the
module). That’s the end of the iteration, so the value of the entire
line
is Bar, which is the value of the method call, which is what you wanted
in
the first place.
Actually in retrospect, I should have used double quotes in the
raise and if you know you’re always gonna be eval-ing these files,
you probably want to get rid of the .dll and .so extensions.
One more thing, this won’t work with gems unless you use require_gem.
e.g.
% irb
irb(main):001:0>
irb(main):002:0* def find_require_file(filename)
irb(main):003:1> $LOAD_PATH.each do |directory|
irb(main):004:2* [ “.rb”, “.so”, “.dll” ].each do |ext|
irb(main):005:3* if File.exists?(f = File.join(directory,
filename + ext))irb(main):006:4> return f
irb(main):007:4> end
irb(main):008:3> end
irb(main):009:2> end
irb(main):010:1> # otherwise we didn’t find it
irb(main):011:1* raise ‘Could not find #{filename} in $LOAD_PATH.’
irb(main):012:1> end
=> nil
irb(main):013:0> find_require_file(‘parse_tree’)
RuntimeError: Could not find #{filename} in $LOAD_PATH.
from (irb):11:in find_require_file' from (irb):13 irb(main):014:0> require 'rubygems' => true irb(main):015:0> find_require_file('parse_tree') RuntimeError: Could not find #{filename} in $LOAD_PATH. from (irb):11:infind_require_file’
from (irb):15
irb(main):016:0> require_gem ‘ParseTree’
=> true
irb(main):017:0> find_require_file(‘parse_tree’)
=> “/usr/local/ruby/lib/ruby/gems/1.8/gems/ParseTree-1.4.1/lib/
parse_tree.rb”
irb(main):018:0>
Since of how rubygems alter the load path. If you are using a gem, as
long as it doesn’t use the deprecated (and evil) autorequire
attribute, you should still be able to safely stick it in its own
namespace.
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.