Gem包制作及CocoaPods 插件

一、创建gem模板项目

假设我的gem命名为mytool
执行gem指令:

bundler gem mytool

二、项目开发

1、结构

将在当前目录下获取一个模板项目,结构如下:
mytool
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│ ├── console
│ └── setup
├── lib
│ ├── mytool
│ │ └── version.rb
│ └── mytool.rb
└── mytool.gemspec

我们查看lib文件夹下面的mytool.rb

require "mytool/version"

module Mytool
  class Error < StandardError; end
  # Your code goes here...
end

这是入口文件,我们可以在这里添加我们的代码。
如果需要新建其他rb文件,添加在lib文件目录下,并在mytool.rb里引入

require "yournewfile"

2、使gem支持CIL调用

一般我们使用gem安装后,执行通过CIL命令的方式进行调用,这里我们使用thor支持CIL交互接口。
具体可参考 http://whatisthor.com/
下面我们在我们的mytool.rb文件中添加代码:

require "mytool/version"
require "thor"

module Mytool
  class Error < StandardError; end

  class CLI < Thor
    desc "hello_test", "hello_test to test your gem"
    long_desc <<-LONGDESC
      hello_test to test your gem
      action    : str, test your gem
    LONGDESC
    def hello_test(action)
      puts "It is do #{action}"
    end
  end

end

我们新增个CLI的class,继承于Thor。
方法前的desc和long_desc为方法说明,后面使用指令调用时,在控制输入mytool --help hello_test可打印该说明。
完成以上代码,这样,我们就可以通过

Mytool::CLI.start(ARGV)

来接受CIL交互的调用了,
我们把bin/console文件复制一份,重命名mytool,删除console文件
我们把代码加到bin/mytool文件中

#!/usr/bin/env ruby

require "bundler/setup"
require "mytool"

# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.

# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start

# require "irb"
# IRB.start(__FILE__)

Mytool::CLI.start(ARGV)

3、修改gemspec文件

该文件描述了该gem包的基本信息,以及指定安装gem包需要的文件,这里主要修改几个地方:

spec.summary       = %q{TODO: Write a short summary, because RubyGems requires one.}
spec.description   = %q{TODO: Write a longer description or delete this line.}
spec.homepage      = "TODO: Put your gem's website or public repo URL here."
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
spec.bindir        = "exe"
spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }

summary和description:介绍和描述你这个gem包的用途
homepage:可以指定你上传git后的地址
allowed_push_host:非必须,可注释
source_code_uri:你gem代码所在git地址,这里直接指定为homepage
changelog_uri:日志记录的地址,随便写
bindir:可执行脚本在gem中的路径,这里指定为bin
executables:gem中包含的可执行文件,这些文件在bindr指定的路径下,我们这里指定console文件
更多详细信息参阅:https://guides.rubygems.org/specification-reference/

另外,因为使用了thor,也期望用户可以通过gemfile使用bundler命令安装gem,我们也要加上gem的依赖(如果gem里使用了xcodeproj也要相应加上):

 spec.add_runtime_dependency "bundler", "~> 2.0", '>= 2.0.2'
 spec.add_runtime_dependency "xcodeproj", "~> 1.0", '>= 1.9.0'
 spec.add_runtime_dependency "thor", "~> 1.0", '>= 1.0.1'

修改后文件如下:

require_relative 'lib/mytool/version'

