merb-gen 0.9.13 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@ require 'fileutils'
5
5
  require 'yaml'
6
6
 
7
7
  # Important - don't change this line or its position
8
- MERB_THOR_VERSION = '0.1.0'
8
+ MERB_THOR_VERSION = '0.2.1'
9
9
 
10
10
  ##############################################################################
11
11
 
@@ -335,7 +335,7 @@ end
335
335
 
336
336
  # use gems dir if ../gems exists - eg. only for ./bin/#{bin_file_name}
337
337
  if File.directory?(gems_dir = File.join(File.dirname(__FILE__), '..', 'gems'))
338
- $BUNDLE = true; Gem.clear_paths; Gem.path.replace([gems_dir])
338
+ $BUNDLE = true; Gem.clear_paths; Gem.path.replace([File.expand_path(gems_dir)])
339
339
  ENV["PATH"] = "\#{File.dirname(__FILE__)}:\#{gems_dir}/bin:\#{ENV["PATH"]}"
340
340
  if (local_gem = Dir[File.join(gems_dir, "specifications", "#{spec.name}-*.gemspec")].last)
341
341
  version = File.basename(local_gem)[/-([\\.\\d]+)\\.gemspec$/, 1]
@@ -446,6 +446,10 @@ module MerbThorHelper
446
446
  base.extend ColorfulMessages
447
447
  end
448
448
 
449
+ def use_edge_gem_server
450
+ ::Gem.sources << 'http://edge.merbivore.com'
451
+ end
452
+
449
453
  def source_manager
450
454
  @_source_manager ||= SourceManager.new(source_dir)
451
455
  end
@@ -643,166 +647,112 @@ end
643
647
  $SILENT = true # don't output all the mess some rake package tasks spit out
644
648
 
645
649
  module Merb
650
+
651
+ class Gem < Thor
646
652
 
647
- class Dependencies < Thor
648
-
649
- # The Dependencies tasks will install dependencies based on actual application
650
- # dependencies. For this, the application is queried for any dependencies.
651
- # All operations will be performed within this context.
653
+ include MerbThorHelper
654
+ extend GemManagement
652
655
 
653
656
  attr_accessor :system, :local, :missing
654
657
 
655
- include MerbThorHelper
656
-
657
658
  global_method_options = {
658
659
  "--merb-root" => :optional, # the directory to operate on
659
- "--include-dependencies" => :boolean, # gather sub-dependencies
660
- "--stack" => :boolean, # gather only stack dependencies
661
- "--no-stack" => :boolean, # gather only non-stack dependencies
662
- "--config" => :boolean, # gather dependencies from yaml config
663
- "--config-file" => :optional, # gather from the specified yaml config
664
- "--version" => :optional # gather specific version of framework
660
+ "--version" => :optional, # gather specific version of gem
661
+ "--ignore-dependencies" => :boolean # don't install sub-dependencies
665
662
  }
666
663
 
667
664
  method_options global_method_options
668
665
  def initialize(*args); super; end
669
666
 
670
- # List application dependencies.
667
+ # List gems that match the specified criteria.
671
668
  #
672
- # By default all dependencies are listed, partitioned into system, local and
673
- # currently missing dependencies. The first argument allows you to filter
674
- # on any of the partitionings. A second argument can be used to filter on
675
- # a set of known components, like all merb-more gems for example.
669
+ # By default all local gems are listed. When the first argument is 'all' the
670
+ # list is partitioned into system an local gems; specify 'system' to show
671
+ # only system gems. A second argument can be used to filter on a set of known
672
+ # components, like all merb-more gems for example.
676
673
  #
677
674
  # Examples:
678
675
  #
679
- # merb:dependencies:list # list all dependencies - the default
680
- # merb:dependencies:list local # list only local gems
681
- # merb:dependencies:list all merb-more # list only merb-more related dependencies
682
- # merb:dependencies:list --stack # list framework dependencies
683
- # merb:dependencies:list --no-stack # list 3rd party dependencies
684
- # merb:dependencies:list --config # list dependencies from the default config
685
- # merb:dependencies:list --config-file file.yml # list from the specified config file
676
+ # merb:gem:list # list all local gems - the default
677
+ # merb:gem:list all # list system and local gems
678
+ # merb:gem:list system # list only system gems
679
+ # merb:gem:list all merb-more # list only merb-more related gems
680
+ # merb:gem:list --version 0.9.8 # list gems that match the version
686
681
 
687
- desc 'list [all|local|system|missing] [comp]', 'Show application dependencies'
688
- def list(filter = 'all', comp = nil)
682
+ desc 'list [all|local|system] [comp]', 'Show installed gems'
683
+ def list(filter = 'local', comp = nil)
689
684
  deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
690
685
  self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
691
686
  case filter
692
687
  when 'all'
693
- message 'Installed system gem dependencies:'
688
+ message 'Installed system gems:'
694
689
  display_gemspecs(system)
695
- message 'Installed local gem dependencies:'
690
+ message 'Installed local gems:'
696
691
  display_gemspecs(local)
697
- unless missing.empty?
698
- error 'Missing gem dependencies:'
699
- display_dependencies(missing)
700
- end
701
692
  when 'system'
702
- message 'Installed system gem dependencies:'
693
+ message 'Installed system gems:'
703
694
  display_gemspecs(system)
704
695
  when 'local'
705
- message 'Installed local gem dependencies:'
696
+ message 'Installed local gems:'
706
697
  display_gemspecs(local)
707
- when 'missing'
708
- error 'Missing gem dependencies:'
709
- display_dependencies(missing)
710
698
  else
711
699
  warning "Invalid listing filter '#{filter}'"
712
700
  end
713
- if missing.size > 0
714
- info "Some dependencies are currently missing!"
715
- elsif local.size == deps.size
716
- info "All dependencies have been bundled with the application."
717
- elsif local.size > system.size
718
- info "Most dependencies have been bundled with the application."
719
- elsif system.size > 0 && local.size > 0
720
- info "Some dependencies have been bundled with the application."
721
- elsif local.empty? && system.size == deps.size
722
- info "All dependencies are available on the system."
723
- end
724
701
  end
725
702
 
726
- # Install application dependencies.
703
+ # Install the specified gems.
727
704
  #
728
- # By default all required dependencies are installed. The first argument
729
- # specifies which strategy to use: stable or edge. A second argument can be
730
- # used to filter on a set of known components.
705
+ # All arguments should be names of gems to install.
731
706
  #
732
- # Existing dependencies will be clobbered; when :force => true then all gems
733
- # will be cleared first, otherwise only existing local dependencies of the
734
- # gems to be installed will be removed.
707
+ # When :force => true then any existing versions of the gems to be installed
708
+ # will be uninstalled first. It's important to note that so-called meta-gems
709
+ # or gems that exactly match a set of Merb::Stack.components will have their
710
+ # sub-gems uninstalled too. For example, uninstalling merb-more will install
711
+ # all contained gems: merb-action-args, merb-assets, merb-gen, ...
735
712
  #
736
713
  # Examples:
737
714
  #
738
- # merb:dependencies:install # install all dependencies using stable strategy
739
- # merb:dependencies:install stable --version 0.9.8 # install a specific version of the framework
740
- # merb:dependencies:install stable missing # install currently missing gems locally
741
- # merb:dependencies:install stable merb-more # install only merb-more related dependencies
742
- # merb:dependencies:install stable --stack # install framework dependencies
743
- # merb:dependencies:install stable --no-stack # install 3rd party dependencies
744
- # merb:dependencies:install stable --config # read dependencies from the default config
745
- # merb:dependencies:install stable --config-file file.yml # read from the specified config file
746
- #
747
- # In addition to the options above, edge install uses the following:
748
- #
749
- # merb:dependencies:install edge # install all dependencies using edge strategy
750
- # merb:dependencies:install edge --sources file.yml # install edge from the specified git sources config
751
-
752
- desc 'install [stable|edge] [comp]', 'Install application dependencies'
753
- method_options "--sources" => :optional, # only for edge strategy
754
- "--local" => :boolean, # force local install
755
- "--dry-run" => :boolean,
756
- "--force" => :boolean
757
- def install(strategy = 'stable', comp = nil)
758
- if strategy?(strategy)
759
- # Force local dependencies by creating ./gems before proceeding
760
- create_if_missing(default_gem_dir) if options[:local]
761
-
762
- where = gem_dir ? 'locally' : 'system-wide'
763
-
764
- # When comp == 'missing' then filter on missing dependencies
765
- if only_missing = comp == 'missing'
766
- message "Preparing to install missing gems #{where} using #{strategy} strategy..."
767
- comp = nil
768
- clobber = false
769
- else
770
- message "Preparing to install #{where} using #{strategy} strategy..."
771
- clobber = true
772
- end
773
-
774
- # If comp given, filter on known stack components
775
- deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
776
- self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
777
-
778
- # Only install currently missing gems (for comp == missing)
779
- if only_missing
780
- deps.reject! { |dep| not missing.include?(dep) }
781
- end
782
-
783
- if deps.empty?
784
- warning "No dependencies to install..."
715
+ # merb:gem:install merb-core merb-slices # install all specified gems
716
+ # merb:gem:install merb-core --version 0.9.8 # install a specific version of a gem
717
+ # merb:gem:install merb-core --force # uninstall then subsequently install the gem
718
+ # merb:gem:install merb-core --cache # try to install locally from system gems
719
+ # merb:gem:install merb --merb-edge # install from edge.merbivore.com
720
+
721
+ desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from rubygems'
722
+ method_options "--cache" => :boolean,
723
+ "--dry-run" => :boolean,
724
+ "--force" => :boolean,
725
+ "--merb-edge" => :boolean
726
+ def install(*names)
727
+ opts = { :version => options[:version], :cache => options[:cache] }
728
+ use_edge_gem_server if options[:"merb-edge"]
729
+ current_gem = nil
730
+
731
+ # uninstall existing gems of the ones we're going to install
732
+ uninstall(*names) if options[:force]
733
+
734
+ message "Installing #{names.length} #{names.length == 1 ? 'gem' : 'gems'}..."
735
+ puts "This may take a while..."
736
+
737
+ names.each do |gem_name|
738
+ current_gem = gem_name
739
+ if dry_run?
740
+ note "Installing #{current_gem}..."
785
741
  else
786
- puts "#{deps.length} dependencies to install..."
787
- install_dependencies(strategy, deps, clobber)
742
+ message "Installing #{current_gem}..."
743
+ self.class.install(gem_name, default_install_options.merge(opts))
788
744
  end
789
-
790
- # Show current dependency info now that we're done
791
- puts # Seperate output
792
- list('local', comp)
793
- else
794
- warning "Invalid install strategy '#{strategy}'"
795
- puts
796
- message "Please choose one of the following installation strategies: stable or edge:"
797
- puts "$ thor merb:dependencies:install stable"
798
- puts "$ thor merb:dependencies:install edge"
799
- end
745
+ end
746
+ rescue => e
747
+ error "Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})"
800
748
  end
801
749
 
802
- # Uninstall application dependencies.
750
+ # Uninstall the specified gems.
803
751
  #
804
- # By default all required dependencies are installed. An optional argument
805
- # can be used to filter on a set of known components.
752
+ # By default all specified gems are uninstalled. It's important to note that
753
+ # so-called meta-gems or gems that match a set of Merb::Stack.components will
754
+ # have their sub-gems uninstalled too. For example, uninstalling merb-more
755
+ # will install all contained gems: merb-action-args, merb-assets, ...
806
756
  #
807
757
  # Existing dependencies will be clobbered; when :force => true then all gems
808
758
  # will be cleared, otherwise only existing local dependencies of the
@@ -810,21 +760,32 @@ module Merb
810
760
  #
811
761
  # Examples:
812
762
  #
813
- # merb:dependencies:uninstall # uninstall all dependencies - the default
814
- # merb:dependencies:uninstall merb-more # uninstall merb-more related gems locally
815
- # merb:dependencies:uninstall --config # read dependencies from the default config
763
+ # merb:gem:uninstall merb-core merb-slices # uninstall all specified gems
764
+ # merb:gem:uninstall merb-core --version 0.9.8 # uninstall a specific version of a gem
816
765
 
