facter 1.7.6 → 2.0.1.rc1

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.

Potentially problematic release.


This version of facter might be problematic. Click here for more details.

Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/COMMITTERS.md +194 -0
  3. data/CONTRIBUTING.md +63 -235
  4. data/Gemfile +12 -8
  5. data/README.md +1 -2
  6. data/Rakefile +1 -1
  7. data/bin/facter +0 -4
  8. data/ext/build_defaults.yaml +2 -2
  9. data/ext/project_data.yaml +18 -0
  10. data/install.rb +1 -16
  11. data/lib/facter.rb +171 -171
  12. data/lib/facter/application.rb +65 -54
  13. data/lib/facter/core/aggregate.rb +220 -0
  14. data/lib/facter/core/directed_graph.rb +46 -0
  15. data/lib/facter/core/execution.rb +100 -0
  16. data/lib/facter/core/execution/base.rb +73 -0
  17. data/lib/facter/core/execution/posix.rb +50 -0
  18. data/lib/facter/core/execution/windows.rb +57 -0
  19. data/lib/facter/core/logging.rb +169 -0
  20. data/lib/facter/core/resolvable.rb +94 -0
  21. data/lib/facter/core/suitable.rb +117 -0
  22. data/lib/facter/domain.rb +15 -9
  23. data/lib/facter/filesystems.rb +1 -1
  24. data/lib/facter/hardwaremodel.rb +1 -1
  25. data/lib/facter/hostname.rb +3 -3
  26. data/lib/facter/interfaces.rb +6 -1
  27. data/lib/facter/ipaddress.rb +2 -2
  28. data/lib/facter/kernel.rb +1 -1
  29. data/lib/facter/kernelrelease.rb +1 -1
  30. data/lib/facter/ldom.rb +1 -1
  31. data/lib/facter/lsbdistcodename.rb +1 -1
  32. data/lib/facter/lsbdistdescription.rb +1 -1
  33. data/lib/facter/lsbdistid.rb +1 -1
  34. data/lib/facter/lsbdistrelease.rb +1 -1
  35. data/lib/facter/lsbrelease.rb +1 -1
  36. data/lib/facter/macaddress.rb +1 -14
  37. data/lib/facter/macosx.rb +2 -2
  38. data/lib/facter/memory.rb +8 -19
  39. data/lib/facter/operatingsystem.rb +1 -1
  40. data/lib/facter/operatingsystemrelease.rb +34 -1
  41. data/lib/facter/physicalprocessorcount.rb +6 -6
  42. data/lib/facter/processor.rb +11 -10
  43. data/lib/facter/selinux.rb +4 -15
  44. data/lib/facter/ssh.rb +5 -2
  45. data/lib/facter/util/architecture.rb +2 -2
  46. data/lib/facter/util/collection.rb +42 -38
  47. data/lib/facter/util/config.rb +19 -9
  48. data/lib/facter/util/confine.rb +34 -4
  49. data/lib/facter/util/ec2.rb +1 -1
  50. data/lib/facter/util/fact.rb +108 -36
  51. data/lib/facter/util/file_read.rb +7 -6
  52. data/lib/facter/util/formatter.rb +38 -0
  53. data/lib/facter/util/ip.rb +3 -3
  54. data/lib/facter/util/loader.rb +62 -42
  55. data/lib/facter/util/macosx.rb +7 -8
  56. data/lib/facter/util/manufacturer.rb +3 -3
  57. data/lib/facter/util/memory.rb +13 -13
  58. data/lib/facter/util/monkey_patches.rb +0 -31
  59. data/lib/facter/util/netmask.rb +3 -3
  60. data/lib/facter/util/normalization.rb +94 -0
  61. data/lib/facter/util/nothing_loader.rb +3 -6
  62. data/lib/facter/util/parser.rb +3 -5
  63. data/lib/facter/util/plist/generator.rb +1 -1
  64. data/lib/facter/util/processor.rb +15 -15
  65. data/lib/facter/util/resolution.rb +112 -289
  66. data/lib/facter/util/solaris_zones.rb +4 -4
  67. data/lib/facter/util/uptime.rb +8 -3
  68. data/lib/facter/util/values.rb +67 -1
  69. data/lib/facter/util/virtual.rb +10 -10
  70. data/lib/facter/util/xendomains.rb +1 -1
  71. data/lib/facter/version.rb +42 -39
  72. data/lib/facter/virtual.rb +6 -7
  73. data/lib/facter/zfs_version.rb +3 -3
  74. data/lib/facter/zpool_version.rb +3 -3
  75. data/spec/fixtures/unit/netmask/darwin_10_8_5.txt +30 -0
  76. data/spec/unit/application_spec.rb +46 -1
  77. data/spec/unit/core/aggregate_spec.rb +125 -0
  78. data/spec/unit/core/directed_graph_spec.rb +79 -0
  79. data/spec/unit/core/execution/base_spec.rb +119 -0
  80. data/spec/unit/core/execution/posix_spec.rb +86 -0
  81. data/spec/unit/core/execution/windows_spec.rb +106 -0
  82. data/spec/unit/core/execution_spec.rb +37 -0
  83. data/spec/unit/core/logging_spec.rb +104 -0
  84. data/spec/unit/core/resolvable_spec.rb +81 -0
  85. data/spec/unit/core/suitable_spec.rb +96 -0
  86. data/spec/unit/domain_spec.rb +5 -5
  87. data/spec/unit/facter_spec.rb +61 -222
  88. data/spec/unit/filesystems_spec.rb +2 -2
  89. data/spec/unit/hardwareisa_spec.rb +5 -5
  90. data/spec/unit/hardwaremodel_spec.rb +1 -1
  91. data/spec/unit/hostname_spec.rb +4 -4
  92. data/spec/unit/id_spec.rb +3 -3
  93. data/spec/unit/interfaces_spec.rb +10 -0
  94. data/spec/unit/ipaddress6_spec.rb +4 -4
  95. data/spec/unit/ipaddress_spec.rb +1 -1
  96. data/spec/unit/kernel_spec.rb +2 -2
  97. data/spec/unit/kernelmajversion_spec.rb +1 -1
  98. data/spec/unit/kernelrelease_spec.rb +4 -4
  99. data/spec/unit/kernelversion_spec.rb +2 -2
  100. data/spec/unit/ldom_spec.rb +2 -2
  101. data/spec/unit/lsbdistcodename_spec.rb +2 -2
  102. data/spec/unit/lsbdistdescription_spec.rb +2 -2
  103. data/spec/unit/lsbdistid_spec.rb +2 -2
  104. data/spec/unit/lsbdistrelease_spec.rb +2 -2
  105. data/spec/unit/lsbrelease_spec.rb +2 -2
  106. data/spec/unit/manufacturer_spec.rb +1 -1
  107. data/spec/unit/memory_spec.rb +24 -31
  108. data/spec/unit/netmask_spec.rb +9 -0
  109. data/spec/unit/operatingsystem_spec.rb +1 -1
  110. data/spec/unit/operatingsystemrelease_spec.rb +62 -4
  111. data/spec/unit/physicalprocessorcount_spec.rb +10 -10
  112. data/spec/unit/processor_spec.rb +11 -11
  113. data/spec/unit/selinux_spec.rb +2 -8
  114. data/spec/unit/ssh_spec.rb +3 -2
  115. data/spec/unit/uniqueid_spec.rb +3 -3
  116. data/spec/unit/util/collection_spec.rb +37 -35
  117. data/spec/unit/util/config_spec.rb +20 -0
  118. data/spec/unit/util/confine_spec.rb +21 -0
  119. data/spec/unit/util/directory_loader_spec.rb +1 -0
  120. data/spec/unit/util/ec2_spec.rb +6 -6
  121. data/spec/unit/util/fact_spec.rb +92 -90
  122. data/spec/unit/util/ip_spec.rb +2 -2
  123. data/spec/unit/util/loader_spec.rb +127 -186
  124. data/spec/unit/util/macaddress_spec.rb +2 -2
  125. data/spec/unit/util/macosx_spec.rb +8 -8
  126. data/spec/unit/util/manufacturer_spec.rb +3 -3
  127. data/spec/unit/util/normalization_spec.rb +113 -0
  128. data/spec/unit/util/parser_spec.rb +25 -3
  129. data/spec/unit/util/processor_spec.rb +2 -2
  130. data/spec/unit/util/resolution_spec.rb +60 -631
  131. data/spec/unit/util/solaris_zones_spec.rb +5 -5
  132. data/spec/unit/util/uptime_spec.rb +1 -1
  133. data/spec/unit/util/values_spec.rb +131 -0
  134. data/spec/unit/util/virtual_spec.rb +16 -16
  135. data/spec/unit/util/xendomains_spec.rb +2 -2
  136. data/spec/unit/virtual_spec.rb +39 -39
  137. data/spec/unit/zfs_version_spec.rb +11 -11
  138. data/spec/unit/zonename_spec.rb +2 -2
  139. data/spec/unit/zones_spec.rb +1 -1
  140. data/spec/unit/zpool_version_spec.rb +11 -11
  141. metadata +466 -447
  142. data/lib/facter/util/cfpropertylist.rb +0 -6
  143. data/lib/facter/util/cfpropertylist/LICENSE +0 -19
  144. data/lib/facter/util/cfpropertylist/README +0 -44
  145. data/lib/facter/util/cfpropertylist/Rakefile +0 -44
  146. data/lib/facter/util/cfpropertylist/THANKS +0 -7
  147. data/lib/facter/util/cfpropertylist/lib/cfpropertylist.rb +0 -6
  148. data/lib/facter/util/cfpropertylist/lib/rbBinaryCFPropertyList.rb +0 -562
  149. data/lib/facter/util/cfpropertylist/lib/rbCFPlistError.rb +0 -26
  150. data/lib/facter/util/cfpropertylist/lib/rbCFPropertyList.rb +0 -407
  151. data/lib/facter/util/cfpropertylist/lib/rbCFTypes.rb +0 -244
  152. data/lib/facter/util/cfpropertylist/lib/rbLibXMLParser.rb +0 -135
  153. data/lib/facter/util/cfpropertylist/lib/rbNokogiriParser.rb +0 -140
  154. data/lib/facter/util/cfpropertylist/lib/rbREXMLParser.rb +0 -136
  155. data/spec/unit/util/monkey_patches_spec.rb +0 -42
