对于从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. 20145301&20145321&20145335实验四

    20145301&20145321&20145335实验四 这次实验我的组员为:20145301赵嘉鑫.20145321曾子誉.20145335郝昊 实验内容详见:实验四

  2. 《Python标准库》 目录

    目录 译者序序前言第1章 文本1.1 string—文本常量和模板1.1.1 函数1.1.2 模板1.1.3 高级模板1.2 textwrap—格式化文本段落1.2.1 示例数据1.2.2 填充段落1 ...

  3. SQL 数据库性能问题排查

    一个项目的运行,总伴随着性能问题,系统查询过慢,如何快速查询等 下面将简单讲解一下,如何去排查及解决这些问题. 开发过程中: 1:不要绝对的三范式,适当建立冗余能够提高查询速度,不用多表关联 2:能用 ...

  4. tcp/udp高并发和高吐吞性能测试工具

    在编写一个网络服务的时候都比较关心这个服务能达到多少并发连接,而在这连接的基础上又能达到一个怎样的交互能力.编写服务已经是一件很花力气的事情,而还要去编写一个能够体现结果的测试工具就更加消耗工作时间. ...

  5. Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

  6. Javascript事件机制兼容性解决方案

    本文的解决方案可以用于Javascript native对象和宿主对象(dom元素),通过以下的方式来绑定和触发事件: 或者 var input = document.getElementsByTag ...

  7. 《CLR.via.C#第三版》第一部分读书笔记(一)

    最近开始仔细研读<CLR.via.C#第三版>这本书.读pdf文档确实很累.建议有条件的朋友还是买书看吧. 我的笔记用来记录我对这本书的理解,简化下逻辑,对每个部分我觉得是要点的进行归纳总 ...

  8. PyQt5应用与实践

    一个典型的GUI应用程序可以抽象为:主界面(菜单栏.工具栏.状态栏.内容区域),二级界面(模态.非模态),信息提示(Tooltip),程序图标等组成.本篇根据作者使用PyQt5编写的一个工具,介绍如何 ...

  9. Android手动签名

    在生成release build时可实现自动签名,所谓手动签名,就是在命令行下完成签名,落实到Android Studio里面,就是在terminal里面做. 下面是三个命令,第一个签名,第二个验证, ...

  10. 三天学会HTML5 ——多媒体元素的使用

    目录 1. HTML5 Media-Video 2. HTML5 Media-Audio 3. 拖拽操作 4. 获取位置信息 5. 使用Google 地图获取位置信息 多媒体是互联网中的最重要的一部分 ...