public
Description: A set of conventions, rake tasks and other scripts to keep your Merb app's gems in order (all app-local)
Clone URL: git://github.com/joe/merb-app-local-gem-management.git
name age message
file README Thu Jun 05 16:43:28 -0700 2008 Spullingggg mistake [joe]
directory config/ Thu Jun 05 16:07:00 -0700 2008 Updates with comments, the Rake tasks and a few [joe]
directory scripts/ Thu Jun 05 16:07:00 -0700 2008 Updates with comments, the Rake tasks and a few [joe]
directory tasks/ Thu Jun 05 16:26:11 -0700 2008 Updated the README, added another Rake task... [joe]
README
THIS IS A DRAFT. I REPEAT, THIS IS A DRAFT!
===========================================

Introduction
============

Merb App-Local Gem Management is all about keeping your Merb app's requirements as local as possible. Right now, the 
"requirements" managed by this collection of stuff are limited to gems.

Why???, you ask?

- With this approach, everything is versioned along with your app. As the gems and gem versions you require evolve, that 
evolution is recorded by your repository.

- Getting a new developer up with your app and keeping developers in sync is much simpler. Running a rake task to update 
all the necessary gems is much simpler than IM'ing and emailing and reading commit logs to figure out what gems to 
install/update.

- This approach forces you to DRY-yet-completely-specify the environment necessary for your app to run successfully. All 
gems and exact versions of those gems are specified exactly, exactly once.

Perhaps I'm preaching to the choir with the above two points and you're just asking the question "why not just keep all 
your gems 'frozen' in a gems or frameworks directory?" My reasons are two:

- Many gems require native build steps, this debilitates keeping gems pre-installed in the app.

- You might want to use git's submodules to track a few projects outside of their gem release cycle. 

- Related to my cause, frozen merb and other "just install gems into gems or frameworks and version that" still require 
that you install (and therefore worry about versioning manually) some of merb in order to be "app-local." (It's worth 
noting that this bullet can be covered by just doing the PATH/GEM\_HOME process that is a part of the total gem 
management approach described below.)

The scripts and conventions contained herein keep you structured with both of all of those issues solved.


How to "Enable" a Merb Application with These Scripts
=====================================================

1. Copy the scripts/local\_paths and scripts/gem\_install\_rake BASH scripts to your Merb app's scripts directory 
(creating that directory if necessary).

2. Copy the tasks/gems.rake tasks file over to your Merb app's tasks directory (creating that directory if necessary).