817
- desc 'uninstall [comp]', 'Uninstall application dependencies'
818
- method_options "--dry-run" => :boolean, "--force" => :boolean
819
- def uninstall(comp = nil)
820
- # If comp given, filter on known stack components
821
- deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
822
- self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
823
- # Clobber existing local dependencies - based on self.local
824
- clobber_dependencies!
766
+ desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem'
767
+ method_options "--dry-run" => :boolean
768
+ def uninstall(*names)
769
+ opts = { :version => options[:version] }
770
+ current_gem = nil
771
+ if dry_run?
772
+ note "Uninstalling any existing gems of: #{names.join(', ')}"
773
+ else
774
+ message "Uninstalling any existing gems of: #{names.join(', ')}"
775
+ names.each do |gem_name|
776
+ current_gem = gem_name
777
+ Merb::Gem.uninstall(gem_name, default_uninstall_options) rescue nil
778
+ # if this gem is a meta-gem or a component set name, remove sub-gems
779
+ (Merb::Stack.components(gem_name) || []).each do |comp|
780
+ Merb::Gem.uninstall(comp, default_uninstall_options) rescue nil
781
+ end
782
+ end
783
+ end
784
+ rescue => e
785
+ error "Failed to uninstall #{current_gem ? current_gem : 'gem'} (#{e.message})"
825
786
  end
826
787
 
827
- # Recreate binary gems on the current platform.
788
+ # Recreate all gems from gems/cache on the current platform.
828
789
  #
829
790
  # This task should be executed as part of a deployment setup, where the
830
791
  # deployment system runs this after the app has been installed.
@@ -833,27 +794,32 @@ module Merb
833
794
  # It will regenerate gems from the bundled gems cache for any gem that has
834
795
  # C extensions - which need to be recompiled for the target deployment platform.
835
796
  #
836
- # Note: gems/cache should be in your SCM for this to work correctly.
797
+ # Note: at least gems/cache and gems/specifications should be in your SCM.
837
798
 
838
- desc 'redeploy', 'Recreate any binary gems on the target platform'
839
- method_options "--dry-run" => :boolean
799
+ desc 'redeploy', 'Recreate all gems on the current platform'
800
+ method_options "--dry-run" => :boolean, "--force" => :boolean
840
801
  def redeploy
841
802
  require 'tempfile' # for Dir::tmpdir access
842
803
  if gem_dir && File.directory?(cache_dir = File.join(gem_dir, 'cache'))
843
- local_gemspecs.each do |gemspec|
844
- unless gemspec.extensions.empty?
845
- if File.exists?(gem_file = File.join(cache_dir, "#{gemspec.full_name}.gem"))
846
- gem_file_copy = File.join(Dir::tmpdir, File.basename(gem_file))
847
- if dry_run?
848
- note "Recreating #{gemspec.full_name}"
849
- else
850
- message "Recreating #{gemspec.full_name}"
851
- # Copy the gem to a temporary file, because otherwise RubyGems/FileUtils
852
- # will complain about copying identical files (same source/destination).
853
- FileUtils.cp(gem_file, gem_file_copy)
854
- Merb::Gem.install(gem_file_copy, :install_dir => gem_dir)
855
- File.delete(gem_file_copy)
856
- end
804
+ specs = local_gemspecs
805
+ message "Recreating #{specs.length} gems from cache..."
806
+ puts "This may take a while..."
807
+ specs.each do |gemspec|
808
+ if File.exists?(gem_file = File.join(cache_dir, "#{gemspec.full_name}.gem"))
809
+ gem_file_copy = File.join(Dir::tmpdir, File.basename(gem_file))
810
+ if dry_run?
811
+ note "Recreating #{gemspec.full_name}"
812
+ else
813
+ message "Recreating #{gemspec.full_name}"
814
+ if options[:force] && File.directory?(gem = File.join(gem_dir, 'gems', gemspec.full_name))
815
+ puts "Removing existing #{gemspec.full_name}"
816
+ FileUtils.rm_rf(gem)
817
+ end
818
+ # Copy the gem to a temporary file, because otherwise RubyGems/FileUtils
819
+ # will complain about copying identical files (same source/destination).
820
+ FileUtils.cp(gem_file, gem_file_copy)
821
+ Merb::Gem.install(gem_file_copy, :install_dir => gem_dir, :ignore_dependencies => true)
822
+ File.delete(gem_file_copy)
857
823
  end
858
824
  end
859
825
  end
@@ -862,1124 +828,1193 @@ module Merb
862
828
  end
863
829
  end
864
830
 
865
- # Create a dependencies configuration file.
866
- #
867
- # A configuration yaml file will be created from the extracted application
868
- # dependencies. The format of the configuration is as follows:
869
- #
870
- # ---
871
- # - merb-core (= 0.9.8, runtime)
872
- # - merb-slices (= 0.9.8, runtime)
873
- #
874
- # This format is exactly the same as Gem::Dependency#to_s returns.
875
- #
876
- # Examples:
877
- #
878
- # merb:dependencies:configure --force # overwrite the default config file
879
- # merb:dependencies:configure --version 0.9.8 # configure specific framework version
880
- # merb:dependencies:configure --config-file file.yml # write to the specified config file
831
+ private
881
832
 
882
- desc 'configure [comp]', 'Create a dependencies config file'
883
- method_options "--dry-run" => :boolean, "--force" => :boolean, "--versions" => :boolean
884
- def configure(comp = nil)
885
- # If comp given, filter on known stack components
886
- deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
887
-
888
- # If --versions is set, update the version_requirements with the actual version available
889
- if options[:versions]
890
- specs = local_gemspecs
891
- deps.each do |dep|
892
- if spec = specs.find { |s| s.name == dep.name }
893
- dep.version_requirements = ::Gem::Requirement.create(spec.version)
894
- end
895
- end
896
- end
897
-
898
- config = YAML.dump(deps.map { |d| d.to_s })
899
- puts "#{config}\n"
900
- if File.exists?(config_file) && !options[:force]
901
- error "File already exists! Use --force to overwrite."
833
+ # Return dependencies for all installed gems; both system-wide and locally;
834
+ # optionally filters on :version requirement.
835
+ def dependencies
836
+ version_req = if options[:version]
837
+ ::Gem::Requirement.create(options[:version])
902
838
  else
903
- if dry_run?
904
- note "Written #{config_file}"
905
- else
906
- FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
907
- File.open(config_file, 'w') { |f| f.write config }
908
- success "Written #{config_file}"
909
- end
910
- end
911
- rescue
912
- error "Failed to write to #{config_file}"
913
- end
914
-
915
- ### Helper Methods
916
-
917
- def strategy?(strategy)
918
- if self.respond_to?(method = :"#{strategy}_strategy", true)
919
- method
920
- end
921
- end
922
-
923
- def install_dependencies(strategy, deps, clobber = true)
924
- if method = strategy?(strategy)
925
- # Clobber existing local dependencies
926
- clobber_dependencies! if clobber
927
-
928
- # Run the chosen strategy - collect files installed from stable gems
929
- installed_from_stable = send(method, deps).map { |d| d.name }
930
-
931
- unless dry_run?
932
- # Sleep a bit otherwise the following steps won't see the new files
933
- sleep(deps.length) if deps.length > 0 && deps.length <= 10
934
-
935
- # Leave a file to denote the strategy that has been used for this dependency
936
- self.local.each do |spec|
937
- next unless File.directory?(spec.full_gem_path)
938
- unless installed_from_stable.include?(spec.name)
939
- FileUtils.touch(File.join(spec.full_gem_path, "#{strategy}.strategy"))
940
- else
941
- FileUtils.touch(File.join(spec.full_gem_path, "stable.strategy"))
942
- end
943
- end
944
- end
945
- return true
946
- end
947
- false
948
- end
949
-
950
- def dependencies
951
- if use_config?
952
- # Use preconfigured dependencies from yaml file
953
- deps = config_dependencies
954
- else
955
- # Extract dependencies from the current application
956
- deps = Merb::Stack.core_dependencies(gem_dir, ignore_dependencies?)
957
- deps += Merb::Dependencies.extract_dependencies(working_dir)
958
- end
959
-
960
- stack_components = Merb::Stack.components
961
-
962
- if options[:stack]
963
- # Limit to stack components only
964
- deps.reject! { |dep| not stack_components.include?(dep.name) }
965
- elsif options[:"no-stack"]
966
- # Limit to non-stack components
967
- deps.reject! { |dep| stack_components.include?(dep.name) }
839
+ ::Gem::Requirement.default
968
840
  end
969
-
970
- if options[:version]
971
- version_req = ::Gem::Requirement.create("= #{options[:version]}")
972
- elsif core = deps.find { |d| d.name == 'merb-core' }
973
- version_req = core.version_requirements
841
+ if gem_dir
842
+ ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
843
+ ::Gem.source_index.refresh!
974
844
  end
975
-
976
- if version_req
977
- # Handle specific version requirement for framework components
978
- framework_components = Merb::Stack.framework_components
979
- deps.each do |dep|
980
- if framework_components.include?(dep.name)
981
- dep.version_requirements = version_req
982
- end
845
+ deps = []
846
+ ::Gem.source_index.each do |fullname, gemspec|
847
+ if version_req.satisfied_by?(gemspec.version)
848
+ deps << ::Gem::Dependency.new(gemspec.name, "= #{gemspec.version}")
983
849
  end
984
850
  end
985
-
986
- deps
851
+ ::Gem.clear_paths if gem_dir
852
+ deps.sort
987
853
  end
988
854
 
989
- def config_dependencies
990
- if File.exists?(config_file)
991
- self.class.parse_dependencies_yaml(File.read(config_file))
992
- else
993
- []
994
- end
995
- end
855
+ public
996
856
 
997
- def use_config?
998
- options[:config] || options[:"config-file"]
857
+ # Install gem with some default options.
858
+ def self.install(name, options = {})
859
+ defaults = {}
860
+ defaults[:cache] = false unless opts[:install_dir]
861
+ install_gem(name, defaults.merge(options))
999
862
  end
1000
863
 
1001
- def config_file
1002
- @config_file ||= begin
1003
- options[:"config-file"] || File.join(working_dir, 'config', 'dependencies.yml')
1004
- end
864
+ # Uninstall gem with some default options.
865
+ def self.uninstall(name, options = {})
866
+ defaults = { :ignore => true, :executables => true }
867
+ uninstall_gem(name, defaults.merge(options))
1005
868
  end
1006
869
 
1007
- def config_dir
1008
- File.dirname(config_file)
1009
- end
870
+ end
871
+
872
+ class Tasks < Thor
1010
873
 
1011
- ### Strategy handlers
874
+ include MerbThorHelper
1012
875
 
1013
- private
876
+ # Show merb.thor version information
877
+ #
878
+ # merb:tasks:version # show the current version info
879
+ # merb:tasks:version --info # show extended version info
1014
880
 
1015
- def stable_strategy(deps)
1016
- installed_from_rubygems = []
1017
- if core = deps.find { |d| d.name == 'merb-core' }
1018
- if dry_run?
1019
- note "Installing #{core.name}..."
1020
- else
1021
- if install_dependency(core)
1022
- installed_from_rubygems << core
1023
- else
1024
- msg = "Try specifying a lower version of merb-core with --version"
1025
- if version_no = core.version_requirements.to_s[/([\.\d]+)$/, 1]
1026
- num = "%03d" % (version_no.gsub('.', '').to_i - 1)
1027
- puts "The required version (#{version_no}) probably isn't available as a stable rubygem yet."
1028
- info "#{msg} #{num.split(//).join('.')}"
1029
- else
1030
- puts "The required version probably isn't available as a stable rubygem yet."
1031
- info msg
1032
- end
1033
- end
1034
- end
1035
- end
1036
-
1037
- deps.each do |dependency|
1038
- next if dependency.name == 'merb-core'
1039
- if dry_run?
1040
- note "Installing #{dependency.name}..."
1041
- else
1042
- install_dependency(dependency)
1043
- installed_from_rubygems << dependency
1044
- end
881
+ desc 'version', 'Show verion info'
882
+ method_options "--info" => :boolean
883
+ def version
884
+ message "Currently installed merb.thor version: #{MERB_THOR_VERSION}"
885
+ if options[:version]
886
+ self.options = { :"dry-run" => true }
887
+ self.update # run update task with dry-run enabled
1045
888
  end
1046
- installed_from_rubygems
1047
889
  end
1048
890
 
1049
- def edge_strategy(deps)
1050
- installed_from_rubygems = []
1051
-
1052
- # Selectively update repositories for the matching dependencies
1053
- update_dependency_repositories(deps) unless dry_run?
891
+ # Update merb.thor tasks from remotely available version
892
+ #
893
+ # merb:tasks:update # update merb.thor
894
+ # merb:tasks:update --force # force-update merb.thor
895
+ # merb:tasks:update --dry-run # show version info only
896
+
897
+ desc 'update [URL]', 'Fetch the latest merb.thor and install it locally'
898
+ method_options "--dry-run" => :boolean, "--force" => :boolean
899
+ def update(url = 'http://merbivore.com/merb.thor')
900
+ require 'open-uri'
901
+ require 'rubygems/version'
902
+ remote_file = open(url)
903
+ code = remote_file.read
1054
904
 