@@ -1,3 +1,7 @@
1
+ require 'optparse'
2
+ require 'facter'
3
+ require 'facter/util/formatter'
4
+
1
5
  module Facter
2
6
  module Application
3
7
 
@@ -7,7 +11,7 @@ module Facter
7
11
  begin
8
12
  Facter::Util::Config.ext_fact_loader = Facter::Util::DirectoryLoader.loader_for(dir)
9
13
  rescue Facter::Util::DirectoryLoader::NoSuchDirectoryError => error
10
- $stderr.puts "Specified external facts directory #{dir} does not exist."
14
+ Facter.log_exception(error, "Specified external facts directory #{dir} does not exist.")
11
15
  exit(1)
12
16
  end
13
17
  end
@@ -17,9 +21,6 @@ module Facter
17
21
  end
18
22
 
19
23
  def self.run(argv)
20
- require 'optparse'
21
- require 'facter'
22
-
23
24
  options = parse(argv)
24
25
 
25
26
  # Accept fact names to return from the command line
@@ -35,8 +36,8 @@ module Facter
35
36
  begin
36
37
  facts[name] = Facter.value(name)
37
38
  rescue => error
38
- $stderr.puts "Could not retrieve #{name}: #{error}"
39
- exit 10
39
+ Facter.log_exception(error, "Could not retrieve #{name}: #{error.message}")
40
+ exit(10)
40
41
  end
