Reputation: 6937
Given a gem that defines top-level classes that clash with some code I have written, is it possible to require the gem in such a way that all its classes are grouped inside a module I can define? For example, if an unsafe_gem defines a class:
class Word
# ... some code
end
I would need something like:
class Word
# My word class.
end
module SafeContainer
# This obviously doesn't work
# (i.e. the gem still defines ::Word).
require 'unsafe_gem'
end
So that I can distinguish between:
Word.new # => The class I defined.
SafeContainer::Word.new # => The class defined by the gem.
Some further details: My code (e.g. the 'Word' class) is already wrapped in its own namespace. However, I want to be able to provide the user with the option of enabling a form of "syntactic sugar", which makes some classes directly accessible under the top-level namespace. This, however, creates a name clash with one of the gems I am using, which defines a top-level class. None of the currently proposed solutions work because the gem actually relies on its globally-defined class being there; so undefining the class breaks the gem. Of course, the gem has more than one file, and individually requiring its files into a module seems to be a very brittle solution. Currently, the only workaround I have found is this:
begin
# Require the faulty gem.
require 'rbtagger'
rescue
# If syntactic sugar is enabled...
if NAT.edulcorated?
# Temporarily remove the sugar for the clashing class.
Object.const_unset(:Word); retry
else; raise; end
ensure
# Restore syntactic sugar for the clashing class.
if NAT.edulcorated?
Object.const_set(:Word, NAT::Entities::Word)
end
end
I don't know why, but this makes my toenails curl. Anybody have a better solution?
Upvotes: 10
Views: 4202
Reputation: 20184
Another, possibly better answer, comes from this question.
Take advantage of the fact that classes and modules are just objects, like so:
require 'unsafe_gem'
namespaced_word = Word
Word = nil
# now we can use namespaced_word to refer to the Word class from 'unsafe_gem'
#now your own code
class Word
#awesome code
end
You have to make sure that unsafe_gem
only defines one class, and that you require
it before you define your own classes and modules so you don't accidentally set your own stuff to nil
.
Upvotes: 5
Reputation: 16834
The simple answer is "no"
If we have a file 'word.rb';
class Word
def say
puts "I'm a word"
end
end
and we try and require
it, it will always load in the global scope.
If you knew the gem was just a single file, you could, however do the following.
module SafeContainer
module_eval(File.read("word.rb"))
end
but this is unlikely to work in your case.
Upvotes: 1
Reputation: 20184
I think your best bet is to wrap your own code in a module. Depending on how much code you have written, this may or may not be a huge pain. However, it is the best way to be sure that your code won't clash with someone else's.
So your Word
class becomes
module LoismsProject
class Word
#some awesome code
end
end
That way you can safely require 'unsafe_gem'
.
Upvotes: 1