Class: Cukedep::FeatureModel
- Inherits:
-
Object
- Object
- Cukedep::FeatureModel
- Defined in:
- lib/cukedep/feature-model.rb
Overview
The internal representation of a set of feature files. Dependencies: use topological sort TSort module http://ruby-doc.org/stdlib-1.9.3/libdoc/tsort/rdoc/TSort.html See also: Is this topological sort in Ruby flawed?
Defined Under Namespace
Classes: DepGraph, FeatureDependencies
Instance Attribute Summary collapse
-
#dependencies ⇒ Object
readonly
An Array of FeatureDependencies.
-
#feature_files ⇒ Object
readonly
Returns the value of attribute feature_files.
Instance Method Summary collapse
-
#anonymous_features ⇒ Object
The list of feature files without identifiers.
-
#dependency_links ⇒ Object
Build an array of FileDependencies objects.
-
#draw_dependency_graph(theDOTfile, isVerbose = false) ⇒ Object
Create a graphical representation of the dependencies.
-
#draw_edge(anIO, aDependency) ⇒ Object
Draw an edge between feature files having dependencies.
-
#draw_node(anIO, aFeatureFile, anIndex) ⇒ Object
Draw a refinement node in DOT format.
-
#emit_body(anIO) ⇒ Object
Output the nodes as graph vertices + their edges with parent node.
- #emit_heading(anIO) ⇒ Object
-
#emit_trailing(anIO) ⇒ Object
Output the closing part of the graph drawing.
- #generate_rake_tasks(rakefile, theProjDir) ⇒ Object
-
#initialize(theFeatureFiles) ⇒ FeatureModel
constructor
A new instance of FeatureModel.
-
#mapping_reports(fileFeature2id, fileId2Feature, isVerbose = false) ⇒ Object
Generate CSV files detailing the feature to identifier mapping and vise versa.
-
#select_by_ids(*theIds) ⇒ Object
Retrieve the feature file matching the given feature identifiers theIds one or more Strings, each being one feature identifier.
-
#sort_features_by_dep ⇒ Object
Sort the feature files by dependency order.
Constructor Details
#initialize(theFeatureFiles) ⇒ FeatureModel
Returns a new instance of FeatureModel.
59 60 61 |
# File 'lib/cukedep/feature-model.rb', line 59 def initialize(theFeatureFiles) @feature_files = validated_model(theFeatureFiles) end |
Instance Attribute Details
#dependencies ⇒ Object (readonly)
An Array of FeatureDependencies
57 58 59 |
# File 'lib/cukedep/feature-model.rb', line 57 def dependencies @dependencies end |
#feature_files ⇒ Object (readonly)
Returns the value of attribute feature_files.
54 55 56 |
# File 'lib/cukedep/feature-model.rb', line 54 def feature_files @feature_files end |
Instance Method Details
#anonymous_features ⇒ Object
The list of feature files without identifiers
79 80 81 |
# File 'lib/cukedep/feature-model.rb', line 79 def anonymous_features() feature_files.select { |ff| ff.feature.anonymous? } end |
#dependency_links ⇒ Object
Build an array of FileDependencies objects.
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/cukedep/feature-model.rb', line 84 def dependency_links() if @dependencies.nil? # Build the mapping: feature identifier => feature features_by_id = id2features # Resolve the dependency tags resolve_dependencies(features_by_id) end return @dependencies end |
#draw_dependency_graph(theDOTfile, isVerbose = false) ⇒ Object
Create a graphical representation of the dependencies. The result is a DOT file that can be rendered via the DOT application from the GraphViz distribution.
136 137 138 139 140 141 142 |
# File 'lib/cukedep/feature-model.rb', line 136 def draw_dependency_graph(theDOTfile, isVerbose = false) puts " #{theDOTfile}" if isVerbose dot_file = File.open(theDOTfile, 'w') emit_heading(dot_file) emit_body(dot_file) emit_trailing(dot_file) end |
#draw_edge(anIO, aDependency) ⇒ Object
Draw an edge between feature files having dependencies.
212 213 214 215 216 217 218 219 220 221 |
# File 'lib/cukedep/feature-model.rb', line 212 def draw_edge(anIO, aDependency) source_id = feature_files.find_index(aDependency.dependee) target_ids = aDependency.dependents.map do |a_target| feature_files.find_index(a_target) end target_ids.each do |t_id| anIO.puts "\tnode_#{source_id} -> node_#{t_id};" end end |
#draw_node(anIO, aFeatureFile, anIndex) ⇒ Object
Draw a refinement node in DOT format
200 201 202 203 204 205 206 207 208 209 |
# File 'lib/cukedep/feature-model.rb', line 200 def draw_node(anIO, aFeatureFile, anIndex) basename = File.basename(aFeatureFile.filepath, '.feature') its_feature = aFeatureFile.feature if its_feature.anonymous? id_suffix = '' else id_suffix = " -- #{its_feature.identifier}" end anIO.puts %Q| node_#{anIndex} [label = "#{basename}#{id_suffix}"];| end |
#emit_body(anIO) ⇒ Object
Output the nodes as graph vertices + their edges with parent node
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/cukedep/feature-model.rb', line 166 def emit_body(anIO) anIO.puts <<-EOS subgraph island { node [shape = box, style=filled, color=lightgray]; EOS feature_files.each_with_index do |ff, i| draw_node(anIO, ff, i) if ff.feature.anonymous? end anIO.puts <<-EOS label = "Isolated features"; } subgraph dependencies { node [shape = box, fillcolor = none]; EOS feature_files.each_with_index do |ff, i| draw_node(anIO, ff, i) unless ff.feature.anonymous? end anIO.puts <<-EOS label = "Dependencies"; } // The edges represent dependencies EOS dependencies.each { |a_dep| draw_edge(anIO, a_dep) } end |
#emit_heading(anIO) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/cukedep/feature-model.rb', line 145 def emit_heading(anIO) dir = File.dirname(File.absolute_path(feature_files[0].filepath)) heading = <<-EOS // Graph of dependencies of feature files in directory: // '#{dir}' // This file uses the DOT syntax, a free utility from the Graphviz toolset. // Graphviz is available at: www.graphviz.org // File generated on #{Time.now.asctime}. digraph g { size = "7, 11"; // Dimensions in inches... center = true; rankdir = BT; // Draw from bottom to top label = "\\nDependency graph of '#{dir}'"; // Nodes represent feature files EOS anIO.write heading end |
#emit_trailing(anIO) ⇒ Object
Output the closing part of the graph drawing
195 196 197 |
# File 'lib/cukedep/feature-model.rb', line 195 def emit_trailing(anIO) anIO.puts '} // End of graph' end |
#generate_rake_tasks(rakefile, theProjDir) ⇒ Object
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/cukedep/feature-model.rb', line 224 def generate_rake_tasks(rakefile, theProjDir) puts " #{rakefile}" grandparent_path = Pathname.new(File.dirname(__FILE__)).parent.parent template_source = File.read(grandparent_path + './templates/rake.erb') # Create one template engine instance engine = ERB.new(template_source) source_dir = File.absolute_path(Dir.getwd) proj_dir = File.absolute_path(theProjDir) anonymous = anonymous_features.map { |ff| ff.basename } feature_ids = feature_files.map { |ff| ff.feature.identifier } feature_ids.compact! deps = dependencies.reject { |dep| dep.dependee.feature.anonymous? } # Generate the text representation with given context file_source = engine.result(binding) File.open(rakefile, 'w') { |f| f.write(file_source) } end |
#mapping_reports(fileFeature2id, fileId2Feature, isVerbose = false) ⇒ Object
Generate CSV files detailing the feature to identifier mapping and vise versa
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/cukedep/feature-model.rb', line 109 def mapping_reports(fileFeature2id, fileId2Feature, isVerbose = false) puts " #{fileFeature2id}" if isVerbose # Generate the feature file name => feature identifier report CSV.open(fileFeature2id, 'wb') do |f| f << ['Feature file', 'Identifier'] feature_files.each do |ff| identifier = ff.feature.identifier filename = File.basename(ff.filepath) f << [filename, identifier.nil? ? 'nil' : identifier] end end # Generate the feature file name => feature identifier report puts " #{fileId2Feature}" if isVerbose CSV.open(fileId2Feature, 'wb') do |f| f << ['identifier', 'feature file'] feature_files.each do |ff| identifier = ff.feature.identifier filename = File.basename(ff.filepath) f << [identifier, filename] unless identifier.nil? end end end |
#select_by_ids(*theIds) ⇒ Object
Retrieve the feature file matching the given feature identifiers theIds one or more Strings, each being one feature identifier
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/cukedep/feature-model.rb', line 65 def select_by_ids(*theIds) features_by_ids = id2features selection = theIds.each_with_object([]) do |an_id, sub_result| found_feature = features_by_ids[an_id] if found_feature.nil? fail(StandardError, "No feature file with identifier '#{an_id}'.") end sub_result << found_feature end return selection end |
#sort_features_by_dep ⇒ Object
Sort the feature files by dependency order.
98 99 100 101 102 103 104 105 |
# File 'lib/cukedep/feature-model.rb', line 98 def sort_features_by_dep() dep_links = dependency_links graph = DepGraph.new(dep_links) sorted_deps = graph.tsort all_sorted = sorted_deps.map(&:dependee) @sorted_features = all_sorted.reject { |f| f.feature.anonymous? } end |