cukedep 0.1.11 → 0.2.00
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.
- checksums.yaml +6 -14
- data/.ruby-version +1 -1
- data/.travis.yml +10 -10
- data/CHANGELOG.md +6 -0
- data/Gemfile +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/Rakefile +30 -30
- data/bin/cukedep +15 -15
- data/lib/cukedep/application.rb +105 -112
- data/lib/cukedep/cli/cmd-line.rb +11 -13
- data/lib/cukedep/config.rb +85 -89
- data/lib/cukedep/constants.rb +5 -5
- data/lib/cukedep/cuke-runner.rb +191 -198
- data/lib/cukedep/customization.rb +30 -30
- data/lib/cukedep/feature-model.rb +43 -46
- data/lib/cukedep/feature-rep.rb +9 -11
- data/lib/cukedep/file-action.rb +11 -18
- data/lib/cukedep/gherkin-facade.rb +11 -6
- data/lib/cukedep/gherkin-listener.rb +12 -42
- data/lib/cukedep/hook-dsl.rb +78 -78
- data/lib/cukedep/sandbox.rb +15 -16
- data/lib/cukedep.rb +1 -2
- data/sample/features/step_definitions/steps.rb +2 -2
- data/sample/model/model.rb +19 -20
- data/spec/cukedep/application_spec.rb +80 -80
- data/spec/cukedep/cli/cmd-line_spec.rb +88 -88
- data/spec/cukedep/cuke-runner_spec.rb +74 -74
- data/spec/cukedep/customization_spec.rb +31 -31
- data/spec/cukedep/debug-file-action.rb +29 -29
- data/spec/cukedep/feature-model_spec.rb +100 -100
- data/spec/cukedep/feature-rep_spec.rb +2 -1
- data/spec/cukedep/file-action_spec.rb +365 -366
- data/spec/cukedep/file-parsing.rb +39 -41
- data/spec/cukedep/gherkin-facade_spec.rb +48 -49
- data/spec/cukedep/gherkin-listener_spec.rb +55 -57
- data/spec/cukedep/hook-dsl_spec.rb +182 -182
- data/spec/cukedep/sample_features/cukedep_hooks.rb +30 -30
- data/spec/cukedep/sample_features/standalone.feature +1 -1
- data/templates/rake.erb +12 -21
- metadata +80 -58
- data/sample/result.html +0 -472
- data/spec/cukedep/sample_features/cukedep.rake +0 -215
- data/spec/cukedep/sample_features/dependencies.dot +0 -38
- data/spec/cukedep/sample_features/feature2id.csv +0 -7
|
@@ -18,19 +18,19 @@ class FeatureModel
|
|
|
18
18
|
# topological sort of the nodes.
|
|
19
19
|
class DepGraph
|
|
20
20
|
include TSort # Mix-in module for topological sorting
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
attr_reader(:dependencies)
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
# Inverse lookup: from the feature file => FeatureDependencies
|
|
25
25
|
attr_reader(:lookup)
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
def initialize(theDependencies)
|
|
28
28
|
@dependencies = theDependencies
|
|
29
29
|
@lookup = dependencies.each_with_object({}) do |f_deps, subresult|
|
|
30
30
|
subresult[f_deps.dependee] = f_deps
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
# Method required by TSort module.
|
|
35
35
|
# It is used to iterate over all the nodes of the dependency graph
|
|
36
36
|
def tsort_each_node(&aBlock)
|
|
@@ -48,7 +48,7 @@ class FeatureModel
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
attr_reader(:feature_files)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
# An Array of FeatureDependencies
|
|
53
53
|
attr_reader(:dependencies)
|
|
54
54
|
|
|
@@ -63,35 +63,34 @@ class FeatureModel
|
|
|
63
63
|
selection = theIds.each_with_object([]) do |an_id, sub_result|
|
|
64
64
|
found_feature = features_by_ids[an_id]
|
|
65
65
|
if found_feature.nil?
|
|
66
|
-
|
|
66
|
+
raise StandardError, "No feature file with identifier '#{an_id}'."
|
|
67
67
|
end
|
|
68
68
|
sub_result << found_feature
|
|
69
69
|
end
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
return selection
|
|
72
72
|
end
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
# The list of feature files without identifiers
|
|
75
|
-
def anonymous_features
|
|
75
|
+
def anonymous_features
|
|
76
76
|
feature_files.select { |ff| ff.feature.anonymous? }
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
# Build an array of FileDependencies objects.
|
|
80
|
-
def dependency_links
|
|
80
|
+
def dependency_links
|
|
81
81
|
if @dependencies.nil?
|
|
82
82
|
# Build the mapping: feature identifier => feature
|
|
83
83
|
features_by_id = id2features
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
# Resolve the dependency tags
|
|
86
86
|
resolve_dependencies(features_by_id)
|
|
87
87
|
end
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
return @dependencies
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
|
|
93
92
|
# Sort the feature files by dependency order.
|
|
94
|
-
def sort_features_by_dep
|
|
93
|
+
def sort_features_by_dep
|
|
95
94
|
dep_links = dependency_links
|
|
96
95
|
graph = DepGraph.new(dep_links)
|
|
97
96
|
sorted_deps = graph.tsort
|
|
@@ -99,7 +98,7 @@ class FeatureModel
|
|
|
99
98
|
all_sorted = sorted_deps.map(&:dependee)
|
|
100
99
|
@sorted_features = all_sorted.reject { |f| f.feature.anonymous? }
|
|
101
100
|
end
|
|
102
|
-
|
|
101
|
+
|
|
103
102
|
# Generate CSV files detailing the feature to identifier mapping
|
|
104
103
|
# and vise versa
|
|
105
104
|
def mapping_reports(fileFeature2id, fileId2Feature, isVerbose = false)
|
|
@@ -113,7 +112,7 @@ class FeatureModel
|
|
|
113
112
|
f << [filename, identifier.nil? ? 'nil' : identifier]
|
|
114
113
|
end
|
|
115
114
|
end
|
|
116
|
-
|
|
115
|
+
|
|
117
116
|
# Generate the feature file name => feature identifier report
|
|
118
117
|
puts " #{fileId2Feature}" if isVerbose
|
|
119
118
|
CSV.open(fileId2Feature, 'wb') do |f|
|
|
@@ -123,7 +122,7 @@ class FeatureModel
|
|
|
123
122
|
filename = File.basename(ff.filepath)
|
|
124
123
|
f << [identifier, filename] unless identifier.nil?
|
|
125
124
|
end
|
|
126
|
-
end
|
|
125
|
+
end
|
|
127
126
|
end
|
|
128
127
|
|
|
129
128
|
# Create a graphical representation of the dependencies.
|
|
@@ -136,11 +135,10 @@ class FeatureModel
|
|
|
136
135
|
emit_body(dot_file)
|
|
137
136
|
emit_trailing(dot_file)
|
|
138
137
|
end
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
|
|
141
139
|
def emit_heading(anIO)
|
|
142
140
|
dir = File.dirname(File.absolute_path(feature_files[0].filepath))
|
|
143
|
-
heading = <<-
|
|
141
|
+
heading = <<-DOT
|
|
144
142
|
// Graph of dependencies of feature files in directory:
|
|
145
143
|
// '#{dir}'
|
|
146
144
|
// This file uses the DOT syntax, a free utility from the Graphviz toolset.
|
|
@@ -149,49 +147,49 @@ class FeatureModel
|
|
|
149
147
|
|
|
150
148
|
digraph g {
|
|
151
149
|
size = "7, 11"; // Dimensions in inches...
|
|
152
|
-
center = true;
|
|
150
|
+
center = true;
|
|
153
151
|
rankdir = BT; // Draw from bottom to top
|
|
154
|
-
label = "\\nDependency graph of '#{dir}'";
|
|
155
|
-
|
|
152
|
+
label = "\\nDependency graph of '#{dir}'";
|
|
153
|
+
|
|
156
154
|
// Nodes represent feature files
|
|
157
|
-
|
|
155
|
+
DOT
|
|
158
156
|
anIO.write heading
|
|
159
157
|
end
|
|
160
|
-
|
|
158
|
+
|
|
161
159
|
# Output the nodes as graph vertices + their edges with parent node
|
|
162
160
|
def emit_body(anIO)
|
|
163
|
-
anIO.puts <<-
|
|
161
|
+
anIO.puts <<-DOT
|
|
164
162
|
subgraph island {
|
|
165
163
|
node [shape = box, style=filled, color=lightgray];
|
|
166
|
-
|
|
164
|
+
DOT
|
|
167
165
|
feature_files.each_with_index do |ff, i|
|
|
168
166
|
draw_node(anIO, ff, i) if ff.feature.anonymous?
|
|
169
167
|
end
|
|
170
|
-
|
|
171
|
-
anIO.puts <<-
|
|
168
|
+
|
|
169
|
+
anIO.puts <<-DOT
|
|
172
170
|
label = "Isolated features";
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
subgraph dependencies {
|
|
176
174
|
node [shape = box, fillcolor = none];
|
|
177
|
-
|
|
178
|
-
feature_files.each_with_index do |ff, i|
|
|
175
|
+
DOT
|
|
176
|
+
feature_files.each_with_index do |ff, i|
|
|
179
177
|
draw_node(anIO, ff, i) unless ff.feature.anonymous?
|
|
180
178
|
end
|
|
181
|
-
anIO.puts <<-
|
|
179
|
+
anIO.puts <<-DOT
|
|
182
180
|
label = "Dependencies";
|
|
183
181
|
}
|
|
184
182
|
|
|
185
183
|
// The edges represent dependencies
|
|
186
|
-
|
|
184
|
+
DOT
|
|
187
185
|
dependencies.each { |a_dep| draw_edge(anIO, a_dep) }
|
|
188
186
|
end
|
|
189
|
-
|
|
187
|
+
|
|
190
188
|
# Output the closing part of the graph drawing
|
|
191
189
|
def emit_trailing(anIO)
|
|
192
190
|
anIO.puts '} // End of graph'
|
|
193
191
|
end
|
|
194
|
-
|
|
192
|
+
|
|
195
193
|
# Draw a refinement node in DOT format
|
|
196
194
|
def draw_node(anIO, aFeatureFile, anIndex)
|
|
197
195
|
basename = File.basename(aFeatureFile.filepath, '.feature')
|
|
@@ -203,7 +201,7 @@ EOS
|
|
|
203
201
|
end
|
|
204
202
|
anIO.puts %Q( node_#{anIndex} [label = "#{basename}#{id_suffix}"];)
|
|
205
203
|
end
|
|
206
|
-
|
|
204
|
+
|
|
207
205
|
# Draw an edge between feature files having dependencies.
|
|
208
206
|
def draw_edge(anIO, aDependency)
|
|
209
207
|
source_id = feature_files.find_index(aDependency.dependee)
|
|
@@ -216,13 +214,12 @@ EOS
|
|
|
216
214
|
end
|
|
217
215
|
end
|
|
218
216
|
|
|
219
|
-
|
|
220
217
|
def generate_rake_tasks(rakefile, theProjDir)
|
|
221
218
|
puts " #{rakefile}"
|
|
222
219
|
grandparent_path = Pathname.new(File.dirname(__FILE__)).parent.parent
|
|
223
220
|
template_source = File.read(grandparent_path + './templates/rake.erb')
|
|
224
221
|
|
|
225
|
-
# Create one template engine instance
|
|
222
|
+
# Create one template engine instance
|
|
226
223
|
engine = ERB.new(template_source)
|
|
227
224
|
|
|
228
225
|
source_dir = File.absolute_path(Dir.getwd)
|
|
@@ -245,7 +242,7 @@ EOS
|
|
|
245
242
|
end
|
|
246
243
|
|
|
247
244
|
# Build the mapping: feature identifier => feature
|
|
248
|
-
def id2features
|
|
245
|
+
def id2features
|
|
249
246
|
mapping = feature_files.each_with_object({}) do |file, mp|
|
|
250
247
|
feature_id = file.feature.identifier
|
|
251
248
|
mp[feature_id] = file unless feature_id.nil?
|
|
@@ -253,14 +250,14 @@ EOS
|
|
|
253
250
|
|
|
254
251
|
return mapping
|
|
255
252
|
end
|
|
256
|
-
|
|
253
|
+
|
|
257
254
|
# Given a feature identifier => feature mapping,
|
|
258
255
|
# resolve the dependency tags; that is,
|
|
259
256
|
# Establish links between a feature file object and its
|
|
260
257
|
# dependent feature file objects.
|
|
261
258
|
def resolve_dependencies(aMapping)
|
|
262
259
|
@dependencies = []
|
|
263
|
-
|
|
260
|
+
|
|
264
261
|
feature_files.each do |feature_file|
|
|
265
262
|
feature = feature_file.feature
|
|
266
263
|
its_id = feature.identifier
|
|
@@ -268,22 +265,22 @@ EOS
|
|
|
268
265
|
# Complain when self dependency detected
|
|
269
266
|
if dep_tags.include?(its_id)
|
|
270
267
|
msg = "Feature with identifier #{its_id} depends on itself!"
|
|
271
|
-
|
|
268
|
+
raise StandardError, msg
|
|
272
269
|
end
|
|
273
|
-
|
|
270
|
+
|
|
274
271
|
# Complain when dependency tag refers to an unknown feature
|
|
275
272
|
dependents = dep_tags.map do |a_tag|
|
|
276
273
|
unless aMapping.include?(a_tag)
|
|
277
274
|
msg_p1 = "Feature with identifier '#{its_id}'"
|
|
278
275
|
msg_p2 = " depends on unknown feature '#{a_tag}'"
|
|
279
|
-
|
|
276
|
+
raise StandardError, msg_p1 + msg_p2
|
|
280
277
|
end
|
|
281
278
|
aMapping[a_tag]
|
|
282
279
|
end
|
|
283
|
-
|
|
280
|
+
|
|
284
281
|
@dependencies << FeatureDependencies.new(feature_file, dependents)
|
|
285
282
|
end
|
|
286
|
-
|
|
283
|
+
|
|
287
284
|
return @dependencies
|
|
288
285
|
end
|
|
289
286
|
end # class
|
data/lib/cukedep/feature-rep.rb
CHANGED
|
@@ -5,39 +5,37 @@ module Cukedep # This module is used as a namespace
|
|
|
5
5
|
class FeatureRep
|
|
6
6
|
# Constant that specifies how feature identifier tags should begin
|
|
7
7
|
FeatureIdPrefix = /^feature:/
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
# Constant that specifies how dependency tags should begin
|
|
10
10
|
DependencyPrefix = /^depends_on:/
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
# The sorted list of all tags of the feature.
|
|
13
13
|
# The @ prefix is stripped from each tag text.
|
|
14
14
|
attr_reader(:tags)
|
|
15
|
-
|
|
16
|
-
# The identifier of the feature.
|
|
15
|
+
|
|
16
|
+
# The identifier of the feature.
|
|
17
17
|
# It comes from a tag with the following syntax '@feature:' + identifier.
|
|
18
18
|
# Note that the @feature: prefix is removed.
|
|
19
19
|
attr_reader(:identifier)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
# theTags the tags objects from the Gherkin parser
|
|
22
|
+
# @param theTags [Array<String>] the tags objects from the Gherkin parser
|
|
23
23
|
def initialize(theTags)
|
|
24
24
|
# Strip first character of tag literal.
|
|
25
25
|
@tags = theTags.map { |t| t[1..-1] }
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
@identifier = tags.find { |tg| tg =~ FeatureIdPrefix }
|
|
28
28
|
@identifier = @identifier.sub(FeatureIdPrefix, '') unless identifier.nil?
|
|
29
29
|
end
|
|
30
|
-
|
|
31
|
-
public
|
|
32
30
|
|
|
33
31
|
# The list of all feature identifiers retrieved from the dependency tags
|
|
34
|
-
def dependency_tags
|
|
32
|
+
def dependency_tags
|
|
35
33
|
dep_tags = tags.select { |t| t =~ DependencyPrefix }
|
|
36
34
|
return dep_tags.map { |t| t.sub(DependencyPrefix, '') }
|
|
37
35
|
end
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
# Return true iff the identifier of the feature is nil.
|
|
40
|
-
def anonymous?
|
|
38
|
+
def anonymous?
|
|
41
39
|
return identifier.nil?
|
|
42
40
|
end
|
|
43
41
|
end # class
|
data/lib/cukedep/file-action.rb
CHANGED
|
@@ -5,23 +5,22 @@ require 'fileutils'
|
|
|
5
5
|
|
|
6
6
|
module Cukedep # This module is used as a namespace
|
|
7
7
|
class FileAction
|
|
8
|
-
|
|
8
|
+
attr_accessor(:patterns)
|
|
9
9
|
attr_reader(:delta)
|
|
10
10
|
|
|
11
11
|
# Constructor.
|
|
12
12
|
# [thePatterns] An array of file patterns.
|
|
13
13
|
def initialize(thePatterns, aDelta = nil)
|
|
14
14
|
@patterns = validate_file_patterns(thePatterns)
|
|
15
|
-
@delta
|
|
15
|
+
@delta = validate_delta(aDelta)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
# Datavalue semantic: FileActions don't have identity
|
|
20
19
|
def ==(other)
|
|
21
20
|
return true if object_id == other.object_id
|
|
22
21
|
return false if self.class != other.class
|
|
23
22
|
|
|
24
|
-
attrs = [
|
|
23
|
+
attrs = %I[patterns delta]
|
|
25
24
|
equality = true
|
|
26
25
|
|
|
27
26
|
attrs.each do |accessor|
|
|
@@ -32,15 +31,14 @@ module Cukedep # This module is used as a namespace
|
|
|
32
31
|
return equality
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
|
|
36
34
|
protected
|
|
37
35
|
|
|
38
36
|
def validate_file_patterns(filePatterns)
|
|
39
37
|
err_msg = 'Expecting a list of file patterns'
|
|
40
|
-
|
|
41
|
-
filePatterns.each do |
|
|
42
|
-
err_msg = "Invalid value in list of file patterns: #{
|
|
43
|
-
|
|
38
|
+
raise StandardError, err_msg unless filePatterns.is_a?(Array)
|
|
39
|
+
filePatterns.each do |file_patt|
|
|
40
|
+
err_msg = "Invalid value in list of file patterns: #{file_patt}"
|
|
41
|
+
raise StandardError, err_msg unless file_patt.is_a?(String)
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
return filePatterns
|
|
@@ -52,7 +50,7 @@ module Cukedep # This module is used as a namespace
|
|
|
52
50
|
when String
|
|
53
51
|
validated = aDelta.empty? ? nil : aDelta
|
|
54
52
|
else
|
|
55
|
-
|
|
53
|
+
raise StandardError, "Invalid relative path #{aDelta}"
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
return validated
|
|
@@ -74,7 +72,6 @@ module Cukedep # This module is used as a namespace
|
|
|
74
72
|
end # class
|
|
75
73
|
|
|
76
74
|
|
|
77
|
-
|
|
78
75
|
# A delete action object has for purpose to
|
|
79
76
|
# delete files matching one of its file patterns.
|
|
80
77
|
# These file are deleted from (a subdir of) a given 'target' directory.
|
|
@@ -87,7 +84,7 @@ module Cukedep # This module is used as a namespace
|
|
|
87
84
|
|
|
88
85
|
def run!(targetDir)
|
|
89
86
|
return if patterns.empty?
|
|
90
|
-
orig_dir = Dir.getwd
|
|
87
|
+
orig_dir = Dir.getwd # Store current work directory
|
|
91
88
|
# pp orig_dir
|
|
92
89
|
|
|
93
90
|
begin
|
|
@@ -109,7 +106,6 @@ module Cukedep # This module is used as a namespace
|
|
|
109
106
|
end # class
|
|
110
107
|
|
|
111
108
|
|
|
112
|
-
|
|
113
109
|
# A copy action object has for purpose to
|
|
114
110
|
# copy files matching one of its file patterns.
|
|
115
111
|
# These file are copied from a given 'source' directory
|
|
@@ -161,16 +157,14 @@ module Cukedep # This module is used as a namespace
|
|
|
161
157
|
theActionSettings[:copy_subdir])
|
|
162
158
|
end
|
|
163
159
|
|
|
164
|
-
|
|
165
160
|
def ==(other)
|
|
166
161
|
return true if object_id == other.object_id
|
|
167
162
|
|
|
168
163
|
return (save_action == other.save_action) &&
|
|
169
|
-
|
|
170
|
-
|
|
164
|
+
(delete_action == other.delete_action) &&
|
|
165
|
+
(copy_action == other.copy_action)
|
|
171
166
|
end
|
|
172
167
|
|
|
173
|
-
|
|
174
168
|
# Launch the file actions in sequence.
|
|
175
169
|
def run!(currentDir, projectDir)
|
|
176
170
|
save_action.run!(projectDir, currentDir)
|
|
@@ -178,7 +172,6 @@ module Cukedep # This module is used as a namespace
|
|
|
178
172
|
copy_action.run!(currentDir, projectDir)
|
|
179
173
|
end
|
|
180
174
|
|
|
181
|
-
|
|
182
175
|
# Retrieve the 'built-in' action triplet associated with the given event.
|
|
183
176
|
# Return nil if no triplet was found for the event.
|
|
184
177
|
def self.builtin(anEvent)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# File: gherkin-facade.rb
|
|
2
|
+
require 'gherkin/parser'
|
|
2
3
|
|
|
3
|
-
require 'gherkin'
|
|
4
|
-
require 'gherkin/lexer/encoding'
|
|
5
4
|
|
|
6
5
|
module Cukedep # This module is used as a namespace
|
|
7
6
|
# Facade design pattern: A facade is an object that provides
|
|
@@ -16,7 +15,6 @@ module Cukedep # This module is used as a namespace
|
|
|
16
15
|
# as expected by the mode argument of the IO#new method
|
|
17
16
|
attr_reader(:external_encoding)
|
|
18
17
|
|
|
19
|
-
|
|
20
18
|
def initialize(isVerbose, anExternalEncoding)
|
|
21
19
|
@verbose = isVerbose
|
|
22
20
|
@external_encoding = anExternalEncoding
|
|
@@ -27,25 +25,32 @@ module Cukedep # This module is used as a namespace
|
|
|
27
25
|
# Parse events are sent to the passed listener object.
|
|
28
26
|
def parse_features(aListener, file_patterns)
|
|
29
27
|
# Create a Gherkin parser
|
|
30
|
-
parser = Gherkin::Parser
|
|
28
|
+
parser = Gherkin::Parser.new
|
|
31
29
|
|
|
32
30
|
puts "\nParsing:" if verbose
|
|
33
31
|
# List all .feature files in work directory that match the pattern
|
|
34
32
|
filenames = []
|
|
35
33
|
file_patterns.each { |patt| filenames.concat(Dir.glob(patt)) }
|
|
36
|
-
|
|
37
34
|
# Parse them
|
|
38
35
|
filenames.each do |fname|
|
|
39
36
|
puts " #{fname}" if verbose
|
|
40
37
|
# To prevent encoding issue, open the file
|
|
41
38
|
# with an explicit external encoding
|
|
42
39
|
File.open(fname, "r:#{external_encoding}") do |f|
|
|
43
|
-
parser.parse(f.read
|
|
40
|
+
raw = parser.parse(f.read)
|
|
41
|
+
parse_raw_feature(raw[:feature], fname, aListener) if raw[:feature]
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
return aListener
|
|
48
46
|
end
|
|
47
|
+
|
|
48
|
+
def parse_raw_feature(raw_feature, file_name, listener)
|
|
49
|
+
listener.uri(file_name)
|
|
50
|
+
raw_tags = raw_feature[:tags]
|
|
51
|
+
tag_names = raw_tags.map { |raw_tag| raw_tag[:name] }
|
|
52
|
+
listener.feature_tags(tag_names)
|
|
53
|
+
end
|
|
49
54
|
end # class
|
|
50
55
|
end # module
|
|
51
56
|
|
|
@@ -4,15 +4,15 @@ require_relative 'feature-rep'
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
module Cukedep # This module is used as a namespace
|
|
7
|
-
class FeatureFileRep
|
|
7
|
+
class FeatureFileRep
|
|
8
8
|
attr_reader(:filepath)
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
attr_accessor(:feature)
|
|
10
|
+
|
|
11
11
|
def initialize(aFilepath)
|
|
12
12
|
@filepath = aFilepath
|
|
13
13
|
end
|
|
14
|
-
|
|
15
|
-
def basename
|
|
14
|
+
|
|
15
|
+
def basename
|
|
16
16
|
File.basename(filepath)
|
|
17
17
|
end
|
|
18
18
|
end # class
|
|
@@ -27,12 +27,13 @@ module Cukedep # This module is used as a namespace
|
|
|
27
27
|
attr_reader(:feature_files)
|
|
28
28
|
|
|
29
29
|
# Internal representation of the feature being parsed
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
attr_accessor(:current_feature)
|
|
31
|
+
|
|
32
|
+
# Constructor
|
|
33
|
+
def initialize
|
|
33
34
|
@feature_files = []
|
|
34
35
|
end
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
######################################
|
|
37
38
|
# Event listening methods
|
|
38
39
|
######################################
|
|
@@ -45,44 +46,13 @@ module Cukedep # This module is used as a namespace
|
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
# aFeature is a Gherkin::Formatter::Model::Feature instance
|
|
48
|
-
def
|
|
49
|
-
tag_names = aFeature.tags.map(&:name)
|
|
49
|
+
def feature_tags(tag_names)
|
|
50
50
|
@current_feature = feature_files.last.feature = FeatureRep.new(tag_names)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
# aBackground is a Gherkin::Formatter::Model::Background instance
|
|
54
|
-
def background(_aBackground)
|
|
55
|
-
; # Do nothing
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# aScenario is a Gherkin::Formatter::Model::Scenario instance
|
|
59
|
-
def scenario(_aScenario)
|
|
60
|
-
; # Do nothing
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# aScenarioOutline is a Gherkin::Formatter::Model::ScenarioOutline instance
|
|
64
|
-
def scenario_outline(_aScenarioOutline)
|
|
65
|
-
; # Do nothing
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# theExamples is a Gherkin::Formatter::Model::Examples instance
|
|
69
|
-
def examples(_theExamples)
|
|
70
|
-
; # Do nothing
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# aStep is a Gherkin::Formatter::Model::Step instance
|
|
74
|
-
def step(_aStep)
|
|
75
|
-
; # Do nothing
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# End of feature file notification.
|
|
79
|
-
def eof()
|
|
80
|
-
; # Do nothing
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
|
|
84
53
|
# Catch all method
|
|
85
54
|
def method_missing(message, *_args)
|
|
55
|
+
puts caller(1, 5).join("\n")
|
|
86
56
|
puts "Method #{message} is not implemented (yet)."
|
|
87
57
|
end
|
|
88
58
|
end # class
|