<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,4 @@
-class Exceptions &lt; Application
+class Exceptions &lt; Merb::Controller
   
   # handle NotFound exceptions (404)
   def not_found</diff>
      <filename>app/controllers/exceptions.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@
 # You'll need to setup your db as per the salted_user mixin, and you'll need
 # To use :password, and :password_confirmation when creating a user
 #
-# see config/merb-auth/setup.rb to see how to disable the salted_user mixin
+# see merb/merb-auth/setup.rb to see how to disable the salted_user mixin
 # 
 # You will need to setup your database and create a user.
 class User</diff>
      <filename>app/models/user.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1 +1,2 @@
-Autotest.add_discovery { &quot;merb&quot; }
\ No newline at end of file
+Autotest.add_discovery { &quot;merb&quot; }
+Autotest.add_discovery { &quot;rspec&quot; }
\ No newline at end of file</diff>
      <filename>autotest/discover.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,7 +15,10 @@ class Autotest::Merb &lt; Autotest
     initialize_test_layout
     
     # Ignore any happenings in these directories
-    add_exception %r%^\./(?:doc|log|public|tmp)%
+    add_exception %r%^\./(?:doc|log|public|tmp|\.git|\.hg|\.svn|framework|gems|schema|\.DS_Store|autotest|bin|.*\.sqlite3)% 
+    # Ignore SCM directories and custom Autotest mappings
+    %w[.svn .hg .git .autotest].each { |exception| add_exception(exception) }
+    
     
     # Ignore any mappings that Autotest may have already set up
     clear_mappings
@@ -36,7 +39,7 @@ class Autotest::Merb &lt; Autotest
       ]
     end
     
-    # Any change to a test or test will cause it to be run
+    # Any change to a test will cause it to be run
     add_mapping %r%^test/(unit|models|integration|controllers|views|functional)/.*rb$% do |filename, _|
       filename
     end</diff>
      <filename>autotest/merb.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@ class Autotest::MerbRspec &lt; Autotest
     super
 
     # Ignore any happenings in these directories
-    add_exception %r%^\./(?:doc|log|public|tmp|\.git|\.hg|\.svn|framework|gems|schema|\.DS_Store|autotest|bin|.*\.sqlite3)% 
+    add_exception %r%^\./(?:doc|log|public|tmp|\.git|\.hg|\.svn|framework|gems|schema|\.DS_Store|autotest|bin|.*\.sqlite3|.*\.thor)% 
     # Ignore SCM directories and custom Autotest mappings
     %w[.svn .hg .git .autotest].each { |exception| add_exception(exception) }
 
@@ -44,7 +44,7 @@ class Autotest::MerbRspec &lt; Autotest
     # Any change to global_helpers will result in all view and controller
     # tests being run
     add_mapping %r%^app/helpers/global_helpers\.rb% do
-      files_matching %r%^spec/(views|controllers|helpers)/.*_spec\.rb$%
+      files_matching %r%^spec/(views|controllers|helpers|requests)/.*_spec\.rb$%
     end
 
     # Any change to a helper will cause its spec to be run</diff>
      <filename>autotest/merb_rspec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ Merb::Config.use { |c|
   # log less in testing environment
   c[:log_level]         = :error
 
-  c[:log_file]  = Merb.root / &quot;log&quot; / &quot;test.log&quot;
+  #c[:log_file]  = Merb.root / &quot;log&quot; / &quot;test.log&quot;
   # or redirect logger using IO handle
-  # c[:log_stream] = STDOUT
+  c[:log_stream] = STDOUT
 }</diff>
      <filename>config/environments/test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 # Go to http://wiki.merbivore.com/pages/init-rb
  
-require File.join(File.dirname(__FILE__), 'dependencies.rb')
+require 'config/dependencies.rb'
  
 use_orm :datamapper
 use_test :rspec
@@ -11,11 +11,12 @@ Merb::Config.use do |c|
   c[:session_store] = 'cookie'  # can also be 'memory', 'memcache', 'container', 'datamapper
   
   # cookie session store configuration
-  c[:session_secret_key]  = '1205346b9baa87cf8e49f78124c8d17a31ac0971'  # required for cookie session store
-  # c[:session_id_key] = '_session_id' # cookie session id key, defaults to &quot;_session_id&quot;
+  c[:session_secret_key]  = '6347fdc1ecdb73cb2dce47980ae0b094779575aa'  # required for cookie session store
+  c[:session_id_key] = '_astrotrain_session_id' # cookie session id key, defaults to &quot;_session_id&quot;
 end
  
 Merb::BootLoader.before_app_loads do
+  # This will get executed after dependencies have been loaded but before your app's classes have loaded.
 end
  
 Merb::BootLoader.after_app_loads do</diff>
      <filename>config/init.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,3 @@
-
 # use PathPrefix Middleware if :path_prefix is set in Merb::Config
 if prefix = ::Merb::Config[:path_prefix]
   use Merb::Rack::PathPrefix, prefix</diff>
      <filename>config/rack.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,4 +14,4 @@ RewriteCond %{REQUEST_FILENAME} !-f
 RewriteRule ^(.*)$ merb.fcgi [QSA,L]
 
 
-ErrorDocument 500 &quot;&lt;h2&gt;Application Error&lt;/h2&gt;Merb could not be reached
+ErrorDocument 500 &quot;&lt;h2&gt;Application Error&lt;/h2&gt;Merb could not be reached&quot;</diff>
      <filename>public/.htaccess</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@ require 'fileutils'
 require 'yaml'
 
 # Important - don't change this line or its position
-MERB_THOR_VERSION = '0.0.5'
+MERB_THOR_VERSION = '0.2.1'
 
 ##############################################################################
 