41
42
  end
42
43
  end
@@ -44,44 +45,24 @@ module Facter
44
45
  # Print everything if they didn't ask for specific facts.
45
46
  facts ||= Facter.to_hash
46
47
 
47
- # Print the facts as YAML and exit
48
- if options[:yaml]
49
- require 'yaml'
50
- puts YAML.dump(facts)
51
- exit(0)
52
- end
48
+ output = nil
53
49
 
54
- # Print the facts as JSON and exit
55
- if options[:json]
56
- begin
57
- require 'json'
58
- puts JSON.dump(facts)
59
- exit(0)
60
- rescue LoadError
61
- $stderr.puts "You do not have JSON support in your version of Ruby. JSON output disabled"
62
- exit(1)
63
- end
64
- end
65
-
66
- # Print the value of a single fact, otherwise print a list sorted by fact
67
- # name and separated by "=>"
68
- if facts.length == 1
69
- if value = facts.values.first
70
- puts value
71
- end
50
+ if options[:yaml]
51
+ output = Facter::Util::Formatter.format_yaml(facts)
52
+ elsif options[:json]
53
+ output = Facter::Util::Formatter.format_json(facts)
54
+ elsif options[:plaintext]
55
+ output = Facter::Util::Formatter.format_plaintext(facts)
72
56
  else
