Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load Ruby gem into a user-defined namespace

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?

like image 547
user2398029 Avatar asked Jan 06 '12 18:01

user2398029


2 Answers

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'.

like image 150
jergason Avatar answered Oct 23 '22 02:10

jergason


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.

like image 1
Matthew Rudy Avatar answered Oct 23 '22 02:10

Matthew Rudy