3. Edit your app's .gitignore file. Add the line 'gems/**' to it.

4. Edit your app's Rakefile to march on (but log) even if Merb and its dependencies are not yet installed. This allows 
us to use Rake for building the environment itself (including Merb and all its dependencies). For example:

  <pre><code>
  require 'rubygems'
  Gem.clear_paths
  Gem.path.unshift(File.join(File.dirname(__FILE__), "gems"))

  require 'rake'
  require 'rake/rdoctask'
  require 'rake/testtask'
  begin
    require 'spec/rake/spectask'
    require 'fileutils'
  rescue LoadError
    puts "* Loading spec/rake/spectask or fileutils failed! Continuing in case you're doing rake tasks that does not 
    require it."  
  end
  begin
    require 'merb-core'
  rescue LoadError
    puts "* Loading merb-core failed! Continuing in case you're doing rake tasks that does not require it."
  end
  begin
    require 'rubigen'
  rescue LoadError
    puts "* Loading rubigen failed! Continuing in case you're doing rake tasks that does not require it."  
  end
  include FileUtils

  init_env = ENV['MERB_ENV'] || 'rake'
  begin
    Merb.load_dependencies(:environment => init_env)
    Merb::Plugins.rakefiles.each { |r| require r } 
  rescue LoadError, Exception
    puts "* Loading merb's dependencies failed! Continuing in case you're doing rake tasks that do not require them."
  end
  </pre></code>
  
5. Edit your app's config/init.rb to clearly declare every gem that you app depends on. Here's a quick example set of 
dependency declarations:

  <pre><code>
  dependency 'activesupport', '= 2.0.2'
  dependency 'rubigen','= 1.3.1'
  dependency 'fastthread','= 1.0.1'
  dependency 'hpricot','= 0.6'
  dependency 'rspec', '= 1.1.3', 'spec'
  </pre></code>

  Each dependency declaration takes the following parameters: 
  1. the name of the gem as it should be called with a 'gem install xyz' 
  2. the exact version of the gem that you require for your application
  3. the string to use in a 'require' statement for loading the gem if that string differs from the gem's name (this is 
  optional for those gems that do not follow the friendly convention of installing and requiring under the same name).
  
  As you can see, RSpec (at least through 1.1.3) is an example case where the 3rd parameter to dependency is necessary. 
  You do a 'gem install rspec' but need to do a 'require 'spec'.'
  
  NOTA BENE: That 3rd parameter to dependency is NOT in wycat's mainline of merb-core ... hopefully it will be soon. If 
  you'd like to use my fork of merb-core in order to get that dependency fix, please do. Or, cherry pick my commit that 
  makes the necessary fix (42ae4c8).

6. Optionally, put a copy of each gem (e.g. the rspec-1.1.3.gem file) that you use in your app inside of a directory 
named gem\_cache. The gem\_cache directory should be in your app's base directory. This saves you and everyone else from 
downloading the gem from rubyforge or somewhere else (and doing the dreaded bulk update of the gem index).

If you want to keep track of some projects/gems (including the merb-* and dm-* framework gems) as git submodules, also 
do the following:

1. Create a sources directory inside your app's base directory. Use git's submodules feature to "add" git submodules for 
each into your app. For example:

  git submodule add git://github.com/wycats/merb-core.git sources/merb-core
  (repeat that for each git project you want)
  git submodule init
  git submodule update

2. Copy the config/gems.rb file over to your Merb app's config directory. Edit the file to suit your git submodules ... 
note the way the example file is populated with paths to the specific projects in the cases of merb-more, merb-plugins 
and dm-more.

The above two steps make it so that the rake tasks for installing basic gems will *not* install the gems for the git 
submodules that you want to track in your sources directory. For example, if you have merb-core tracked as a source and 
indicated in your config/gems.rb, then you don't want to gem install it from rubforge. Instead you'll use the 
gems:submodule\_ tasks to update/install the gems from the sources.


How to Use an "Enabled" Merb Application as a Developer/Deployer
================================================================

1. Install Ruby, Ruby Gems and git on your development/deployment box. (Ideally, even this process would be automated by 
a script inside your app.) And if you use MySQL, etc, you'll need to install those separately as well.

2. Clone your app. Move into your app's base directory.

3. Set up app-local PATH and GEM\_HOME environment variables. If you're using BASH or a compatible shell, then you can 
use my pre-made script via 'source scripts/local_paths.' You'll need to set these paths each time you go to develop or 
deploy your app.

4. Install Rake inside your app's local gem environment. If you're using BASH or a compatible shell, then you can use my 
pre-made script via 'source scripts/gem\_install\_rake.'

5. Run 'rake gems:install\_all.' This will install all of the gems specified in your config/init.rb (excluding the gems 
mentioned in config/gems.rb if you're going that route) and (if you're going this route) also package and install the 
specified gems from your sources directory.

From there on out, any time you call "merb" or "rake" or any other gem that has a bin file, you'll be 100% app-local. 
Just as the heart of Merb meant it to be!!! ;)


How to Add a "Managed" Gem to Your "Enabled" Merb Application
================================================================

1. You're using your app-local PATH and GEM\_HOME environment, right? :)

2. Go ahead and do a "gem install thenewgem" to get it in your app's gem system. Develop, test, enjoy. Ready to make 
sure that gem is included for you and others in the future? Great! 

3. If you're using the gem\_cache directory approach, copy the gem file (and any dependencies installed when you did 
your gem install) from your app's local gems/cache directory over to your gem\_cache directory.

4. Make sure there is a proper dependency line in your config/init.rb file (it should be there already if you're 
managing your gems within Merb properly). For example:

  <pre><code>
  dependency 'thenewgem', '1.2.0'
  </pre></code>

5. Commit the gem\_cache addition and the update to config/init.rb. Either advise fellow developers to re-run "rake 
gem:install_all" or build a git commit hook that takes care of it automatically.

6. Enjoy cleaner, more reliable living.


FAQ
===

Got questions? Send them my way and I'll update this section! :)