Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load two Ruby Modules/Gems with the same name

I'm trying to use two Gems to access Amazon Web Services (AWS). One is the Amazon 'aws-sdk', the other is 'amazon-ec2'. I'm using the second as the aws-sdk does not cover the cloudwatch section of the amazon services.

The issue is that both load into the same namespace.

require 'aws-sdk'         # aws-sdk gem
require 'AWS'             # amazon-ec2 gem

config = {:access_key_id => 'abc', :secret_key => 'xyz'}

# start using the API with aws-sdk
ec2 = AWS::EC2.new(config)  

# start using the API for anazon-ec2
cw = AWS::Cloudwatch::Base.new(config) 

Now this understandably throws an error on the last line as the AWS module is pointing at the first required library, in this case aws-sdk.

NameError: uninitialized constant AWS::Cloudwatch

So, is it possible for me to load one of those into another namespace? Something like

require 'aws-sdk', 'AWS_SDK'
require 'AWS', 'AWS_EC2'

ec2 = AWS_SDK::EC2.new(config)  
cw = AWS_EC2::Cloudwatch::Base.new(config) 

Or is there another trick I could use here?

Thanks

like image 399
Jon Avatar asked Nov 13 '11 14:11

Jon


2 Answers

In Ruby, modules with the same name from different gems don't replace each other. If one gem implements

module AWS
  class Foo
  end
end

and another implements

module AWS
  class Bar
  end
end

and you require them both, you will end up with an AWS module that contains both a class Foo and a class Bar (unless the second does something really tricky like explicitly undefining anything already present in the module, before defining its own stuff, which is very unlikely). As long as the second gem doesn't redefine any methods in the first gem (or attempts to use a module as a class or vice versa), they should both work fine. I think you may be looking for the wrong solution.

Edit: And in fact, what happens for me (in an environment with only these gems present (aws-sdk 1.2.3 and amazon-ec2 0.9.17) and the exact code you listed above) is exactly that:

.rvm/gems/ree-1.8.7-2011.03@ec2/gems/amazon-ec2-0.9.17/lib/AWS/EC2.rb:2: EC2 is not a module (TypeError)

Could it be that an error gets swallowed somewhere and that the module AWS::Cloudwatch hasn't been defined, simply because the initialization of the gem goes awry?

like image 131
Confusion Avatar answered Nov 09 '22 21:11

Confusion


I think I've found a solution that works, let me illustrate it with an example. Suppose we have to files a.rb and b.rb that define the same module with actual name clashes:

#file a.rb
module A
  def self.greet
    puts 'A'
  end
end

#file b.rb
module A
  def self.greet
    puts 'other A'
  end
end

If you need to require both of them, the following seems to do the trick:

require_relative 'a'
TMP_A = A.dup
A.greet # => A
TMP_A.greet # => A
require_relative 'b'
TMP_A2 = A
A.greet # => other A
TMP_A2.greet # => other A
TMP_A.greet # => A

Without the dup, TMP_A will also point to the A defined in b.rb after the require_relative, but the dup will ensure that a real copy is produced instead of simply holding a reference to the module.

like image 38
emboss Avatar answered Nov 09 '22 22:11

emboss