内容简介:CocoaPods 为开发者提供了插件注册功能,可以使用刚开始写 CocoaPods 插件时,对其怎么执行没有首先,由于
CocoaPods 为开发者提供了插件注册功能,可以使用 pod plugins create NAME
命令创建插件,并在 Podfile 中通过 plugin 'NAME'
语句引入插件。虽然在一般情况下很少使用这个功能,但在某些场景下,利用插件能比较方便快捷地解决问题,比如
清除 input
, output
文件
、 创建 Podfile DSL
等。
刚开始写 CocoaPods 插件时,对其怎么执行没有 require
的插件代码比较好奇,但限于对 ruby 以及 gem 知识的了解,也就没有进一步地探索其实现原理,毕竟首要任务是解决工作问题。如今回过头来看这个问题,发现实现起来还是比较简单的。来看下 CocoaPods 是如何实现的。
实现探索
首先,由于 pod install
过程会涉及到插件的加载,所以直接查看 installer.rb
文件:
# Runs the registered callbacks for the plugins post install hooks. # def run_plugins_post_install_hooks context = PostInstallHooksContext.generate(sandbox, aggregate_targets) HooksManager.run(:post_install, context, plugins) end # Runs the registered callbacks for the plugins pre install hooks. # # @return [void] # def run_plugins_pre_install_hooks context = PreInstallHooksContext.generate(sandbox, podfile, lockfile) HooksManager.run(:pre_install, context, plugins) end # Ensures that all plugins specified in the {#podfile} are loaded. # # @return [void] # def ensure_plugins_are_installed! require 'claide/command/plugin_manager' loaded_plugins = Command::PluginManager.specifications.map(&:name) podfile.plugins.keys.each do |plugin| unless loaded_plugins.include? plugin raise Informative, "Your Podfile requires that the plugin `#{plugin}` be installed. Please install it and try installation again." end end end
其中 run_plugins_pre_install_hooks
和 run_plugins_post_install_hooks
分别执行了插件注册的 pre_install
和 pod_install
方法, ensure_plugins_are_installed
则确认插件是否已被安装。
接下来看下 Command::PluginManager
,这个类在 claide/command/plugin_manager
文件内,属于 claide
gem :
# @return [Array<Gem::Specification>] Loads plugins via RubyGems looking # for files named after the `PLUGIN_PREFIX_plugin` and returns the # specifications of the gems loaded successfully. # Plugins are required safely. # def self.load_plugins(plugin_prefix) loaded_plugins[plugin_prefix] ||= plugin_gems_for_prefix(plugin_prefix).map do |spec, paths| spec if safe_activate_and_require(spec, paths) end.compact end # @group Helper Methods # @return [Array<[Gem::Specification, Array<String>]>] # Returns an array of tuples containing the specifications and # plugin files to require for a given plugin prefix. # def self.plugin_gems_for_prefix(prefix) glob = "#{prefix}_plugin#{Gem.suffix_pattern}" Gem::Specification.latest_specs(true).map do |spec| matches = spec.matches_for_glob(glob) [spec, matches] unless matches.empty? end.compact end # Activates the given spec and requires the given paths. # If any exception occurs it is caught and an # informative message is printed. # # @param [Gem::Specification] spec # The spec to be activated. # # @param [String] paths # The paths to require. # # @return [Bool] Whether activation and requiring succeeded. # def self.safe_activate_and_require(spec, paths) spec.activate paths.each { |path| require(path) } true rescue Exception => exception # rubocop:disable RescueException message = "\n---------------------------------------------" message << "\nError loading the plugin `#{spec.full_name}`.\n" message << "\n#{exception.class} - #{exception.message}" message << "\n#{exception.backtrace.join("\n")}" message << "\n---------------------------------------------\n" warn message.ansi.yellow false end
以上代码调用几个的 Gem::Specification
方法如下:
# 获取最新 spec 集合 # Return the latest specs, optionally including prerelease specs if prerelease is true. latest_specs(prerelease = false) # 获取 gem 中匹配的文件路径 # Return all files in this gem that match for glob. matches_for_glob(glob) # 激活 spec,注册并将其 lib 路径添加到 $LOAD_PATH ($LOAD_PATH 环境变量存储 require 文件时查找的路径) # Activate this spec, registering it as a loaded spec and adding it's lib paths to $LOAD_PATH. Returns true if the spec was activated, false if it was previously activated. Freaks out if there are conflicts upon activation. activate()
可以看到在 loaded_plugins[plugin_prefix]
为空的情况下,程序会执行 plugin_gems_for_prefix
方法, plugin_gems_for_prefix
方法通过 latest_specs
获取了最新的 spec ,并通过 spec 的 matches_for_glob
方法对文件进行匹配,当 spec 中存在匹配 "#{prefix}_plugin#{Gem.suffix_pattern}"
格式的文件时,则视其为 CocoaPods 插件。在拿到插件及其匹配文件后, safe_activate_and_require
方法将文件加入 $LOAD_PATH 中并 require 之。
另外 CLAide::Command
类会在 run
类方法中加载所有插件,然后根据解析后的信息,执行对应的命令:
# @param [Array, ARGV] argv # A list of (remaining) parameters. # # @return [Command] An instance of the command class that was matched by # going through the arguments in the parameters and drilling down # command classes. # def self.run(argv = []) plugin_prefixes.each do |plugin_prefix| PluginManager.load_plugins(plugin_prefix) end argv = ARGV.coerce(argv) command = parse(argv) ANSI.disabled = !command.ansi_output? unless command.handle_root_options(argv) command.validate! command.run end rescue Object => exception handle_exception(command, exception) end
对于通过 pod plugin create
命令创建的插件来说,lib 目录下都会自动生成一个 cocoapods_plugin.rb
文件,这个文件就是用来标识此 gem 为 CocoaPods 插件的。如果想手动创建 CocoaPods 插件,需要满足以下两个条件:
# Handles plugin related logic logic for the `Command` class. # # Plugins are loaded the first time a command run and are identified by the # prefix specified in the command class. Plugins must adopt the following # conventions: # # - Support being loaded by a file located under the # `lib/#{plugin_prefix}_plugin` relative path. # - Be stored in a folder named after the plugin. # - 支持通过 `lib/#{plugin_prefix}_plugin` 路径的文件加载 # (也就是说,如果要对外暴露插件内部存的方法,需要在此文件中 require 之,比如自定义的 Podfile DSL 文件) # - 保存在以插件命名的文件夹中
在 CocoaPods 上下文中,以上的 plugin_prefix
如下:
self.plugin_prefixes = %w(claide cocoapods)
小结
如果需要外部 gem 以插件的形式提供某些功能,可以通过和 CocoaPods 一样的方式实现,即规定特定的命名规则,然后通过 Gem::Specification
提供的方法获取满足条件的 gem ,再 require 入口文件:
spec = Gem::Specification.find_by_name('naruto') spec.activate matches = spec.matches_for_glob('naruto') matches.each do |path| require(path) end
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用 Webapck 优化 VS Code 插件加载性能
- webpack loader—自己写一个按需加载插件
- Rust 实现动态库加载和基于此功能实现的插件管理
- shade插件解决打包后无法加载spring xsd文件办法
- 页面滚动无限加载jQuery插件jquery.infinitescroll.js使用说明
- 图片懒加载插件Vue-Lazyload@1.3.0存在bug及临时解决方法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。