Gem::Specification.new do |spec|
  spec.name          = "mytool"
  spec.version       = Mytool::VERSION
  spec.authors       = ["lph"]
  spec.email         = ["luph@qq.com"]

  spec.summary       = "mytool to test my gem"
  spec.description   = "mytool to test my gem. prinf someting"
  spec.homepage      = "https://github.com/opensource/mytool"
  spec.license       = "MIT"
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")

  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"

  spec.metadata["homepage_uri"] = spec.homepage
  spec.metadata["source_code_uri"] = spec.homepage
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"

  # Specify which files should be added to the gem when it is released.
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
  spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
    `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  end
  spec.bindir        = "bin"
  spec.executables   = spec.name
  spec.require_paths = ["lib"]

  spec.add_runtime_dependency "bundler", "~> 2.0", '>= 2.0.2'
  spec.add_runtime_dependency "xcodeproj", "~> 1.0", '>= 1.9.0'
  spec.add_runtime_dependency "thor", "~> 1.0", '>= 1.0.1'
end

三、Gem发布

1、本地安装gem包测试

除了把gem发布,我们可以先在本地安装gem包进行测试
在gem工程目录下执行

$ gem build mytool.gemspec

  Successfully built RubyGem
  Name: mytool
  Version: 0.1.0
  File: mytool-0.1.0.gem

将在当前目录下生成mytool-0.1.0.gem,安装该gem包

$ gem install mytool-0.1.0.gem
mytool-0.1.0.gem
Successfully installed mytool-0.1.0
Parsing documentation for mytool-0.1.0
Installing ri documentation for mytool-0.1.0
Done installing documentation for mytool after 0 seconds
1 gem installed

这样gem包就直接安装在了本地,下面我们可执行:

$ mytool --help hello_test

Usage:
  mytool hello_test

Description:
  hello_test to test your gem action : str, test your gem

输出方法说明。

执行:

$ mytool hello_test daydayup

It is do daydayup

成功调用方法。
使用以下指令移除gem包:

gem uninstall mytool

PS:这里的指令mytool本质就是调用了我们bin/mytool的可执行文件,所有刚才才重新命名了文件。

若执行命令报错:

$ mytool hello_test daydayup

Could not find gem 'rake (~> 12.0)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.

我们可使用gem list --local查看本地已安装的命令有没有rake包和版本
再查看项目Gemfile的配置:

source "https://rubygems.org"

# Specify your gem's dependencies in mytool.gemspec
gemspec

gem "rake", "~> 12.0"

若版本不对说明没有对应版本,可以选择:
1、修改gemfile rake版本号
2、将rake加到mytool.gemspec的开发依赖spec.add_development_dependency 'rake'重新build gem包安装
3、在工程目录下,bundle install

2、将代码上传git

将工程上传git仓库,作为私有tool提供给有访问权限的人使用。
使用者只需在自己的Gemfile中添加:

source "https://gems.ruby-china.com/"

# gems for xxx
gem 'mytool', git: 'https://github.com/opensource/mytool.git',tag: '0.6.3'

在所在目录中,bundle install便可安装指定git tag版本的gem包

3、Gemfile发布rubygems网站

$ curl -u tom https://rubygems.org/api/v1/api_key.yaml >  
~/.gem/credentials  
Enter host password for user 'tom':  

设定完之后发布

% gem push mytool-0.1.0.gem  
Pushing gem to RubyGems.org...  
Successfully registered gem: mytool (0.1.0) 

这样任何一个人都可以使用你写的gem了。

三、CocoaPods 插件

CocoaPods 插件本质就是个gem包,或者说cocopod本身就是一个Gem

1、创建插件模板工程

制作pod插件跟gem基本一样
安装pod gem 工具:

gem install cocoapods-plugins

执行:

pod plugins create myplugin

创建插件工程如下:
.
└── cocoapods-myplugin
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── cocoapods-myplugin.gemspec
├── lib
│ ├── cocoapods-myplugin
│ │ ├── command
│ │ │ └── myplugin.rb #Command类,设置命令
│ │ ├── command.rb
│ │ └── gem_version.rb
│ ├── cocoapods-myplugin.rb
│ └── cocoapods_plugin.rb #编写插件逻辑
└── spec
├── command
│ └── myplugin_spec.rb
└── spec_helper.rb

目录结构和gem包模板基本相同。

cocospod插件可支持两种使用方式:
plugin:在podfile中设置plugin 'cocoapods-yyplugin'
pod命令:在终端输入pod自定义命令

2、编写插件

我们这里实现pod hook,在pod install 或者update的时候,打印文本
在lib/cocoapods-myplugin/command下新建cocoapods_test_handler.rb文件,输入:

module CocoapodsMyplugin
  class PrinterTest
    def printHello
      puts "this is myplugin action"
    end
  end
end

在lib/cocoapods_plugin.rb中增加代码:

require 'cocoapods-myplugin/command'

require 'cocoapods'
require_relative 'cocoapods-myplugin/command/cocoapods_test_handler'

module CocoapodsMyplugin
  Pod::HooksManager.register('cocoapods-myplugin', :post_install) do |context|
      PrinterTest.new.printHello()
  end

  Pod::HooksManager.register('cocoapods-myplugin', :post_update) do |context|
      PrinterTest.new.printHello()
  end
end

以上,我们注册hook钩子,执行内容为printHello打印文本。
这里的context为pod的上下文,你可以使用context.methods打印看看相关方法这里就不展开了。

接着,我们执行一下脚本,重新安装插件

gem uninstall cocoapods-myplugin && gem build cocoapods-myplugin.gemspec && gem install cocoapods-myplugin-0.0.1.gem

PS: 我们可以使用pod plugins installed查看已安装的pod插件:

Installed CocoaPods Plugins:
    - cocoapods-deintegrate                 : 1.0.5
    - cocoapods-disable-podfile-validations : 0.1.1
    - cocoapods-generate                    : 2.1.0
    - cocoapods-myplugin                    : 0.0.1 (post_install and
    post_update hooks)
    - cocoapods-open                        : 0.0.8
    - cocoapods-plugins                     : 1.0.0
    - cocoapods-repo-update                 : 0.0.4 (pre_install hook)
    - cocoapods-search                      : 1.0.1
    - cocoapods-stats                       : 1.1.0 (post_install hook)
    - cocoapods-trunk                       : 1.5.0
    - cocoapods-try                         : 1.2.0

然后在Demo工程中修改podfile,新增代码plugin 'cocoapods-yyplugin'

platform :ios, '10.0'

plugin 'cocoapods-yyplugin'

source 'https://github.com/CocoaPods/Specs.git'

target 'HeifSDKDemo' do
  pod 'heif', '1.0.16'
end

然后我们pod install,出现如下输出:

Analyzing dependencies
Downloading dependencies
Generating Pods project
Integrating client project
this is myplugin action

说明插件已正常运行

3、自定义pod命令

我们打开lib/cocoapods-myplugin/command/myplugin.rb,看注释,模板已经举例告诉你如何创建一个pod list 的子命令了。这里我们先编写自己的插件命令。
主要修改的地方:
self.summary :简述
self.description:详细模式
def initialize(argv):命令的的初始化方法,在此接收参数
def validate!:用于检查输入实参的有效性,如果校验失败,会通过调用 help! 方法来输出帮助信息
self.arguments:设置接收参数的内容
修改后文件内容如下:

require_relative 'cocoapods_test_handler'

module Pod
  class Command
    # This is an example of a cocoapods plugin adding a top-level subcommand
    # to the 'pod' command.
    #
    # You can also create subcommands of existing or new commands. Say you
    # wanted to add a subcommand to `list` to show newly deprecated pods,
    # (e.g. `pod list deprecated`), there are a few things that would need
    # to change.
    #
    # - move this file to `lib/pod/command/list/deprecated.rb` and update
    #   the class to exist in the the Pod::Command::List namespace
    # - change this class to extend from `List` instead of `Command`. This
    #   tells the plugin system that it is a subcommand of `list`.
    # - edit `lib/cocoapods_plugins.rb` to require this file
    #
    # @todo Create a PR to add your plugin to CocoaPods/cocoapods.org
    #       in the `plugins.json` file, once your plugin is released.
    #
    class Myplugin < Command
      self.summary = 'Short description of cocoapods-myplugin.'

      self.description = <<-DESC
        Longer description of cocoapods-myplugin.
      DESC

      #用于返回该命令的可选项及对应的描述,concat(super)把父类的option拼上
      def self.options
        [
            ['--no-milk', 'Don’t add milk to the beverage'], #milk=false
            ['--sweetener=[sugar|honey]', 'Use one of the available sweeteners'], #sweetener
        ].concat(super)
      end

      self.arguments = [
          CLAide::Argument.new('ACTION', false, true ),
      ]

      def initialize(argv)
        @name = argv.shift_argument # 取第一个参数
        @actions = argv.arguments! # 取第一个参数之外剩下的参数(不包括flag、option)
        @add_milk = argv.flag?('milk', true) #取bool类型的值,--no-代表false,默认true
        @sweetener = argv.option('sweetener') #取option的值
        super
      end

      #用于检查输入实参的有效性,如果校验失败,会通过调用 help! 方法来输出帮助信息
      def validate!
        super
        if @sweetener && !%w(sugar honey).include?(@sweetener)
          help! "`#{@sweetener}' is not a valid sweetener."
        end
      end

      def run
        if @add_milk
          puts '* Adding milk…'
        end
        if @sweetener
          puts "* Adding #{@sweetener}…"
        end
        puts "action to #{@actions} #{@name}"
        CocoapodsMyplugin::PrinterTest.new.printHello()
      end

    end
  end
