高级特性 #

原生扩展 #

许多 Gem 包含 C 扩展以提高性能。了解如何构建和分发原生扩展。

基本原生扩展 #

目录结构 #

text
mygem/
├── ext/
│   └── mygem/
│       ├── extconf.rb
│       ├── mygem.c
│       └── mygem.h
├── lib/
│   └── mygem.rb
└── mygem.gemspec

extconf.rb #

ruby
# ext/mygem/extconf.rb
require 'mkmf'

# 检查依赖库
have_header('stdio.h')
have_library('c')

# 创建 Makefile
create_makefile('mygem/mygem')

C 扩展代码 #

c
// ext/mygem/mygem.c
#include <ruby.h>
#include "mygem.h"

VALUE MygemModule = Qnil;

void Init_mygem(void) {
    MygemModule = rb_define_module("Mygem");
    
    rb_define_method(MygemModule, "hello", method_hello, 0);
}

VALUE method_hello(VALUE self) {
    return rb_str_new_cstr("Hello from C!");
}

Ruby 接口 #

ruby
# lib/mygem.rb
require 'mygem/mygem'

module Mygem
  class Error < StandardError; end
  
  def self.greet
    hello
  end
end

gemspec 配置 #

ruby
# mygem.gemspec
Gem::Specification.new do |spec|
  spec.name          = "mygem"
  spec.version       = "0.1.0"
  spec.extensions    = ["ext/mygem/extconf.rb"]
  spec.files         = Dir["{ext,lib}/**/*"]
  spec.require_paths = ["lib"]
  
  # 开发依赖
  spec.add_development_dependency "rake-compiler"
end

Rakefile 配置 #

ruby
# Rakefile
require "bundler/gem_tasks"
require "rake/extensiontask"

Rake::ExtensionTask.new("mygem") do |ext|
  ext.lib_dir = "lib/mygem"
end

task default: :compile

跨平台编译 #

ruby
# Rakefile
require "rake/extensiontask"

Rake::ExtensionTask.new("mygem") do |ext|
  ext.lib_dir = "lib/mygem"
  ext.cross_compile = true
  ext.cross_platform = %w[x86-mingw32 x64-mingw32]
end

预编译二进制 #

ruby
# 使用 rake-compiler-dock
# Rakefile
require "rake/extensiontask"
require "rake/compiler_dock"

Rake::ExtensionTask.new("mygem") do |ext|
  ext.cross_compile = true
  ext.cross_platform = %w[x86-mingw32 x64-mingw32]
end

namespace :gem do
  desc "Build cross-compiled gems"
  task :cross do
    Rake::CompilerDock.sh "bundle exec rake gem:native"
  end
end

平台支持 #

平台标识 #

平台 标识
Linux x86_64 x86_64-linux
Linux ARM arm-linux
macOS x86_64 x86_64-darwin
macOS ARM arm64-darwin
Windows 32-bit x86-mingw32
Windows 64-bit x64-mingw32
Java java

平台特定代码 #

ruby
# lib/mygem.rb
module Mygem
  if RUBY_PLATFORM =~ /darwin/
    require 'mygem/platform/darwin'
  elsif RUBY_PLATFORM =~ /linux/
    require 'mygem/platform/linux'
  elsif RUBY_PLATFORM =~ /mingw|mswin/
    require 'mygem/platform/windows'
  else
    raise "Unsupported platform: #{RUBY_PLATFORM}"
  end
end

Gemfile 平台配置 #

ruby
# Gemfile
platforms :ruby do
  gem 'pg'
end

platforms :jruby do
  gem 'activerecord-jdbcpostgresql-adapter'
end

platforms :mingw, :x64_mingw do
  gem 'tzinfo-data'
end

Gem 签名 #

生成证书 #

bash
# 生成私钥
gem cert --build your@email.com

# 输出
# Private key:   /Users/you/.gem/gem-private_key.pem
# Certificate:   /Users/you/.gem/gem-public_cert.pem

签名 Gem #

ruby
# mygem.gemspec
Gem::Specification.new do |spec|
  spec.name          = "mygem"
  spec.version       = "0.1.0"
  
  # 签名配置
  spec.signing_key   = File.expand_path("~/.gem/gem-private_key.pem")
  spec.cert_chain    = [File.expand_path("~/.gem/gem-public_cert.pem")]
end

构建签名 Gem #

bash
# 构建签名 Gem
gem build mygem.gemspec

