对于从Node.js转Ruby的人很可能会有和我一样的疑惑,为什么要有Bundler这个东西?Rubygems不够吗?

从Node.js到Ruby的包管理器

在Node的世界里,依赖管理是由npm来完成的。所有依赖信息都写在package.json里面之后,一个npm install就能安装所有的依赖,然后直接运行程序即可。简单方便。

那当然是方便了,因为Node出来的太晚了,包管理器已经很成熟,所以才成了现在的这个样子。

RubyGems登场

我们把时间倒回去回到Ruby的早期,其包管理器RubyGems被创造出来的时候。在当时,RubyGems的使命其实很简单,就是安装依赖包(Ruby的包有个专门的名字叫做gem)。具体来说是,指定一个包名和版本号,由包管理器来安装指定版本的包以及其所有依赖的包。(当然如果没有指定版本号那当然是默认安装最新的了)。

在这个用例上,npm install xxxxgem install xxxx的功能是一样的。

只不过在行为上有一点区别。npm默认装在当前目录(在设计的时候就考虑到这个功能),而gem装在系统目录(和pip一样,早期的包管理器都是装在全局的,Ruby的每个包都带有版本号)

在gem里,它的依赖信息都在写在xxx.gemspec文件里(注意不是Gemfile,Bundler的文件)。当你gem install的时候,gem会根据gemspec里面的依赖信息来获取对应的依赖包。

团队协作的大麻烦

有了gem命令以后,Ruby开发起来就确实方便多了。需要什么包就用gem下一个,然后查查文档就能用了。对于个人开发者来说很好用。

但是一旦有团队协作,问题就来了。现在假设你要接手开发一个Ruby项目,你手上有项目的全部源码。那么首先你要要做的第一步就是安装所有的依赖库,因为你至少要先跑起来嘛!

好,装吧。

npm install,不对哟,这里是Ruby世界。哦,那么gem install。对不起,你会得到一个错误告诉你必须至少指定一个gem名称!想想吧,gem是用来装单个gem的,而不能用来装一个项目的依赖。这主要是因为:

  • 绝大多数Ruby项目本身不是gem,所以没必要有.gemspec文件(顾名思义这个文件是用来描述gem的元信息的)

  • 由于没有.gemspec文件,也就无从记录依赖有哪些了。这导致gem install就算想装也不知道依赖的包是什么

当然,你可以说不是有源文件吗,里面不都记录了require了什么东西吗?理论上讲只要把里面require的东西全部装一遍不就好了吗?

可以是可以。但是真的这么做的话会发现另外一个问题:你安装的gem和作者当时用的gem很可能不是一个版本。因为使用默认的gem install安装的总是最新的gem。那么只要任何一个依赖的gem在其版本更新中变更了现有的API,那么你复现安装的项目几乎必然是有问题的。

Bundler comes and saves the world!

说起来,解决这个问题的方法很简单,只要在最初开发的时候用一个文件记录了项目里用了什么包什么版本就好了嘛!对的,Bundler就是干这个事的。

具体来说,先创建一个Gemfile文件,在里面写上你要的gem名称和版本,然后用bundler install命令就可以安装所有的依赖了。虽然比npm麻烦了一点,但还算过得去。

普通的项目这样就可以了。但是还有一种情况就是,如果团队开发的项目本身就是一个gem呢?这样就会同时存在.gemspec和Gemfile两个文件,一个记录gem的元信息,一个是bundler要用来安装项目依赖的文件。这样的话每添加一个gem依赖就要同时在两边加(而且不幸的是,这两个文件书写依赖的格式还不太一样,不能简单的复制粘贴)

有没有觉得哪里不对?是的,Gemfile包含的信息其实就是.gemspec的子集,换句话说Gemfile里有的东西.gemspec里也都有。Bundler也想到了这个问题,所以你只要在Gemfile里加上gemspec这一句,bundler在安装的时候就会自动到.gemspec里面去找依赖,因此Gemfile剩下的就什么也不用写了(当然第一行指定安装源的命令还是要写的)。

版本选择

依赖全都装完了,那我们是不是可以愉快地开始用了呢?还不行,还差一步。现在出现的一个问题是,我们requrie到的gem的版本还是有可能不对的。为什么?

最直接的理由:require不能直接指定版本号!所以当你require一个包的时候,即便你装了Gemfile中指定版本的gem,如果这个gem你的系统里有好几个不同的版本(可能是以前装的),Ruby怎么知道你要依赖哪个?于是只能默认require最新的那一个了。

这当然也不是缺陷,Ruby中Kernel有个方法叫做gem。在require之前调用这个方法可以指定之后require进来的gem要用哪个版本。这个调用在本质上就是找到安装的gem包,然后把它的地址加到$LOAD_PATH里面去(注意仅仅是修改了$LOAD_PATH,包本身还没有被require进来)。下次require这个包的时候,会首先搜索$LOAD_PATH,找到这个包后就加载。这就达到了挑选gem版本的目的。

但这显然不方便啊,require一个包之前还要指定一下版本?那我改了依赖版本怎么办?

Bundler提供了一个解决方案。在require你要的包之前加一句require 'bundler/setup'即可。

