Module: Sauerkraut

Defined in:
lib/sauerkraut.rb,
lib/sauerkraut/version.rb

Constant Summary collapse

VERSION =
"0.0.5"

Class Method Summary collapse

Class Method Details

.array_trim(a) ⇒ Object



102
103
104
105
106
# File 'lib/sauerkraut.rb', line 102

def self.array_trim(a)
  while (a.last.nil? or a.last.chomp == "")
    a.pop
  end
end

.colorize_output(output) ⇒ Object



120
121
122
123
124
125
126
127
128
# File 'lib/sauerkraut.rb', line 120

def self.colorize_output(output)
  output.map! do |line|
    if line =~ /^#/
      line.chomp.cyan
    else
      line
    end
  end
end

.display_helpObject



11
12
13
# File 'lib/sauerkraut.rb', line 11

def self.display_help
  puts @banner
end

.enblank(var) ⇒ Object



98
99
100
# File 'lib/sauerkraut.rb', line 98

def self.enblank(var)
  ""
end

.encomment(var) ⇒ Object



94
95
96
# File 'lib/sauerkraut.rb', line 94

def self.encomment(var)
  "# #{var}"
end

.enquote(var) ⇒ Object



108
109
110
# File 'lib/sauerkraut.rb', line 108

def self.enquote(var)
  "\"#{var}\""
end

.exit_with_help(message) ⇒ Object



15
16
17
18
19
20
# File 'lib/sauerkraut.rb', line 15

def self.exit_with_help(message)
  display_help
  puts message.red
  puts "\t(you specified " + "#{@args.join(" ")}".yellow + ")"
  exit
end

.feature_file_exists?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/sauerkraut.rb', line 48

def self.feature_file_exists?
  File.file? @args.first.split(":").first
end

.get_output_fileObject



22
23
24
# File 'lib/sauerkraut.rb', line 22

def self.get_output_file
  @args[(@args.find_index "-o") + 1]
end

.has_block_variables?(line) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/sauerkraut.rb', line 90

def self.has_block_variables?(line)
  line.reverse.strip =~ /^\|/
end

.highlight_bvar!(bvar, line) ⇒ Object



131
132
133
134
# File 'lib/sauerkraut.rb', line 131

