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.
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.
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/cukedep/feature-model.rb', line 208 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
196 197 198 199 200 201 202 203 204 205 |
# File 'lib/cukedep/feature-model.rb', line 196 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
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 188 |
# File 'lib/cukedep/feature-model.rb', line 162 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
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/cukedep/feature-model.rb', line 141 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
191 192 193 |
# File 'lib/cukedep/feature-model.rb', line 191 def emit_trailing(anIO) anIO.puts '} // End of graph' end |
#generate_rake_tasks(rakefile, theProjDir) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/cukedep/feature-model.rb', line 220 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 |
# 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? 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.
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 |