librarian-puppet-lmco 0.9.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.md +187 -0
  5. data/bin/librarian-puppet +9 -0
  6. data/lib/librarian/puppet.rb +22 -0
  7. data/lib/librarian/puppet/cli.rb +85 -0
  8. data/lib/librarian/puppet/dsl.rb +16 -0
  9. data/lib/librarian/puppet/environment.rb +54 -0
  10. data/lib/librarian/puppet/extension.rb +47 -0
  11. data/lib/librarian/puppet/lockfile/parser.rb +53 -0
  12. data/lib/librarian/puppet/source.rb +4 -0
  13. data/lib/librarian/puppet/source/forge.rb +348 -0
  14. data/lib/librarian/puppet/source/git.rb +121 -0
  15. data/lib/librarian/puppet/source/githubtarball.rb +249 -0
  16. data/lib/librarian/puppet/source/local.rb +57 -0
  17. data/lib/librarian/puppet/source/path.rb +12 -0
  18. data/lib/librarian/puppet/templates/Puppetfile +10 -0
  19. data/lib/librarian/puppet/version.rb +5 -0
  20. data/vendor/librarian/CHANGELOG.md +224 -0
  21. data/vendor/librarian/Gemfile +6 -0
  22. data/vendor/librarian/MIT-LICENSE +20 -0
  23. data/vendor/librarian/README.md +401 -0
  24. data/vendor/librarian/Rakefile +34 -0
  25. data/vendor/librarian/bin/librarian-chef +7 -0
  26. data/vendor/librarian/bin/librarian-mock +7 -0
  27. data/vendor/librarian/config/cucumber.yaml +1 -0
  28. data/vendor/librarian/features/chef/cli/init.feature +11 -0
  29. data/vendor/librarian/features/chef/cli/install.feature +64 -0
  30. data/vendor/librarian/features/chef/cli/show.feature +77 -0
  31. data/vendor/librarian/features/chef/cli/version.feature +11 -0
  32. data/vendor/librarian/features/support/env.rb +9 -0
  33. data/vendor/librarian/lib/librarian.rb +11 -0
  34. data/vendor/librarian/lib/librarian/action.rb +5 -0
  35. data/vendor/librarian/lib/librarian/action/base.rb +24 -0
  36. data/vendor/librarian/lib/librarian/action/clean.rb +44 -0
  37. data/vendor/librarian/lib/librarian/action/ensure.rb +24 -0
  38. data/vendor/librarian/lib/librarian/action/install.rb +95 -0
  39. data/vendor/librarian/lib/librarian/action/persist_resolution_mixin.rb +51 -0
  40. data/vendor/librarian/lib/librarian/action/resolve.rb +46 -0
  41. data/vendor/librarian/lib/librarian/action/update.rb +44 -0
  42. data/vendor/librarian/lib/librarian/chef.rb +1 -0
  43. data/vendor/librarian/lib/librarian/chef/cli.rb +47 -0
  44. data/vendor/librarian/lib/librarian/chef/dsl.rb +16 -0
  45. data/vendor/librarian/lib/librarian/chef/environment.rb +27 -0
  46. data/vendor/librarian/lib/librarian/chef/extension.rb +9 -0
  47. data/vendor/librarian/lib/librarian/chef/integration/knife.rb +46 -0
  48. data/vendor/librarian/lib/librarian/chef/manifest_reader.rb +59 -0
  49. data/vendor/librarian/lib/librarian/chef/source.rb +4 -0
  50. data/vendor/librarian/lib/librarian/chef/source/git.rb +25 -0
  51. data/vendor/librarian/lib/librarian/chef/source/github.rb +27 -0
  52. data/vendor/librarian/lib/librarian/chef/source/local.rb +69 -0
  53. data/vendor/librarian/lib/librarian/chef/source/path.rb +12 -0
  54. data/vendor/librarian/lib/librarian/chef/source/site.rb +442 -0
  55. data/vendor/librarian/lib/librarian/chef/templates/Cheffile +15 -0
  56. data/vendor/librarian/lib/librarian/cli.rb +223 -0
  57. data/vendor/librarian/lib/librarian/cli/manifest_presenter.rb +93 -0
  58. data/vendor/librarian/lib/librarian/config.rb +7 -0
  59. data/vendor/librarian/lib/librarian/config/database.rb +205 -0
  60. data/vendor/librarian/lib/librarian/config/file_source.rb +47 -0
  61. data/vendor/librarian/lib/librarian/config/hash_source.rb +33 -0
  62. data/vendor/librarian/lib/librarian/config/source.rb +149 -0
  63. data/vendor/librarian/lib/librarian/dependency.rb +147 -0
  64. data/vendor/librarian/lib/librarian/dsl.rb +108 -0
  65. data/vendor/librarian/lib/librarian/dsl/receiver.rb +46 -0
  66. data/vendor/librarian/lib/librarian/dsl/target.rb +171 -0
  67. data/vendor/librarian/lib/librarian/environment.rb +182 -0
  68. data/vendor/librarian/lib/librarian/error.rb +4 -0
  69. data/vendor/librarian/lib/librarian/helpers.rb +13 -0
  70. data/vendor/librarian/lib/librarian/linter/source_linter.rb +55 -0
  71. data/vendor/librarian/lib/librarian/lockfile.rb +29 -0
  72. data/vendor/librarian/lib/librarian/lockfile/compiler.rb +66 -0
  73. data/vendor/librarian/lib/librarian/lockfile/parser.rb +123 -0
  74. data/vendor/librarian/lib/librarian/logger.rb +46 -0
  75. data/vendor/librarian/lib/librarian/manifest.rb +140 -0
  76. data/vendor/librarian/lib/librarian/manifest_set.rb +151 -0
  77. data/vendor/librarian/lib/librarian/mock.rb +1 -0
  78. data/vendor/librarian/lib/librarian/mock/cli.rb +19 -0
  79. data/vendor/librarian/lib/librarian/mock/dsl.rb +15 -0
  80. data/vendor/librarian/lib/librarian/mock/environment.rb +24 -0
  81. data/vendor/librarian/lib/librarian/mock/extension.rb +9 -0
  82. data/vendor/librarian/lib/librarian/mock/source.rb +1 -0
  83. data/vendor/librarian/lib/librarian/mock/source/mock.rb +80 -0
  84. data/vendor/librarian/lib/librarian/mock/source/mock/registry.rb +83 -0
  85. data/vendor/librarian/lib/librarian/resolution.rb +46 -0
  86. data/vendor/librarian/lib/librarian/resolver.rb +81 -0
  87. data/vendor/librarian/lib/librarian/resolver/implementation.rb +223 -0
  88. data/vendor/librarian/lib/librarian/source.rb +2 -0
  89. data/vendor/librarian/lib/librarian/source/basic_api.rb +45 -0
  90. data/vendor/librarian/lib/librarian/source/git.rb +134 -0
  91. data/vendor/librarian/lib/librarian/source/git/repository.rb +217 -0
  92. data/vendor/librarian/lib/librarian/source/local.rb +54 -0
  93. data/vendor/librarian/lib/librarian/source/path.rb +56 -0
  94. data/vendor/librarian/lib/librarian/spec.rb +13 -0
  95. data/vendor/librarian/lib/librarian/spec_change_set.rb +173 -0
  96. data/vendor/librarian/lib/librarian/specfile.rb +17 -0
  97. data/vendor/librarian/lib/librarian/support/abstract_method.rb +21 -0
  98. data/vendor/librarian/lib/librarian/ui.rb +64 -0
  99. data/vendor/librarian/lib/librarian/version.rb +3 -0
  100. data/vendor/librarian/librarian.gemspec +35 -0
  101. data/vendor/librarian/spec/functional/chef/cli_spec.rb +194 -0
  102. data/vendor/librarian/spec/functional/chef/source/git_spec.rb +432 -0
  103. data/vendor/librarian/spec/functional/chef/source/site_spec.rb +266 -0
  104. data/vendor/librarian/spec/functional/source/git/repository_spec.rb +150 -0
  105. data/vendor/librarian/spec/integration/chef/source/git_spec.rb +441 -0
  106. data/vendor/librarian/spec/integration/chef/source/site_spec.rb +217 -0
  107. data/vendor/librarian/spec/support/cli_macro.rb +114 -0
  108. data/vendor/librarian/spec/support/method_patch_macro.rb +30 -0
  109. data/vendor/librarian/spec/support/with_env_macro.rb +20 -0
  110. data/vendor/librarian/spec/unit/action/base_spec.rb +18 -0
  111. data/vendor/librarian/spec/unit/action/clean_spec.rb +102 -0
  112. data/vendor/librarian/spec/unit/action/ensure_spec.rb +37 -0
  113. data/vendor/librarian/spec/unit/action/install_spec.rb +111 -0
  114. data/vendor/librarian/spec/unit/config/database_spec.rb +327 -0
  115. data/vendor/librarian/spec/unit/dependency_spec.rb +212 -0
  116. data/vendor/librarian/spec/unit/dsl_spec.rb +173 -0
  117. data/vendor/librarian/spec/unit/environment_spec.rb +173 -0
  118. data/vendor/librarian/spec/unit/lockfile/parser_spec.rb +162 -0
  119. data/vendor/librarian/spec/unit/lockfile_spec.rb +65 -0
  120. data/vendor/librarian/spec/unit/manifest_set_spec.rb +202 -0
  121. data/vendor/librarian/spec/unit/manifest_spec.rb +36 -0
  122. data/vendor/librarian/spec/unit/mock/source/mock_spec.rb +22 -0
  123. data/vendor/librarian/spec/unit/resolver_spec.rb +233 -0
  124. data/vendor/librarian/spec/unit/source/git_spec.rb +29 -0
  125. data/vendor/librarian/spec/unit/spec_change_set_spec.rb +169 -0
  126. metadata +220 -0
