ruby - 当我的 gem 被激活时,如何自动运行一些代码?

标签 ruby plugins rubygems monkeypatching

我创建了一个 gem,它本质上是现有 Ruby 应用程序的插件/扩展。该应用程序使用 bundler 对此进行了一些考虑;启动时它会自动执行 Bundle.require :misc

我已将 Gem 添加到 Gemfile 中的 :misc 组,并且我的 Gem 已按预期添加到加载路径。挑战在于,为了使我的扩展正常工作,我需要修补一些现有的类。

我的所有猴子补丁代码都包含在 gem 中的 ruby​​ 文件中(比如说 lib/mygem/patch.rb)。如果我手动编辑基本应用程序的源代码以在 Bundle.require :misc 的现有行之后调用 require 'mygem/patch' ,那么一切都会很好。然而,这是草率的,每次我重新安装 gems 或移动到新机器时都需要编辑基本应用程序已安装的 gem。

# Currently I can load my gem and execute the monkey patch in 2 lines
gem 'mygem'
require 'mygem/patch'

# Or with bundler (mygem is in the :misc group)
Bundle.require :misc
require 'mygem/patch'

# I want to achieve the same result in only one line
gem 'mygem'

# or
Bundle.require :misc

每当从基本应用程序激活扩展 gem 时,我想自动运行扩展 gem 中的一些代码,而无需手动请求该文件。该解决方案在使用 gem 'mygem' 语句单独激活 gem 时,或者作为 bundle 组的一部分(如 Bundle.require :misc 中)时都应该有效。

这可能吗?是否有更好的模式来解决“插件/扩展”gem 中的猴子修补问题?

谢谢!

最佳答案

尚不清楚您希望代码何时运行。您希望它在 Ruby 启动程序时运行,还是在 Bundler 为应用程序构建 gem 列表时运行?

如果您谈论的是 Ruby 何时加载代码来运行它......

Ruby 支持在初始化变量之前运行的 BEGIN {...}END {...} block 。它们按照所看到的方式运行,但是显然, END {...} 将在程序结束时运行,就在解释器退出之前。

参见"Programming Ruby" :

BEGIN and END Blocks

Every Ruby source file can declare blocks of code to be run as the file is being loaded (the BEGIN blocks) and after the program has finished executing (the END blocks).

BEGIN {
  begin code
}


END {
  end code
}

A program may include multiple BEGIN and END blocks. BEGIN blocks are executed in the order they are encountered. END blocks are executed in reverse order.

您还可以将代码添加到 gem、类或模块文件中,这些代码将在文件加载时运行,以初始化动态定义的变量/常量。对此没有特殊要求,只是不要将其放入 classdef 中。主级别的代码将按预期运行,包括它是否在模块中——“主级别”部分很重要。

请小心执行这些操作,因为您不想影响 gem 的加载或退出时间,从而对程序产生不利影响。

此外,在 Ruby 中对现有类进行猴子修补时要非常小心。如果更改现有方法的功能,可能会严重破坏用户的 Ruby 应用程序。如果您添加的方法名称与用户定义的方法名称冲突,那么也可能会发生相同的情况,并且无论哪种情况,都可能很难追踪和修复。


详细说明:

当 Ruby 需要类或模块文件时,它会加载它,然后运行它在其中找到的代码。所有类或常量都会被初始化,并且主级别的所有代码都会运行。在文件的底部,Ruby 将继续执行下一个“require”语句。

考虑一个包含以下内容的文件:

class Foo
  def initialize
    puts "Inside Foo.new()"
  end
end

运行不会返回任何内容,也不会执行任何内容。该类将被解析,但由于没有任何内容调用 Foo.new,我们甚至看不到输出。这就是为什么我们不嵌入我们想要在类中自动运行的代码,因为除非有明确的调用它,否则它不会运行。要求它会得到相同的结果。

将代码更改为以下内容并运行它会输出“Inside Foo.new()”:

class Foo
  def initialize
    puts "Inside Foo.new()"
  end
end

Foo.new()
# >> Inside Foo.new()

这是显式调用类初始值设定项。

如果另一个文件需要第一个代码,那么当它运行时,什么也不会发生,因为直到所需代码中的某些内容或随后加载的文件告诉 Ruby 运行它时,才会调用该类。运行类初始值设定项会导致:

Inside Foo.new()

无论如何,方法或类定义在被 fooFoo.new() 调用之前不会运行,然后导致新实例被定义,允许初始化程序运行。

如果OP想要在他自己的gem中运行某些东西,将其放入defclass中将隐藏它。 Ruby 必须在主级别才能运行它。并且,再次假设问题是关于 Ruby 加载脚本时运行的代码,而不是关于 Rubygems 或 Bundler 在安装时可以运行的代码。

关于ruby - 当我的 gem 被激活时,如何自动运行一些代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18858595/

相关文章:

javascript - 如何为 watir webdriver 执行 javascript 以更改跨度 ID 内的文本

ruby-on-rails - 如何在不导致 ruby​​/rails 中出现浮点错误的情况下存储加密货币余额

ruby-on-rails - 运行 bundle install 时导致未定义方法 `dir_mode=' 的原因

javascript - 如何将 JavaScript 打包为 .NET Internet Explorer 9 插件?

macOS Catalina AudioServerPlugIn 安装

ruby - 如何从 Gemfile 安装 gems?

ruby - 如何在公司代理后面的 Mac 上安装 Ruby gems?

ruby - Sublime Text 3 和 Rspec,构建错误

ruby - 准备好的语句已经存在

css - 定位 Facebook 点赞按钮