# 验证签名
gem cert --verify mygem-0.1.0.gem

安装签名 Gem #

bash
# 添加信任证书
gem cert --add ~/.gem/gem-public_cert.pem

# 安装时验证签名
gem install mygem -P HighSecurity

安全级别 #

级别 描述
NoSecurity 不验证签名
LowSecurity 验证签名但不检查证书链
MediumSecurity 验证签名和证书链
HighSecurity 验证签名、证书链和信任度

私有源 #

搭建私有源 #

使用 Gemstash #

bash
# 安装 Gemstash
gem install gemstash

# 启动服务
gemstash start

# 默认地址
# http://localhost:9292

配置 Gemstash #

yaml
# ~/.gemstash/config.yml
:cache_type: memory
:base_path: ~/.gemstash
:db_adapter: sqlite3
:bind: tcp://0.0.0.0:9292

使用私有源 #

ruby
# Gemfile
source 'https://rubygems.org'
source 'http://localhost:9292/private' do
  gem 'my_private_gem'
end

使用 Gemfury #

bash
# 安装 Gemfury CLI
gem install gemfury

# 推送 Gem
fury push mygem-0.1.0.gem --api-token YOUR_TOKEN

# 配置源
gem sources --add https://TOKEN@gem.fury.io/your-org/

使用 Nexus #

ruby
# Gemfile
source 'https://nexus.example.com/repository/rubygems/'

gem 'my_private_gem'

认证配置 #

bash
# 设置 API Key
gem config set https://gems.example.com api_key YOUR_API_KEY

# 或使用环境变量
export GEM_HOST_API_KEY=YOUR_API_KEY

插件系统 #

创建 Gem 插件 #

ruby
# lib/rubygems_plugin.rb
module Gem
  class Commands::MyCommand < Gem::Command
    def initialize
      super 'mycommand', 'My custom gem command'
    end

    def execute
      puts "Executing my custom command!"
    end
  end
end

Gem::CommandManager.instance.register_command :mycommand

插件安装钩子 #

ruby
# lib/rubygems_plugin.rb
Gem.post_install do |installer|
  puts "Gem #{installer.spec.name} has been installed!"
end

Gem.pre_uninstall do |uninstaller|
  puts "Gem #{uninstaller.spec.name} will be uninstalled!"
end

插件命令 #

ruby
# lib/rubygems_plugin.rb
module Gem
  class Commands::AuditCommand < Gem::Command
    def initialize
      super 'audit', 'Audit installed gems for vulnerabilities'
      add_option('--all', 'Audit all gems') do |value, options|
        options[:all] = true
      end
    end

    def execute
      if options[:all]
        audit_all_gems
      else
        audit_gem(get_one_gem_name)
      end
    end

    private

    def audit_all_gems
      Gem::Specification.each do |spec|
        puts "Auditing #{spec.name}..."
      end
    end

    def audit_gem(name)
      spec = Gem::Specification.find_by_name(name)
      puts "Auditing #{spec.name} version #{spec.version}..."
    end
  end
end

Gem::CommandManager.instance.register_command :audit

Rake 任务 #

内置 Rake 任务 #

ruby
# Rakefile
require "bundler/gem_tasks"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec)

task default: :spec

# Bundler 提供的任务
# rake build    - 构建 gem
# rake install  - 安装 gem
# rake release  - 发布 gem

自定义 Rake 任务 #

ruby
# Rakefile
namespace :gem do
  desc "Build and push gem"
  task :publish => [:build, :push]

  task :build do
    sh "gem build mygem.gemspec"
  end

  task :push do
    sh "gem push mygem-*.gem"
  end

  desc "Clean build artifacts"
  task :clean do
    sh "rm -f *.gem"
  end
end

namespace :docs do
  desc "Generate YARD documentation"
  task :generate do
    sh "yard doc lib/**/*.rb"
  end

  desc "Start YARD server"
  task :server do
    sh "yard server"
  end
end

Gem 规范高级字段 #

完整 gemspec 示例 #

ruby
# mygem.gemspec
# frozen_string_literal: true

