【问题标题】:Require Rails lib file that has the same name as a core Ruby file需要与核心 Ruby 文件同名的 Rails 库文件
【发布时间】:2014-11-03 10:28:28
【问题描述】:

在rails之外,我有以下内容:

|- file1.rb
|- matrix.rb

内容如下:

# in matrix.rb
class Matrix
  def foo
    puts 'foo'
  end
end

# in file1.rb
require_relative 'matrix'
require 'matrix'

Matrix.build(1,1) { 0 }.foo

运行ruby file1.rb 输出:

我能够包含和调用我的矩阵文件和核心矩阵类。

在 Rails 中,我有以下目录结构:

|- lib
|-- core_ext
|--- matrix.rb
|- test
|-- lib
|--- core_ext
|---- matrix_test.rb

还有内容:

# in test/lib/core_ext/matrix_test.rb
require 'test_helper'
require_relative "#{Dir.pwd}/lib/core_ext/matrix.rb"
require 'matrix'

class MatrixTest < ActiveSupport::TestCase
  test 'matrix foo' do
    Matrix.new.foo
    Matrix.build(1,1) { 0 }.foo
    assert true
  end
end

当我运行 rake test test/lib/core_ext/matrix_test.rb 时,我得到 NoMethodError: undefined method 'build' for Matrix:Class ,这意味着核心“矩阵”文件尚未加载。

我通过重命名我的 lib 文件来解决这个问题,但我确实希望在不重命名文件的情况下同时包含 Ruby 的核心 Matrix 和我的。有什么建议吗?

ps:我使用的是 ruby​​ 2.1.4p265 和 Rails 4.1.6

【问题讨论】:

  • 尝试添加 require 'matrix.rb'
  • 感谢您的建议,@Hemali。还是一样。我认为 require 认为 'matrix' 已经加载,因为它与我的 lib/matrix 同名。
  • 理想情况下,我会选择命名空间解决方案。喜欢myproject/matrix 以避免这样的名称冲突。除非您故意扩展核心类。如果您有意扩展核心类,我通常会在初始化程序中(在 rails 中)这样做。
  • 您应该将自定义类名更改为其他名称,而不是 Matrix
  • 我实际上是在修补核心 class=)

标签: ruby-on-rails ruby


【解决方案1】:

尝试将 Matrix 类包装在模块中,然后 refine ... do; enddef 方法:

# lib/core_ext/my_ext_matrix.rb
module MyExtMatrix
  refine Matrix do
    def foo
      puts 'foo'
    end
  end
end

现在using你分机:

# .... some code here ....

class MatrixTest < ActiveSupport::TestCase
  using MyExtMatrix

  test 'matrix foo' do
    Matrix.new.foo
    Matrix.build(1,1) { 0 }.foo
    assert true
  end
end

using 之后,您应该可以使用核心Matrix 中的方法并拥有refineed。

【讨论】:

  • 太棒了!这很有意义!
  • 其实,我以为这就是我要找的。这是一个很好的改进答案。不过,我希望我的猴子补丁是全球性的。我不想每次都“使用”我的“MyExtMatrix”。
  • @Abdo 我很高兴,我认为refine 非常强大的功能在正确的地方。
  • 你可以在运行时添加using方法,在你需要的地方,或者当Rails在某处开始send(:using, MyExtMatrix)时使用ext模块:)
  • 好主意 :-) 不过,我想在很多地方重用这段代码,只需将它添加到 lib 并在自动加载中使用 lib。无论我忘记添加send(:using, MyExtMatrix),该功能都会随处可用。我不想忘记这一点。另外,如果我们在全球范围内这样做,为什么我们使用refine 而不是简单的猴子补丁?
【解决方案2】:

问题是您首先需要矩阵,而不是核心文件。这样您的矩阵就不会修补,而是定义Matrix 类。在猴子修补之前明确要求某些东西是一个很好的规则。

尝试将require 'matrix' 添加到您的core_ext/matrix

你也可以使用require 'core_ext/matrix'而不使用_relative。如果还没有,只需修复搜索路径 ($:) 以包含您的 lib 目录。

【讨论】:

  • 感谢您抽出宝贵时间回答我的问题 :-) 在上面的 Ruby 示例中,我需要在 Ruby 核心之前使用矩阵,但它仍然有效。为什么它不能在 Rails 中工作?
  • 我建议你试试pry gem。将binding.pry 添加到测试用例的开头,并在调试器中检查show-method Matrix 并检查类的祖先。
  • 我用 pry 进行了调试,require 'matrix' 给了我错误,好像核心矩阵库已加载但事实并非如此。这就是我在上面给出答案的原因:stackoverflow.com/a/26715448/226255
【解决方案3】:

所以我认为这与 Rails 的自动加载有关,它会覆盖 const_missing,并弄乱了 Ruby 原生需要文件的方式。 http://urbanautomaton.com/blog/2013/08/27/rails-autoloading-hell/

问题是,无论我在 Ruby 中要求的顺序如何,我的“矩阵”和核心矩阵都在加载,并且代码的行为符合预期。

当 Rails 运行并尝试自动加载 lib/core_ext/matrix.rb 时,它期望它定义 CoreExt::Matrix。当我在代码中执行require 'matrix' 时,返回错误:核心矩阵库没有被加载,因为rails 假设它已经加载? (我对此不确定,但这可能会发生这种情况:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/dependencies.rb

我要做的就是将我的matrix.rb 重命名为matrix_ext.rb,即使matrix_ext.rb 中定义的类是Matrix。这是我在问题中提到的解决方法,但我不知道为什么会这样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 1970-01-01
    • 2013-12-23
    • 2012-03-16
    • 1970-01-01
    • 2013-07-10
    相关资源
    最近更新 更多