merb-gen 0.9.13 → 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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