@@ -92,6 +92,7 @@ module GemManagement
       if installer.installed_gems.empty? &amp;&amp; exception
         error &quot;Failed to install gem '#{gem} (#{version || 'any version'})' (#{exception.message})&quot;
       end
+      ensure_bin_wrapper_for_installed_gems(installer.installed_gems, options)
       installer.installed_gems.each do |spec|
         success &quot;Successfully installed #{spec.full_name}&quot;
       end
@@ -119,6 +120,7 @@ module GemManagement
     if installer.installed_gems.empty? &amp;&amp; exception
       error &quot;Failed to install gem '#{gem}' (#{e.message})&quot;
     end
+    ensure_bin_wrapper_for_installed_gems(installer.installed_gems, options)
     installer.installed_gems.each do |spec|
       success &quot;Successfully installed #{spec.full_name}&quot;
     end
@@ -132,8 +134,8 @@ module GemManagement
   # install_gem_from_source(source_dir, :skip =&gt; [...])
   def install_gem_from_source(source_dir, *args)
     installed_gems = []
-    Dir.chdir(source_dir) do
-      opts = args.last.is_a?(Hash) ? args.pop : {}
+    opts = args.last.is_a?(Hash) ? args.pop : {}
+    Dir.chdir(source_dir) do      
       gem_name     = args[0] || File.basename(source_dir)
       gem_pkg_dir  = File.join(source_dir, 'pkg')
       gem_pkg_glob = File.join(gem_pkg_dir, &quot;#{gem_name}-*.gem&quot;)
@@ -173,6 +175,8 @@ module GemManagement
         end
       end
       
+      ensure_bin_wrapper_for(opts[:install_dir], opts[:bin_dir], *installed_gems)
+      
       # Finally install the main gem
       if install_pkg(Dir[gem_pkg_glob][0], opts.merge(:refresh =&gt; refresh))
         installed_gems = refresh
@@ -198,7 +202,7 @@ module GemManagement
       options[:version] = Gem::Requirement.new [&quot;= #{options[:version]}&quot;]
     end
     update_source_index(options[:install_dir]) if options[:install_dir]
-    Gem::Uninstaller.new(gem, options).uninstall
+    Gem::Uninstaller.new(gem, options).uninstall rescue nil
   end
 
   def clobber(source_dir)
@@ -260,7 +264,7 @@ module GemManagement
         gemspecs = ::Gem.source_index.search(dep)
         local = gemspecs.reverse.find { |s| s.loaded_from.index(gem_dir) == 0 }
         if local
-          local_specs  &lt;&lt; local
+          local_specs &lt;&lt; local
         elsif gemspecs.last
           system_specs &lt;&lt; gemspecs.last
         else
@@ -268,6 +272,15 @@ module GemManagement
         end
       end
       ::Gem.clear_paths
+    else
+      dependencies.each do |dep|
+        gemspecs = ::Gem.source_index.search(dep)
+        if gemspecs.last
+          system_specs &lt;&lt; gemspecs.last
+        else
+          missing_deps &lt;&lt; dep
+        end
+      end
     end
     [system_specs, local_specs, missing_deps]
   end
@@ -292,7 +305,14 @@ module GemManagement
       end
     end
   end
-
+  
+  def ensure_bin_wrapper_for_installed_gems(gemspecs, options)
+    if options[:install_dir] &amp;&amp; options[:bin_dir]
+      gems = gemspecs.map { |spec| spec.name }
+      ensure_bin_wrapper_for(options[:install_dir], options[:bin_dir], *gems)
+    end
+  end
+  
   private
 
   def executable_wrapper(spec, bin_file_name, minigems = true)
@@ -313,12 +333,16 @@ rescue LoadError
   require '#{then_req}'
 end
 
-if File.directory?(gems_dir = File.join(Dir.pwd, 'gems')) ||
-   File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
-  $BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir)
+# use gems dir if ../gems exists - eg. only for ./bin/#{bin_file_name}
+if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
+  $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
+  ENV[&quot;PATH&quot;] = &quot;\#{File.dirname(__FILE__)}:\#{gems_dir}/bin:\#{ENV[&quot;PATH&quot;]}&quot;
+  if (local_gem = Dir[File.join(gems_dir, &quot;specifications&quot;, &quot;#{spec.name}-*.gemspec&quot;)].last)
+    version = File.basename(local_gem)[/-([\\.\\d]+)\\.gemspec$/, 1]
+  end
 end
 
-version = &quot;#{Gem::Requirement.default}&quot;
+version ||= &quot;#{Gem::Requirement.default}&quot;
 
 if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
   version = $1
@@ -415,16 +439,15 @@ end
 
 module MerbThorHelper
   
-  attr_accessor :include_dependencies
+  attr_accessor :force_gem_dir
   
   def self.included(base)
     base.send(:include, ColorfulMessages)
     base.extend ColorfulMessages
   end
-
-  def install_dependency(dependency, opts = {})
-    opts[:version] ||= dependency.version_requirements.to_s
-    Merb::Gem.install(dependency.name, default_install_options.merge(opts))
+  
+  def use_edge_gem_server
+    ::Gem.sources &lt;&lt; 'http://edge.merbivore.com'
   end
   
   def source_manager
@@ -467,6 +490,12 @@ module MerbThorHelper
       end
     end
   end
+  
+  def install_dependency(dependency, opts = {})
+    version = dependency.version_requirements.to_s
+    install_opts = default_install_options.merge(:version =&gt; version)
+    Merb::Gem.install(dependency.name, install_opts.merge(opts))
+  end
 
   def install_dependency_from_source(dependency, opts = {})
     matches = Dir[File.join(source_dir, &quot;**&quot;, dependency.name, &quot;{Rakefile,Thorfile}&quot;)]
@@ -506,7 +535,7 @@ module MerbThorHelper
         note 'Uninstalling existing local gems:'
         local.each { |gemspec| note &quot;Uninstalled #{gemspec.name}&quot; }
       else
-        message 'Uninstalling existing local gems:' 
+        message 'Uninstalling existing local gems:' if local.size &gt; 1
         local.each do |gemspec|
           Merb::Gem.uninstall(gemspec.name, default_uninstall_options)
         end
@@ -538,11 +567,11 @@ module MerbThorHelper
   end
   
   def default_install_options
-    { :install_dir =&gt; gem_dir, :ignore_dependencies =&gt; ignore_dependencies? }
+    { :install_dir =&gt; gem_dir, :bin_dir =&gt; bin_dir, :ignore_dependencies =&gt; ignore_dependencies? }
   end
   
   def default_uninstall_options
-    { :install_dir =&gt; gem_dir, :ignore =&gt; true, :all =&gt; true, :executables =&gt; true }
+    { :install_dir =&gt; gem_dir, :bin_dir =&gt; bin_dir, :ignore =&gt; true, :all =&gt; true, :executables =&gt; true }
   end
   
   def dry_run?
@@ -550,11 +579,7 @@ module MerbThorHelper
   end
   
   def ignore_dependencies?
-    options[:&quot;ignore-dependencies&quot;] || !include_dependencies?
-  end
-  
-  def include_dependencies?
-    options[:&quot;include-dependencies&quot;] || self.include_dependencies
+    options[:&quot;ignore-dependencies&quot;]
   end
   
   # The current working directory, or Merb app root (--merb-root option).
@@ -571,6 +596,7 @@ module MerbThorHelper
     
   # If a local ./gems dir is found, return it.
   def gem_dir
+    return force_gem_dir if force_gem_dir
     if File.directory?(dir = default_gem_dir)
       dir
     end
@@ -596,16 +622,12 @@ module MerbThorHelper
   def create_if_missing(path)
     FileUtils.mkdir(path) unless File.exists?(path)
   end
-
-  def ensure_bin_wrapper_for(*gems)
-    Merb::Gem.ensure_bin_wrapper_for(gem_dir, bin_dir, *gems)
-  end
   
   def sudo
     ENV['THOR_SUDO'] ||= &quot;sudo&quot;
     sudo = Gem.win_platform? ? &quot;&quot; : ENV['THOR_SUDO']
   end
-  
+    
   def local_gemspecs(directory = gem_dir)
     if File.directory?(specs_dir = File.join(directory, 'specifications'))
       Dir[File.join(specs_dir, '*.gemspec')].map do |gemspec_path|
@@ -625,23 +647,538 @@ end
 $SILENT = true # don't output all the mess some rake package tasks spit out
 
 module Merb
+  
+  class Gem &lt; Thor
+    
+    include MerbThorHelper
+    extend GemManagement
+    
+    attr_accessor :system, :local, :missing
     
+    global_method_options = {
+      &quot;--merb-root&quot;            =&gt; :optional,  # the directory to operate on
+      &quot;--version&quot;              =&gt; :optional,  # gather specific version of gem
+      &quot;--ignore-dependencies&quot;  =&gt; :boolean    # don't install sub-dependencies
+    }
+    
+    method_options global_method_options
+    def initialize(*args); super; end
+    
+    # List gems that match the specified criteria.
+    #
+    # By default all local gems are listed. When the first argument is 'all' the
+    # list is partitioned into system an local gems; specify 'system' to show
+    # only system gems. A second argument can be used to filter on a set of known
+    # components, like all merb-more gems for example.
+    # 
+    # Examples:
+    #
+    # merb:gem:list                                    # list all local gems - the default
+    # merb:gem:list all                                # list system and local gems
+    # merb:gem:list system                             # list only system gems
+    # merb:gem:list all merb-more                      # list only merb-more related gems
+    # merb:gem:list --version 0.9.8                    # list gems that match the version    
+       
+    desc 'list [all|local|system] [comp]', 'Show installed gems'
+    def list(filter = 'local', comp = nil)
+      deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
+      self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
+      case filter
+      when 'all'
+        message 'Installed system gems:'
+        display_gemspecs(system)
+        message 'Installed local gems:'
+        display_gemspecs(local)
+      when 'system'
+        message 'Installed system gems:'
+        display_gemspecs(system)
+      when 'local'
+        message 'Installed local gems:'
+        display_gemspecs(local)
+      else
+        warning &quot;Invalid listing filter '#{filter}'&quot;
+      end
+    end
+    
+    # Install the specified gems.
+    #
+    # All arguments should be names of gems to install.
+    #
+    # When :force =&gt; true then any existing versions of the gems to be installed
+    # will be uninstalled first. It's important to note that so-called meta-gems
+    # or gems that exactly match a set of Merb::Stack.components will have their
+    # sub-gems uninstalled too. For example, uninstalling merb-more will install
+    # all contained gems: merb-action-args, merb-assets, merb-gen, ...
+    # 
+    # Examples:
+    #
+    # merb:gem:install merb-core merb-slices          # install all specified gems
+    # merb:gem:install merb-core --version 0.9.8      # install a specific version of a gem
+    # merb:gem:install merb-core --force              # uninstall then subsequently install the gem
+    # merb:gem:install merb-core --cache              # try to install locally from system gems
+    # merb:gem:install merb --merb-edge               # install from edge.merbivore.com
+     
+    desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from rubygems'
+    method_options &quot;--cache&quot;        =&gt; :boolean,
+                   &quot;--dry-run&quot;      =&gt; :boolean,
+                   &quot;--force&quot;        =&gt; :boolean,
+                   &quot;--merb-edge&quot;    =&gt; :boolean
+    def install(*names)
+      opts = { :version =&gt; options[:version], :cache =&gt; options[:cache] }
+      use_edge_gem_server if options[:&quot;merb-edge&quot;]
+      current_gem = nil
+      
+      # uninstall existing gems of the ones we're going to install
+      uninstall(*names) if options[:force]
+      
+      message &quot;Installing #{names.length} #{names.length == 1 ? 'gem' : 'gems'}...&quot;
+      puts &quot;This may take a while...&quot;
+      
+      names.each do |gem_name|
+        current_gem = gem_name      
+        if dry_run?
+          note &quot;Installing #{current_gem}...&quot;
+        else
+          message &quot;Installing #{current_gem}...&quot;
+          self.class.install(gem_name, default_install_options.merge(opts))
+        end
+      end
+    rescue =&gt; e
+      error &quot;Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})&quot;
+    end
+    
+    # Uninstall the specified gems.
+    #
+    # By default all specified gems are uninstalled. It's important to note that 
+    # so-called meta-gems or gems that match a set of Merb::Stack.components will 
+    # have their sub-gems uninstalled too. For example, uninstalling merb-more 
+    # will install all contained gems: merb-action-args, merb-assets, ...
+    #
+    # Existing dependencies will be clobbered; when :force =&gt; true then all gems
+    # will be cleared, otherwise only existing local dependencies of the
+    # matching component set will be removed.
+    #
+    # Examples:
+    #
+    # merb:gem:uninstall merb-core merb-slices        # uninstall all specified gems
+    # merb:gem:uninstall merb-core --version 0.9.8    # uninstall a specific version of a gem
+    
+    desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem'
+    method_options &quot;--dry-run&quot; =&gt; :boolean
+    def uninstall(*names)
+      opts = { :version =&gt; options[:version] }
+      current_gem = nil
+      if dry_run?
+        note &quot;Uninstalling any existing gems of: #{names.join(', ')}&quot;
+      else
+        message &quot;Uninstalling any existing gems of: #{names.join(', ')}&quot;
+        names.each do |gem_name|
+          current_gem = gem_name
+          Merb::Gem.uninstall(gem_name, default_uninstall_options) rescue nil
+          # if this gem is a meta-gem or a component set name, remove sub-gems
+          (Merb::Stack.components(gem_name) || []).each do |comp|
+            Merb::Gem.uninstall(comp, default_uninstall_options) rescue nil
+          end
+        end
+      end 
+    rescue =&gt; e
+      error &quot;Failed to uninstall #{current_gem ? current_gem : 'gem'} (#{e.message})&quot;
+    end
+    
+    # Recreate all gems from gems/cache on the current platform.
+    #
+    # This task should be executed as part of a deployment setup, where the 
+    # deployment system runs this after the app has been installed.
+    # Usually triggered by Capistrano, God...
+    #
+    # It will regenerate gems from the bundled gems cache for any gem that has 
+    # C extensions - which need to be recompiled for the target deployment platform.
+    #
+    # Note: at least gems/cache and gems/specifications should be in your SCM.
+    
+    desc 'redeploy', 'Recreate all gems on the current platform'
+    method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean
+    def redeploy
+      require 'tempfile' # for Dir::tmpdir access
+      if gem_dir &amp;&amp; File.directory?(cache_dir = File.join(gem_dir, 'cache'))
+        specs = local_gemspecs
+        message &quot;Recreating #{specs.length} gems from cache...&quot;
+        puts &quot;This may take a while...&quot;
+        specs.each do |gemspec|
+          if File.exists?(gem_file = File.join(cache_dir, &quot;#{gemspec.full_name}.gem&quot;))
+            gem_file_copy = File.join(Dir::tmpdir, File.basename(gem_file))
+            if dry_run?
+              note &quot;Recreating #{gemspec.full_name}&quot;
+            else
+              message &quot;Recreating #{gemspec.full_name}&quot;       
+              if options[:force] &amp;&amp; File.directory?(gem = File.join(gem_dir, 'gems', gemspec.full_name))
+                puts &quot;Removing existing #{gemspec.full_name}&quot;
+                FileUtils.rm_rf(gem) 
+              end              
+              # Copy the gem to a temporary file, because otherwise RubyGems/FileUtils
+              # will complain about copying identical files (same source/destination).
+              FileUtils.cp(gem_file, gem_file_copy)
+              Merb::Gem.install(gem_file_copy, :install_dir =&gt; gem_dir, :ignore_dependencies =&gt; true)
+              File.delete(gem_file_copy)
+            end
+          end
+        end
+      else
+        error &quot;No application local gems directory found&quot;
+      end
+    end
+    
+    private
+    
+    # Return dependencies for all installed gems; both system-wide and locally;
+    # optionally filters on :version requirement.
+    def dependencies
+      version_req = if options[:version]
+        ::Gem::Requirement.create(options[:version])
+      else
+        ::Gem::Requirement.default
+      end
+      if gem_dir
+        ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
+        ::Gem.source_index.refresh!
+      end
+      deps = []
+      ::Gem.source_index.each do |fullname, gemspec| 
+        if version_req.satisfied_by?(gemspec.version)
+          deps &lt;&lt; ::Gem::Dependency.new(gemspec.name, &quot;= #{gemspec.version}&quot;)
+        end
+      end
+      ::Gem.clear_paths if gem_dir
+      deps.sort
+    end
+    
+    public
+    
+    # Install gem with some default options.
+    def self.install(name, options = {})
+      defaults = {}
+      defaults[:cache] = false unless opts[:install_dir]
+      install_gem(name, defaults.merge(options))
+    end
+    
+    # Uninstall gem with some default options.
+    def self.uninstall(name, options = {})
+      defaults = { :ignore =&gt; true, :executables =&gt; true }
+      uninstall_gem(name, defaults.merge(options))
+    end
+    
+  end
+  
+  class Tasks &lt; Thor
+    
+    include MerbThorHelper
+    
+    # Show merb.thor version information
+    #
+    # merb:tasks:version                                        # show the current version info
+    # merb:tasks:version --info                                 # show extended version info
+    
+    desc 'version', 'Show verion info'
+    method_options &quot;--info&quot; =&gt; :boolean
+    def version
+      message &quot;Currently installed merb.thor version: #{MERB_THOR_VERSION}&quot;
+      if options[:version]
+        self.options = { :&quot;dry-run&quot; =&gt; true }
+        self.update # run update task with dry-run enabled
+      end
+    end
+    
+    # Update merb.thor tasks from remotely available version
+    #
+    # merb:tasks:update                                        # update merb.thor
+    # merb:tasks:update --force                                # force-update merb.thor
+    # merb:tasks:update --dry-run                              # show version info only
+    
+    desc 'update [URL]', 'Fetch the latest merb.thor and install it locally'
+    method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean
+    def update(url = 'http://merbivore.com/merb.thor')
+      require 'open-uri'
+      require 'rubygems/version'
+      remote_file = open(url)
+      code = remote_file.read
+      
+      # Extract version information from the source code
+      if version = code[/^MERB_THOR_VERSION\s?=\s?('|&quot;)([\.\d]+)('|&quot;)/,2]
+        # borrow version comparison from rubygems' Version class
+        current_version = ::Gem::Version.new(MERB_THOR_VERSION)
+        remote_version  = ::Gem::Version.new(version)
+        
+        if current_version &gt;= remote_version
+          puts &quot;currently installed: #{current_version}&quot;
+          if current_version != remote_version
+            puts &quot;available version:   #{remote_version}&quot;
+          end
+          info &quot;No update of merb.thor necessary#{options[:force] ? ' (forced)' : ''}&quot;
+          proceed = options[:force]
+        elsif current_version &lt; remote_version
+          puts &quot;currently installed: #{current_version}&quot;
+          puts &quot;available version:   #{remote_version}&quot;
+          proceed = true
+        end
+          
+        if proceed &amp;&amp; !dry_run?
+          File.open(File.join(__FILE__), 'w') do |f|
+            f.write(code)
+          end
+          success &quot;Installed the latest merb.thor (v#{version})&quot;
+        end
+      else
+        raise &quot;invalid source-code data&quot;
+      end      
+    rescue OpenURI::HTTPError
+      error &quot;Error opening #{url}&quot;
+    rescue =&gt; e
+      error &quot;An error occurred (#{e.message})&quot;
+    end
+    
+  end
+  
+  #### MORE LOW-LEVEL TASKS ####
+  
+  class Source &lt; Thor
+    
+    group 'core'
+        
+    include MerbThorHelper
+    extend GemManagement
+    
+    attr_accessor :system, :local, :missing
+    
+    global_method_options = {
+      &quot;--merb-root&quot;            =&gt; :optional,  # the directory to operate on
+      &quot;--ignore-dependencies&quot;  =&gt; :boolean,   # don't install sub-dependencies
+      &quot;--sources&quot;              =&gt; :optional   # a yml config to grab sources from
+    }
+    
+    method_options global_method_options
+    def initialize(*args); super; end
+        
+    # List source repositories, of either local or known sources.
+    #
+    # Examples:
+    #
+    # merb:source:list                                   # list all local sources
+    # merb:source:list available                         # list all known sources
+    
+    desc 'list [local|available]', 'Show git source repositories'
+    def list(mode = 'local')
+      if mode == 'available'
+        message 'Available source repositories:'
+        repos = self.class.repos(options[:sources])
+        repos.keys.sort.each { |name| puts &quot;- #{name}: #{repos[name]}&quot; }
+      elsif mode == 'local'
+        message 'Current source repositories:'
+        Dir[File.join(source_dir, '*')].each do |src|
+          next unless File.directory?(src)
+          src_name = File.basename(src)
+          unless (repos = source_manager.existing_repos(src_name)).empty?
+            puts &quot;#{src_name}&quot;
+            repos.keys.sort.each { |b| puts &quot;- #{b}: #{repos[b]}&quot; }
+          end
+        end
+      else
+        error &quot;Unknown listing: #{mode}&quot;
+      end
+    end
+
+    # Install the specified gems.
+    #
+    # All arguments should be names of gems to install.
+    #
+    # When :force =&gt; true then any existing versions of the gems to be installed
+    # will be uninstalled first. It's important to note that so-called meta-gems
+    # or gems that exactly match a set of Merb::Stack.components will have their
+    # sub-gems uninstalled too. For example, uninstalling merb-more will install
+    # all contained gems: merb-action-args, merb-assets, merb-gen, ...
+    # 
+    # Examples:
+    #
+    # merb:source:install merb-core merb-slices          # install all specified gems
+    # merb:source:install merb-core --force              # uninstall then subsequently install the gem
+    # merb:source:install merb-core --wipe               # clear repo then install the gem
+
+    desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from git source/edge'
+    method_options &quot;--dry-run&quot;      =&gt; :boolean,
+                   &quot;--force&quot;        =&gt; :boolean,
+                   &quot;--wipe&quot;         =&gt; :boolean
+    def install(*names)
+      use_edge_gem_server
+      # uninstall existing gems of the ones we're going to install
+      uninstall(*names) if options[:force] || options[:wipe]
+      
+      # We want dependencies instead of just names
+      deps = names.map { |n| ::Gem::Dependency.new(n, ::Gem::Requirement.default) }
+      
+      # Selectively update repositories for the matching dependencies
+      update_dependency_repositories(deps) unless dry_run?
+      
+      current_gem = nil
+      deps.each do |dependency|
+        current_gem = dependency.name      
+        if dry_run?
+          note &quot;Installing #{current_gem} from source...&quot;
+        else
+          message &quot;Installing #{current_gem} from source...&quot;
+          puts &quot;This may take a while...&quot;
+          unless install_dependency_from_source(dependency)
+            raise &quot;gem source not found&quot;
+          end
+        end
+      end
+    rescue =&gt; e
+      error &quot;Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})&quot;
+    end
+    
+    # Uninstall the specified gems.
+    #
+    # By default all specified gems are uninstalled. It's important to note that 
+    # so-called meta-gems or gems that match a set of Merb::Stack.components will 
+    # have their sub-gems uninstalled too. For example, uninstalling merb-more 
+    # will install all contained gems: merb-action-args, merb-assets, ...
+    #
+    # Existing dependencies will be clobbered; when :force =&gt; true then all gems
+    # will be cleared, otherwise only existing local dependencies of the
+    # matching component set will be removed. Additionally when :wipe =&gt; true, 
+    # the matching git repositories will be removed from the source directory.
+    #
+    # Examples:
+    #
+    # merb:source:uninstall merb-core merb-slices       # uninstall all specified gems
+    # merb:source:uninstall merb-core --wipe            # force-uninstall a gem and clear repo
+    
+    desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem (specify --force to remove the repo)'
+    method_options &quot;--version&quot; =&gt; :optional, &quot;--dry-run&quot; =&gt; :boolean, &quot;--wipe&quot; =&gt; :boolean
+    def uninstall(*names)
+      # Remove the repos that contain the gem
+      if options[:wipe] 
+        extract_repositories(names).each do |(name, url)|
+          if File.directory?(src = File.join(source_dir, name))
+            if dry_run?
+              note &quot;Removing #{src}...&quot;
+            else
+              info &quot;Removing #{src}...&quot;
+              FileUtils.rm_rf(src)
+            end
+          end
+        end
+      end
+      
+      # Use the Merb::Gem#uninstall task to handle this
+      gem_tasks = Merb::Gem.new
+      gem_tasks.options = options
+      gem_tasks.uninstall(*names)
+    end
+    
+    # Update the specified source repositories.
+    #
+    # The arguments can be actual repository names (from Merb::Source.repos)
+    # or names of known merb stack gems. If the repo doesn't exist already,
+    # it will be created and cloned.
+    #
+    # merb:source:pull merb-core                         # update source of specified gem
+    # merb:source:pull merb-slices                       # implicitly updates merb-more
+    
+    desc 'pull REPO_NAME [GEM_NAME, ...]', 'Update git source repository from edge'
+    def pull(*names)
+      repos = extract_repositories(names)
+      update_repositories(repos)
+      unless repos.empty?
+        message &quot;Updated the following repositories:&quot;
+        repos.each { |name, url| puts &quot;- #{name}: #{url}&quot; }
+      else
+        warning &quot;No repositories found to update!&quot;
+      end
+    end    
+    
+    # Clone a git repository into ./src. 
+    
+    # The repository can be a direct git url or a known -named- repository.
+    #
+    # Examples:
+    #
+    # merb:source:clone merb-core 
+    # merb:source:clone dm-core awesome-repo
+    # merb:source:clone dm-core --sources ./path/to/sources.yml
+    # merb:source:clone git://github.com/sam/dm-core.git
+    
+    desc 'clone (REPO_NAME|URL) [DIR_NAME]', 'Clone git source repository by name or url'
+    def clone(repository, name = nil)
+      if repository =~ /^git:\/\//
+        repository_url  = repository
+        repository_name = File.basename(repository_url, '.git')
+      elsif url = Merb::Source.repo(repository, options[:sources])
+        repository_url = url
+        repository_name = repository
+      end
+      source_manager.clone(name || repository_name, repository_url)
+    end
+    
+    # Git repository sources - pass source_config option to load a yaml 
+    # configuration file - defaults to ./config/git-sources.yml and
+    # ~/.merb/git-sources.yml - which you need to create yourself. 
+    #
+    # Example of contents:
+    #
+    # merb-core: git://github.com/myfork/merb-core.git
+    # merb-more: git://github.com/myfork/merb-more.git
+    
+    def self.repos(source_config = nil)
+      source_config ||= begin
+        local_config = File.join(Dir.pwd, 'config', 'git-sources.yml')
+        user_config  = File.join(ENV[&quot;HOME&quot;] || ENV[&quot;APPDATA&quot;], '.merb', 'git-sources.yml')
+        File.exists?(local_config) ? local_config : user_config
+      end
+      if source_config &amp;&amp; File.exists?(source_config)
+        default_repos.merge(YAML.load(File.read(source_config)))
+      else
+        default_repos
+      end
+    end
+    
+    def self.repo(name, source_config = nil)
+      self.repos(source_config)[name]
+    end
+    
+    # Default Git repositories
+    def self.default_repos
+      @_default_repos ||= { 
+        'merb'          =&gt; &quot;git://github.com/wycats/merb.git&quot;,
+        'merb-plugins'  =&gt; &quot;git://github.com/wycats/merb-plugins.git&quot;,
+        'extlib'        =&gt; &quot;git://github.com/sam/extlib.git&quot;,
+        'dm-core'       =&gt; &quot;git://github.com/sam/dm-core.git&quot;,
+        'dm-more'       =&gt; &quot;git://github.com/sam/dm-more.git&quot;,
+        'sequel'        =&gt; &quot;git://github.com/wayneeseguin/sequel.git&quot;,
+        'do'            =&gt; &quot;git://github.com/sam/do.git&quot;,
+        'thor'          =&gt; &quot;git://github.com/wycats/thor.git&quot;,
+        'rake'          =&gt; &quot;git://github.com/jimweirich/rake.git&quot;
+      }
+    end
+       
+  end
+  
   class Dependencies &lt; Thor
+  
+    group 'core'
     
     # The Dependencies tasks will install dependencies based on actual application
     # dependencies. For this, the application is queried for any dependencies.
     # All operations will be performed within this context.
     
-    attr_accessor :system, :local, :missing
+    attr_accessor :system, :local, :missing, :extract_dependencies
     
     include MerbThorHelper
     
     global_method_options = {
       &quot;--merb-root&quot;            =&gt; :optional,  # the directory to operate on
-      &quot;--include-dependencies&quot; =&gt; :boolean,   # gather sub-dependencies
+      &quot;--ignore-dependencies&quot;  =&gt; :boolean,   # ignore sub-dependencies
       &quot;--stack&quot;                =&gt; :boolean,   # gather only stack dependencies
       &quot;--no-stack&quot;             =&gt; :boolean,   # gather only non-stack dependencies
-      &quot;--config&quot;               =&gt; :boolean,   # gather dependencies from yaml config
+      &quot;--extract&quot;              =&gt; :boolean,   # gather dependencies from the app itself
       &quot;--config-file&quot;          =&gt; :optional,  # gather from the specified yaml config
       &quot;--version&quot;              =&gt; :optional   # gather specific version of framework
     }
@@ -663,7 +1200,7 @@ module Merb
     # merb:dependencies:list all merb-more                      # list only merb-more related dependencies
     # merb:dependencies:list --stack                            # list framework dependencies
     # merb:dependencies:list --no-stack                         # list 3rd party dependencies
-    # merb:dependencies:list --config                           # list dependencies from the default config
+    # merb:dependencies:list --extract                          # list dependencies by extracting them
     # merb:dependencies:list --config-file file.yml             # list from the specified config file
        
     desc 'list [all|local|system|missing] [comp]', 'Show application dependencies'
@@ -723,7 +1260,7 @@ module Merb
     # merb:dependencies:install stable merb-more                # install only merb-more related dependencies
     # merb:dependencies:install stable --stack                  # install framework dependencies
     # merb:dependencies:install stable --no-stack               # install 3rd party dependencies
-    # merb:dependencies:install stable --config                 # read dependencies from the default config
+    # merb:dependencies:install stable --extract                # extract dependencies from the actual app
     # merb:dependencies:install stable --config-file file.yml   # read from the specified config file
     #
     # In addition to the options above, edge install uses the following: 
@@ -747,8 +1284,10 @@ module Merb
         if only_missing = comp == 'missing'
           message &quot;Preparing to install missing gems #{where} using #{strategy} strategy...&quot;
           comp = nil
+          clobber = false
         else
           message &quot;Preparing to install #{where} using #{strategy} strategy...&quot;
+          clobber = true
         end
         
         # If comp given, filter on known stack components
@@ -764,7 +1303,8 @@ module Merb
           warning &quot;No dependencies to install...&quot;
         else
           puts &quot;#{deps.length} dependencies to install...&quot;
-          install_dependencies(strategy, deps)
+          puts &quot;This may take a while...&quot;
+          install_dependencies(strategy, deps, clobber)
         end
         
         # Show current dependency info now that we're done
@@ -792,7 +1332,6 @@ module Merb
     #
     # merb:dependencies:uninstall                               # uninstall all dependencies - the default
     # merb:dependencies:uninstall merb-more                     # uninstall merb-more related gems locally
-    # merb:dependencies:uninstall --config                      # read dependencies from the default config
     
     desc 'uninstall [comp]', 'Uninstall application dependencies'
     method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean
@@ -804,42 +1343,17 @@ module Merb
       clobber_dependencies!
     end
     
-    # Recreate binary gems on the current platform.
-    #
-    # This task should be executed as part of a deployment setup, where the 
-    # deployment system runs this after the app has been installed.
-    # Usually triggered by Capistrano, God...
-    #
-    # It will regenerate gems from the bundled gems cache for any gem that has 
-    # C extensions - which need to be recompiled for the target deployment platform.
+    # Recreate all gems from gems/cache on the current platform.
     #
-    # Note: gems/cache should be in your SCM for this to work correctly.
+    # Note: use merb:gem:redeploy instead
     
-    desc 'redeploy', 'Recreate any binary gems on the target platform'
-    method_options &quot;--dry-run&quot; =&gt; :boolean
+    desc 'redeploy', 'Recreate all gems on the current platform'
+    method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean
     def redeploy
-      require 'tempfile' # for Dir::tmpdir access
-      if gem_dir &amp;&amp; File.directory?(cache_dir = File.join(gem_dir, 'cache'))
-        local_gemspecs.each do |gemspec|
-          unless gemspec.extensions.empty?
-            if File.exists?(gem_file = File.join(cache_dir, &quot;#{gemspec.full_name}.gem&quot;))
-              gem_file_copy = File.join(Dir::tmpdir, File.basename(gem_file))
-              if dry_run?
-                note &quot;Recreating #{gemspec.full_name}&quot;
-              else
-                message &quot;Recreating #{gemspec.full_name}&quot;
-                # Copy the gem to a temporary file, because otherwise RubyGems/FileUtils
-                # will complain about copying identical files (same source/destination).
-                FileUtils.cp(gem_file, gem_file_copy)
-                Merb::Gem.install(gem_file_copy, :install_dir =&gt; gem_dir)
-                File.delete(gem_file_copy)
-              end
-            end
-          end
-        end
-      else
-        error &quot;No application local gems directory found&quot;
-      end
+      warning 'Warning: merb:dependencies:redeploy has been deprecated - use merb:gem:redeploy instead'
+      gem = Merb::Gem.new
+      gem.options = options
+      gem.redeploy
     end
     
     # Create a dependencies configuration file.
@@ -860,10 +1374,22 @@ module Merb
     # merb:dependencies:configure --config-file file.yml        # write to the specified config file 
     
     desc 'configure [comp]', 'Create a dependencies config file'
-    method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean
+    method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean, &quot;--versions&quot; =&gt; :boolean
     def configure(comp = nil)
+      self.extract_dependencies = true # of course we need to consult the app itself
       # If comp given, filter on known stack components
       deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
+      
+      # If --versions is set, update the version_requirements with the actual version available
+      if options[:versions]
+        specs = local_gemspecs
+        deps.each do |dep|
+          if spec = specs.find { |s| s.name == dep.name }
+            dep.version_requirements = ::Gem::Requirement.create(spec.version)
+          end
+        end
+      end
+      
       config = YAML.dump(deps.map { |d| d.to_s })
       puts &quot;#{config}\n&quot;
       if File.exists?(config_file) &amp;&amp; !options[:force]
@@ -889,50 +1415,41 @@ module Merb
       end
     end
     
-    def install_dependencies(strategy, deps)
+    def install_dependencies(strategy, deps, clobber = true)
       if method = strategy?(strategy)
         # Clobber existing local dependencies
-        clobber_dependencies!
-    
+        clobber_dependencies! if clobber
+        
         # Run the chosen strategy - collect files installed from stable gems
         installed_from_stable = send(method, deps).map { |d| d.name }
-      
-        # Sleep a bit otherwise the following steps won't see the new files
-        sleep(deps.length) if deps.length &gt; 0
-      
-        # Leave a file to denote the strategy that has been used for this dependency
-        self.local.each do |spec|
-          next unless File.directory?(spec.full_gem_path)
-          unless installed_from_stable.include?(spec.name)
-            FileUtils.touch(File.join(spec.full_gem_path, &quot;#{strategy}.strategy&quot;))
-          else
-            FileUtils.touch(File.join(spec.full_gem_path, &quot;stable.strategy&quot;))
-          end           
-        end
-        
-        # Ask for system installation of minigem - needs sudo...
-        if deps.find { |d| d.name == 'minigems' }
-          info &quot;Installing minigems.rb on your system...&quot;
-          `#{sudo} minigem install`
-        end
+
+        unless dry_run?
+          # Sleep a bit otherwise the following steps won't see the new files
+          sleep(deps.length) if deps.length &gt; 0 &amp;&amp; deps.length &lt;= 10
           
-        # Add local binaries for the installed framework dependencies
-        comps = Merb::Stack.all_components &amp; deps.map { |d| d.name }
-        comps &lt;&lt; { :no_minigems =&gt; 'merb-gen' }
-        ensure_bin_wrapper_for(*comps)
+          # Leave a file to denote the strategy that has been used for this dependency
+          self.local.each do |spec|
+            next unless File.directory?(spec.full_gem_path)
+            unless installed_from_stable.include?(spec.name)
+              FileUtils.touch(File.join(spec.full_gem_path, &quot;#{strategy}.strategy&quot;))
+            else
+              FileUtils.touch(File.join(spec.full_gem_path, &quot;stable.strategy&quot;))
+            end           
+          end
+        end
         return true
       end
       false
     end
     
     def dependencies
-      if use_config?
-        # Use preconfigured dependencies from yaml file
-        deps = config_dependencies
-      else
+      if extract_dependencies?
         # Extract dependencies from the current application
         deps = Merb::Stack.core_dependencies(gem_dir, ignore_dependencies?)
-        deps += Merb::Dependencies.extract_dependencies(working_dir)
+        deps += Merb::Dependencies.extract_dependencies(working_dir)        
+      else
+        # Use preconfigured dependencies from yaml file
+        deps = config_dependencies
       end
       
       stack_components = Merb::Stack.components
@@ -960,7 +1477,7 @@ module Merb
           end
         end
       end
-            
+      
       deps
     end
     
@@ -968,12 +1485,13 @@ module Merb
       if File.exists?(config_file)
         self.class.parse_dependencies_yaml(File.read(config_file))
       else
+        warning &quot;No dependencies.yml file found at: #{config_file}&quot;
         []
       end
     end
     
-    def use_config?
-      options[:config] || options[:&quot;config-file&quot;]
+    def extract_dependencies?
+      options[:extract] || extract_dependencies
     end
     
     def config_file
@@ -1025,20 +1543,18 @@ module Merb
     end
     
     def edge_strategy(deps)
+      use_edge_gem_server
       installed_from_rubygems = []
       
       # Selectively update repositories for the matching dependencies
       update_dependency_repositories(deps) unless dry_run?
       
-      # Skip gem dependencies to prevent them from being installed from stable;
-      # however, core dependencies will be retrieved from source when available
-      install_opts = { :ignore_dependencies =&gt; true }
       if core = deps.find { |d| d.name == 'merb-core' }
         if dry_run?
           note &quot;Installing #{core.name}...&quot;
         else
-          if install_dependency_from_source(core, install_opts)
-          elsif install_dependency(core, install_opts)
+          if install_dependency_from_source(core)
+          elsif install_dependency(core)
             info &quot;Installed #{core.name} from rubygems...&quot;
             installed_from_rubygems &lt;&lt; core
           end
@@ -1050,8 +1566,8 @@ module Merb
         if dry_run?
           note &quot;Installing #{dependency.name}...&quot;
         else
-          if install_dependency_from_source(dependency, install_opts)
-          elsif install_dependency(dependency, install_opts)
+          if install_dependency_from_source(dependency)
+          elsif install_dependency(dependency)
             info &quot;Installed #{dependency.name} from rubygems...&quot;
             installed_from_rubygems &lt;&lt; dependency
           end
@@ -1072,26 +1588,29 @@ module Merb
     end
     
     # Extract application dependencies by querying the app directly.
-    def self.extract_dependencies(merb_root, env = 'production')
+    def self.extract_dependencies(merb_root)
       require 'merb-core'
       if !@_merb_loaded || Merb.root != merb_root
         Merb.start_environment(
+          :log_level =&gt; :fatal,
           :testing =&gt; true, 
           :adapter =&gt; 'runner', 
-          :environment =&gt; env, 
+          :environment =&gt; ENV['MERB_ENV'] || 'development', 
           :merb_root =&gt; merb_root
         )
         @_merb_loaded = true
       end
       Merb::BootLoader::Dependencies.dependencies
-    rescue =&gt; e
+    rescue StandardError =&gt; e     
       error &quot;Couldn't extract dependencies from application!&quot;
       error e.message
-      
-      p e.backtrace
-      
-      puts  &quot;Make sure you're executing the task from your app (--merb-root), or&quot;
-      puts  &quot;specify a config option (--config or --config-file=YAML_FILE)&quot;
+      puts  &quot;Make sure you're executing the task from your app (--merb-root)&quot;
+      return []
+    rescue SystemExit      
+      error &quot;Couldn't extract dependencies from application!&quot;
+      error &quot;application failed to run&quot;
+      puts  &quot;Please check if your application runs using 'merb'; for example,&quot;
+      puts  &quot;look for any gem version mismatches in dependencies.rb&quot;
       return []
     end
         
@@ -1111,25 +1630,29 @@ module Merb
       dependencies
     end
     
-  end  
+  end
   
   class Stack &lt; Thor
     
+    group 'core'
+    
     # The Stack tasks will install dependencies based on known sets of gems,
     # regardless of actual application dependency settings.
     
     DM_STACK = %w[
+      extlib
+      data_objects
       dm-core
       dm-aggregates
       dm-migrations
       dm-timestamps
       dm-types
       dm-validations
+      merb_datamapper
     ]
     
     MERB_STACK = %w[
-      minigems
-      
+      extlib
       merb-core
       merb-action-args
       merb-assets
@@ -1138,11 +1661,15 @@ module Merb
       merb-mailer
       merb-slices
       merb-auth
+      merb-auth-core
+      merb-auth-more 
+      merb-auth-slice-password
+      merb-param-protection
+      merb-exceptions
     ] + DM_STACK
     
     MERB_BASICS = %w[
-      minigems
-      
+      extlib
       merb-core
       merb-action-args
       merb-assets
@@ -1215,13 +1742,22 @@ module Merb
       dm-rest-adapter
     ]
     
+    DATA_OBJECTS = %w[
+      data_objects 
+      do_derby do_hsqldb 
+      do_jdbc
+      do_mysql
+      do_postgres
+      do_sqlite3
+    ]
+    
     attr_accessor :system, :local, :missing
     
     include MerbThorHelper
     
     global_method_options = {
       &quot;--merb-root&quot;            =&gt; :optional,  # the directory to operate on
-      &quot;--include-dependencies&quot; =&gt; :boolean,   # gather sub-dependencies
+      &quot;--ignore-dependencies&quot;  =&gt; :boolean,   # skip sub-dependencies
       &quot;--version&quot;              =&gt; :optional   # gather specific version of framework    
     }
     
@@ -1270,6 +1806,7 @@ module Merb
                     &quot;--dry-run&quot;   =&gt; :boolean,
                     &quot;--strategy&quot;  =&gt; :optional
     def install(*comps)
+      use_edge_gem_server if options[:edge]
       mngr = self.dependency_manager
       deps = gather_dependencies(comps)
       mngr.system, mngr.local, mngr.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
@@ -1281,6 +1818,7 @@ module Merb
     # See also: Merb::Dependencies#uninstall
     #
     # Examples:
+    #
     # merb:stack:uninstall                                      # uninstall the default merb stack
     # merb:stack:uninstall merb-more                            # uninstall merb-more
     # merb:stack:uninstall merb-core thor merb-slices           # uninstall the specified gems
@@ -1294,6 +1832,48 @@ module Merb
       clobber_dependencies!
     end
     
+    # Install or uninstall minigems from the system.
+    #
+    # Due to the specific nature of MiniGems it can only be installed system-wide.
+    #
+    # Examples:
+    #
+    # merb:stack:minigems install                               # install minigems
+    # merb:stack:minigems uninstall                             # uninstall minigems
+    
+    desc 'minigems (install|uninstall)', 'Install or uninstall minigems (needs sudo privileges)'
+    def minigems(action)
+      case action
+      when 'install'
+        Kernel.system &quot;#{sudo} thor merb:stack:install_minigems&quot;
+      when 'uninstall'
+        Kernel.system &quot;#{sudo} thor merb:stack:uninstall_minigems&quot;
+      else
+        error &quot;Invalid command: merb:stack:minigems #{action}&quot;
+      end
+    end    
+    
+    # hidden minigems install task
+    def install_minigems
+      message &quot;Installing MiniGems&quot;
+      mngr = self.dependency_manager
+      deps = gather_dependencies('minigems')
+      mngr.system, mngr.local, mngr.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
+      mngr.force_gem_dir = ::Gem.dir
+      mngr.install_dependencies(strategy, deps)
+      Kernel.system &quot;#{sudo} minigem install&quot;
+    end
+    
+    # hidden minigems uninstall task
+    def uninstall_minigems
+      message &quot;Uninstalling MiniGems&quot;
+      Kernel.system &quot;#{sudo} minigem uninstall&quot;
+      deps = gather_dependencies('minigems')
+      self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
+      # Clobber existing local dependencies - based on self.local
+      clobber_dependencies!      
+    end
+    
     protected
     
     def gather_dependencies(comps = [])
@@ -1342,6 +1922,7 @@ module Merb
         comps[&quot;merb-more&quot;]    = MERB_MORE.sort
         comps[&quot;merb-plugins&quot;] = MERB_PLUGINS.sort
         comps[&quot;dm-more&quot;]      = DM_MORE.sort
+        comps[&quot;do&quot;]           = DATA_OBJECTS.sort
         
         comps
       end     
@@ -1367,7 +1948,7 @@ module Merb
     end
     
     def self.framework_components
-      %w[merb-core merb-more merb-plugins].inject([]) do |all, comp| 
+      %w[merb-core merb-more].inject([]) do |all, comp| 
         all + components(comp)
       end
     end
@@ -1389,7 +1970,7 @@ module Merb
     end
     
     def self.base_components
-      %w[thor rake]
+      %w[thor rake extlib]
     end
     
     def self.all_components
@@ -1401,6 +1982,7 @@ module Merb
     def self.core_dependencies(gem_dir = nil, ignore_deps = false)
       @_core_dependencies ||= begin
         if gem_dir # add local gems to index
+          orig_gem_path = ::Gem.path
           ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
         end
         deps = []
@@ -1415,7 +1997,7 @@ module Merb
             deps += gemspec.dependencies
           end
         end
-        ::Gem.clear_paths if gem_dir # reset
+        ::Gem.path.replace(orig_gem_path) if gem_dir # reset
         deps
       end
     end
@@ -1435,476 +2017,4 @@ module Merb
     
   end
   
-  class Tasks &lt; Thor
-    
-    include MerbThorHelper
-    
-    # Show merb.thor version information
-    #
-    # merb:tasks:version                                        # show the current version info
-    # merb:tasks:version --info                                 # show extended version info
-    
-    desc 'version', 'Show verion info'
-    method_options &quot;--info&quot; =&gt; :boolean
-    def version
-      message &quot;Currently installed merb.thor version: #{MERB_THOR_VERSION}&quot;
-      if options[:version]
-        self.options = { :&quot;dry-run&quot; =&gt; true }
-        self.update # run update task with dry-run enabled
-      end
-    end
-    
-    # Update merb.thor tasks from remotely available version
-    #
-    # merb:tasks:update                                        # update merb.thor
-    # merb:tasks:update --force                                # force-update merb.thor
-    # merb:tasks:update --dry-run                              # show version info only
-    
-    desc 'update [URL]', 'Fetch the latest merb.thor and install it locally'
-    method_options &quot;--dry-run&quot; =&gt; :boolean, &quot;--force&quot; =&gt; :boolean
-    def update(url = 'http://merbivore.com/merb.thor')
-      require 'open-uri'
-      require 'rubygems/version'
-      remote_file = open(url)
-      code = remote_file.read
-      
-      # Extract version information from the source code
-      if version = code[/^MERB_THOR_VERSION\s?=\s?('|&quot;)([\.\d]+)('|&quot;)/,2]
-        # borrow version comparison from rubygems' Version class
-        current_version = ::Gem::Version.new(MERB_THOR_VERSION)
-        remote_version  = ::Gem::Version.new(version)
-        
-        if current_version &gt;= remote_version
-          puts &quot;currently installed: #{current_version}&quot;
-          if current_version != remote_version
-            puts &quot;available version:   #{remote_version}&quot;
-          end
-          info &quot;No update of merb.thor necessary#{options[:force] ? ' (forced)' : ''}&quot;
-          proceed = options[:force]
-        elsif current_version &lt; remote_version
-          puts &quot;currently installed: #{current_version}&quot;
-          puts &quot;available version:   #{remote_version}&quot;
-          proceed = true
-        end
-          
-        if proceed &amp;&amp; !dry_run?
-          File.open(File.join(__FILE__), 'w') do |f|
-            f.write(code)
-          end
-          success &quot;Installed the latest merb.thor (v#{version})&quot;
-        end
-      else
-        raise &quot;invalid source-code data&quot;
-      end      
-    rescue OpenURI::HTTPError
-      error &quot;Error opening #{url}&quot;
-    rescue =&gt; e
-      error &quot;An error occurred (#{e.message})&quot;
-    end
-    
-  end
-  
-  #### MORE LOW-LEVEL TASKS ####
-  
-  class Gem &lt; Thor
-    
-    group 'core'
-    
-    include MerbThorHelper
-    extend GemManagement
-    
-    attr_accessor :system, :local, :missing
-    
-    global_method_options = {
-      &quot;--merb-root&quot;            =&gt; :optional,  # the directory to operate on
-      &quot;--version&quot;              =&gt; :optional,  # gather specific version of gem
-      &quot;--ignore-dependencies&quot;  =&gt; :boolean    # don't install sub-dependencies
-    }
-    
-    method_options global_method_options
-    def initialize(*args); super; end
-    
-    # List gems that match the specified criteria.
-    #
-    # By default all local gems are listed. When the first argument is 'all' the
-    # list is partitioned into system an local gems; specify 'system' to show
-    # only system gems. A second argument can be used to filter on a set of known
-    # components, like all merb-more gems for example.
-    # 
-    # Examples:
-    #
-    # merb:gem:list                                    # list all local gems - the default
-    # merb:gem:list all                                # list system and local gems
-    # merb:gem:list system                             # list only system gems
-    # merb:gem:list all merb-more                      # list only merb-more related gems
-    # merb:gem:list --version 0.9.8                    # list gems that match the version    
-       
-    desc 'list [all|local|system] [comp]', 'Show installed gems'
-    def list(filter = 'local', comp = nil)
-      deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
-      self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
-      case filter
-      when 'all'
-        message 'Installed system gems:'
-        display_gemspecs(system)
-        message 'Installed local gems:'
-        display_gemspecs(local)
-      when 'system'
-        message 'Installed system gems:'
-        display_gemspecs(system)
-      when 'local'
-        message 'Installed local gems:'
-        display_gemspecs(local)
-      else
-        warning &quot;Invalid listing filter '#{filter}'&quot;
-      end
-    end
-    
-    # Install the specified gems.
-    #
-    # All arguments should be names of gems to install.
-    #
-    # When :force =&gt; true then any existing versions of the gems to be installed
-    # will be uninstalled first. It's important to note that so-called meta-gems
-    # or gems that exactly match a set of Merb::Stack.components will have their
-    # sub-gems uninstalled too. For example, uninstalling merb-more will install
-    # all contained gems: merb-action-args, merb-assets, merb-gen, ...
-    # 
-    # Examples:
-    #
-    # merb:gem:install merb-core merb-slices          # install all specified gems
-    # merb:gem:install merb-core --version 0.9.8      # install a specific version of a gem
-    # merb:gem:install merb-core --force              # uninstall then subsequently install the gem
-    # merb:gem:install merb-core --cache              # try to install locally from system gems
-    # merb:gem:install merb-core --binaries           # also install adapted bin wrapper
-     
-    desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from rubygems'
-    method_options &quot;--cache&quot;     =&gt; :boolean,
-                   &quot;--binaries&quot;  =&gt; :boolean,
-                   &quot;--dry-run&quot;   =&gt; :boolean,
-                   &quot;--force&quot;     =&gt; :boolean
-    def install(*names)
-      self.include_dependencies = true # deal with dependencies by default
-      opts = { :version =&gt; options[:version], :cache =&gt; options[:cache] }
-      current_gem = nil
-      
-      # uninstall existing gems of the ones we're going to install
-      uninstall(*names) if options[:force]
-      
-      names.each do |gem_name|
-        current_gem = gem_name      
-        if dry_run?
-          note &quot;Installing #{current_gem}...&quot;
-        else
-          message &quot;Installing #{current_gem}...&quot;
-          self.class.install(gem_name, default_install_options.merge(opts))
-          ensure_bin_wrapper_for(gem_name) if options[:binaries]
-        end
-      end
-    rescue =&gt; e
-      error &quot;Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})&quot;
-    end
-    
-    # Uninstall the specified gems.
-    #
-    # By default all specified gems are uninstalled. It's important to note that 
-    # so-called meta-gems or gems that match a set of Merb::Stack.components will 
-    # have their sub-gems uninstalled too. For example, uninstalling merb-more 
-    # will install all contained gems: merb-action-args, merb-assets, ...
-    #
-    # Existing dependencies will be clobbered; when :force =&gt; true then all gems
-    # will be cleared, otherwise only existing local dependencies of the
-    # matching component set will be removed.
-    #
-    # Examples:
-    #
-    # merb:gem:uninstall merb-core merb-slices        # uninstall all specified gems
-    # merb:gem:uninstall merb-core --version 0.9.8    # uninstall a specific version of a gem
-    
-    desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem'
-    method_options &quot;--dry-run&quot; =&gt; :boolean
-    def uninstall(*names)
-      self.include_dependencies = true # deal with dependencies by default
-      opts = { :version =&gt; options[:version] }
-      current_gem = nil
-      if dry_run?
-        note &quot;Uninstalling any existing gems of: #{names.join(', ')}&quot;
-      else
-        message &quot;Uninstalling any existing gems of: #{names.join(', ')}&quot;
-        names.each do |gem_name|
-          current_gem = gem_name
-          Merb::Gem.uninstall(gem_name, default_uninstall_options) rescue nil
-          # if this gem is a meta-gem or a component set name, remove sub-gems
-          (Merb::Stack.components(gem_name) || []).each do |comp|
-            Merb::Gem.uninstall(comp, default_uninstall_options) rescue nil
-          end
-        end
-      end 
-    rescue =&gt; e
-      error &quot;Failed to uninstall #{current_gem ? current_gem : 'gem'} (#{e.message})&quot;
-    end
-    
-    private
-    
-    # Return dependencies for all installed gems; both system-wide and locally;
-    # optionally filters on :version requirement.
-    def dependencies
-      version_req = if options[:version]
-        ::Gem::Requirement.create(options[:version])
-      else
-        ::Gem::Requirement.default
-      end
-      if gem_dir
-        ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
-        ::Gem.source_index.refresh!
-      end
-      deps = []
-      ::Gem.source_index.each do |fullname, gemspec| 
-        if version_req.satisfied_by?(gemspec.version)
-          deps &lt;&lt; ::Gem::Dependency.new(gemspec.name, &quot;= #{gemspec.version}&quot;)
-        end
-      end
-      ::Gem.clear_paths if gem_dir
-      deps.sort
-    end
-    
-    public
-    
-    # Install gem with some default options.
-    def self.install(name, options = {})
-      defaults = {}
-      defaults[:cache] = false unless opts[:install_dir]
-      install_gem(name, defaults.merge(options))
-    end
-    
-    # Uninstall gem with some default options.
-    def self.uninstall(name, options = {})
-      defaults = { :ignore =&gt; true, :executables =&gt; true }
-      uninstall_gem(name, defaults.merge(options))
-    end
-    
-  end
-  
-  class Source &lt; Thor
-    
-    group 'core'
-        
-    include MerbThorHelper
-    extend GemManagement
-    
-    attr_accessor :system, :local, :missing
-    
-    global_method_options = {
-      &quot;--merb-root&quot;            =&gt; :optional,  # the directory to operate on
-      &quot;--include-dependencies&quot; =&gt; :boolean,   # gather sub-dependencies
-      &quot;--sources&quot;              =&gt; :optional   # a yml config to grab sources from
-    }
-    
-    method_options global_method_options
-    def initialize(*args); super; end
-        
-    # List source repositories, of either local or known sources.
-    #
-    # Examples:
-    #
-    # merb:source:list                                   # list all local sources
-    # merb:source:list available                         # list all known sources
-    
-    desc 'list [local|available]', 'Show git source repositories'
-    def list(mode = 'local')
-      if mode == 'available'
-        message 'Available source repositories:'
-        repos = self.class.repos(options[:sources])
-        repos.keys.sort.each { |name| puts &quot;- #{name}: #{repos[name]}&quot; }
-      elsif mode == 'local'
-        message 'Current source repositories:'
-        Dir[File.join(source_dir, '*')].each do |src|
-          next unless File.directory?(src)
-          src_name = File.basename(src)
-          unless (repos = source_manager.existing_repos(src_name)).empty?
-            puts &quot;#{src_name}&quot;
-            repos.keys.sort.each { |b| puts &quot;- #{b}: #{repos[b]}&quot; }
-          end
-        end
-      else
-        error &quot;Unknown listing: #{mode}&quot;
-      end
-    end
-
-    # Install the specified gems.
-    #
-    # All arguments should be names of gems to install.
-    #
-    # When :force =&gt; true then any existing versions of the gems to be installed
-    # will be uninstalled first. It's important to note that so-called meta-gems
-    # or gems that exactly match a set of Merb::Stack.components will have their
-    # sub-gems uninstalled too. For example, uninstalling merb-more will install
-    # all contained gems: merb-action-args, merb-assets, merb-gen, ...
-    # 
-    # Examples:
-    #
-    # merb:source:install merb-core merb-slices          # install all specified gems
-    # merb:source:install merb-core --force              # uninstall then subsequently install the gem
-    # merb:source:install merb-core --wipe               # clear repo then install the gem
-    # merb:source:install merb-core --binaries           # also install adapted bin wrapper
-
-    desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from git source/edge'
-    method_options &quot;--binaries&quot;  =&gt; :boolean,
-                   &quot;--dry-run&quot;   =&gt; :boolean,
-                   &quot;--force&quot;     =&gt; :boolean,
-                   &quot;--wipe&quot;      =&gt; :boolean
-    def install(*names)
-      # uninstall existing gems of the ones we're going to install
-      uninstall(*names) if options[:force] || options[:wipe]
-      
-      # We want dependencies instead of just names
-      deps = names.map { |n| ::Gem::Dependency.new(n, ::Gem::Requirement.default) }
-      
-      # Selectively update repositories for the matching dependencies
-      update_dependency_repositories(deps) unless dry_run?
-      
-      current_gem = nil
-      deps.each do |dependency|
-        current_gem = dependency.name      
-        if dry_run?
-          note &quot;Installing #{current_gem} from source...&quot;
-        else
-          message &quot;Installing #{current_gem} from source...&quot;
-          if install_dependency_from_source(dependency)
-            ensure_bin_wrapper_for(dependency.name) if options[:binaries]
-          end
-        end
-      end
-    rescue =&gt; e
-      error &quot;Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})&quot;
-    end
-    
-    # Uninstall the specified gems.
-    #
-    # By default all specified gems are uninstalled. It's important to note that 
-    # so-called meta-gems or gems that match a set of Merb::Stack.components will 
-    # have their sub-gems uninstalled too. For example, uninstalling merb-more 
-    # will install all contained gems: merb-action-args, merb-assets, ...
-    #
-    # Existing dependencies will be clobbered; when :force =&gt; true then all gems
-    # will be cleared, otherwise only existing local dependencies of the
-    # matching component set will be removed. Additionally when :wipe =&gt; true, 
-    # the matching git repositories will be removed from the source directory.
-    #
-    # Examples:
-    #
-    # merb:source:uninstall merb-core merb-slices       # uninstall all specified gems
-    # merb:source:uninstall merb-core --wipe            # force-uninstall a gem and clear repo
-    
-    desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem (specify --force to remove the repo)'
-    method_options &quot;--version&quot; =&gt; :optional, &quot;--dry-run&quot; =&gt; :boolean, &quot;--wipe&quot; =&gt; :boolean
-    def uninstall(*names)
-      # Remove the repos that contain the gem
-      if options[:wipe] 
-        extract_repositories(names).each do |(name, url)|
-          if File.directory?(src = File.join(source_dir, name))
-            if dry_run?
-              note &quot;Removing #{src}...&quot;
-            else
-              info &quot;Removing #{src}...&quot;
-              FileUtils.rm_rf(src)
-            end
-          end
-        end
-      end
-      
-      # Use the Merb::Gem#uninstall task to handle this
-      gem_tasks = Merb::Gem.new
-      gem_tasks.options = options
-      gem_tasks.uninstall(*names)
-    end
-    
-    # Update the specified source repositories.
-    #
-    # The arguments can be actual repository names (from Merb::Source.repos)
-    # or names of known merb stack gems. If the repo doesn't exist already,
-    # it will be created and cloned.
-    #
-    # merb:source:pull merb-core                         # update source of specified gem
-    # merb:source:pull merb-slices                       # implicitly updates merb-more
-    
-    desc 'pull REPO_NAME [GEM_NAME, ...]', 'Update git source repository from edge'
-    def pull(*names)
-      repos = extract_repositories(names)
-      update_repositories(repos)
-      unless repos.empty?
-        message &quot;Updated the following repositories:&quot;
-        repos.each { |name, url| puts &quot;- #{name}: #{url}&quot; }
-      else
-        warning &quot;No repositories found to update!&quot;
-      end
-    end    
-    
-    # Clone a git repository into ./src. 
-    
-    # The repository can be a direct git url or a known -named- repository.
-    #
-    # Examples:
-    #
-    # merb:source:clone merb-core 
-    # merb:source:clone dm-core awesome-repo
-    # merb:source:clone dm-core --sources ./path/to/sources.yml
-    # merb:source:clone git://github.com/sam/dm-core.git
-    
-    desc 'clone (REPO_NAME|URL) [DIR_NAME]', 'Clone git source repository by name or url'
-    def clone(repository, name = nil)
-      if repository =~ /^git:\/\//
-        repository_url  = repository
-        repository_name = File.basename(repository_url, '.git')
-      elsif url = Merb::Source.repo(repository, options[:sources])
-        repository_url = url
-        repository_name = repository
-      end
-      source_manager.clone(name || repository_name, repository_url)
-    end
-    
-    # Git repository sources - pass source_config option to load a yaml 
-    # configuration file - defaults to ./config/git-sources.yml and
-    # ~/.merb/git-sources.yml - which you need to create yourself. 
-    #
-    # Example of contents:
-    #
-    # merb-core: git://github.com/myfork/merb-core.git
-    # merb-more: git://github.com/myfork/merb-more.git
-    
-    def self.repos(source_config = nil)
-      source_config ||= begin
-        local_config = File.join(Dir.pwd, 'config', 'git-sources.yml')
-        user_config  = File.join(ENV[&quot;HOME&quot;] || ENV[&quot;APPDATA&quot;], '.merb', 'git-sources.yml')
-        File.exists?(local_config) ? local_config : user_config
-      end
-      if source_config &amp;&amp; File.exists?(source_config)
-        default_repos.merge(YAML.load(File.read(source_config)))
-      else
-        default_repos
-      end
-    end
-    
-    def self.repo(name, source_config = nil)
-      self.repos(source_config)[name]
-    end
-    
-    # Default Git repositories
-    def self.default_repos
-      @_default_repos ||= { 
-        'merb'          =&gt; &quot;git://github.com/wycats/merb.git&quot;,
-        'merb-plugins'  =&gt; &quot;git://github.com/wycats/merb-plugins.git&quot;,
-        'extlib'        =&gt; &quot;git://github.com/sam/extlib.git&quot;,
-        'dm-core'       =&gt; &quot;git://github.com/sam/dm-core.git&quot;,
-        'dm-more'       =&gt; &quot;git://github.com/sam/dm-more.git&quot;,
-        'sequel'        =&gt; &quot;git://github.com/wayneeseguin/sequel.git&quot;,
-        'do'            =&gt; &quot;git://github.com/sam/do.git&quot;,
-        'thor'          =&gt; &quot;git://github.com/wycats/thor.git&quot;,
-        'rake'          =&gt; &quot;git://github.com/jimweirich/rake.git&quot;,
-        'minigems'      =&gt; &quot;git://github.com/fabien/minigems.git&quot;
-      }
-    end
-       
-  end
-  
 end
\ No newline at end of file</diff>
      <filename>tasks/merb.thor</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>b85a953e6b679c1bdc393afba9bd0e42723e3e0c</id>
    </parent>
  </parents>
  <author>
    <name>rick</name>
    <email>technoweenie@gmail.com</email>
  </author>
  <url>http://github.com/jnunemaker/astrotrain/commit/e51e3884cd2d215656ad65c2f4a2e65b4d46ed34</url>
  <id>e51e3884cd2d215656ad65c2f4a2e65b4d46ed34</id>
  <committed-date>2008-11-21T16:18:42-08:00</committed-date>
  <authored-date>2008-11-21T16:18:42-08:00</authored-date>
  <message>update merb support files</message>
  <tree>f747e45571958d896a7e8fc22cbca5db2de8a616</tree>
  <committer>
    <name>rick</name>
    <email>technoweenie@gmail.com</email>
  </committer>
</commit>
