关于bundler

Jan 17, 2011

    在rails项目在生产环境中使用的是mysql,而开发和测试环境中使用是sqlite,这种方案使用起来还真是很方便.生产环境是 在一台Ubuntu虚拟机上面,而开发和测试环境都是在Mac上面. 在SCM仓库中的Gemfile是这样的:
source 'http://rubygems.org'
gem 'rails', '3.0.3'
 gem 'haml', "3.0.24"
 gem 'ruby-mysql','2.9.3'
 gem 'paperclip', '2.3.6'
 gem 'acts-as-taggable-on', '2.0.6'
 gem 'rdiscount', '1.6.5'
group :test, :development do
 gem 'hirb' , '0.3.5'
 gem 'sqlite3-ruby', '1.3.2'
 gem 'rspec-rails', '2.2.1'
 gem 'mongrel', '1.1.5'
 ....
 end
    然后运行bundle install,首先会抛出错误,ruby-mysql gem需要安装Mysql数据库. 然后你需要安装mysql数据库,我使用了很简单的方式 ,详情请看此文Install and configure MySQL 5 with MacPorts
sudo port install mysql5 +server
    默认会将其安装到/usr/local/mysql目录下.     然后再次运行bundle install 一切顺利,然后可以干活了.     事情很顺利直到有一天下午下班第二天早上上班时发现生产环境崩溃了,然后就每天都有发生这样的问题.顺便提到生产环境使用的是Passenger+Ngnix作为部署方案. 查看了一下错误日志, 发现里面只有反复的出现一句话
"Broken pipe"
    ,大家google半天,实在是一是因为大家都是rails新手,没有太多经验;二是这个错误信息太模糊了,搜索了半天也没搞清楚,各种说法的都有, 最后实在是没辙了,就去问米高,米高说这是mysql driver的问题,需要将ruby-mysql gem 换成mysql gem,换过来之后问题解决了.所以这个问题给我的想法是在Ubuntu上安装ruby-mysql时一切顺利,没有出现任何的问题,而在运行过程中却反复出现问题,而且错误信息给的还真是对新手而言不知所云.但一阵沮丧之后,大家要开始编码了,后来发现系统都崩溃了,原来是上次没有将开发过程中也将Gemfile中的ruby-mysql改过来. 那就改过来呗,但却发现安装mysql gem还真是有点麻烦.mysql-gem它因要引用Mysql的lib和headers,使用ruby进行编译和安装mysql驱动.当然你得首先安装了Mysql才行,这样它才行知道Mysql的库(lib)和头部文件(headers)在哪. 安装mysql gem ,如果你在Ubuntu上,
gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
    你可能看到很多"Building native extensions.  This could take a while... ERROR:  While executing gem ... (Gem::Installer::ExtensionBuildError) ERROR: Failed to build gem native extension."     因为在Ubuntu上面,出现这种情况就表示有一些lib或者lib-dev缺失.你可能需要通过sudo apt-get来额外安装这些依赖.     可以尝试:
sudo apt-get install libmysqlclient15-dev
现在运行应该就OK了.     在Mac上面安装其实貌似也不是很困难,至少我感觉比Ubuntu上要简单很多.简单运行
gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
就搞定.     但是我在想能不能有其他的办法了,在生产环境下要求安装mysql gem和Mysql,但是在开发和测试环境中不需要去安装Mysql gem 甚至不用安装Mysql了? 因为在目前这个项目中,我们几乎没有碰到过说要在开发的机子上以production的方式起起来. 那因为Mysql的安装是要在装mysql gem的时候必须要求的,所以能不能在bundler装gem包的时候,在生产环境中装mysql gem而在开发测试环境中装sqlite?     结果是有的.这个答案可以很简单两句话能搞定,但是我想讲讲bundler的工作方式,算是抛砖引入把~~     一切都很好,就是在Ubuntu上面安装mysql gem貌似有点小小的麻烦,你需要装另外其他的一些本地的依赖.但是它却是又不是非常明确告诉你需要装那些本地依赖,给出的错误信息有点模糊.         

关于Bundler

    在Gemfile中来描述所需要的一些依赖,然后运行bundle install,下载和安装所需要的依赖,同时将其所安装的gem文件加入$LOAD_PATH路径.同时,你可能也注意到了Gemfile.lock这个文件.那这两个文件的关系就是每次运行bundle install时,bundler会将Gemfile中指定的依赖进行解析,比如在Gemfile中有定义gem 'rails' ,'3.0.0.rc' 那实际上rails 3.0.0.rc还会依赖rack ~> 1.2.1,而这个依赖的解析和完全展开之后都会写到Gemfile.lock文件中.所以当下次你再次运行bundle install,它会先解析Gemfile的依赖,并检查当前的Gemfile.lock中时候已有这个依赖的解析.如果有,直接使用;如果没有,更改Gemfile.lock,并下载安装对应的gem.     Gemfile语法中还可以指定group,比如上面我的Gemfile,在group :test, :development do *** end 之外的都是属于default这个群.那这里就有三个group,而bundle install会默认执行default group(当然你可以配置), 然后让它选择性的执行某一个group.
source 'http://rubygems.org'

gem 'rails', '3.0.3'

gem 'haml', "3.0.24"
gem 'paperclip', '2.3.6'
gem 'acts-as-taggable-on', '2.0.6'
gem  'roo','1.3.11' 

group :production do
 gem 'mysql','2.8.1'
end

group :test, :development do
 gem 'hirb' , '0.3.5'
 gem 'sqlite3-ruby', '1.3.2'
 gem 'rspec-rails', '2.2.1'
 gem 'mongrel', '1.1.5'
 gem 'cucumber-rails', '0.3.2'
 gem 'cucumber', '0.9.3'
 gem 'spork','0.8.4'
end
    你可以看到我现在有将mysql gem单独仍在了:production这个group中的,这个好处是我可以显示的指示我想运行某一个group与否     在开发环境中,不希望使用mysql gem ,所以可以调用:
"bundle install --without production"
在产品环境中,不希望使用test/developement中的gem文件,可以使用
"bundle install --without development test"
( 使用bundle install  --deployment也许更适合) 其实不就是安装个mysql gem吗? 用bundle install 的不行的原因是因为它需要安装native extention吗? 那能不能在bundler中有一种方式指定了?     答案有两个: 首先,如果你安装的是mysql2 gem的话,它的依赖都会通过它的头部信息而发现,就不需要第二个答案了. 第二个答案是:在bundle config命令尾部加油一个mysql的extention标记:
bundle config build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
以后你运行bundle install都会去使用同一个配置,而这个配置在第一次运行此命令之后保持在#{RAILS_ROOT}/.bundle/config文件记录下来. References: Understanding Bundler Using the New Gem Bundler Today