def self.highlight_bvar!(bvar, line)
  #line.gsub!(/([\W])(#{bvar})(\W)/) {|m| "#{$1}" + "#{bvar}".magenta + "#{$3}"}
  line.gsub!(/([\[\(-\/=\\& :\+\-])(#{bvar})([^\w]|\z)/) {|m| "#{$1}" + "#{bvar}".magenta + "#{$3}"}
end

.invalid_feature_file?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/sauerkraut.rb', line 44

def self.invalid_feature_file?
  !(@args[0] =~ /\.feature(:\d+)?(:\d+)?/)
end

.invalid_range?Boolean

Returns:

  • (Boolean)


56
57
58
59
60
61
# File 'lib/sauerkraut.rb', line 56

def self.invalid_range?
  (
    (@args[0] =~ /\.feature:\d+:/) &&
    !(@args[0] =~ /\.feature(:\d+)(:\d+)/)
  )
end

.is_def?(line) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/sauerkraut.rb', line 76

def self.is_def?(line)
  line.strip =~ /^def /
end

.is_line_scenario?(line) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/sauerkraut.rb', line 67

def self.is_line_scenario?(line)
  line =~ /Scenario/
end

.is_line_step_def?(line) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
# File 'lib/sauerkraut.rb', line 86

def self.is_line_step_def?(line)
  line.strip =~ /^(Given|When|Then|And|But)/
end

.is_step_def?(line) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
# File 'lib/sauerkraut.rb', line 72

def self.is_step_def?(line)
  line.strip =~ /^(Given|When|Then|And|But)/
end

.no_feature_file?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/sauerkraut.rb', line 40

def self.no_feature_file?
  @args.count == 0
end

.no_line_number?Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/sauerkraut.rb', line 52

def self.no_line_number?
  !(@args[0].include? ":")
end

.no_output_file_specified?Boolean

Returns:

  • (Boolean)


33
34
35
36
37
38
# File 'lib/sauerkraut.rb', line 33

def self.no_output_file_specified?
  (
    (@args.include? "-o") &&
    (@args.last == "-o")
  )
end

.output_file?Boolean

Returns:

  • (Boolean)


26
27
28
29
30
31
# File 'lib/sauerkraut.rb', line 26

def self.output_file?
  (
    (@args.include? "-o") &&
    (@args.last != "-o")
  )
end

.range?Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/sauerkraut.rb', line 63

def self.range?
  @args[0] =~ /\.feature(:\d+)(:\d+)/
end

.remove_first_word(line) ⇒ Object



80
81
82
83
84
# File 'lib/sauerkraut.rb', line 80

def self.remove_first_word(line)
  a=line.split(" ")
  a.shift
  return a.join(" ")
end

.run(args) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/sauerkraut.rb', line 136

def self.run(args)
  @args = args

  # PARSING
  exit_with_help("must specify a feature file") if no_feature_file?
  exit_with_help("must specify a valid feature file") if invalid_feature_file?
  exit_with_help("must specify an existing feature file") if !feature_file_exists?
  exit_with_help("specify range with :N:M") if invalid_range?

  exit_with_help("must specify an output file") if no_output_file_specified?
  @options[:output] = get_output_file if output_file?


  # get feature file
  if no_line_number?
    feature_file = @args[0]
    line_number = 0
    range_number = nil
  else
    feature_file = @args[0].split(":").first
    line_number = @args[0].split(":")[1].to_i
    range_number = range? ? (@args[0].split(":").last.to_i) : nil
  end
  feature_file_array = IO.readlines feature_file




  # if no lines, get entire file
  if no_line_number?
    steps = []
    feature_file_array[0..-1].each do |line|
      next if is_line_scenario?(line)
      steps << line.strip
    end
  else


    # if one line, get scenario
    if !range?
      exit_with_help("specify a line number that is a scenario") if !is_line_scenario?(feature_file_array[line_number-1])
      scenario = feature_file_array[line_number-1]
      steps = []
      feature_file_array[line_number..-1].each do |line|
        break if is_line_scenario?(line)
        steps << line.strip
      end
    end

    # if two lines, gather given when thens
    if range?
      steps = []
      feature_file_array[line_number..range_number].each do |line|
        next if is_line_scenario?(line)
        steps << line.strip
      end
    end

  end


  # get regexes
  pattern = /(?:Given|When|Then|And|But).+\/(.*)\/.+/
  pattern2 = /\/(.*)\//
  all_the_regexes = []
  stepfiles = File.join("**", "*steps.rb")
  Dir.glob(stepfiles) do |file|
    linenumber = 1
    File.open(file).each do |line|
      if line =~ pattern then
        regexstring = (line.scan pattern2).flatten.first
        regex = Regexp.new(regexstring)
        all_the_regexes << [regex, file, linenumber]
      end
      linenumber += 1
    end
  end


  # use stepfinder to find sources
  # compute block variables
  headlands = []
  steps.each do |step|
    next if !is_step_def?(step)
    all_the_regexes.each do |regexarr|
      if remove_first_word(step) =~ regexarr.first
        headlands << {
          :step => step,
          :sourcefile => regexarr[1],
          :lineno => regexarr[2],
          :block_vars => remove_first_word(step).scan(regexarr[0]).flatten,
        }
      end
    end
  end



  # get each code section from source
  headlands.each do |headland|

    # get code block
    patch = IO.readlines headland[:sourcefile]
    headland[:step_def] = patch[ headland[:lineno]-1]

    # get block variables
    if has_block_variables?( patch[headland[:lineno]-1])
      headland[:block_var_names] = patch[ headland[:lineno]-1].reverse.scan(/\|(.*?)\|/).flatten.first.reverse.split(",").map{|v| v.strip}
    else
      headland[:block_var_names] = nil
    end

    #step through until the next given when then,
    #  collecting the lines
    cabbage = []
    patch[ headland[:lineno]..-1].each do |line|
      break if (is_step_def?(line) || is_def?(line))
      cabbage << line
    end

    # comment bottoms of code sections
    end_reached = false
    cabbage.reverse!
    cabbage.map! do |line|
      break if end_reached
      if line =~ /end/
        end_reached = true
        #encomment(line)
        enblank(line)
      else
        line
      end
    end
    cabbage.reverse!
    array_trim(cabbage)
    headland[:cabbage] = cabbage

  end




  # display block variables
  # display cabbages

  output = []
  headlands.each do |headland|
    output <<  "#{encomment (headland[:sourcefile] + ":" + headland[:lineno].to_s)}"
    output <<  "#{encomment headland[:step_def]}"

    if headland[:block_var_names]
      output << "# block vars ------"
      headland[:block_var_names].each_with_index do |name,i|
        bvar = "#{name}"
        bvar = bvar.magenta if !@options[:output]
        line = bvar + " = #{enquote headland[ :block_vars][i]}"
        output << line
      end
      output << "# -----------------"
    end

    # search for block vars and highlight
    if !@options[:output] && headland[:block_var_names]
      cabbage = headland[:cabbage].map! do |line|
        headland[:block_var_names].each do |bvar|
          if line =~ /\W#{bvar}\W/
            highlight_bvar!(bvar, line)
          end
        end
        line
      end
    else
      cabbage = headland[:cabbage]
    end


    output += cabbage

    output <<  "\n"
  end




  # output to file
  if @options[:output]
    write_array_to_file(output, @options[:output])
    puts "output written to #{@options[:output]}"
  else
    puts colorize_output output
    #puts output
  end

end

.write_array_to_file(array, file) ⇒ Object



113
114
115
116
117
# File 'lib/sauerkraut.rb', line 113

def self.write_array_to_file(array, file)
  f = File.open(file,'w')
  array.each { |line| f.write "#{line.chomp}\n" }
  f.close
end