end

a、option选项
这里我们增加了option选项,通过self.options可设置选项说明。
option:这里增加了–sweetener选项,通过@sweetener = argv.option('sweetener')获取值。
flag:是一个特殊的option,表示只有bool值。这里 --no-milk是一个特殊的选项,其中–no-表示milk=false。bool选项通过argv.flag?('milk', true)获取,设置了默认true

b、参数列表
参数说明

self.arguments = [
          CLAide::Argument.new('ACTION', false, true ),
      ]

声明了命令所接受的参数值的Usage输出(就是–help后命令的说明)
我们查看CLAided的构造方法:

module CLAide
  class Argument
   
    def initialize(names, required, repeatable = false)
      @names = Array(names)
      @required = required
      @repeatable = repeatable
    end
end
  • names 就是在 Usage 中输出的 [ACTION …]
  • require 表示该 Argument 是否为必传参数,可选参数Usage会用 [ ] 将其包裹起来。也就是命令默认是不需要传 ACTION
  • repeatable 表示该 Argument 是否可以重复多次出现,类似可变参数的意思。如果设置为true,那么会在names 的Usage输出信息后面会添加 … 表示该参数为复数参数

参数获取
通过@name = argv.shift_argument我们可获取参数列表的第一个值
通过@actions = argv.arguments!获取除第一个参数的之外的其他参数list
注意:这里获取的arguments是不包括option的

例如:我们输入

pod myplugin a b c --sweetener=sugar

@name -> a
@actions -> [b,c]

b、数据验证
我们通过def validate!方法编写参数校验逻辑,例如这里我们

if @sweetener && !%w(sugar honey).include?(@sweetener)
     help! "`#{@sweetener}' is not a valid sweetener."
end

判断option固定的两个可选值,否则就报help!的内容。

完成以上代码后,我们执行一下脚本重新安装插件

gem uninstall cocoapods-myplugin && gem build cocoapods-myplugin.gemspec && gem install cocoapods-myplugin-0.0.1.gem

执行pod自定义命令:

$ pod myplugin asa  --sweetener=sugar aasa dss
* Adding milk…
* Adding sugar…
action to ["aasa", "dss"] asa
this is myplugin actio

参考博文:
https://www.jianshu.com/p/5889b25a85dd
https://juejin.cn/post/6871919379391447047

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值