加载两个名称相同的Ruby Modules / Gems

时间:2011-11-13 14:00:37

标签: ruby rubygems amazon-ec2 amazon-web-services

我正在尝试使用两个Gems来访问Amazon Web Services(AWS)。一个是亚马逊'aws-sdk',另一个是'amazon-ec2'。我正在使用第二个,因为aws-sdk不包括亚马逊服务的cloudwatch部分。

问题是两者都加载到同一名称空间。

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) 

现在,这可以理解地在最后一行引发错误,因为AWS模块指向第一个必需的库,在本例中为aws-sdk。

NameError: uninitialized constant AWS::Cloudwatch

那么,我可以将其中一个加载到另一个名称空间中吗?像

这样的东西
require 'aws-sdk', 'AWS_SDK'
require 'AWS', 'AWS_EC2'

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

或者我可以在这里使用另一种技巧吗?

谢谢

2 个答案:

答案 0 :(得分:2)

在Ruby中,来自不同gem的同名模块不会互相替换。如果一个宝石实现

module AWS
  class Foo
  end
end

和另一个实现

module AWS
  class Bar
  end
end

并且你需要它们两个,你最终会得到一个包含类Foo和类Bar的AWS模块(除非第二个做了一些非常棘手的事情,比如在定义自己的东西之前明确地取消定义模块中已有的东西) ,这是非常不可能的)。只要第二个gem没有重新定义第一个gem中的任何方法(或者尝试将模块用作类,反之亦然),它们都应该可以正常工作。我想你可能正在寻找错误的解决方案。

编辑: 事实上,对我来说(在只有这些宝石存在的环境中(aws-sdk 1.2.3和amazon-ec2 0.9.17)以及上面列出的确切代码)恰恰是这样:

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

可能是因为错误被某个地方吞没而且模块AWS :: Cloudwatch尚未定义,只是因为宝石的初始化出错了?

答案 1 :(得分:1)

我想我找到了一个有效的解决方案,让我用一个例子来说明。假设我们必须使用文件a.rb和b.rb来定义具有实际名称冲突的相同模块:

#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

如果您需要同时使用它们,以下似乎可以解决问题:

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

如果没有dupTMP_A也会指向A后b.rb中定义的require_relative,但dup将确保生成真实副本而不是简单地持有对模块的引用。