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.
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
75 76 77 |
# File 'lib/cukedep/feature-model.rb', line 75 def anonymous_features feature_files.select { |ff| ff.feature.anonymous? } end |
#dependency_links ⇒ Object
Build an array of FileDependencies objects.
80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/cukedep/feature-model.rb', line 80 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.
131 132 133 134 135 136 137 |
# File 'lib/cukedep/feature-model.rb', line 131 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.
206 207 208 209 210 211 212 213 214 215 |
# File 'lib/cukedep/feature-model.rb', line 206 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
194 195 196 197 198 199 200 201 202 203 |
# File 'lib/cukedep/feature-model.rb', line 194 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
160 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 |
# File 'lib/cukedep/feature-model.rb', line 160 def emit_body(anIO) anIO.puts "subgraph island {\n node [shape = box, style=filled, color=lightgray];\n" feature_files.each_with_index do |ff, i| draw_node(anIO, ff, i) if ff.feature.anonymous? end anIO.puts " label = \"Isolated features\";\n }\n\nsubgraph dependencies {\n node [shape = box, fillcolor = none];\n" feature_files.each_with_index do |ff, i| draw_node(anIO, ff, i) unless ff.feature.anonymous? end anIO.puts " label = \"Dependencies\";\n}\n\n// The edges represent dependencies\n" dependencies.each { |a_dep| draw_edge(anIO, a_dep) } end |
#emit_heading(anIO) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/cukedep/feature-model.rb', line 139 def emit_heading(anIO) dir = File.dirname(File.absolute_path(feature_files[0].filepath)) heading = "// Graph of dependencies of feature files in directory:\n// '\#{dir}'\n// This file uses the DOT syntax, a free utility from the Graphviz toolset.\n// Graphviz is available at: www.graphviz.org\n// File generated on \#{Time.now.asctime}.\n\ndigraph g {\nsize = \"7, 11\"; // Dimensions in inches...\ncenter = true;\nrankdir = BT; // Draw from bottom to top\nlabel = \"\\\\nDependency graph of '\#{dir}'\";\n\n// Nodes represent feature files\n" anIO.write heading end |
#emit_trailing(anIO) ⇒ Object
Output the closing part of the graph drawing
189 190 191 |
# File 'lib/cukedep/feature-model.rb', line 189 def emit_trailing(anIO) anIO.puts '} // End of graph' end |
#generate_rake_tasks(rakefile, theProjDir) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/cukedep/feature-model.rb', line 217 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
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/cukedep/feature-model.rb', line 104 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 |
# 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.
93 94 95 96 97 98 99 100 |
# File 'lib/cukedep/feature-model.rb', line 93 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 |