@@ -0,0 +1,29 @@
1
+ require 'librarian/lockfile/compiler'
2
+ require 'librarian/lockfile/parser'
3
+
4
+ module Librarian
5
+ class Lockfile
6
+
7
+ attr_accessor :environment
8
+ private :environment=
9
+ attr_reader :path
10
+
11
+ def initialize(environment, path)
12
+ self.environment = environment
13
+ @path = path
14
+ end
15
+
16
+ def save(resolution)
17
+ Compiler.new(environment).compile(resolution)
18
+ end
19
+
20
+ def load(string)
21
+ Parser.new(environment).parse(string)
22
+ end
23
+
24
+ def read
25
+ load(path.read)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,66 @@
1
+ module Librarian
2
+ class Lockfile
3
+ class Compiler
4
+
5
+ attr_accessor :environment
6
+ private :environment=
7
+
8
+ def initialize(environment)
9
+ self.environment = environment
10
+ end
11
+
12
+ def compile(resolution)
13
+ out = StringIO.new
14
+ save_sources(out, resolution.manifests)
15
+ save_dependencies(out, resolution.dependencies)
16
+ out.string
17
+ end
18
+
19
+ private
20
+
21
+ def save_sources(out, manifests)
22
+ dsl_class.source_types.map{|t| t[1]}.each do |type|
23
+ type_manifests = manifests.select{|m| type === m.source}
24
+ sources = type_manifests.map{|m| m.source}.uniq.sort_by{|s| s.to_s}
25
+ sources.each do |source|
26
+ source_manifests = type_manifests.select{|m| source == m.source}
27
+ save_source(out, source, source_manifests)
28
+ end
29
+ end
30
+ end
31
+
32
+ def save_source(out, source, manifests)
33
+ out.puts "#{source.class.lock_name}"
34
+ options = source.to_lock_options
35
+ remote = options.delete(:remote)
36
+ out.puts " remote: #{remote}"
37
+ options.to_a.sort_by{|a| a[0].to_s}.each do |o|
38
+ out.puts " #{o[0]}: #{o[1]}"
39
+ end
40
+ out.puts " specs:"
41
+ manifests.sort_by{|a| a.name}.each do |manifest|
42
+ out.puts " #{manifest.name} (#{manifest.version})"
43
+ manifest.dependencies.sort_by{|a| a.name}.each do |dependency|
44
+ out.puts " #{dependency.name} (#{dependency.requirement})"
45
+ end
46
+ end
47
+ out.puts ""
48
+ end
49
+
50
+ def save_dependencies(out, dependencies)
51
+ out.puts "DEPENDENCIES"
52
+ dependencies.sort_by{|a| a.name}.each do |d|
53
+ res = "#{d.name}"
54
+ res << " (#{d.requirement})" if d.requirement
55
+ out.puts " #{res}"
56
+ end
57
+ out.puts ""
58
+ end
59
+
60
+ def dsl_class
61
+ environment.dsl_class
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,123 @@
1
+ require 'librarian/manifest'
2
+ require 'librarian/dependency'
3
+ require 'librarian/manifest_set'
4
+
5
+ module Librarian
6
+ class Lockfile
7
+ class Parser
8
+
9
+ class ManifestPlaceholder
10
+ attr_reader :source, :name, :version, :dependencies
11
+ def initialize(source, name, version, dependencies)
12
+ @source, @name, @version, @dependencies = source, name, version, dependencies
13
+ end
14
+ end
15
+
16
+ attr_accessor :environment
17
+ private :environment=
18
+
19
+ def initialize(environment)
20
+ self.environment = environment
21
+ end
22
+
23
+ def parse(string)
24
+ lines = string.lines.map{|l| l.sub(/\s+\z/, '')}.reject(&:empty?)
25
+ sources = extract_and_parse_sources(lines)
26
+ manifests = compile(sources)
27
+ manifests_index = Hash[manifests.map{|m| [m.name, m]}]
28
+ raise StandardError, "Expected DEPENDENCIES topic!" unless lines.shift == "DEPENDENCIES"
29
+ dependencies = extract_and_parse_dependencies(lines, manifests_index)
30
+ Resolution.new(dependencies, manifests)
31
+ end
32
+
33
+ private
34
+
35
+ def extract_and_parse_sources(lines)
36
+ sources = []
37
+ while source_type_names.include?(lines.first)
38
+ source = {}
39
+ source_type_name = lines.shift
40
+ source[:type] = source_type_names_map[source_type_name]
41
+ options = {}
42
+ while lines.first =~ /^ {2}([\w-]+):\s+(.+)$/
43
+ lines.shift
44
+ options[$1.to_sym] = $2
45
+ end
46
+ source[:options] = options
47
+ lines.shift # specs
48
+ manifests = {}
49
+ while lines.first =~ /^ {4}([\w-]+) \((.*)\)$/
50
+ lines.shift
51
+ name = $1
52
+ manifests[name] = {:version => $2, :dependencies => {}}
53
+ while lines.first =~ /^ {6}([\w-]+) \((.*)\)$/
54
+ lines.shift
55
+ manifests[name][:dependencies][$1] = $2.split(/,\s*/)
56
+ end
57
+ end
58
+ source[:manifests] = manifests
59
+ sources << source
60
+ end
61
+ sources
62
+ end
63
+
64
+ def extract_and_parse_dependencies(lines, manifests_index)
65
+ dependencies = []
66
+ while lines.first =~ /^ {2}([\w-]+)(?: \((.*)\))?$/
67
+ lines.shift
68
+ name, requirement = $1, $2.split(/,\s*/)
69
+ dependencies << Dependency.new(name, requirement, manifests_index[name].source)
70
+ end
71
+ dependencies
72
+ end
73
+
74
+ def compile_placeholder_manifests(sources_ast)
75
+ manifests = {}
76
+ sources_ast.each do |source_ast|
77
+ source_type = source_ast[:type]
78
+ source = source_type.from_lock_options(environment, source_ast[:options])
79
+ source_ast[:manifests].each do |manifest_name, manifest_ast|
80
+ manifests[manifest_name] = ManifestPlaceholder.new(
81
+ source,
82
+ manifest_name,
83
+ manifest_ast[:version],
84
+ manifest_ast[:dependencies].map{|k, v| Dependency.new(k, v, nil)}
85
+ )
86
+ end
87
+ end
88
+ manifests
89
+ end
90
+
91
+ def compile(sources_ast)
92
+ manifests = compile_placeholder_manifests(sources_ast)
93
+ manifests = manifests.map do |name, manifest|
94
+ dependencies = manifest.dependencies.map do |d|
95
+ Dependency.new(d.name, d.requirement, manifests[d.name].source)
96
+ end
97
+ real = Manifest.new(manifest.source, manifest.name)
98
+ real.version = manifest.version
99
+ real.dependencies = manifest.dependencies
100
+ real
101
+ end
102
+ ManifestSet.sort(manifests)
103
+ end
104
+
105
+ def dsl_class
106
+ environment.dsl_class
107
+ end
108
+
109
+ def source_type_names_map
110
+ @source_type_names_map ||= begin
111
+ Hash[dsl_class.source_types.map{|t| [t[1].lock_name, t[1]]}]
112
+ end
113
+ end
114
+
115
+ def source_type_names
116
+ @source_type_names ||= begin
117
+ dsl_class.source_types.map{|t| t[1].lock_name}
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,46 @@
1
+ module Librarian
2
+ class Logger
3
+
4
+ librarian_path = Pathname(__FILE__)
5
+ librarian_path = librarian_path.dirname until librarian_path.join("lib").directory?
6
+ LIBRARIAN_PATH = librarian_path
7
+
8
+ attr_accessor :environment
9
+ private :environment=
10
+
11
+ def initialize(environment)
12
+ self.environment = environment
13
+ end
14
+
15
+ def info(string = nil, &block)
16
+ return unless ui
17
+
18
+ ui.info(string || yield)
19
+ end
20
+
21
+ def debug(string = nil, &block)
22
+ return unless ui
23
+
24
+ if ui.respond_to?(:debug_line_numbers) && ui.debug_line_numbers
25
+ loc = caller.find{|l| !(l =~ /in `debug'$/)}
26
+ if loc =~ /^(.+):(\d+):in `(.+)'$/
27
+ loc = "#{Pathname.new($1).relative_path_from(LIBRARIAN_PATH)}:#{$2}:in `#{$3}'"
28
+ end
29
+ ui.debug { "[Librarian] #{string || yield} [#{loc}]" }
30
+ else
31
+ ui.debug { "[Librarian] #{string || yield}" }
32
+ end
33
+ end
34
+
35
+ def relative_path_to(path)
36
+ environment.project_relative_path_to(path)
37
+ end
38
+
39
+ private
40
+
41
+ def ui
42
+ environment.ui
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,140 @@
1
+ require 'rubygems'
2
+
3
+ module Librarian
4
+ class Manifest
5
+
6
+ class Version
7
+ include Comparable
8
+
9
+ def initialize(*args)
10
+ args = initialize_normalize_args(args)
11
+
12
+ self.backing = Gem::Version.new(*args)
13
+ end
14
+
15
+ def to_gem_version
16
+ backing
17
+ end
18
+
19
+ def <=>(other)
20
+ to_gem_version <=> other.to_gem_version
21
+ end
22
+
23
+ def to_s
24
+ to_gem_version.to_s
25
+ end
26
+
27
+ private
28
+
29
+ def initialize_normalize_args(args)
30
+ args.map do |arg|
31
+ arg = [arg] if self.class === arg
32
+ arg
33
+ end
34
+ end
35
+
36
+ attr_accessor :backing
37
+ end
38
+
39
+ attr_accessor :source, :name, :extra
40
+ private :source=, :name=, :extra=
41
+
42
+ def initialize(source, name, extra = nil)
43
+ assert_name_valid! name
44
+
45
+ self.source = source
46
+ self.name = name
47
+ self.extra = extra
48
+ end
49
+
50
+ def to_s
51
+ "#{name}/#{version} <#{source}>"
52
+ end
53
+
54
+ def version
55
+ defined_version || fetched_version
56
+ end
57
+
58
+ def version=(version)
59
+ self.defined_version = _normalize_version(version)
60
+ end
61
+
62
+ def version?
63
+ return unless defined_version
64
+
65
+ defined_version == fetched_version
66
+ end
67
+
68
+ def latest
69
+ @latest ||= source.manifests(name).first
70
+ end
71
+
72
+ def outdated?
73
+ latest.version > version
74
+ end
75
+
76
+ def dependencies
77
+ defined_dependencies || fetched_dependencies
78
+ end
79
+
80
+ def dependencies=(dependencies)
81
+ self.defined_dependencies = _normalize_dependencies(dependencies)
82
+ end
83
+
84
+ def dependencies?
85
+ return unless defined_dependencies
86
+
87
+ defined_dependencies.zip(fetched_dependencies).all? do |(a, b)|
88
+ a.name == b.name && a.requirement == b.requirement
89
+ end
90
+ end
91
+
92
+ def satisfies?(dependency)
93
+ dependency.requirement.satisfied_by?(version)
94
+ end
95
+
96
+ def install!
97
+ source.install!(self)
98
+ end
99
+
100
+ private
101
+
102
+ attr_accessor :defined_version, :defined_dependencies
103
+
104
+ def environment
105
+ source.environment
106
+ end
107
+
108
+ def fetched_version
109
+ @fetched_version ||= _normalize_version(fetch_version!)
110
+ end
111
+
112
+ def fetched_dependencies
113
+ @fetched_dependencies ||= _normalize_dependencies(fetch_dependencies!)
114
+ end
115
+
116
+ def fetch_version!
117
+ source.fetch_version(name, extra)
118
+ end
119
+
120
+ def fetch_dependencies!
121
+ source.fetch_dependencies(name, version, extra)
122
+ end
123
+
124
+ def _normalize_version(version)
125
+ Version.new(version)
126
+ end
127
+
128
+ def _normalize_dependencies(dependencies)
129
+ if Hash === dependencies
130
+ dependencies = dependencies.map{|k, v| Dependency.new(k, v, nil)}
131
+ end
132
+ dependencies.sort_by(&:name)
133
+ end
134
+
135
+ def assert_name_valid!(name)
136
+ raise ArgumentError, "name (#{name.inspect}) must be sensible" unless name =~ /\A\S(?:.*\S)?\z/
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,151 @@
1
+ require 'set'
2
+ require 'tsort'
3
+
4
+ module Librarian
5
+ class ManifestSet
6
+
7
+ class GraphHash < Hash
8
+ include TSort
9
+ alias tsort_each_node each_key
10
+ def tsort_each_child(node, &block)
11
+ self[node].each(&block)
12
+ end
13
+ end
14
+
15
+ class << self
16
+ def shallow_strip(manifests, names)
17
+ new(manifests).shallow_strip!(names).send(method_for(manifests))
18
+ end
19
+ def deep_strip(manifests, names)
20
+ new(manifests).deep_strip!(names).send(method_for(manifests))
21
+ end
22
+ def shallow_keep(manifests, names)
23
+ new(manifests).shallow_keep!(names).send(method_for(manifests))
24
+ end
25
+ def deep_keep(manifests, names)
26
+ new(manifests).deep_keep!(names).send(method_for(manifests))
27
+ end
28
+ def sort(manifests)
29
+ manifests = Hash[manifests.map{|m| [m.name, m]}] if Array === manifests
30
+ manifest_pairs = GraphHash[manifests.map{|k, m| [k, m.dependencies.map{|d| d.name}]}]
31
+ manifest_names = manifest_pairs.tsort
32
+ manifest_names.map{|n| manifests[n]}
33
+ end
34
+ private
35
+ def method_for(manifests)
36
+ case manifests
37
+ when Hash
38
+ :to_hash
39
+ when Array
40
+ :to_a
41
+ end
42
+ end
43
+ end
44
+
45
+ def initialize(manifests)
46
+ self.index = Hash === manifests ? manifests.dup : index_by(manifests, &:name)
47
+ end
48
+
49
+ def to_a
50
+ index.values
51
+ end
52
+
53
+ def to_hash
54
+ index.dup
55
+ end
56
+
57
+ def dup
58
+ self.class.new(index)
59
+ end
60
+
61
+ def shallow_strip(names)
62
+ dup.shallow_strip!(names)
63
+ end
64
+
65
+ def shallow_strip!(names)
66
+ assert_strings!(names)
67
+
68
+ names.each do |name|
69
+ index.delete(name)
70
+ end
71
+ self
72
+ end
73
+
74
+ def deep_strip(names)
75
+ dup.deep_strip!(names)
76
+ end
77
+
78
+ def deep_strip!(names)
79
+ strippables = dependencies_of(names)
80
+ shallow_strip!(strippables)
81
+
82
+ self
83
+ end
84
+
85
+ def shallow_keep(names)
86
+ dup.shallow_keep!(names)
87
+ end
88
+
89
+ def shallow_keep!(names)
90
+ assert_strings!(names)
91
+
92
+ names = Set.new(names) unless Set === names
93
+ index.reject! { |k, v| !names.include?(k) }
94
+ self
95
+ end
96
+
97
+ def deep_keep(names)
98
+ dup.conservative_strip!(names)
99
+ end
100
+
101
+ def deep_keep!(names)
102
+ keepables = dependencies_of(names)
103
+ shallow_keep!(keepables)
104
+
105
+ self
106
+ end
107
+
108
+ def consistent?
109
+ index.values.all? do |manifest|
110
+ in_compliance_with?(manifest.dependencies)
111
+ end
112
+ end
113
+
114
+ def in_compliance_with?(dependencies)
115
+ dependencies.all? do |dependency|
116
+ manifest = index[dependency.name]
117
+ manifest && manifest.satisfies?(dependency)
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ attr_accessor :index
124
+
125
+ def assert_strings!(names)
126
+ non_strings = names.reject{|name| String === name}
127
+ non_strings.empty? or raise TypeError, "names must all be strings"
128
+ end
129
+
130
+ # Straightforward breadth-first graph traversal algorithm.
131
+ def dependencies_of(names)
132
+ names = Array === names ? names.dup : names.to_a
133
+ assert_strings!(names)
134
+
135
+ deps = Set.new
136
+ until names.empty?
137
+ name = names.shift
138
+ next if deps.include?(name)
139
+
140
+ deps << name
141
+ names.concat index[name].dependencies.map(&:name)
142
+ end
143
+ deps.to_a
144
+ end
145
+
146
+ def index_by(enum)
147
+ Hash[enum.map{|obj| [yield(obj), obj]}]
148
+ end
149
+
150
+ end
151
+ end