本质上说,require了bundler/setup的这个包的时候,bundler读取Gemfile文件,在你所有依赖的库上调用一下gem方法,以此来指定要用的gem的版本,把它们加到$LOAD_PATH上面去。之后你在require的时候就自然地使用了在Gemfie里指定的包了。你所要做的就是加一句require 'bundler/setup'而已。

总结

Bundler其实是一个项目依赖管理器,而不是一个包管理器。如果那只是想下一个gem,那么Rubygems足矣。如果你是想管理项目的依赖(gem自身也是一个项目),那么在大多数情况下使用bundler会方便得多。

为什么需要Bundler的更多相关文章

  1. vs合并压缩css,js插件——Bundler & Minifier

    之前做了一个大转盘的抽奖活动,因为比较火,部分用户反馈看不到页面的情况,我怀疑js加载请求过慢导致,所以今天针对之前的一个页面进行调试优化. 首先想到的是对页面的js和css进行压缩优化,百度了下vs ...

  2. 避免每次输入bundler Exec命令

    bundle在ruby的世界里是个好东西,它可以用来管理应用程序的依赖库.它能自动的下载和安装指定的gem,也可以随时更新指定的gem. rvm则是一个命令行工具,能帮助你轻松的安装,管理多个ruby ...

  3. brew,gem,rvm 和 bundler软件包的管理工具

    brew是OS X上提供软件包的管理.Homebrew将软件包安装到单独的目录,然后符号链接到/usr/local 中,完全基于git和ruby.使用gem来安装你的gems,用brew来搞定他们的依 ...

  4. linux安装ruby ruby-devel rubygems bundler

    linux安装ruby ruby-devel rubygems yum install ruby ruby-devel rubygems 安装bundler gem install bundleror ...

  5. gem install bundler

    http://stackoverflow.com/questions/7483515/rake-aborted-no-such-file-to-load-bundler-setup-rails-3-1 ...

  6. 在CYGWIN下编译和运行软件Bundler ,以及PMVS,CMVS的编译与使用

    本人按照 http://blog.csdn.net/zzzblog/article/details/17166869 http://oliver.zheng.blog.163.com/blog/sta ...

  7. Laravel 5.2 INSTALL- node's npm and ruby's bundler.

    https://getcomposer.org/doc/00-intro.md Introduction# Composer is a tool for dependency management i ...

  8. rails 部署 can't find gem bundler (>= 0.a) with executable bundle

    多方寻找终得果,先感谢原作者,原作者博文 原因是本地项目bundler 和 服务器 bundler 版本不一致导致,项目是在本地建立,故Gemfile.lock最后一行BUNDLED WITH中是1. ...

  9. bundler简介(ruby gem)

    簡介 Bundler   RubyGem 是包裝.散佈Ruby程式庫的標準方式,相關文件可以參考 RubyGems Guides 的說明,或是 簡介 plugins 中的第二個例子.在使用rails ...

随机推荐

  1. Fragment、Activity 保存状态

    Activity 保存状态1. void onCreate(Bundle savedInstanceState) 当Activity被第首次加载时执行.我们新启动一个程序的时候其主窗体的onCreat ...

  2. 关于Android的背景色配色小结

    三基色原理:三基色是指红,绿,蓝三色,人眼对红.绿.蓝最为敏感,大多数的颜色可以通过红.绿.蓝三色按照不同的比例合成产生.同样绝大多数单色光也可以分解成红绿蓝三种色光.这是色度学的最基本原理,即三基色 ...

  3. 重拾java之路之webservice

         Web service平台是一套标准,它定义了应用程序如何在Web上实现互操作性.你可以用任何你喜欢的语言,在任何你喜欢的平台上写Web service ,只要我们可以通过Web servi ...

  4. On One Side Kolmogorov Type Inequalities

    Let \(X_1,X_2,\ldots,X_n\) be independent random variables. Denote \[S_n=\sum_{i=1}^n X_i.\] The  we ...

  5. Yii2中的环境配置

    默认的Debug配置 在入口文件中 defined ( 'YII_DEBUG' ) or define ( 'YII_DEBUG', true ); defined ( 'YII_ENV' ) or ...

  6. 【腾讯Bugly干货分享】移动互联网测试到质量的转变

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ee0934b690d84c3188d7c7 Dev Club 是一个交流移动 ...

  7. 公共代码参考(Volley)

    Volley 是google提供的一个网络库,相对于自己写httpclient确实方便很多,本文参考部分网上例子整理如下,以作备忘: 定义一个缓存类: public class BitmapCache ...

  8. 科蓝软件急招前端开发、PHP、.NET工程师

    职位:前端开发 工作年限:不限     学历要求:大专     招聘人数:2   专业:不限    薪酬:面议 工作地点:浙江嘉兴.北京 岗位职责: 1.负责公司项目的UI设计: 2.负责将UI静态化 ...

  9. wamp虚拟主机配置

    1.首先打开apache的配置文件httpd.conf,并去掉#Include conf/extra/httpd-vhosts.conf前面的#,启用虚拟主机功能 2.先把localhost配置好,免 ...

  10. jdk研究——java.lang

    jdk研究 volatile 是什么意思? 如何看jdk源码? 如何调试源码!---------仔细解读关键类,关键代码,常用的api的解释! 自己有疑问的不懂地方-------- 不懂的太多怎么办. ...