前因
为什么要学习Ruby?我们都知道iOS 强大的包管理工具Cocoapods,Cocoapods正是使用Ruby进行编写,那么想要了解Cocoapods内部的实现原理,以及他架构方式,想必我们必须要学习他所使用的语言---即ruby。
初识
Ruby 背景
Ruby 这门语言呢是由日本人松本行弘 在20世纪90年代设计并开发的一种语言。那么Ruby呢,其实是一中完全面向对象的语言,在Ruby 里也是万物皆对象。当然Ruby 和 Python、JavaScript等脚本语言一样,它是一种需要解释的语言(也就是说它不需要进行编译、链接形成可执行二进制文件,而是需要一个解释器去解释执行)。据说火起来其实在2000年以后了(这个我就不知道了....)
Ruby 安装
MacOS 会内置Ruby, 默认Ruby一般版本较低,且不好升级
一般情况下,我们Linux 、MacOs 会采用三种方式安装Ruby:
采用homebrew安装Ruby,
brew install ruby
, 配置环境变量 ``` echo 'export PATH="/usr/local/opt/ruby/bin:$PATH"' >> ~/.zshrc, 但是这是属于自管理ruby版本的方式,不推荐使用-
采用 RVM安装, RVM也就是Ruby Version Manager的首字母。可想而知他就是Ruby 版本管理器。
首先需要安装RVM:
curl -sSL https://get.rvm.io | bash -s stable --auto-dotfiles
,--auto-dotfiles 其实就是在安装rvm的时候,他会主动将环境变量配置好,不用我们自己在打开.bashrc/.zshrc 配置了。重启终端 | source ~/.zshrc, 输入命令
rvm --verison
, 输出正常版本号则安装rvm成功rvm list known
列出已知的所有Ruby版本-
rvm install 3.1.2
安装制定ruby 版本-
可能会出现一个以下错误 :
Error running '__rvm_make -j12',
please read /Users/haoyh02/.rvm/log/1704359555_ruby-3.1.2/make.log
There has been an error while running make. Halting the installation.通过查看make.log,我们发现报错是发生在openssl@3,大概原因应该就是安装Ruby的时候需要编译用到openssl, 当我们没有指定openssl路径的情况下,它就会默认到/usr/local/Cellar/ 下的openssl使用,但是正常我们使用brew 更新或者安装过openssl的话路径并不在这里,应该在/usr/local/opt/openssl@3 这里。这也就是导致编译的时候openssl的某些方法找不到等等错误
-
解决版本则是:使用命令
rvm install 3.1.2 --with-openssl-dir=
brew --prefix openssl| rvm install 3.1.2 --with-openssl-dir='/usr/local/opt/openssl@3'
:) 这个问题也是让我很长时间找不到问题的原因。
-
安装Ruby成功后, 使用
rvm use 3.1.2 --default
, 我们也可以使用rvm use 其他版本切换ruby 版本ruby --verison
查看版本是否是我们安装的版本
-
采用rbenv 安装,rbenv 是Ruby environment 的缩写,也就是Ruby 环境,同样也是一个Ruby 版本管理器。
安装rbenv :
brew install rbenv ruby-build
, 当然也可以采用非home brew 手动安装:https://ruby-china.org/wiki/rbenv-guide, 手动安装就不赘述了,麻烦一些。安装完成后,
rbenv install --list
列出可安装的Ruby 版本rbenv install 3.1.2
安装制定ruby 版本rbenv global 3.1.2
全局使用3.1.2的ruby, 类似于rvm use
那不禁就想问:那这三种方式的区别是什么,我们应该用哪一种?
那么对于homebrew 的方式(也就是第一种方式),只能安装一个版本的ruby,如果你多ruby开发,那么这种方式不合适了,因为它并不是ruby 版本管理工具,对吧?
其他两种来说,我也查阅了一些资料,总体来说rvm出来的更早,功能更全,但是rbenv也很好。换言之就是用谁都行,只要你觉得好就好~
所以推荐来说的话,还是使用rvm或者rbenv进行ruby安装以及版本管理~
对于Windows的安装请查阅文档:https://rubyinstaller.cn/ (因为我是Mac, 没有Windows,没有尝试,所以没有发言权😭)
ok,现在来说我们已经拥有基本的开发ruby的条件。
相关概念
对于我刚开始学习的时候,这几个概念也是对我造成了一定的困扰,并且我们要知道以下几个东西会一直陪在我们开发过程中的。所以对于他们的了解,可想而知多重要~
IRb
可交互式Ruby shell, 什么意思呢?就是我们可以直接在终端进行用IRb 命令解释Ruby 代码,举个栗子:
% IRb
A gemspec development dependency (bacon, ~> 1.1) is being overridden by a Gemfile dependency (bacon, >= 0).
This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement
3.2.1 :001 > puts "hello world"
hello world
=> nil
3.2.1 :002 >
如上,我们在命令行输入IRb后回车,就会进入到可交互shell 编程,我们可以去编写ruby,然后单行解释。
Gem
Gem 相当于是用Ruby 写的三方Gem 库的管理,通过Gem 我们可以安装所有现有的三方gem库。其实三方gem 包管理器。通过gem 可以进行安装gem 包。我们可以通过gem list --local 查看本地安装了的gem 包。其实我们回想一下cocoapods的安装,gem install cocoapods 对吧?所以其实我们的cocoapods他就是一个gem 包。
Bundler
说到bundler 那就不得不提,另外的两个工程里的文件:
Gemfile
Gemfile.lock
看着这两个是否有点感觉好熟悉的样子?想一想我们iOS工程采用Cocoapods管理的时候是否有类似的文件?
是的,我们iOS 工程如果采用Cocoapods管理的话,我们会有Podfile和Podfile.lock。 如果你对这两个文件了解的话,那其实Gemfile Gemfile.lock 也是同样的功能,因为Cocoapods的设计就是从Gem的设计借鉴而来的。
好了,我们还是解释一下这两个文件的作用吧
Gemfile: 同样是用来描述当前包依赖的三方gem包的,如下:
SKIP_UNRELEASED_VERSIONS = false
def cp_gem(name, repo_name, branch = 'master', path: false)
return gem name if SKIP_UNRELEASED_VERSIONS
opts = if path
{ :path => "../#{repo_name}" }
else
url = "https://github.com/CocoaPods/#{repo_name}.git"
{ :git => url, :branch => branch }
end
gem name, opts
end
source 'https://rubygems.org'
gemspec
group :development do
cp_gem 'claide', 'CLAide'
cp_gem 'cocoapods-core', 'Core'
cp_gem 'cocoapods-deintegrate', 'cocoapods-deintegrate'
cp_gem 'cocoapods-downloader', 'cocoapods-downloader'
end
Gemfile.lock: 如同它的类型名一样,他是用来锁定的,锁定什么?锁定你所依赖的三方gem包的版本的。同样和Podfile.lock一样防止多人开发协作,版本不一致的问题。
OK,那么我们来说一说它与Bundler的关系:
其实Bundler 和 我们Pod 命令很相似,在当前根目录下执行bundler install, 它会进行依赖解析、安装依赖。这是不是就和我们pod install 一样呢?
那么这么看呢?bundler 其实用来工程依赖解析、安装装的,其实具体的安装是通过gem安装的。
这样他们之间的关系是不是更加清晰了~
Rake
当我第一眼看到这个单词,我就想到了Make,但事实上他并不是一个单词,它是一个组合:Ruby Make。
我们在工程里可以看到有一个文件叫Rakefile。那么其实他们是相关联的。
刚才我们提到了Make,这个可不是仅仅是一个词啊,我想到的是C语言的构建工具Make, 同样他也有对应的文件makefile。
也就是说我们可以在Rakefile 定义一些列的任务,相当于任务脚本,执行rake 命令就可以执行任务。
比如CocoaPods工程下的Rakefile为:
# Bootstrap task
#-----------------------------------------------------------------------------#
desc 'Initializes your working copy to run the specs'
task :bootstrap, :use_bundle_dir? do |_, args|
title 'Environment bootstrap'
puts 'Updating submodules'
execute_command 'git submodule update --init --recursive'
if system('which bundle')
puts 'Installing gems'
if args[:use_bundle_dir?]
execute_command 'env bundle install --path ./travis_bundle_dir'
else
execute_command 'env bundle install'
end
else
$stderr.puts "\033[0;31m" \
"[!] Please install the bundler gem manually:\n" \
' $ [sudo] gem install bundler' \
"\e[0m"
exit 1
end
end
....
开发环境
开发环境可以说是我搞的时间最长的一个东西了。你知道如果你用VSCode 打开一个Gem项目,它的所有依赖、类的快速跳转、解释都没有、甚至提示都没有这对于开发来说多痛苦,更对于我想要阅读Cocoapods源码来说多痛苦,更是让我想到了?怎么开发gem 如果我有依赖三方gem。
首先我选择是VSCode Ide,作为开发IDE。其次经过研究,需要安装两个插件Ruby LSP、 Ruby Solargraph。安装了这两个插件按理来说就能实现方法、类跳转、解释。但是实际远比我想的复杂。当我安装了这两个插件之后,用VS code 打开工程,一直报错,错误则是LSP 无法连接服务、Solargraph启动失败等等,对应的VSCode 就不会有任何的解释、提示、跳转。
解决问题的过程总是复杂的、艰难的。最后发现我们需要进行以下几个步骤:
需要gem install solargraph
修改插件的设置,将LSP 的 ruby 管理工具改为你使用的,我则是rvm
增加setting.json 设置
{
"solargraph.commandPath": "/Users/haoyh02/.rvm/rubies/ruby-3.2.1/bin/",
"rubyLsp.rubyVersionManager": "rvm",
"solargraph.useBundler": true,
"solargraph.references": true,
"solargraph.autoformat": true,
"solargraph.formatting": true
}
- 在Gemfile中引用 LSP, Solargraph
gem 'ruby-lsp'
gem 'solargraph'
删掉Gemfile.lock, bundler install
重新打开vsCode, 打开工程即可
总结
兜兜转转,一直在学习的路上,但是新手么,难免会踩坑,当然踩坑也没有更好的方式解决,只能自己查资料、尝试解决。但是解决了就会收获成就感哦。
希望以上的经验可以帮助正在需要了解Ruby, 或者和我一样想要了解Cocoapods底层的朋友一些帮助。