Gem::Specification.new do |spec|
  # 基本信息
  spec.name          = "mygem"
  spec.version       = "0.1.0"
  spec.authors       = ["Your Name"]
  spec.email         = ["your@email.com"]
  spec.summary       = "A short summary"
  spec.description   = "A longer description"
  spec.homepage      = "https://example.com"
  spec.license       = "MIT"
  
  # Ruby 版本要求
  spec.required_ruby_version = ">= 2.7.0"
  spec.required_rubygems_version = ">= 3.0.0"
  
  # 文件配置
  spec.files         = Dir["{lib,exe}/**/*", "LICENSE", "README.md"]
  spec.bindir        = "exe"
  spec.executables   = ["mygem"]
  spec.require_paths = ["lib"]
  
  # 原生扩展
  spec.extensions    = ["ext/mygem/extconf.rb"]
  
  # 签名
  spec.signing_key   = File.expand_path("~/.gem/gem-private_key.pem")
  spec.cert_chain    = [File.expand_path("~/.gem/gem-public_cert.pem")]
  
  # 元数据
  spec.metadata = {
    "homepage_uri" => "https://example.com",
    "source_code_uri" => "https://github.com/user/mygem",
    "changelog_uri" => "https://github.com/user/mygem/blob/main/CHANGELOG.md",
    "bug_tracker_uri" => "https://github.com/user/mygem/issues",
    "documentation_uri" => "https://docs.example.com",
    "mailing_list_uri" => "https://groups.google.com/group/mygem",
    "wiki_uri" => "https://wiki.example.com",
    "funding_uri" => "https://patreon.com/user",
    "rubygems_mfa_required" => "true"
  }
  
  # 运行时依赖
  spec.add_dependency "nokogiri", "~> 1.14"
  spec.add_dependency "json", ">= 2.6"
  
  # 开发依赖
  spec.add_development_dependency "rspec", "~> 3.12"
  spec.add_development_dependency "rubocop", "~> 1.50"
  spec.add_development_dependency "yard", "~> 0.9"
  
  # 可选依赖
  spec.add_optional_dependency "redis", "~> 5.0"
end

性能优化 #

延迟加载 #

ruby
# lib/mygem.rb
module Mygem
  autoload :Client, 'mygem/client'
  autoload :Request, 'mygem/request'
  autoload :Response, 'mygem/response'
end

条件依赖 #

ruby
# lib/mygem.rb
module Mygem
  def self.redis_available?
    defined?(Redis)
  end
  
  def self.redis
    @redis ||= Redis.new if redis_available?
  end
end

缓存机制 #

ruby
# lib/mygem/cache.rb
module Mygem
  class Cache
    def initialize
      @cache = {}
    end

    def fetch(key, &block)
      @cache[key] ||= yield
    end

    def clear
      @cache.clear
    end
  end
end

调试技巧 #

本地开发调试 #

ruby
# 使用 binding.irb
def debug_method
  result = some_calculation
  binding.irb  # 打开交互式调试
  result
end

日志记录 #

ruby
# lib/mygem/logger.rb
require 'logger'

module Mygem
  class << self
    attr_accessor :logger

    def logger
      @logger ||= Logger.new($stdout).tap do |log|
        log.level = Logger::INFO
      end
    end
  end
end

# 使用
Mygem.logger.debug "Debug message"
Mygem.logger.info "Info message"

错误处理 #

ruby
# lib/mygem/errors.rb
module Mygem
  class Error < StandardError; end
  class ConfigurationError < Error; end
  class ConnectionError < Error; end
  class TimeoutError < Error; end
  class AuthenticationError < Error; end
  class RateLimitError < Error; end

  class << self
    def with_error_handling
      yield
    rescue Timeout::Error => e
      raise TimeoutError, "Request timed out: #{e.message}"
    rescue SocketError => e
      raise ConnectionError, "Connection failed: #{e.message}"
    rescue => e
      raise Error, "Unexpected error: #{e.message}"
    end
  end
end

多版本共存 #

版本选择 #

ruby
# lib/mygem.rb
module Mygem
  module Version1
    # 旧版本 API
  end

  module Version2
    # 新版本 API
  end

  def self.use_version(version)
    case version
    when 1
      include Version1
    when 2
      include Version2
    else
      raise "Unknown version: #{version}"
    end
  end
end

弃用警告 #

ruby
# lib/mygem/deprecated.rb
module Mygem
  module Deprecated
    def self.included(base)
      warn "[DEPRECATION] #{base} is deprecated. Use #{base}V2 instead."
    end
  end
end

下一步 #

现在你已经掌握了 RubyGems 的高级特性,接下来学习 最佳实践 了解更多开发技巧!

最后更新:2026-03-28