73
- facts.sort_by{ |fact| fact.first }.each do |name,value|
74
- puts "#{name} => #{value}"
75
- end
57
+ output = Facter::Util::Formatter.format_plaintext(facts)
76
58
  end
77
59
 
60
+ puts output
61
+ exit(0)
62
+
78
63
  rescue => e
79
- if options && options[:trace]
80
- raise e
81
- else
82
- $stderr.puts "Error: #{e}"
83
- exit(12)
84
- end
64
+ Facter.log_exception(e)
65
+ exit(12)
85
66
  end
86
67
 
87
68
  private
@@ -95,18 +76,23 @@ module Facter
95
76
  options = {}
96
77
  parser = OptionParser.new do |opts|
97
78
  opts.banner = <<-BANNER
98
- Synopsis
99
- ========
79
+ facter(8) -- Gather system information
80
+ ======
81
+
82
+ SYNOPSIS
83
+ --------
100
84
 
101
85
  Collect and display facts about the system.
102
86
 
103
- Usage
104
- =====
87
+ USAGE
88
+ -----
105
89
 
106
- facter [-d|--debug] [-h|--help] [-p|--puppet] [-v|--version] [-y|--yaml] [-j|--json] [--external-dir DIR] [--no-external-dir] [fact] [fact] [...]
90
+ facter [-h|--help] [-t|--timing] [-d|--debug] [-p|--puppet] [-v|--version]
91
+ [-y|--yaml] [-j|--json] [--plaintext] [--external-dir DIR] [--no-external-dir]
92
+ [fact] [fact] [...]
107
93
 
108
- Description
109
- ===========
94
+ DESCRIPTION
95
+ -----------
110
96
 
111
97
  Collect and display facts about the current system. The library behind
112
98
  Facter is easy to expand, making Facter an easy way to collect information
@@ -115,19 +101,42 @@ about a system from within the shell or within Ruby.
115
101
  If no facts are specifically asked for, then all facts will be returned.
116
102
 
117
103
  EXAMPLE
118
- =======
119
- facter kernel
104
+ -------
105
+
106
+ Display all facts:
107
+
108
+ $ facter
109
+ architecture => amd64
110
+ blockdevices => sda,sr0
111
+ domain => example.com
112
+ fqdn => puppet.example.com
113
+ hardwaremodel => x86_64
114
+ [...]
115
+
116
+ Display a single fact:
117
+
118
+ $ facter kernel
119
+ Linux
120
+
121
+ Format facts as JSON:
122
+
123
+ $ facter --json architecture kernel hardwaremodel
124
+ {
125
+ "architecture": "amd64",
126
+ "kernel": "Linux",
127
+ "hardwaremodel": "x86_64"
128
+ }
120
129
 
121
130
  AUTHOR
122
- ======
131
+ ------
123
132
  Luke Kanies
124
133
 
125
134
  COPYRIGHT
126
- =========
127
- Copyright (c) 2011-2012 Puppet Labs, Inc Licensed under the Apache 2.0 license
135
+ ---------
136
+ Copyright (c) 2011-2014 Puppet Labs, Inc Licensed under the Apache 2.0 license
128
137
 
129
- USAGE
130
- =====
138
+ OPTIONS
139
+ -------
131
140
  BANNER
132
141
  opts.on("-y",
133
142
  "--yaml",
@@ -135,8 +144,10 @@ USAGE
135
144
  opts.on("-j",
136
145
  "--json",
137
146
  "Emit facts in JSON format.") { |v| options[:json] = v }
147
+ opts.on("--plaintext",
148
+ "Emit facts in plaintext format.") { |v| options[:plaintext] = v }
138
149
  opts.on("--trace",
139
- "Enable backtraces.") { |v| options[:trace] = v }
150
+ "Enable backtraces.") { |v| Facter.trace(true) }
140
151
  opts.on("--external-dir DIR",
141
152
  "The directory to use for external facts.") { |v| create_directory_loader(v) }
