对于从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. 利用nodejs实现登录并转码视频(原创)

    nodejs的出现让前端人员可以使用js打造后台,也许哪天就真的摆脱了对java或者php的依赖了. 今天跟大家分享一个利用nodejs接受前端post请求,并实现视频转码的这样一个例子.视频转码用到 ...

  2. spring MVC原理

    spring MVC原理   Spring MVC工作流程图   图一   图二    Spring工作流程描述       1. 用户向服务器发送请求,请求被Spring 前端控制Servelt D ...

  3. Knockoutjs 实践入门 (1) 属性绑定

    1  是什么? 使用MVVM模式的简单,动态的Javascript UI.           2   优点  声明式绑定  UI 自动更新 依赖追踪  模板化          3 如何使用   & ...

  4. Android Scroll分析——滑动效果产生

    相对于在Android2.x版本上出现的长按.点击事件的效果,不得不说,滑动操作具有更好的用户体验.因此,从Android 4.X版本开始,出现了更多滑动操作的效果.越来越多第三方应用模仿这样的效果, ...

  5. Microsoft 参考源代码系统更新,有惊喜哦。

    在以前,MS的参考源代码在单步调试时时好用时不好用,最后我找到了原因,那就是如果想用MS的参考源代码进行单步调试,那么你就得想尽办法把系统上的.NET FX降级到RTM版本(卸载各种相关补丁),今天我 ...

  6. [Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

    一.引言 在前一篇文章已经详细介绍了SignalR了,并且简单介绍它在Asp.net MVC 和WPF中的应用.在上篇博文介绍的都是群发消息的实现,然而,对于SignalR是为了实时聊天而生的,自然少 ...

  7. [php入门] 3、WAMP中的集成MySQL相关基础操作

    前言:本文以小白视角了解WAMP集成开发环境中的MYSQL,涉及的面广而浅,算是导读性质. 1.启动运行熟悉WAMP中的MySQL 先有库.再有表.数据最终以记录的形式插入表中.其中对数据进行操作使用 ...

  8. 2、CC2541芯片中级教程-OSAL操作系统(进一步了解-OLED && 普通按键和5方向按键-中断!!!)这个系统驱动层和应用层不一样~

    本文根据一周CC2541笔记汇总得来—— 适合概览和知识快速索引—— 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 ...

  9. 去除NSString里面的空格

    NSString *password = @"12  34"; [password stringByTrimmingCharactersInSet:[NSCharacterSet ...

  10. Java的String字符串内容总结

    String--字符串 获取字符串的长度 使用Sring类的length()方法可获取字符串对象的长度,例: str.length(); str代表指定的字符串对象;返回值为返回指定字符串的长度.例: ...