1055
- if core = deps.find { |d| d.name == 'merb-core' }
1056
- if dry_run?
1057
- note "Installing #{core.name}..."
1058
- else
1059
- if install_dependency_from_source(core)
1060
- elsif install_dependency(core)
1061
- info "Installed #{core.name} from rubygems..."
1062
- installed_from_rubygems << core
905
+ # Extract version information from the source code
906
+ if version = code[/^MERB_THOR_VERSION\s?=\s?('|")([\.\d]+)('|")/,2]
907
+ # borrow version comparison from rubygems' Version class
908
+ current_version = ::Gem::Version.new(MERB_THOR_VERSION)
909
+ remote_version = ::Gem::Version.new(version)
910
+
911
+ if current_version >= remote_version
912
+ puts "currently installed: #{current_version}"
913
+ if current_version != remote_version
914
+ puts "available version: #{remote_version}"
1063
915
  end
916
+ info "No update of merb.thor necessary#{options[:force] ? ' (forced)' : ''}"
917
+ proceed = options[:force]
918
+ elsif current_version < remote_version
919
+ puts "currently installed: #{current_version}"
920
+ puts "available version: #{remote_version}"
921
+ proceed = true
1064
922
  end
1065
- end
1066
-
1067
- deps.each do |dependency|
1068
- next if dependency.name == 'merb-core'
1069
- if dry_run?
1070
- note "Installing #{dependency.name}..."
1071
- else
1072
- if install_dependency_from_source(dependency)
1073
- elsif install_dependency(dependency)
1074
- info "Installed #{dependency.name} from rubygems..."
1075
- installed_from_rubygems << dependency
923
+
924
+ if proceed && !dry_run?
925
+ File.open(File.join(__FILE__), 'w') do |f|
926
+ f.write(code)
1076
927
  end
1077
- end
1078
- end
1079
-
1080
- installed_from_rubygems
928
+ success "Installed the latest merb.thor (v#{version})"
929
+ end
930
+ else
931
+ raise "invalid source-code data"
932
+ end
933
+ rescue OpenURI::HTTPError
934
+ error "Error opening #{url}"
935
+ rescue => e
936
+ error "An error occurred (#{e.message})"
1081
937
  end
1082
938
 
1083
- ### Class Methods
939
+ end
940
+
941
+ #### MORE LOW-LEVEL TASKS ####
942
+
943
+ class Source < Thor
1084
944
 
1085
- public
945
+ group 'core'
946
+
947
+ include MerbThorHelper
948
+ extend GemManagement
1086
949
 
1087
- def self.list(filter = 'all', comp = nil, options = {})
1088
- instance = Merb::Dependencies.new
1089
- instance.options = options
1090
- instance.list(filter, comp)
1091
- end
950
+ attr_accessor :system, :local, :missing
1092
951
 
1093
- # Extract application dependencies by querying the app directly.
1094
- def self.extract_dependencies(merb_root)
1095
- require 'merb-core'
1096
- if !@_merb_loaded || Merb.root != merb_root
1097
- Merb.start_environment(
1098
- :log_level => :fatal,
1099
- :testing => true,
1100
- :adapter => 'runner',
1101
- :environment => ENV['MERB_ENV'] || 'development',
1102
- :merb_root => merb_root
1103
- )
1104
- @_merb_loaded = true
952
+ global_method_options = {
953
+ "--merb-root" => :optional, # the directory to operate on
954
+ "--ignore-dependencies" => :boolean, # don't install sub-dependencies
955
+ "--sources" => :optional # a yml config to grab sources from
956
+ }
957
+
958
+ method_options global_method_options
959
+ def initialize(*args); super; end
960
+
961
+ # List source repositories, of either local or known sources.
962
+ #
963
+ # Examples:
964
+ #
965
+ # merb:source:list # list all local sources
966
+ # merb:source:list available # list all known sources
967
+
968
+ desc 'list [local|available]', 'Show git source repositories'
969
+ def list(mode = 'local')
970
+ if mode == 'available'
971
+ message 'Available source repositories:'
972
+ repos = self.class.repos(options[:sources])
973
+ repos.keys.sort.each { |name| puts "- #{name}: #{repos[name]}" }
974
+ elsif mode == 'local'
975
+ message 'Current source repositories:'
976
+ Dir[File.join(source_dir, '*')].each do |src|
977
+ next unless File.directory?(src)
978
+ src_name = File.basename(src)
979
+ unless (repos = source_manager.existing_repos(src_name)).empty?
980
+ puts "#{src_name}"
981
+ repos.keys.sort.each { |b| puts "- #{b}: #{repos[b]}" }
982
+ end
983
+ end
984
+ else
985
+ error "Unknown listing: #{mode}"
1105
986
  end
1106
- Merb::BootLoader::Dependencies.dependencies
1107
- rescue StandardError => e
1108
- error "Couldn't extract dependencies from application!"
1109
- error e.message
1110
- puts "Make sure you're executing the task from your app (--merb-root), or"
1111
- puts "specify a config option (--config or --config-file=YAML_FILE)"
1112
- return []
1113
- rescue SystemExit
1114
- error "Couldn't extract dependencies from application!"
1115
- error "application failed to run"
1116
- puts "Please check if your application runs using 'merb'; for example,"
1117
- puts "look for any gem version mismatches in dependencies.rb"
1118
- return []
1119
987
  end
1120
-
1121
- # Parse the basic YAML config data, and process Gem::Dependency output.
1122
- # Formatting example: merb_helpers (>= 0.9.8, runtime)
1123
- def self.parse_dependencies_yaml(yaml)
1124
- dependencies = []
1125
- entries = YAML.load(yaml) rescue []
1126
- entries.each do |entry|
1127
- if matches = entry.match(/^(\S+) \(([^,]+)?, ([^\)]+)\)/)
1128
- name, version_req, type = matches.captures
1129
- dependencies << ::Gem::Dependency.new(name, version_req, type.to_sym)
988
+
989
+ # Install the specified gems.
990
+ #
991
+ # All arguments should be names of gems to install.
992
+ #
993
+ # When :force => true then any existing versions of the gems to be installed
994
+ # will be uninstalled first. It's important to note that so-called meta-gems
995
+ # or gems that exactly match a set of Merb::Stack.components will have their
996
+ # sub-gems uninstalled too. For example, uninstalling merb-more will install
997
+ # all contained gems: merb-action-args, merb-assets, merb-gen, ...
998
+ #
999
+ # Examples:
1000
+ #
1001
+ # merb:source:install merb-core merb-slices # install all specified gems
1002
+ # merb:source:install merb-core --force # uninstall then subsequently install the gem
1003
+ # merb:source:install merb-core --wipe # clear repo then install the gem
1004
+
1005
+ desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from git source/edge'
1006
+ method_options "--dry-run" => :boolean,
1007
+ "--force" => :boolean,
1008
+ "--wipe" => :boolean
1009
+ def install(*names)
1010
+ use_edge_gem_server
1011
+ # uninstall existing gems of the ones we're going to install
1012
+ uninstall(*names) if options[:force] || options[:wipe]
1013
+
1014
+ # We want dependencies instead of just names
1015
+ deps = names.map { |n| ::Gem::Dependency.new(n, ::Gem::Requirement.default) }
1016
+
1017
+ # Selectively update repositories for the matching dependencies
1018
+ update_dependency_repositories(deps) unless dry_run?
1019
+
1020
+ current_gem = nil
1021
+ deps.each do |dependency|
1022
+ current_gem = dependency.name
1023
+ if dry_run?
1024
+ note "Installing #{current_gem} from source..."
1130
1025
  else
1131
- error "Invalid entry: #{entry}"
1026
+ message "Installing #{current_gem} from source..."
1027
+ puts "This may take a while..."
1028
+ unless install_dependency_from_source(dependency)
1029
+ raise "gem source not found"
1030
+ end
1132
1031
  end
1133
1032
  end
1134
- dependencies
1033
+ rescue => e
1034
+ error "Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})"
1135
1035
  end
1136
1036
 
1137
- end
1138
-
1139
- class Stack < Thor
1037
+ # Uninstall the specified gems.
1038
+ #
1039
+ # By default all specified gems are uninstalled. It's important to note that
1040
+ # so-called meta-gems or gems that match a set of Merb::Stack.components will
1041
+ # have their sub-gems uninstalled too. For example, uninstalling merb-more
1042
+ # will install all contained gems: merb-action-args, merb-assets, ...
1043
+ #
1044
+ # Existing dependencies will be clobbered; when :force => true then all gems
1045
+ # will be cleared, otherwise only existing local dependencies of the
1046
+ # matching component set will be removed. Additionally when :wipe => true,
1047
+ # the matching git repositories will be removed from the source directory.
1048
+ #
1049
+ # Examples:
1050
+ #
1051
+ # merb:source:uninstall merb-core merb-slices # uninstall all specified gems
1052
+ # merb:source:uninstall merb-core --wipe # force-uninstall a gem and clear repo
1140
1053
 
1141
- # The Stack tasks will install dependencies based on known sets of gems,
1142
- # regardless of actual application dependency settings.
1054
+ desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem (specify --force to remove the repo)'
1055
+ method_options "--version" => :optional, "--dry-run" => :boolean, "--wipe" => :boolean
1056
+ def uninstall(*names)
1057
+ # Remove the repos that contain the gem
1058
+ if options[:wipe]
1059
+ extract_repositories(names).each do |(name, url)|
1060
+ if File.directory?(src = File.join(source_dir, name))
1061
+ if dry_run?
1062
+ note "Removing #{src}..."
1063
+ else
1064
+ info "Removing #{src}..."
1065
+ FileUtils.rm_rf(src)
1066
+ end
1067
+ end
1068
+ end
1069
+ end
1070
+
1071
+ # Use the Merb::Gem#uninstall task to handle this
1072
+ gem_tasks = Merb::Gem.new
1073
+ gem_tasks.options = options
1074
+ gem_tasks.uninstall(*names)
1075
+ end
1143
1076
 
1144
- DM_STACK = %w[
1145
- extlib
1146
- data_objects
1147
- dm-core
1148
- dm-aggregates
1149
- dm-migrations
1150
- dm-timestamps
1151
- dm-types
1152
- dm-validations
1153
- merb_datamapper
1154
- ]
1077
+ # Update the specified source repositories.
1078
+ #
1079
+ # The arguments can be actual repository names (from Merb::Source.repos)
1080
+ # or names of known merb stack gems. If the repo doesn't exist already,
1081
+ # it will be created and cloned.
1082
+ #
1083
+ # merb:source:pull merb-core # update source of specified gem
1084
+ # merb:source:pull merb-slices # implicitly updates merb-more
1155
1085
 
1156
- MERB_STACK = %w[
1157
- extlib
1158
- merb-core
1159
- merb-action-args
1160
- merb-assets
1161
- merb-cache
1162
- merb-helpers
1163
- merb-mailer
1164
- merb-slices
1165
- merb-auth
1166
- merb-auth-core
1167
- merb-auth-more
1168
- merb-auth-slice-password
1169
- merb-param-protection
1170
- merb-exceptions
1171
- ] + DM_STACK
1086
+ desc 'pull REPO_NAME [GEM_NAME, ...]', 'Update git source repository from edge'
1087
+ def pull(*names)
1088
+ repos = extract_repositories(names)
1089
+ update_repositories(repos)
1090
+ unless repos.empty?
1091
+ message "Updated the following repositories:"
1092
+ repos.each { |name, url| puts "- #{name}: #{url}" }
1093
+ else
1094
+ warning "No repositories found to update!"
1095
+ end
1096
+ end
1172
1097
 
1173
- MERB_BASICS = %w[
1174
- extlib
1175
- merb-core
1176
- merb-action-args
1177
- merb-assets
1178
- merb-cache
1179
- merb-helpers
1180
- merb-mailer
1181
- merb-slices
1182
- ]
1098
+ # Clone a git repository into ./src.
1183
1099
 
1184
- # The following sets are meant for repository lookup; unlike the sets above
1185
- # these correspond to specific git repository items.
1100
+ # The repository can be a direct git url or a known -named- repository.
1101
+ #
1102
+ # Examples:
1103
+ #
1104
+ # merb:source:clone merb-core
1105
+ # merb:source:clone dm-core awesome-repo
1106
+ # merb:source:clone dm-core --sources ./path/to/sources.yml
1107
+ # merb:source:clone git://github.com/sam/dm-core.git
1186
1108
 
1187
- MERB_MORE = %w[
1188
- merb-action-args
1189
- merb-assets
1190
- merb-auth
1191
- merb-auth-core
1192
- merb-auth-more
1193
- merb-auth-slice-password
1194
- merb-cache
1195
- merb-exceptions
1196
- merb-gen
1197
- merb-haml
1198
- merb-helpers
1199
- merb-mailer
1200
- merb-param-protection
1201
- merb-slices
1202
- merb_datamapper
1203
- ]
1109
+ desc 'clone (REPO_NAME|URL) [DIR_NAME]', 'Clone git source repository by name or url'
1110
+ def clone(repository, name = nil)
1111
+ if repository =~ /^git:\/\//
1112
+ repository_url = repository
1113
+ repository_name = File.basename(repository_url, '.git')
1114
+ elsif url = Merb::Source.repo(repository, options[:sources])
1115
+ repository_url = url
1116
+ repository_name = repository
1117
+ end
1118
+ source_manager.clone(name || repository_name, repository_url)
1119
+ end
1204
1120
 
1205
- MERB_PLUGINS = %w[
1206
- merb_activerecord
1207
- merb_builder
1208
- merb_jquery
1209
- merb_laszlo
1210
- merb_parts
1211
- merb_screw_unit
1212
- merb_sequel
1213
- merb_stories
1214
- merb_test_unit
1215
- ]
1121
+ # Git repository sources - pass source_config option to load a yaml
1122
+ # configuration file - defaults to ./config/git-sources.yml and
1123
+ # ~/.merb/git-sources.yml - which you need to create yourself.
1124
+ #
1125
+ # Example of contents:
1126
+ #
1127
+ # merb-core: git://github.com/myfork/merb-core.git
1128
+ # merb-more: git://github.com/myfork/merb-more.git
1216
1129
 
1217
- DM_MORE = %w[
1218
- dm-adjust
1219
- dm-aggregates
1220
- dm-ar-finders
1221
- dm-cli
1222
- dm-constraints
1223
- dm-is-example
1224
- dm-is-list
1225
- dm-is-nested_set
1226
- dm-is-remixable
1227
- dm-is-searchable
1228
- dm-is-state_machine
1229
- dm-is-tree
1230
- dm-is-versioned
1231
- dm-migrations
1232
- dm-observer
1233
- dm-querizer
1234
- dm-serializer
1235
- dm-shorthand
1236
- dm-sweatshop
1237
- dm-tags
1238
- dm-timestamps
1239
- dm-types
1240
- dm-validations
1241
-
1242
- dm-couchdb-adapter
1243
- dm-ferret-adapter
1244
- dm-rest-adapter
1245
- ]
1130
+ def self.repos(source_config = nil)
1131
+ source_config ||= begin
1132
+ local_config = File.join(Dir.pwd, 'config', 'git-sources.yml')
1133
+ user_config = File.join(ENV["HOME"] || ENV["APPDATA"], '.merb', 'git-sources.yml')
1134
+ File.exists?(local_config) ? local_config : user_config
1135
+ end
1136
+ if source_config && File.exists?(source_config)
1137
+ default_repos.merge(YAML.load(File.read(source_config)))
1138
+ else
1139
+ default_repos
1140
+ end
1141
+ end
1142
+
1143
+ def self.repo(name, source_config = nil)
1144
+ self.repos(source_config)[name]
1145
+ end
1246
1146
 
1247
- DATA_OBJECTS = %w[
1248
- data_objects
1249
- do_derby do_hsqldb
1250
- do_jdbc
1251
- do_mysql
1252
- do_postgres
1253
- do_sqlite3
1254
- ]
1147
+ # Default Git repositories
1148
+ def self.default_repos
1149
+ @_default_repos ||= {
1150
+ 'merb' => "git://github.com/wycats/merb.git",
1151
+ 'merb-plugins' => "git://github.com/wycats/merb-plugins.git",
1152
+ 'extlib' => "git://github.com/sam/extlib.git",
1153
+ 'dm-core' => "git://github.com/sam/dm-core.git",
1154
+ 'dm-more' => "git://github.com/sam/dm-more.git",
1155
+ 'sequel' => "git://github.com/wayneeseguin/sequel.git",
1156
+ 'do' => "git://github.com/sam/do.git",
1157
+ 'thor' => "git://github.com/wycats/thor.git",
1158
+ 'rake' => "git://github.com/jimweirich/rake.git"
1159
+ }
1160
+ end
1161
+
1162
+ end
1163
+
1164
+ class Dependencies < Thor
1165
+
1166
+ group 'core'
1255
1167
 
1256
- attr_accessor :system, :local, :missing
1168
+ # The Dependencies tasks will install dependencies based on actual application
1169
+ # dependencies. For this, the application is queried for any dependencies.
1170
+ # All operations will be performed within this context.
1171
+
1172
+ attr_accessor :system, :local, :missing, :extract_dependencies
1257
1173
 
1258
1174
  include MerbThorHelper
1259
1175
 
1260
1176
  global_method_options = {
1261
1177
  "--merb-root" => :optional, # the directory to operate on
1262
- "--ignore-dependencies" => :boolean, # skip sub-dependencies
1263
- "--version" => :optional # gather specific version of framework
1178
+ "--ignore-dependencies" => :boolean, # ignore sub-dependencies
1179
+ "--stack" => :boolean, # gather only stack dependencies
1180
+ "--no-stack" => :boolean, # gather only non-stack dependencies
1181
+ "--extract" => :boolean, # gather dependencies from the app itself
1182
+ "--config-file" => :optional, # gather from the specified yaml config
1183
+ "--version" => :optional # gather specific version of framework
1264
1184
  }
1265
1185
 
1266
1186
  method_options global_method_options
1267
1187
  def initialize(*args); super; end
1268
1188
 
1269
- # List components and their dependencies.
1189
+ # List application dependencies.
1270
1190
  #
1271
- # Examples:
1191
+ # By default all dependencies are listed, partitioned into system, local and
1192
+ # currently missing dependencies. The first argument allows you to filter
1193
+ # on any of the partitionings. A second argument can be used to filter on
1194
+ # a set of known components, like all merb-more gems for example.
1272
1195
  #
1273
- # merb:stack:list # list all standard stack components
1274
- # merb:stack:list all # list all component sets
1275
- # merb:stack:list merb-more # list all dependencies of merb-more
1276
-
1277
- desc 'list [all|comp]', 'List available components (optionally filtered, defaults to merb stack)'
1278
- def list(comp = 'stack')
1279
- if comp == 'all'
1280
- Merb::Stack.component_sets.keys.sort.each do |comp|
1281
- unless (components = Merb::Stack.component_sets[comp]).empty?
1282
- message "Dependencies for '#{comp}' set:"
1283
- components.each { |c| puts "- #{c}" }
1284
- end
1196
+ # Examples:
1197
+ #
1198
+ # merb:dependencies:list # list all dependencies - the default
1199
+ # merb:dependencies:list local # list only local gems
1200
+ # merb:dependencies:list all merb-more # list only merb-more related dependencies
1201
+ # merb:dependencies:list --stack # list framework dependencies
1202
+ # merb:dependencies:list --no-stack # list 3rd party dependencies
1203
+ # merb:dependencies:list --extract # list dependencies by extracting them
1204
+ # merb:dependencies:list --config-file file.yml # list from the specified config file
1205
+
1206
+ desc 'list [all|local|system|missing] [comp]', 'Show application dependencies'
1207
+ def list(filter = 'all', comp = nil)
1208
+ deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
1209
+ self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1210
+ case filter
1211
+ when 'all'
1212
+ message 'Installed system gem dependencies:'
1213
+ display_gemspecs(system)
1214
+ message 'Installed local gem dependencies:'
1215
+ display_gemspecs(local)
1216
+ unless missing.empty?
1217
+ error 'Missing gem dependencies:'
1218
+ display_dependencies(missing)
1285
1219
  end
1220
+ when 'system'
1221
+ message 'Installed system gem dependencies:'
1222
+ display_gemspecs(system)
1223
+ when 'local'
1224
+ message 'Installed local gem dependencies:'
1225
+ display_gemspecs(local)
1226
+ when 'missing'
1227
+ error 'Missing gem dependencies:'
1228
+ display_dependencies(missing)
1286
1229
  else
1287
- message "Dependencies for '#{comp}' set:"
1288
- Merb::Stack.components(comp).each { |c| puts "- #{c}" }
1289
- end
1230
+ warning "Invalid listing filter '#{filter}'"
1231
+ end
1232
+ if missing.size > 0
1233
+ info "Some dependencies are currently missing!"
1234
+ elsif local.size == deps.size
1235
+ info "All dependencies have been bundled with the application."
1236
+ elsif local.size > system.size
1237
+ info "Most dependencies have been bundled with the application."
1238
+ elsif system.size > 0 && local.size > 0
1239
+ info "Some dependencies have been bundled with the application."
1240
+ elsif local.empty? && system.size == deps.size
1241
+ info "All dependencies are available on the system."
1242
+ end
1290
1243
  end
1291
1244
 
1292
- # Install stack components or individual gems - from stable rubygems by default.
1245
+ # Install application dependencies.
1293
1246
  #
1294
- # See also: Merb::Dependencies#install and Merb::Dependencies#install_dependencies
1247
+ # By default all required dependencies are installed. The first argument
1248
+ # specifies which strategy to use: stable or edge. A second argument can be
1249
+ # used to filter on a set of known components.
1295
1250
  #
1251
+ # Existing dependencies will be clobbered; when :force => true then all gems
1252
+ # will be cleared first, otherwise only existing local dependencies of the
1253
+ # gems to be installed will be removed.
1254
+ #
1296
1255
  # Examples:
1297
1256
  #
1298
- # merb:stack:install # install the default merb stack
1299
- # merb:stack:install basics # install a basic set of dependencies
1300
- # merb:stack:install merb-core # install merb-core from stable
1301
- # merb:stack:install merb-more --edge # install merb-core from edge
1302
- # merb:stack:install merb-core thor merb-slices # install the specified gems
1303
-
1304
- desc 'install [COMP, ...]', 'Install stack components'
1305
- method_options "--edge" => :boolean,
1306
- "--sources" => :optional,
1307
- "--force" => :boolean,
1308
- "--dry-run" => :boolean,
1309
- "--strategy" => :optional
1310
- def install(*comps)
1311
- mngr = self.dependency_manager
1312
- deps = gather_dependencies(comps)
1313
- mngr.system, mngr.local, mngr.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1314
- mngr.install_dependencies(strategy, deps)
1315
- end
1257
+ # merb:dependencies:install # install all dependencies using stable strategy
1258
+ # merb:dependencies:install stable --version 0.9.8 # install a specific version of the framework
1259
+ # merb:dependencies:install stable missing # install currently missing gems locally
1260
+ # merb:dependencies:install stable merb-more # install only merb-more related dependencies
1261
+ # merb:dependencies:install stable --stack # install framework dependencies
1262
+ # merb:dependencies:install stable --no-stack # install 3rd party dependencies
1263
+ # merb:dependencies:install stable --extract # extract dependencies from the actual app
1264
+ # merb:dependencies:install stable --config-file file.yml # read from the specified config file
1265
+ #
1266
+ # In addition to the options above, edge install uses the following:
1267
+ #
1268
+ # merb:dependencies:install edge # install all dependencies using edge strategy
1269
+ # merb:dependencies:install edge --sources file.yml # install edge from the specified git sources config
1270
+
1271
+ desc 'install [stable|edge] [comp]', 'Install application dependencies'
1272
+ method_options "--sources" => :optional, # only for edge strategy
1273
+ "--local" => :boolean, # force local install
1274
+ "--dry-run" => :boolean,
1275
+ "--force" => :boolean
1276
+ def install(strategy = 'stable', comp = nil)
1277
+ if strategy?(strategy)
1278
+ # Force local dependencies by creating ./gems before proceeding
1279
+ create_if_missing(default_gem_dir) if options[:local]
1316
1280
 
1317
- # Uninstall stack components or individual gems.
1281
+ where = gem_dir ? 'locally' : 'system-wide'
1282
+
1283
+ # When comp == 'missing' then filter on missing dependencies
1284
+ if only_missing = comp == 'missing'
1285
+ message "Preparing to install missing gems #{where} using #{strategy} strategy..."
1286
+ comp = nil
1287
+ clobber = false
1288
+ else
1289
+ message "Preparing to install #{where} using #{strategy} strategy..."
1290
+ clobber = true
1291
+ end
1292
+
1293
+ # If comp given, filter on known stack components
1294
+ deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
1295
+ self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1296
+
1297
+ # Only install currently missing gems (for comp == missing)
1298
+ if only_missing
1299
+ deps.reject! { |dep| not missing.include?(dep) }
1300
+ end
1301
+
1302
+ if deps.empty?
1303
+ warning "No dependencies to install..."
1304
+ else
1305
+ puts "#{deps.length} dependencies to install..."
1306
+ puts "This may take a while..."
1307
+ install_dependencies(strategy, deps, clobber)
1308
+ end
1309
+
1310
+ # Show current dependency info now that we're done
1311
+ puts # Seperate output
1312
+ list('local', comp)
1313
+ else
1314
+ warning "Invalid install strategy '#{strategy}'"
1315
+ puts
1316
+ message "Please choose one of the following installation strategies: stable or edge:"
1317
+ puts "$ thor merb:dependencies:install stable"
1318
+ puts "$ thor merb:dependencies:install edge"
1319
+ end
1320
+ end
1321
+
1322
+ # Uninstall application dependencies.
1318
1323
  #
1319
- # See also: Merb::Dependencies#uninstall
1324
+ # By default all required dependencies are installed. An optional argument
1325
+ # can be used to filter on a set of known components.
1326
+ #
1327
+ # Existing dependencies will be clobbered; when :force => true then all gems
1328
+ # will be cleared, otherwise only existing local dependencies of the
1329
+ # matching component set will be removed.
1320
1330
  #
1321
1331
  # Examples:
1322
1332
  #
1323
- # merb:stack:uninstall # uninstall the default merb stack
1324
- # merb:stack:uninstall merb-more # uninstall merb-more
1325
- # merb:stack:uninstall merb-core thor merb-slices # uninstall the specified gems
1333
+ # merb:dependencies:uninstall # uninstall all dependencies - the default
1334
+ # merb:dependencies:uninstall merb-more # uninstall merb-more related gems locally
1326
1335
 
1327
- desc 'uninstall [COMP, ...]', 'Uninstall stack components'
1336
+ desc 'uninstall [comp]', 'Uninstall application dependencies'
1328
1337
  method_options "--dry-run" => :boolean, "--force" => :boolean
1329
- def uninstall(*comps)
1330
- deps = gather_dependencies(comps)
1338
+ def uninstall(comp = nil)
1339
+ # If comp given, filter on known stack components
1340
+ deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
1331
1341
  self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1332
1342
  # Clobber existing local dependencies - based on self.local
1333
1343
  clobber_dependencies!
1334
1344
  end
1335
1345
 
1336
- # Install or uninstall minigems from the system.
1337
- #
1338
- # Due to the specific nature of MiniGems it can only be installed system-wide.
1339
- #
1340
- # Examples:
1346
+ # Recreate all gems from gems/cache on the current platform.
1341
1347
  #
1342
- # merb:stack:minigems install # install minigems
1343
- # merb:stack:minigems uninstall # uninstall minigems
1344
-
1345
- desc 'minigems (install|uninstall)', 'Install or uninstall minigems (needs sudo privileges)'
1346
- def minigems(action)
1347
- case action
1348
- when 'install'
1349
- Kernel.system "#{sudo} thor merb:stack:install_minigems"
1350
- when 'uninstall'
1351
- Kernel.system "#{sudo} thor merb:stack:uninstall_minigems"
1352
- else
1353
- error "Invalid command: merb:stack:minigems #{action}"
1354
- end
1355
- end
1356
-
1357
- # hidden minigems install task
1358
- def install_minigems
1359
- message "Installing MiniGems"
1360
- mngr = self.dependency_manager
1361
- deps = gather_dependencies('minigems')
1362
- mngr.system, mngr.local, mngr.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1363
- mngr.force_gem_dir = ::Gem.dir
1364
- mngr.install_dependencies(strategy, deps)
1365
- Kernel.system "#{sudo} minigem install"
1366
- end
1348
+ # Note: use merb:gem:redeploy instead
1367
1349
 
1368
- # hidden minigems uninstall task
1369
- def uninstall_minigems
1370
- message "Uninstalling MiniGems"
1371
- Kernel.system "#{sudo} minigem uninstall"
1372
- deps = gather_dependencies('minigems')
1373
- self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1374
- # Clobber existing local dependencies - based on self.local
1375
- clobber_dependencies!
1350
+ desc 'redeploy', 'Recreate all gems on the current platform'
1351
+ method_options "--dry-run" => :boolean, "--force" => :boolean
1352
+ def redeploy
1353
+ warning 'Warning: merb:dependencies:redeploy has been deprecated - use merb:gem:redeploy instead'
1354
+ gem = Merb::Gem.new
1355
+ gem.options = options
1356
+ gem.redeploy
1376
1357
  end
1377
1358
 
1378
- protected
1359
+ # Create a dependencies configuration file.
1360
+ #
1361
+ # A configuration yaml file will be created from the extracted application
1362
+ # dependencies. The format of the configuration is as follows:
1363
+ #
1364
+ # ---
1365
+ # - merb-core (= 0.9.8, runtime)
1366
+ # - merb-slices (= 0.9.8, runtime)
1367
+ #
1368
+ # This format is exactly the same as Gem::Dependency#to_s returns.
1369
+ #
1370
+ # Examples:
1371
+ #
1372
+ # merb:dependencies:configure --force # overwrite the default config file
1373
+ # merb:dependencies:configure --version 0.9.8 # configure specific framework version
1374
+ # merb:dependencies:configure --config-file file.yml # write to the specified config file
1379
1375
 
1380
- def gather_dependencies(comps = [])
1381
- if comps.empty?
1382
- gems = MERB_STACK
1383
- else
1384
- gems = comps.map { |c| Merb::Stack.components(c) }.flatten
1385
- end
1376
+ desc 'configure [comp]', 'Create a dependencies config file'
1377
+ method_options "--dry-run" => :boolean, "--force" => :boolean, "--versions" => :boolean
1378
+ def configure(comp = nil)
1379
+ self.extract_dependencies = true # of course we need to consult the app itself
1380
+ # If comp given, filter on known stack components
1381
+ deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
1386
1382
 
1387
- version_req = if options[:version]
1388
- ::Gem::Requirement.create(options[:version])
1383
+ # If --versions is set, update the version_requirements with the actual version available
1384
+ if options[:versions]
1385
+ specs = local_gemspecs
1386
+ deps.each do |dep|
1387
+ if spec = specs.find { |s| s.name == dep.name }
1388
+ dep.version_requirements = ::Gem::Requirement.create(spec.version)
1389
+ end
1390
+ end
1389
1391
  end
1390
1392
 
1391
- framework_components = Merb::Stack.framework_components
1392
-
1393
- gems.map do |gem|
1394
- if version_req && framework_components.include?(gem)
1395
- ::Gem::Dependency.new(gem, version_req)
1393
+ config = YAML.dump(deps.map { |d| d.to_s })
1394
+ puts "#{config}\n"
1395
+ if File.exists?(config_file) && !options[:force]
1396
+ error "File already exists! Use --force to overwrite."
1397
+ else
1398
+ if dry_run?
1399
+ note "Written #{config_file}"
1396
1400
  else
1397
- ::Gem::Dependency.new(gem, ::Gem::Requirement.default)
1401
+ FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
1402
+ File.open(config_file, 'w') { |f| f.write config }
1403
+ success "Written #{config_file}"
1398
1404
  end
1399
1405
  end
1400
- end
1406
+ rescue
1407
+ error "Failed to write to #{config_file}"
1408
+ end
1401
1409
 
1402
- def strategy
1403
- options[:strategy] || (options[:edge] ? 'edge' : 'stable')
1404
- end
1410
+ ### Helper Methods
1405
1411
 
1406
- def dependency_manager
1407
- @_dependency_manager ||= begin
1408
- instance = Merb::Dependencies.new
1409
- instance.options = options
1410
- instance
1412
+ def strategy?(strategy)
1413
+ if self.respond_to?(method = :"#{strategy}_strategy", true)
1414
+ method
1411
1415
  end
1412
1416
  end
1413
1417
 
1414
- public
1415
-
1416
- def self.repository_sets
1417
- @_repository_sets ||= begin
1418
- # the component itself as a fallback
1419
- comps = Hash.new { |(hsh,c)| [c] }
1420
-
1421
- # git repository based component sets
1422
- comps["merb"] = ["merb-core"] + MERB_MORE
1423
- comps["merb-more"] = MERB_MORE.sort
1424
- comps["merb-plugins"] = MERB_PLUGINS.sort
1425
- comps["dm-more"] = DM_MORE.sort
1426
- comps["do"] = DATA_OBJECTS.sort
1427
-
1428
- comps
1429
- end
1430
- end
1431
-
1432
- def self.component_sets
1433
- @_component_sets ||= begin
1434
- # the component itself as a fallback
1435
- comps = Hash.new { |(hsh,c)| [c] }
1436
- comps.update(repository_sets)
1437
-
1438
- # specific set of dependencies
1439
- comps["stack"] = MERB_STACK.sort
1440
- comps["basics"] = MERB_BASICS.sort
1441
-
1442
- # orm dependencies
1443
- comps["datamapper"] = DM_STACK.sort
1444
- comps["sequel"] = ["merb_sequel", "sequel"]
1445
- comps["activerecord"] = ["merb_activerecord", "activerecord"]
1418
+ def install_dependencies(strategy, deps, clobber = true)
1419
+ if method = strategy?(strategy)
1420
+ # Clobber existing local dependencies
1421
+ clobber_dependencies! if clobber
1446
1422
 
1447
- comps
1423
+ # Run the chosen strategy - collect files installed from stable gems
1424
+ installed_from_stable = send(method, deps).map { |d| d.name }
1425
+
1426
+ unless dry_run?
1427
+ # Sleep a bit otherwise the following steps won't see the new files
1428
+ sleep(deps.length) if deps.length > 0 && deps.length <= 10
1429
+
1430
+ # Leave a file to denote the strategy that has been used for this dependency
1431
+ self.local.each do |spec|
1432
+ next unless File.directory?(spec.full_gem_path)
1433
+ unless installed_from_stable.include?(spec.name)
1434
+ FileUtils.touch(File.join(spec.full_gem_path, "#{strategy}.strategy"))
1435
+ else
1436
+ FileUtils.touch(File.join(spec.full_gem_path, "stable.strategy"))
1437
+ end
1438
+ end
1439
+ end
1440
+ return true
1448
1441
  end
1442
+ false
1449
1443
  end
1450
1444
 
1451
- def self.framework_components
1452
- %w[merb-core merb-more].inject([]) do |all, comp|
1453
- all + components(comp)
1445
+ def dependencies
1446
+ if extract_dependencies?
1447
+ # Extract dependencies from the current application
1448
+ deps = Merb::Stack.core_dependencies(gem_dir, ignore_dependencies?)
1449
+ deps += Merb::Dependencies.extract_dependencies(working_dir)
1450
+ else
1451
+ # Use preconfigured dependencies from yaml file
1452
+ deps = config_dependencies
1453
+ end
1454
+
1455
+ stack_components = Merb::Stack.components
1456
+
1457
+ if options[:stack]
1458
+ # Limit to stack components only
1459
+ deps.reject! { |dep| not stack_components.include?(dep.name) }
1460
+ elsif options[:"no-stack"]
1461
+ # Limit to non-stack components
1462
+ deps.reject! { |dep| stack_components.include?(dep.name) }
1463
+ end
1464
+
1465
+ if options[:version]
1466
+ version_req = ::Gem::Requirement.create("= #{options[:version]}")
1467
+ elsif core = deps.find { |d| d.name == 'merb-core' }
1468
+ version_req = core.version_requirements
1469
+ end
1470
+
1471
+ if version_req
1472
+ # Handle specific version requirement for framework components
1473
+ framework_components = Merb::Stack.framework_components
1474
+ deps.each do |dep|
1475
+ if framework_components.include?(dep.name)
1476
+ dep.version_requirements = version_req
1477
+ end
1478
+ end
1454
1479
  end
1480
+
1481
+ deps
1455
1482
  end
1456
1483
 
1457
- def self.components(comp = nil)
1458
- if comp
1459
- component_sets[comp]
1484
+ def config_dependencies
1485
+ if File.exists?(config_file)
1486
+ self.class.parse_dependencies_yaml(File.read(config_file))
1460
1487
  else
1461
- comps = %w[merb-core merb-more merb-plugins dm-core dm-more]
1462
- comps.inject([]) do |all, grp|
1463
- all + (component_sets[grp] || [])
1464
- end
1488
+ warning "No dependencies.yml file found at: #{config_file}"
1489
+ []
1465
1490
  end
1466
1491
  end
1467
1492
 
1468
- def self.select_component_dependencies(dependencies, comp = nil)
1469
- comps = components(comp) || []
1470
- dependencies.select { |dep| comps.include?(dep.name) }
1493
+ def extract_dependencies?
1494
+ options[:extract] || extract_dependencies
1471
1495
  end
1472
1496
 
1473
- def self.base_components
1474
- %w[thor rake extlib]
1497
+ def config_file
1498
+ @config_file ||= begin
1499
+ options[:"config-file"] || File.join(working_dir, 'config', 'dependencies.yml')
1500
+ end
1475
1501
  end
1476
1502
 
1477
- def self.all_components
1478
- base_components + framework_components
1503
+ def config_dir
1504
+ File.dirname(config_file)
1479
1505
  end
1480
1506
 
1481
- # Find the latest merb-core and gather its dependencies.
1482
- # We check for 0.9.8 as a minimum release version.
1483
- def self.core_dependencies(gem_dir = nil, ignore_deps = false)
1484
- @_core_dependencies ||= begin
1485
- if gem_dir # add local gems to index
1486
- orig_gem_path = ::Gem.path
1487
- ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
1488
- end
1489
- deps = []
1490
- merb_core = ::Gem::Dependency.new('merb-core', '>= 0.9.8')
1491
- if gemspec = ::Gem.source_index.search(merb_core).last
1492
- deps << ::Gem::Dependency.new('merb-core', gemspec.version)
1493
- if ignore_deps
1494
- deps += gemspec.dependencies.select do |d|
1495
- base_components.include?(d.name)
1496
- end
1507
+ ### Strategy handlers
1508
+
1509
+ private
1510
+
1511
+ def stable_strategy(deps)
1512
+ installed_from_rubygems = []
1513
+ if core = deps.find { |d| d.name == 'merb-core' }
1514
+ if dry_run?
1515
+ note "Installing #{core.name}..."
1516
+ else
1517
+ if install_dependency(core)
1518
+ installed_from_rubygems << core
1497
1519
  else
1498
- deps += gemspec.dependencies
1520
+ msg = "Try specifying a lower version of merb-core with --version"
1521
+ if version_no = core.version_requirements.to_s[/([\.\d]+)$/, 1]
1522
+ num = "%03d" % (version_no.gsub('.', '').to_i - 1)
1523
+ puts "The required version (#{version_no}) probably isn't available as a stable rubygem yet."
1524
+ info "#{msg} #{num.split(//).join('.')}"
1525
+ else
1526
+ puts "The required version probably isn't available as a stable rubygem yet."
1527
+ info msg
1528
+ end
1529
+ end
1530
+ end
1531
+ end
1532
+
1533
+ deps.each do |dependency|
1534
+ next if dependency.name == 'merb-core'
1535
+ if dry_run?
1536
+ note "Installing #{dependency.name}..."
1537
+ else
1538
+ install_dependency(dependency)
1539
+ installed_from_rubygems << dependency
1540
+ end
1541
+ end
1542
+ installed_from_rubygems
1543
+ end
1544
+
1545
+ def edge_strategy(deps)
1546
+ use_edge_gem_server
1547
+ installed_from_rubygems = []
1548
+
1549
+ # Selectively update repositories for the matching dependencies
1550
+ update_dependency_repositories(deps) unless dry_run?
1551
+
1552
+ if core = deps.find { |d| d.name == 'merb-core' }
1553
+ if dry_run?
1554
+ note "Installing #{core.name}..."
1555
+ else
1556
+ if install_dependency_from_source(core)
1557
+ elsif install_dependency(core)
1558
+ info "Installed #{core.name} from rubygems..."
1559
+ installed_from_rubygems << core
1499
1560
  end
1500
1561
  end
1501
- ::Gem.path.replace(orig_gem_path) if gem_dir # reset
1502
- deps
1503
1562
  end
1504
- end
1505
-
1506
- def self.lookup_repository_name(item)
1507
- set_name = nil
1508
- # The merb repo contains -more as well, so it needs special attention
1509
- return 'merb' if self.repository_sets['merb'].include?(item)
1510
1563
 
1511
- # Proceed with finding the item in a known component set
1512
- self.repository_sets.find do |set, items|
1513
- next if set == 'merb'
1514
- items.include?(item) ? (set_name = set) : nil
1564
+ deps.each do |dependency|
1565
+ next if dependency.name == 'merb-core'
1566
+ if dry_run?
1567
+ note "Installing #{dependency.name}..."
1568
+ else
1569
+ if install_dependency_from_source(dependency)
1570
+ elsif install_dependency(dependency)
1571
+ info "Installed #{dependency.name} from rubygems..."
1572
+ installed_from_rubygems << dependency
1573
+ end
1574
+ end
1515
1575
  end
1516
- set_name
1576
+
1577
+ installed_from_rubygems
1517
1578
  end
1518
1579
 
1519
- end
1520
-
1521
- class Tasks < Thor
1580
+ ### Class Methods
1522
1581
 
1523
- include MerbThorHelper
1582
+ public
1524
1583
 
1525
- # Show merb.thor version information
1526
- #
1527
- # merb:tasks:version # show the current version info
1528
- # merb:tasks:version --info # show extended version info
1584
+ def self.list(filter = 'all', comp = nil, options = {})
1585
+ instance = Merb::Dependencies.new
1586
+ instance.options = options
1587
+ instance.list(filter, comp)
1588
+ end
1529
1589
 
1530
- desc 'version', 'Show verion info'
1531
- method_options "--info" => :boolean
1532
- def version
1533
- message "Currently installed merb.thor version: #{MERB_THOR_VERSION}"
1534
- if options[:version]
1535
- self.options = { :"dry-run" => true }
1536
- self.update # run update task with dry-run enabled
1590
+ # Extract application dependencies by querying the app directly.
1591
+ def self.extract_dependencies(merb_root)
1592
+ require 'merb-core'
1593
+ if !@_merb_loaded || Merb.root != merb_root
1594
+ Merb.start_environment(
1595
+ :log_level => :fatal,
1596
+ :testing => true,
1597
+ :adapter => 'runner',
1598
+ :environment => ENV['MERB_ENV'] || 'development',
1599
+ :merb_root => merb_root
1600
+ )
1601
+ @_merb_loaded = true
1537
1602
  end
1603
+ Merb::BootLoader::Dependencies.dependencies
1604
+ rescue StandardError => e
1605
+ error "Couldn't extract dependencies from application!"
1606
+ error e.message
1607
+ puts "Make sure you're executing the task from your app (--merb-root)"
1608
+ return []
1609
+ rescue SystemExit
1610
+ error "Couldn't extract dependencies from application!"
1611
+ error "application failed to run"
1612
+ puts "Please check if your application runs using 'merb'; for example,"
1613
+ puts "look for any gem version mismatches in dependencies.rb"
1614
+ return []
1538
1615
  end
1539
-
1540
- # Update merb.thor tasks from remotely available version
1541
- #
1542
- # merb:tasks:update # update merb.thor
1543
- # merb:tasks:update --force # force-update merb.thor
1544
- # merb:tasks:update --dry-run # show version info only
1545
-
1546
- desc 'update [URL]', 'Fetch the latest merb.thor and install it locally'
1547
- method_options "--dry-run" => :boolean, "--force" => :boolean
1548
- def update(url = 'http://merbivore.com/merb.thor')
1549
- require 'open-uri'
1550
- require 'rubygems/version'
1551
- remote_file = open(url)
1552
- code = remote_file.read
1553
-
1554
- # Extract version information from the source code
1555
- if version = code[/^MERB_THOR_VERSION\s?=\s?('|")([\.\d]+)('|")/,2]
1556
- # borrow version comparison from rubygems' Version class
1557
- current_version = ::Gem::Version.new(MERB_THOR_VERSION)
1558
- remote_version = ::Gem::Version.new(version)
1559
1616
 
1560
- if current_version >= remote_version
1561
- puts "currently installed: #{current_version}"
1562
- if current_version != remote_version
1563
- puts "available version: #{remote_version}"
1564
- end
1565
- info "No update of merb.thor necessary#{options[:force] ? ' (forced)' : ''}"
1566
- proceed = options[:force]
1567
- elsif current_version < remote_version
1568
- puts "currently installed: #{current_version}"
1569
- puts "available version: #{remote_version}"
1570
- proceed = true
1571
- end
1572
-
1573
- if proceed && !dry_run?
1574
- File.open(File.join(__FILE__), 'w') do |f|
1575
- f.write(code)
1576
- end
1577
- success "Installed the latest merb.thor (v#{version})"
1617
+ # Parse the basic YAML config data, and process Gem::Dependency output.
1618
+ # Formatting example: merb_helpers (>= 0.9.8, runtime)
1619
+ def self.parse_dependencies_yaml(yaml)
1620
+ dependencies = []
1621
+ entries = YAML.load(yaml) rescue []
1622
+ entries.each do |entry|
1623
+ if matches = entry.match(/^(\S+) \(([^,]+)?, ([^\)]+)\)/)
1624
+ name, version_req, type = matches.captures
1625
+ dependencies << ::Gem::Dependency.new(name, version_req, type.to_sym)
1626
+ else
1627
+ error "Invalid entry: #{entry}"
1578
1628
  end
1579
- else
1580
- raise "invalid source-code data"
1581
- end
1582
- rescue OpenURI::HTTPError
1583
- error "Error opening #{url}"
1584
- rescue => e
1585
- error "An error occurred (#{e.message})"
1629
+ end
1630
+ dependencies
1586
1631
  end
1587
1632
 
1588
1633
  end
1589
1634
 
1590
- #### MORE LOW-LEVEL TASKS ####
1591
-
1592
- class Gem < Thor
1635
+ class Stack < Thor
1593
1636
 
1594
1637
  group 'core'
1595
1638
 
1596
- include MerbThorHelper
1597
- extend GemManagement
1639
+ # The Stack tasks will install dependencies based on known sets of gems,
1640
+ # regardless of actual application dependency settings.
1641
+
1642
+ DM_STACK = %w[
1643
+ extlib
1644
+ data_objects
1645
+ dm-core
1646
+ dm-aggregates
1647
+ dm-migrations
1648
+ dm-timestamps
1649
+ dm-types
1650
+ dm-validations
1651
+ merb_datamapper
1652
+ ]
1653
+
1654
+ MERB_STACK = %w[
1655
+ extlib
1656
+ merb-core
1657
+ merb-action-args
1658
+ merb-assets
1659
+ merb-cache
1660
+ merb-helpers
1661
+ merb-mailer
1662
+ merb-slices
1663
+ merb-auth
1664
+ merb-auth-core
1665
+ merb-auth-more
1666
+ merb-auth-slice-password
1667
+ merb-param-protection
1668
+ merb-exceptions
1669
+ ] + DM_STACK
1670
+
1671
+ MERB_BASICS = %w[
1672
+ extlib
1673
+ merb-core
1674
+ merb-action-args
1675
+ merb-assets
1676
+ merb-cache
1677
+ merb-helpers
1678
+ merb-mailer
1679
+ merb-slices
1680
+ ]
1681
+
1682
+ # The following sets are meant for repository lookup; unlike the sets above
1683
+ # these correspond to specific git repository items.
1684
+
1685
+ MERB_MORE = %w[
1686
+ merb-action-args
1687
+ merb-assets
1688
+ merb-auth
1689
+ merb-auth-core
1690
+ merb-auth-more
1691
+ merb-auth-slice-password
1692
+ merb-cache
1693
+ merb-exceptions
1694
+ merb-gen
1695
+ merb-haml
1696
+ merb-helpers
1697
+ merb-mailer
1698
+ merb-param-protection
1699
+ merb-slices
1700
+ merb_datamapper
1701
+ ]
1702
+
1703
+ MERB_PLUGINS = %w[
1704
+ merb_activerecord
1705
+ merb_builder
1706
+ merb_jquery
1707
+ merb_laszlo
1708
+ merb_parts
1709
+ merb_screw_unit
1710
+ merb_sequel
1711
+ merb_stories
1712
+ merb_test_unit
1713
+ ]
1714
+
1715
+ DM_MORE = %w[
1716
+ dm-adjust
1717
+ dm-aggregates
1718
+ dm-ar-finders
1719
+ dm-cli
1720
+ dm-constraints
1721
+ dm-is-example
1722
+ dm-is-list
1723
+ dm-is-nested_set
1724
+ dm-is-remixable
1725
+ dm-is-searchable
1726
+ dm-is-state_machine
1727
+ dm-is-tree
1728
+ dm-is-versioned
1729
+ dm-migrations
1730
+ dm-observer
1731
+ dm-querizer
1732
+ dm-serializer
1733
+ dm-shorthand
1734
+ dm-sweatshop
1735
+ dm-tags
1736
+ dm-timestamps
1737
+ dm-types
1738
+ dm-validations
1739
+
1740
+ dm-couchdb-adapter
1741
+ dm-ferret-adapter
1742
+ dm-rest-adapter
1743
+ ]
1744
+
1745
+ DATA_OBJECTS = %w[
1746
+ data_objects
1747
+ do_derby do_hsqldb
1748
+ do_jdbc
1749
+ do_mysql
1750
+ do_postgres
1751
+ do_sqlite3
1752
+ ]
1598
1753
 
1599
1754
  attr_accessor :system, :local, :missing
1600
1755
 
1756
+ include MerbThorHelper
1757
+
1601
1758
  global_method_options = {
1602
1759
  "--merb-root" => :optional, # the directory to operate on
1603
- "--version" => :optional, # gather specific version of gem
1604
- "--ignore-dependencies" => :boolean # don't install sub-dependencies
1760
+ "--ignore-dependencies" => :boolean, # skip sub-dependencies
1761
+ "--version" => :optional # gather specific version of framework
1605
1762
  }
1606
1763
 
1607
1764
  method_options global_method_options
1608
1765
  def initialize(*args); super; end
1609
1766
 
1610
- # List gems that match the specified criteria.
1767
+ # List components and their dependencies.
1611
1768
  #
1612
- # By default all local gems are listed. When the first argument is 'all' the
1613
- # list is partitioned into system an local gems; specify 'system' to show
1614
- # only system gems. A second argument can be used to filter on a set of known
1615
- # components, like all merb-more gems for example.
1616
- #
1617
1769
  # Examples:
1618
- #
1619
- # merb:gem:list # list all local gems - the default
1620
- # merb:gem:list all # list system and local gems
1621
- # merb:gem:list system # list only system gems
1622
- # merb:gem:list all merb-more # list only merb-more related gems
1623
- # merb:gem:list --version 0.9.8 # list gems that match the version
1624
-
1625
- desc 'list [all|local|system] [comp]', 'Show installed gems'
1626
- def list(filter = 'local', comp = nil)
1627
- deps = comp ? Merb::Stack.select_component_dependencies(dependencies, comp) : dependencies
1628
- self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1629
- case filter
1630
- when 'all'
1631
- message 'Installed system gems:'
1632
- display_gemspecs(system)
1633
- message 'Installed local gems:'
1634
- display_gemspecs(local)
1635
- when 'system'
1636
- message 'Installed system gems:'
1637
- display_gemspecs(system)
1638
- when 'local'
1639
- message 'Installed local gems:'
1640
- display_gemspecs(local)
1770
+ #
1771
+ # merb:stack:list # list all standard stack components
1772
+ # merb:stack:list all # list all component sets
1773
+ # merb:stack:list merb-more # list all dependencies of merb-more
1774
+
1775
+ desc 'list [all|comp]', 'List available components (optionally filtered, defaults to merb stack)'
1776
+ def list(comp = 'stack')
1777
+ if comp == 'all'
1778
+ Merb::Stack.component_sets.keys.sort.each do |comp|
1779
+ unless (components = Merb::Stack.component_sets[comp]).empty?
1780
+ message "Dependencies for '#{comp}' set:"
1781
+ components.each { |c| puts "- #{c}" }
1782
+ end
1783
+ end
1641
1784
  else
1642
- warning "Invalid listing filter '#{filter}'"
1643
- end
1785
+ message "Dependencies for '#{comp}' set:"
1786
+ Merb::Stack.components(comp).each { |c| puts "- #{c}" }
1787
+ end
1644
1788
  end
1645
1789
 
1646
- # Install the specified gems.
1790
+ # Install stack components or individual gems - from stable rubygems by default.
1647
1791
  #
1648
- # All arguments should be names of gems to install.
1792
+ # See also: Merb::Dependencies#install and Merb::Dependencies#install_dependencies
1649
1793
  #
1650
- # When :force => true then any existing versions of the gems to be installed
1651
- # will be uninstalled first. It's important to note that so-called meta-gems
1652
- # or gems that exactly match a set of Merb::Stack.components will have their
1653
- # sub-gems uninstalled too. For example, uninstalling merb-more will install
1654
- # all contained gems: merb-action-args, merb-assets, merb-gen, ...
1655
- #
1656
1794
  # Examples:
1657
1795
  #
1658
- # merb:gem:install merb-core merb-slices # install all specified gems
1659
- # merb:gem:install merb-core --version 0.9.8 # install a specific version of a gem
1660
- # merb:gem:install merb-core --force # uninstall then subsequently install the gem
1661
- # merb:gem:install merb-core --cache # try to install locally from system gems
1662
-
1663
- desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from rubygems'
1664
- method_options "--cache" => :boolean,
1665
- "--dry-run" => :boolean,
1666
- "--force" => :boolean
1667
- def install(*names)
1668
- opts = { :version => options[:version], :cache => options[:cache] }
1669
- current_gem = nil
1670
-
1671
- # uninstall existing gems of the ones we're going to install
1672
- uninstall(*names) if options[:force]
1796
+ # merb:stack:install # install the default merb stack
1797
+ # merb:stack:install basics # install a basic set of dependencies
1798
+ # merb:stack:install merb-core # install merb-core from stable
1799
+ # merb:stack:install merb-more --edge # install merb-core from edge
1800
+ # merb:stack:install merb-core thor merb-slices # install the specified gems
1673
1801
 
1674
- names.each do |gem_name|
1675
- current_gem = gem_name
1676
- if dry_run?
1677
- note "Installing #{current_gem}..."
1678
- else
1679
- message "Installing #{current_gem}..."
1680
- self.class.install(gem_name, default_install_options.merge(opts))
1681
- end
1682
- end
1683
- rescue => e
1684
- error "Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})"
1802
+ desc 'install [COMP, ...]', 'Install stack components'
1803
+ method_options "--edge" => :boolean,
1804
+ "--sources" => :optional,
1805
+ "--force" => :boolean,
1806
+ "--dry-run" => :boolean,
1807
+ "--strategy" => :optional
1808
+ def install(*comps)
1809
+ use_edge_gem_server if options[:edge]
1810
+ mngr = self.dependency_manager
1811
+ deps = gather_dependencies(comps)
1812
+ mngr.system, mngr.local, mngr.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1813
+ mngr.install_dependencies(strategy, deps)
1685
1814
  end
1686
-
1687
- # Uninstall the specified gems.
1688
- #
1689
- # By default all specified gems are uninstalled. It's important to note that
1690
- # so-called meta-gems or gems that match a set of Merb::Stack.components will
1691
- # have their sub-gems uninstalled too. For example, uninstalling merb-more
1692
- # will install all contained gems: merb-action-args, merb-assets, ...
1815
+
1816
+ # Uninstall stack components or individual gems.
1693
1817
  #
1694
- # Existing dependencies will be clobbered; when :force => true then all gems
1695
- # will be cleared, otherwise only existing local dependencies of the
1696
- # matching component set will be removed.
1818
+ # See also: Merb::Dependencies#uninstall
1697
1819
  #
1698
1820
  # Examples:
1699
1821
  #
1700
- # merb:gem:uninstall merb-core merb-slices # uninstall all specified gems
1701
- # merb:gem:uninstall merb-core --version 0.9.8 # uninstall a specific version of a gem
1822
+ # merb:stack:uninstall # uninstall the default merb stack
1823
+ # merb:stack:uninstall merb-more # uninstall merb-more
1824
+ # merb:stack:uninstall merb-core thor merb-slices # uninstall the specified gems
1702
1825
 
1703
- desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem'
1704
- method_options "--dry-run" => :boolean
1705
- def uninstall(*names)
1706
- opts = { :version => options[:version] }
1707
- current_gem = nil
1708
- if dry_run?
1709
- note "Uninstalling any existing gems of: #{names.join(', ')}"
1710
- else
1711
- message "Uninstalling any existing gems of: #{names.join(', ')}"
1712
- names.each do |gem_name|
1713
- current_gem = gem_name
1714
- Merb::Gem.uninstall(gem_name, default_uninstall_options) rescue nil
1715
- # if this gem is a meta-gem or a component set name, remove sub-gems
1716
- (Merb::Stack.components(gem_name) || []).each do |comp|
1717
- Merb::Gem.uninstall(comp, default_uninstall_options) rescue nil
1718
- end
1719
- end
1720
- end
1721
- rescue => e
1722
- error "Failed to uninstall #{current_gem ? current_gem : 'gem'} (#{e.message})"
1826
+ desc 'uninstall [COMP, ...]', 'Uninstall stack components'
1827
+ method_options "--dry-run" => :boolean, "--force" => :boolean
1828
+ def uninstall(*comps)
1829
+ deps = gather_dependencies(comps)
1830
+ self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1831
+ # Clobber existing local dependencies - based on self.local
1832
+ clobber_dependencies!
1723
1833
  end
1724
1834
 
1725
- private
1835
+ # Install or uninstall minigems from the system.
1836
+ #
1837
+ # Due to the specific nature of MiniGems it can only be installed system-wide.
1838
+ #
1839
+ # Examples:
1840
+ #
1841
+ # merb:stack:minigems install # install minigems
1842
+ # merb:stack:minigems uninstall # uninstall minigems
1726
1843
 
1727
- # Return dependencies for all installed gems; both system-wide and locally;
1728
- # optionally filters on :version requirement.
1729
- def dependencies
1730
- version_req = if options[:version]
1731
- ::Gem::Requirement.create(options[:version])
1844
+ desc 'minigems (install|uninstall)', 'Install or uninstall minigems (needs sudo privileges)'
1845
+ def minigems(action)
1846
+ case action
1847
+ when 'install'
1848
+ Kernel.system "#{sudo} thor merb:stack:install_minigems"
1849
+ when 'uninstall'
1850
+ Kernel.system "#{sudo} thor merb:stack:uninstall_minigems"
1732
1851
  else
1733
- ::Gem::Requirement.default
1734
- end
1735
- if gem_dir
1736
- ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
1737
- ::Gem.source_index.refresh!
1738
- end
1739
- deps = []
1740
- ::Gem.source_index.each do |fullname, gemspec|
1741
- if version_req.satisfied_by?(gemspec.version)
1742
- deps << ::Gem::Dependency.new(gemspec.name, "= #{gemspec.version}")
1743
- end
1852
+ error "Invalid command: merb:stack:minigems #{action}"
1744
1853
  end
1745
- ::Gem.clear_paths if gem_dir
1746
- deps.sort
1747
- end
1748
-
1749
- public
1854
+ end
1750
1855
 
1751
- # Install gem with some default options.
1752
- def self.install(name, options = {})
1753
- defaults = {}
1754
- defaults[:cache] = false unless opts[:install_dir]
1755
- install_gem(name, defaults.merge(options))
1856
+ # hidden minigems install task
1857
+ def install_minigems
1858
+ message "Installing MiniGems"
1859
+ mngr = self.dependency_manager
1860
+ deps = gather_dependencies('minigems')
1861
+ mngr.system, mngr.local, mngr.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1862
+ mngr.force_gem_dir = ::Gem.dir
1863
+ mngr.install_dependencies(strategy, deps)
1864
+ Kernel.system "#{sudo} minigem install"
1756
1865
  end
1757
1866
 
1758
- # Uninstall gem with some default options.
1759
- def self.uninstall(name, options = {})
1760
- defaults = { :ignore => true, :executables => true }
1761
- uninstall_gem(name, defaults.merge(options))
1867
+ # hidden minigems uninstall task
1868
+ def uninstall_minigems
1869
+ message "Uninstalling MiniGems"
1870
+ Kernel.system "#{sudo} minigem uninstall"
1871
+ deps = gather_dependencies('minigems')
1872
+ self.system, self.local, self.missing = Merb::Gem.partition_dependencies(deps, gem_dir)
1873
+ # Clobber existing local dependencies - based on self.local
1874
+ clobber_dependencies!
1762
1875
  end
1763
1876
 
1764
- end
1765
-
1766
- class Source < Thor
1767
-
1768
- group 'core'
1769
-
1770
- include MerbThorHelper
1771
- extend GemManagement
1772
-
1773
- attr_accessor :system, :local, :missing
1774
-
1775
- global_method_options = {
1776
- "--merb-root" => :optional, # the directory to operate on
1777
- "--ignore-dependencies" => :boolean, # don't install sub-dependencies
1778
- "--sources" => :optional # a yml config to grab sources from
1779
- }
1780
-
1781
- method_options global_method_options
1782
- def initialize(*args); super; end
1783
-
1784
- # List source repositories, of either local or known sources.
1785
- #
1786
- # Examples:
1787
- #
1788
- # merb:source:list # list all local sources
1789
- # merb:source:list available # list all known sources
1877
+ protected
1790
1878
 
1791
- desc 'list [local|available]', 'Show git source repositories'
1792
- def list(mode = 'local')
1793
- if mode == 'available'
1794
- message 'Available source repositories:'
1795
- repos = self.class.repos(options[:sources])
1796
- repos.keys.sort.each { |name| puts "- #{name}: #{repos[name]}" }
1797
- elsif mode == 'local'
1798
- message 'Current source repositories:'
1799
- Dir[File.join(source_dir, '*')].each do |src|
1800
- next unless File.directory?(src)
1801
- src_name = File.basename(src)
1802
- unless (repos = source_manager.existing_repos(src_name)).empty?
1803
- puts "#{src_name}"
1804
- repos.keys.sort.each { |b| puts "- #{b}: #{repos[b]}" }
1805
- end
1806
- end
1879
+ def gather_dependencies(comps = [])
1880
+ if comps.empty?
1881
+ gems = MERB_STACK
1807
1882
  else
1808
- error "Unknown listing: #{mode}"
1883
+ gems = comps.map { |c| Merb::Stack.components(c) }.flatten
1809
1884
  end
1810
- end
1811
-
1812
- # Install the specified gems.
1813
- #
1814
- # All arguments should be names of gems to install.
1815
- #
1816
- # When :force => true then any existing versions of the gems to be installed
1817
- # will be uninstalled first. It's important to note that so-called meta-gems
1818
- # or gems that exactly match a set of Merb::Stack.components will have their
1819
- # sub-gems uninstalled too. For example, uninstalling merb-more will install
1820
- # all contained gems: merb-action-args, merb-assets, merb-gen, ...
1821
- #
1822
- # Examples:
1823
- #
1824
- # merb:source:install merb-core merb-slices # install all specified gems
1825
- # merb:source:install merb-core --force # uninstall then subsequently install the gem
1826
- # merb:source:install merb-core --wipe # clear repo then install the gem
1827
-
1828
- desc 'install GEM_NAME [GEM_NAME, ...]', 'Install a gem from git source/edge'
1829
- method_options "--dry-run" => :boolean,
1830
- "--force" => :boolean,
1831
- "--wipe" => :boolean
1832
- def install(*names)
1833
- # uninstall existing gems of the ones we're going to install
1834
- uninstall(*names) if options[:force] || options[:wipe]
1835
1885
 
1836
- # We want dependencies instead of just names
1837
- deps = names.map { |n| ::Gem::Dependency.new(n, ::Gem::Requirement.default) }
1886
+ version_req = if options[:version]
1887
+ ::Gem::Requirement.create(options[:version])
1888
+ end
1838
1889
 
1839
- # Selectively update repositories for the matching dependencies
1840
- update_dependency_repositories(deps) unless dry_run?
1890
+ framework_components = Merb::Stack.framework_components
1841
1891
 
1842
- current_gem = nil
1843
- deps.each do |dependency|
1844
- current_gem = dependency.name
1845
- if dry_run?
1846
- note "Installing #{current_gem} from source..."
1892
+ gems.map do |gem|
1893
+ if version_req && framework_components.include?(gem)
1894
+ ::Gem::Dependency.new(gem, version_req)
1847
1895
  else
1848
- message "Installing #{current_gem} from source..."
1849
- unless install_dependency_from_source(dependency)
1850
- raise "gem source not found"
1851
- end
1896
+ ::Gem::Dependency.new(gem, ::Gem::Requirement.default)
1852
1897
  end
1853
1898
  end
1854
- rescue => e
1855
- error "Failed to install #{current_gem ? current_gem : 'gem'} (#{e.message})"
1856
1899
  end
1857
1900
 
1858
- # Uninstall the specified gems.
1859
- #
1860
- # By default all specified gems are uninstalled. It's important to note that
1861
- # so-called meta-gems or gems that match a set of Merb::Stack.components will
1862
- # have their sub-gems uninstalled too. For example, uninstalling merb-more
1863
- # will install all contained gems: merb-action-args, merb-assets, ...
1864
- #
1865
- # Existing dependencies will be clobbered; when :force => true then all gems
1866
- # will be cleared, otherwise only existing local dependencies of the
1867
- # matching component set will be removed. Additionally when :wipe => true,
1868
- # the matching git repositories will be removed from the source directory.
1869
- #
1870
- # Examples:
1871
- #
1872
- # merb:source:uninstall merb-core merb-slices # uninstall all specified gems
1873
- # merb:source:uninstall merb-core --wipe # force-uninstall a gem and clear repo
1874
-
1875
- desc 'uninstall GEM_NAME [GEM_NAME, ...]', 'Unstall a gem (specify --force to remove the repo)'
1876
- method_options "--version" => :optional, "--dry-run" => :boolean, "--wipe" => :boolean
1877
- def uninstall(*names)
1878
- # Remove the repos that contain the gem
1879
- if options[:wipe]
1880
- extract_repositories(names).each do |(name, url)|
1881
- if File.directory?(src = File.join(source_dir, name))
1882
- if dry_run?
1883
- note "Removing #{src}..."
1884
- else
1885
- info "Removing #{src}..."
1886
- FileUtils.rm_rf(src)
1887
- end
1888
- end
1889
- end
1890
- end
1891
-
1892
- # Use the Merb::Gem#uninstall task to handle this
1893
- gem_tasks = Merb::Gem.new
1894
- gem_tasks.options = options
1895
- gem_tasks.uninstall(*names)
1901
+ def strategy
1902
+ options[:strategy] || (options[:edge] ? 'edge' : 'stable')
1896
1903
  end
1897
1904
 
1898
- # Update the specified source repositories.
1899
- #
1900
- # The arguments can be actual repository names (from Merb::Source.repos)
1901
- # or names of known merb stack gems. If the repo doesn't exist already,
1902
- # it will be created and cloned.
1903
- #
1904
- # merb:source:pull merb-core # update source of specified gem
1905
- # merb:source:pull merb-slices # implicitly updates merb-more
1906
-
1907
- desc 'pull REPO_NAME [GEM_NAME, ...]', 'Update git source repository from edge'
1908
- def pull(*names)
1909
- repos = extract_repositories(names)
1910
- update_repositories(repos)
1911
- unless repos.empty?
1912
- message "Updated the following repositories:"
1913
- repos.each { |name, url| puts "- #{name}: #{url}" }
1914
- else
1915
- warning "No repositories found to update!"
1905
+ def dependency_manager
1906
+ @_dependency_manager ||= begin
1907
+ instance = Merb::Dependencies.new
1908
+ instance.options = options
1909
+ instance
1916
1910
  end
1917
- end
1911
+ end
1918
1912
 
1919
- # Clone a git repository into ./src.
1913
+ public
1920
1914
 
1921
- # The repository can be a direct git url or a known -named- repository.
1922
- #
1923
- # Examples:
1924
- #
1925
- # merb:source:clone merb-core
1926
- # merb:source:clone dm-core awesome-repo
1927
- # merb:source:clone dm-core --sources ./path/to/sources.yml
1928
- # merb:source:clone git://github.com/sam/dm-core.git
1915
+ def self.repository_sets
1916
+ @_repository_sets ||= begin
1917
+ # the component itself as a fallback
1918
+ comps = Hash.new { |(hsh,c)| [c] }
1919
+
1920
+ # git repository based component sets
1921
+ comps["merb"] = ["merb-core"] + MERB_MORE
1922
+ comps["merb-more"] = MERB_MORE.sort
1923
+ comps["merb-plugins"] = MERB_PLUGINS.sort
1924
+ comps["dm-more"] = DM_MORE.sort
1925
+ comps["do"] = DATA_OBJECTS.sort
1926
+
1927
+ comps
1928
+ end
1929
+ end
1929
1930
 
1930
- desc 'clone (REPO_NAME|URL) [DIR_NAME]', 'Clone git source repository by name or url'
1931
- def clone(repository, name = nil)
1932
- if repository =~ /^git:\/\//
1933
- repository_url = repository
1934
- repository_name = File.basename(repository_url, '.git')
1935
- elsif url = Merb::Source.repo(repository, options[:sources])
1936
- repository_url = url
1937
- repository_name = repository
1931
+ def self.component_sets
1932
+ @_component_sets ||= begin
1933
+ # the component itself as a fallback
1934
+ comps = Hash.new { |(hsh,c)| [c] }
1935
+ comps.update(repository_sets)
1936
+
1937
+ # specific set of dependencies
1938
+ comps["stack"] = MERB_STACK.sort
1939
+ comps["basics"] = MERB_BASICS.sort
1940
+
1941
+ # orm dependencies
1942
+ comps["datamapper"] = DM_STACK.sort
1943
+ comps["sequel"] = ["merb_sequel", "sequel"]
1944
+ comps["activerecord"] = ["merb_activerecord", "activerecord"]
1945
+
1946
+ comps
1938
1947
  end
1939
- source_manager.clone(name || repository_name, repository_url)
1940
1948
  end
1941
1949
 
1942
- # Git repository sources - pass source_config option to load a yaml
1943
- # configuration file - defaults to ./config/git-sources.yml and
1944
- # ~/.merb/git-sources.yml - which you need to create yourself.
1945
- #
1946
- # Example of contents:
1947
- #
1948
- # merb-core: git://github.com/myfork/merb-core.git
1949
- # merb-more: git://github.com/myfork/merb-more.git
1950
-
1951
- def self.repos(source_config = nil)
1952
- source_config ||= begin
1953
- local_config = File.join(Dir.pwd, 'config', 'git-sources.yml')
1954
- user_config = File.join(ENV["HOME"] || ENV["APPDATA"], '.merb', 'git-sources.yml')
1955
- File.exists?(local_config) ? local_config : user_config
1950
+ def self.framework_components
1951
+ %w[merb-core merb-more].inject([]) do |all, comp|
1952
+ all + components(comp)
1956
1953
  end
1957
- if source_config && File.exists?(source_config)
1958
- default_repos.merge(YAML.load(File.read(source_config)))
1954
+ end
1955
+
1956
+ def self.components(comp = nil)
1957
+ if comp
1958
+ component_sets[comp]
1959
1959
  else
1960
- default_repos
1960
+ comps = %w[merb-core merb-more merb-plugins dm-core dm-more]
1961
+ comps.inject([]) do |all, grp|
1962
+ all + (component_sets[grp] || [])
1963
+ end
1961
1964
  end
1962
1965
  end
1963
1966
 
1964
- def self.repo(name, source_config = nil)
1965
- self.repos(source_config)[name]
1967
+ def self.select_component_dependencies(dependencies, comp = nil)
1968
+ comps = components(comp) || []
1969
+ dependencies.select { |dep| comps.include?(dep.name) }
1966
1970
  end
1967
1971
 
1968
- # Default Git repositories
1969
- def self.default_repos
1970
- @_default_repos ||= {
1971
- 'merb' => "git://github.com/wycats/merb.git",
1972
- 'merb-plugins' => "git://github.com/wycats/merb-plugins.git",
1973
- 'extlib' => "git://github.com/sam/extlib.git",
1974
- 'dm-core' => "git://github.com/sam/dm-core.git",
1975
- 'dm-more' => "git://github.com/sam/dm-more.git",
1976
- 'sequel' => "git://github.com/wayneeseguin/sequel.git",
1977
- 'do' => "git://github.com/sam/do.git",
1978
- 'thor' => "git://github.com/wycats/thor.git",
1979
- 'rake' => "git://github.com/jimweirich/rake.git"
1980
- }
1972
+ def self.base_components
1973
+ %w[thor rake extlib]
1981
1974
  end
1982
-
1975
+
1976
+ def self.all_components
1977
+ base_components + framework_components
1978
+ end
1979
+
1980
+ # Find the latest merb-core and gather its dependencies.
1981
+ # We check for 0.9.8 as a minimum release version.
1982
+ def self.core_dependencies(gem_dir = nil, ignore_deps = false)
1983
+ @_core_dependencies ||= begin
1984
+ if gem_dir # add local gems to index
1985
+ orig_gem_path = ::Gem.path
1986
+ ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
1987
+ end
1988
+ deps = []
1989
+ merb_core = ::Gem::Dependency.new('merb-core', '>= 0.9.8')
1990
+ if gemspec = ::Gem.source_index.search(merb_core).last
1991
+ deps << ::Gem::Dependency.new('merb-core', gemspec.version)
1992
+ if ignore_deps
1993
+ deps += gemspec.dependencies.select do |d|
1994
+ base_components.include?(d.name)
1995
+ end
1996
+ else
1997
+ deps += gemspec.dependencies
1998
+ end
1999
+ end
2000
+ ::Gem.path.replace(orig_gem_path) if gem_dir # reset
2001
+ deps
2002
+ end
2003
+ end
2004
+
2005
+ def self.lookup_repository_name(item)
2006
+ set_name = nil
2007
+ # The merb repo contains -more as well, so it needs special attention
2008
+ return 'merb' if self.repository_sets['merb'].include?(item)
2009
+
2010
+ # Proceed with finding the item in a known component set
2011
+ self.repository_sets.find do |set, items|
2012
+ next if set == 'merb'
2013
+ items.include?(item) ? (set_name = set) : nil
2014
+ end
2015
+ set_name
2016
+ end
2017
+
1983
2018
  end
1984
2019
 
1985
2020
  end