142
153
  opts.on("--no-external-dir",
@@ -0,0 +1,220 @@
1
+ require 'facter'
2
+ require 'facter/core/directed_graph'
3
+ require 'facter/core/suitable'
4
+ require 'facter/core/resolvable'
5
+ require 'facter/util/values'
6
+
7
+ # Aggregates provide a mechanism for facts to be resolved in multiple steps.
8
+ #
9
+ # Aggregates are evaluated in two parts: generating individual chunks and then
10
+ # aggregating all chunks together. Each chunk is a block of code that generates
11
+ # a value, and may depend on other chunks when it runs. After all chunks have
12
+ # been evaluated they are passed to the aggregate block as Hash<name, result>.
13
+ # The aggregate block converts the individual chunks into a single value that is
14
+ # returned as the final value of the aggregate.
15
+ #
16
+ # @api public
17
+ # @since 2.0.0
18
+ class Facter::Core::Aggregate
19
+
20
+ include Facter::Core::Suitable
21
+ include Facter::Core::Resolvable
22
+
23
+ # @!attribute [r] name
24
+ # @return [Symbol] The name of the aggregate resolution
25
+ attr_reader :name
26
+
27
+ # @!attribute [r] deps
28
+ # @api private
29
+ # @return [Facter::Core::DirectedGraph]
30
+ attr_reader :deps
31
+
32
+ # @!attribute [r] confines
33
+ # @return [Array<Facter::Core::Confine>] An array of confines restricting
34
+ # this to a specific platform
35
+ # @see Facter::Core::Suitable
36
+ attr_reader :confines
37
+
38
+ # @!attribute [r] fact
39
+ # @return [Facter::Util::Fact]
40
+ # @api private
41
+ attr_reader :fact
42
+
43
+ def initialize(name, fact)
44
+ @name = name
45
+ @fact = fact
46
+
47
+ @confines = []
48
+ @chunks = {}
49
+
50
+ @aggregate = nil
51
+ @deps = Facter::Core::DirectedGraph.new
52
+ end
53
+
54
+ def set_options(options)
55
+ if options[:name]
56
+ @name = options.delete(:name)
57
+ end
58
+
59
+ if options.has_key?(:timeout)
60
+ @timeout = options.delete(:timeout)
61
+ end
62
+
63
+ if options.has_key?(:weight)
64
+ @weight = options.delete(:weight)
65
+ end
66
+
67
+ if not options.keys.empty?
68
+ raise ArgumentError, "Invalid aggregate options #{options.keys.inspect}"
69
+ end
70
+ end
71
+
72
+ def evaluate(&block)
73
+ instance_eval(&block)
74
+ end
75
+
76
+ # Define a new chunk for the given aggregate
77
+ #
78
+ # @api public
79
+ #
80
+ # @example Defining a chunk with no dependencies
81
+ # aggregate.chunk(:mountpoints) do
82
+ # # generate mountpoint information
83
+ # end
84
+ #
85
+ # @example Defining an chunk to add mount options
86
+ # aggregate.chunk(:mount_options, :require => [:mountpoints]) do |mountpoints|
87
+ # # `mountpoints` is the result of the previous chunk
88
+ # # generate mount option information based on the mountpoints
89
+ # end
90
+ #
91
+ # @param name [Symbol] A name unique to this aggregate describing the chunk
92
+ # @param opts [Hash]
93
+ # @options opts [Array<Symbol>, Symbol] :require One or more chunks
94
+ # to evaluate and pass to this block.
95
+ # @yield [*Object] Zero or more chunk results
96
+ #
97
+ # @return [void]
98
+ def chunk(name, opts = {}, &block)
99
+ if not block_given?
100
+ raise ArgumentError, "#{self.class.name}#chunk requires a block"
101
+ end
102
+
103
+ deps = Array(opts.delete(:require))
104
+
105
+ if not opts.empty?
106
+ raise ArgumentError, "Unexpected options passed to #{self.class.name}#chunk: #{opts.keys.inspect}"
107
+ end
108
+
109
+ @deps[name] = deps
110
+ @chunks[name] = block
111
+ end
112
+
113
+ # Define how all chunks should be combined
114
+ #
115
+ # @api public
116
+ #
117
+ # @example Merge all chunks
118
+ # aggregate.aggregate do |chunks|
119
+ # final_result = {}
120
+ # chunks.each_value do |chunk|
121
+ # final_result.deep_merge(chunk)
122
+ # end
123
+ # final_result
124
+ # end
125
+ #
126
+ # @example Sum all chunks
127
+ # aggregate.aggregate do |chunks|
128
+ # total = 0
129
+ # chunks.each_value do |chunk|
130
+ # total += chunk
131
+ # end
132
+ # total
133
+ # end
134
+ #
135
+ # @yield [Hash<Symbol, Object>] A hash containing chunk names and
136
+ # chunk values
137
+ #
138
+ # @return [void]
139
+ def aggregate(&block)
140
+ if block_given?
141
+ @aggregate = block
142
+ else
143
+ raise ArgumentError, "#{self.class.name}#aggregate requires a block"
144
+ end
145
+ end
146
+
147
+ def resolution_type
148
+ :aggregate
149
+ end
150
+
151
+ private
152
+
153
+ # Evaluate the results of this aggregate.
154
+ #
155
+ # @see Facter::Core::Resolvable#value
156
+ # @return [Object]
157
+ def resolve_value
158
+ chunk_results = run_chunks()
159
+ aggregate_results(chunk_results)
160
+ end
161
+
162
+ # Order all chunks based on their dependencies and evaluate each one, passing
163
+ # dependent chunks as needed.
164
+ #
165
+ # @return [Hash<Symbol, Object>] A hash containing the chunk that
166
+ # generated value and the related value.
167
+ def run_chunks
168
+ results = {}
169
+ order_chunks.each do |(name, block)|
170
+ input = @deps[name].map { |dep_name| results[dep_name] }
171
+
172
+ output = block.call(*input)
173
+ results[name] = Facter::Util::Values.deep_freeze(output)
174
+ end
175
+
176
+ results
177
+ end
178
+
179
+ # Process the results of all chunks with the aggregate block and return the
180
+ # results. If no aggregate block has been specified, fall back to deep
181
+ # merging the given data structure
182
+ #
183
+ # @param results [Hash<Symbol, Object>] A hash of chunk names and the output
184
+ # of that chunk.
185
+ # @return [Object]
186
+ def aggregate_results(results)
187
+ if @aggregate
188
+ @aggregate.call(results)
189
+ else
190
+ default_aggregate(results)
191
+ end
192
+ end
193
+
194
+ def default_aggregate(results)
195
+ results.values.inject do |result, current|
196
+ Facter::Util::Values.deep_merge(result, current)
197
+ end
198
+ rescue Facter::Util::Values::DeepMergeError => e
199
+ raise ArgumentError, "Could not deep merge all chunks (Original error: " +
200
+ "#{e.message}), ensure that chunks return either an Array or Hash or " +
201
+ "override the aggregate block", e.backtrace
202
+ end
203
+
204
+ # Order chunks based on their dependencies
205
+ #
206
+ # @return [Array<Symbol, Proc>] A list of chunk names and blocks in evaluation order.
207
+ def order_chunks
208
+ if not @deps.acyclic?
209
+ raise DependencyError, "Could not order chunks; found the following dependency cycles: #{@deps.cycles.inspect}"
210
+ end
211
+
212
+ sorted_names = @deps.tsort
213
+
214
+ sorted_names.map do |name|
215
+ [name, @chunks[name]]
216
+ end
217
+ end
218
+
219
+ class DependencyError < StandardError; end
220
+ end
@@ -0,0 +1,46 @@
1
+ require 'set'
2
+ require 'tsort'
3
+
4
+ module Facter
5
+ module Core
6
+ class DirectedGraph < Hash
7
+ include TSort
8
+
9
+ def acyclic?
10
+ cycles.empty?
11
+ end
12
+
13
+ def cycles
14
+ cycles = []
15
+ each_strongly_connected_component do |component|
16
+ cycles << component if component.size > 1
17
+ end
18
+ cycles
19
+ end
20
+
21
+ alias tsort_each_node each_key
22
+
23
+ def tsort_each_child(node)
24
+ fetch(node, []).each do |child|
25
+ yield child
26
+ end
27
+ end
28
+
29
+ def tsort
30
+ missing = Set.new(self.values.flatten) - Set.new(self.keys)
31
+
32
+ if not missing.empty?
33
+ raise MissingVertex, "Cannot sort elements; cannot depend on missing elements #{missing.to_a}"
34
+ end
35
+
36
+ super
37
+
38
+ rescue TSort::Cyclic
39
+ raise CycleError, "Cannot sort elements; found the following cycles: #{cycles.inspect}"
40
+ end
41
+
42
+ class CycleError < StandardError; end
43
+ class MissingVertex < StandardError; end
44
+ end
45
+ end
46
+ end