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-2.6/libdoc/tsort/rdoc/index.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.
55 56 57 |
# File 'lib/cukedep/feature-model.rb', line 55 def initialize(theFeatureFiles) @feature_files = validated_model(theFeatureFiles) end |
Instance Attribute Details
#dependencies ⇒ Object (readonly)
An Array of FeatureDependencies
53 54 55 |
# File 'lib/cukedep/feature-model.rb', line 53 def dependencies @dependencies end |
#feature_files ⇒ Object (readonly)
Returns the value of attribute feature_files.
50 51 52 |
# File 'lib/cukedep/feature-model.rb', line 50 def feature_files @feature_files end |
Instance Method Details
#anonymous_features ⇒ Object
The list of feature files without identifiers
76 77 78 |
# File 'lib/cukedep/feature-model.rb', line 76 def anonymous_features feature_files.select { |ff| ff.feature.anonymous? } end |
#dependency_links ⇒ Object
Build an array of FileDependencies objects.
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/cukedep/feature-model.rb', line 81 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.
132 133 134 135 136 137 138 |
# File 'lib/cukedep/feature-model.rb', line 132 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.
207 208 209 210 211 212 213 214 215 216 |
# File 'lib/cukedep/feature-model.rb', line 207 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
195 196 197 198 199 200 201 202 203 204 |
# File 'lib/cukedep/feature-model.rb', line 195 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
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/cukedep/feature-model.rb', line 161 def emit_body(anIO) anIO.puts <<-DOT subgraph island { node [shape = box, style=filled, color=lightgray]; DOT feature_files.each_with_index do |ff, i| draw_node(anIO, ff, i) if ff.feature.anonymous? end anIO.puts <<-DOT label = "Isolated features"; } subgraph dependencies { node [shape = box, fillcolor = none]; DOT feature_files.each_with_index do |ff, i| draw_node(anIO, ff, i) unless ff.feature.anonymous? end anIO.puts <<-DOT label = "Dependencies"; } // The edges represent dependencies DOT dependencies.each { |a_dep| draw_edge(anIO, a_dep) } end |
#emit_heading(anIO) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/cukedep/feature-model.rb', line 140 def emit_heading(anIO) dir = File.dirname(File.absolute_path(feature_files[0].filepath)) heading = <<-DOT // 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 DOT anIO.write heading end |
#emit_trailing(anIO) ⇒ Object
Output the closing part of the graph drawing
190 191 192 |
# File 'lib/cukedep/feature-model.rb', line 190 def emit_trailing(anIO) anIO.puts '} // End of graph' end |
#generate_rake_tasks(rakefile, theProjDir) ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/cukedep/feature-model.rb', line 218 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(&: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
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/cukedep/feature-model.rb', line 105 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
61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/cukedep/feature-model.rb', line 61 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? raise 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.
94 95 96 97 98 99 100 101 |
# File 'lib/cukedep/feature-model.rb', line 94 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 |