seeing_is_believing 2.2.0 → 3.0.0.beta.1

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Changelog.md +33 -0
  4. data/bin/seeing_is_believing +1 -1
  5. data/features/errors.feature +3 -3
  6. data/features/examples.feature +6 -6
  7. data/features/flags.feature +51 -196
  8. data/features/regression.feature +12 -3
  9. data/features/safe.feature +33 -0
  10. data/features/support/env.rb +20 -0
  11. data/features/xmpfilter-style.feature +156 -0
  12. data/lib/seeing_is_believing.rb +17 -35
  13. data/lib/seeing_is_believing/binary.rb +81 -176
  14. data/lib/seeing_is_believing/binary/align_chunk.rb +5 -7
  15. data/lib/seeing_is_believing/binary/align_file.rb +4 -5
  16. data/lib/seeing_is_believing/binary/align_line.rb +4 -4
  17. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +60 -0
  18. data/lib/seeing_is_believing/binary/annotate_every_line.rb +64 -0
  19. data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +133 -0
  20. data/lib/seeing_is_believing/binary/comment_formatter.rb +19 -5
  21. data/lib/seeing_is_believing/binary/comment_lines.rb +1 -1
  22. data/lib/seeing_is_believing/binary/commentable_lines.rb +1 -1
  23. data/lib/seeing_is_believing/binary/interpret_flags.rb +149 -0
  24. data/lib/seeing_is_believing/binary/parse_args.rb +96 -104
  25. data/lib/seeing_is_believing/binary/remove_annotations.rb +95 -0
  26. data/lib/seeing_is_believing/binary/rewrite_comments.rb +8 -30
  27. data/lib/seeing_is_believing/code.rb +99 -0
  28. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +27 -19
  29. data/lib/seeing_is_believing/evaluate_with_eval_in.rb +27 -0
  30. data/lib/seeing_is_believing/event_stream/consumer.rb +111 -0
  31. data/lib/seeing_is_believing/event_stream/events.rb +16 -0
  32. data/lib/seeing_is_believing/event_stream/producer.rb +106 -0
  33. data/lib/seeing_is_believing/event_stream/update_result.rb +21 -0
  34. data/lib/seeing_is_believing/inspect_expressions.rb +24 -0
  35. data/lib/seeing_is_believing/parser_helpers.rb +1 -11
  36. data/lib/seeing_is_believing/result.rb +14 -56
  37. data/lib/seeing_is_believing/the_matrix.rb +14 -14
  38. data/lib/seeing_is_believing/version.rb +1 -1
  39. data/lib/seeing_is_believing/wrap_expressions.rb +32 -9
  40. data/seeing_is_believing.gemspec +7 -7
  41. data/spec/binary/comment_formatter_spec.rb +169 -18
  42. data/spec/binary/comment_lines_spec.rb +1 -1
  43. data/spec/binary/interpret_flags_spec.rb +307 -0
  44. data/spec/binary/parse_args_spec.rb +93 -91
  45. data/spec/binary/{clean_body_spec.rb → remove_annotations_spec.rb} +29 -22
  46. data/spec/binary/rewrite_comments_spec.rb +13 -13
  47. data/spec/code_spec.rb +49 -0
  48. data/spec/debugger_spec.rb +1 -1
  49. data/spec/evaluate_by_moving_files_spec.rb +7 -3
  50. data/spec/event_stream_spec.rb +390 -0
  51. data/spec/hard_core_ensure_spec.rb +1 -1
  52. data/spec/seeing_is_believing_spec.rb +137 -40
  53. data/spec/spec_helper.rb +3 -3
  54. data/spec/wrap_expressions_spec.rb +48 -35
  55. metadata +58 -35
  56. data/lib/seeing_is_believing/binary/add_annotations.rb +0 -144
  57. data/lib/seeing_is_believing/binary/clean_body.rb +0 -95
  58. data/lib/seeing_is_believing/has_exception.rb +0 -27
  59. data/lib/seeing_is_believing/line.rb +0 -90
  60. data/spec/line_spec.rb +0 -86
@@ -1,10 +1,10 @@
1
1
  require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
- class Binary
4
+ module Binary
5
5
  class AlignChunk
6
- def initialize(body, start_line, end_line)
7
- self.body, self.start_line, self.end_line = body, start_line, end_line
6
+ def initialize(body)
7
+ self.body = body
8
8
  end
9
9
 
10
10
  # max line length of the the chunk (newline separated sections of code exempting comments) + 2 spaces for padding
@@ -14,7 +14,7 @@ class SeeingIsBelieving
14
14
 
15
15
  private
16
16
 
17
- attr_accessor :body, :start_line, :end_line
17
+ attr_accessor :body
18
18
 
19
19
  def line_lengths
20
20
  @line_lengths ||= begin
@@ -24,9 +24,7 @@ class SeeingIsBelieving
24
24
  .sort
25
25
  .slice_before { |line_number| line_num_to_indexes[line_number].last.zero? }
26
26
  .map { |slice|
27
- max_chunk_length = 2 + slice.select { |line_num| start_line <= line_num && line_num <= end_line }
28
- .map { |line_num| line_num_to_indexes[line_num].last }
29
- .max
27
+ max_chunk_length = 2 + slice.map { |line_num| line_num_to_indexes[line_num].last }.max
30
28
  slice.map { |line_number| [line_number, max_chunk_length] }
31
29
  }
32
30
  .flatten(1)
@@ -1,12 +1,12 @@
1
1
  require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
- class Binary
4
+ module Binary
5
5
  class AlignFile
6
- attr_accessor :body, :start_line, :end_line
6
+ attr_accessor :body
7
7
 
8
- def initialize(body, start_line, end_line)
9
- self.body, self.start_line, self.end_line = body, start_line, end_line
8
+ def initialize(body)
9
+ self.body = body
10
10
  end
11
11
 
12
12
  # max line length of the lines to output (exempting comments) + 2 spaces for padding
@@ -14,7 +14,6 @@ class SeeingIsBelieving
14
14
  @max_source_line_length ||= 2 + begin
15
15
  line_num_to_indexes = CommentableLines.new(body).call # {line_number => [index_in_file, index_in_col]}
16
16
  max_value = line_num_to_indexes
17
- .select { |line_num, _| start_line <= line_num && line_num <= end_line }
18
17
  .values
19
18
  .map { |index, col| col }.max
20
19
  max_value || 0
@@ -1,10 +1,10 @@
1
1
  class SeeingIsBelieving
2
- class Binary
2
+ module Binary
3
3
  class AlignLine
4
- attr_accessor :body, :start_line, :end_line
4
+ attr_accessor :body
5
5
 
6
- def initialize(body, start_line, end_line)
7
- self.body, self.start_line, self.end_line = body, start_line, end_line
6
+ def initialize(body)
7
+ self.body = body
8
8
  end
9
9
 
10
10
  # length of the line + 2 spaces for padding
@@ -0,0 +1,60 @@
1
+ require 'seeing_is_believing/binary' # defines the markers
2
+ require 'seeing_is_believing/binary/comment_formatter'
3
+
4
+ class SeeingIsBelieving
5
+ module Binary
6
+ module AnnotateEndOfFile
7
+ extend self
8
+
9
+ # TODO: Switch options to markers
10
+ def add_stdout_stderr_and_exceptions_to(new_body, results, options)
11
+ output = stdout_ouptut_for(results, options) <<
12
+ stderr_ouptut_for(results, options) <<
13
+ exception_output_for(results, options)
14
+
15
+ # this technically could find an __END__ in a string or whatever
16
+ # going to just ignore that, though
17
+ if new_body[/^__END__$/]
18
+ new_body.sub! "\n__END__", "\n#{output}__END__"
19
+ else
20
+ new_body << "\n" unless new_body.end_with? "\n"
21
+ new_body << output
22
+ end
23
+ end
24
+
25
+ def stdout_ouptut_for(results, options)
26
+ return '' unless results.has_stdout?
27
+ output = "\n"
28
+ results.stdout.each_line do |line|
29
+ output << CommentFormatter.call(0, options[:markers][:stdout], line.chomp, options) << "\n"
30
+ end
31
+ output
32
+ end
33
+
34
+ def stderr_ouptut_for(results, options)
35
+ return '' unless results.has_stderr?
36
+ output = "\n"
37
+ results.stderr.each_line do |line|
38
+ output << CommentFormatter.call(0, options[:markers][:stderr], line.chomp, options) << "\n"
39
+ end
40
+ output
41
+ end
42
+
43
+ def exception_output_for(results, options)
44
+ return '' unless results.has_exception?
45
+ exception_marker = options[:markers][:exception]
46
+ exception = results.exception
47
+ output = "\n"
48
+ output << CommentFormatter.new(0, exception_marker, exception.class_name, options).call << "\n"
49
+ exception.message.each_line do |line|
50
+ output << CommentFormatter.new(0, exception_marker, line.chomp, options).call << "\n"
51
+ end
52
+ output << exception_marker.sub(/\s+$/, '') << "\n"
53
+ exception.backtrace.each do |line|
54
+ output << CommentFormatter.new(0, exception_marker, line.chomp, options).call << "\n"
55
+ end
56
+ output
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,64 @@
1
+ class SeeingIsBelieving
2
+ module Binary
3
+ class AnnotateEveryLine
4
+ def self.prepare_body(uncleaned_body, markers)
5
+ require 'seeing_is_believing/binary/remove_annotations'
6
+ RemoveAnnotations.call uncleaned_body, true, markers
7
+ end
8
+
9
+ def self.expression_wrapper(markers)
10
+ require 'seeing_is_believing/inspect_expressions'
11
+ InspectExpressions
12
+ end
13
+
14
+ def self.call(body, results, options)
15
+ new(body, results, options).call
16
+ end
17
+
18
+ def initialize(body, results, options={})
19
+ @options = options
20
+ @body = body
21
+ @results = results
22
+ end
23
+
24
+ def call
25
+ @new_body ||= begin
26
+ require 'seeing_is_believing/binary/comment_lines'
27
+ require 'seeing_is_believing/binary/comment_formatter'
28
+
29
+ alignment_strategy = @options[:alignment_strategy].new(@body)
30
+ exception_lineno = @results.has_exception? ? @results.exception.line_number : -1
31
+ new_body = CommentLines.call @body do |line, line_number|
32
+ options = @options.merge pad_to: alignment_strategy.line_length_for(line_number)
33
+ if exception_lineno == line_number
34
+ result = sprintf "%s: %s", @results.exception.class_name, @results.exception.message.gsub("\n", '\n')
35
+ CommentFormatter.call(line.size, exception_marker, result, options)
36
+ elsif @results[line_number].any?
37
+ result = @results[line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
38
+ CommentFormatter.call(line.size, value_marker, result, options)
39
+ else
40
+ ''
41
+ end
42
+ end
43
+
44
+ require 'seeing_is_believing/binary/annotate_end_of_file'
45
+ AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
46
+
47
+ # What's w/ this debugger? maybe this should move higher?
48
+ @options[:debugger].context "OUTPUT"
49
+ new_body
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def value_marker
56
+ @value_marker ||= @options[:markers][:value]
57
+ end
58
+
59
+ def exception_marker
60
+ @exception_marker ||= @options[:markers][:exception]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,133 @@
1
+ require 'seeing_is_believing/code'
2
+
3
+ class SeeingIsBelieving
4
+ module Binary
5
+ class AnnotateXmpfilterStyle
6
+ def self.prepare_body(uncleaned_body, markers)
7
+ # TODO: There's definitely a lot of overlap in responsibilities with invoking of parser
8
+ # and this is a conspicuous hack, since this functionality should really be provided by RemoveAnnotations
9
+ code = Code.new(uncleaned_body)
10
+ code.inline_comments
11
+ .select { |c| c.whitespace_col == 0 } # TODO: Would be nice to support indentation here
12
+ .slice_before { |c| c.text.start_with? markers[:value] }
13
+ .flat_map { |cs|
14
+ consecutives = cs.each_cons(2).take_while { |c1, c2| c1.line_number.next == c2.line_number }
15
+ cs[1, consecutives.size]
16
+ }
17
+ .select { |c| c.text.start_with? markers[:nextline] }
18
+ .each { |c|
19
+ range_with_preceding_newline = code.range_for(c.comment_range.begin_pos.pred, c.comment_range.end_pos)
20
+ code.rewriter.remove range_with_preceding_newline
21
+ }
22
+ partially_cleaned_body = code.rewriter.process
23
+
24
+ require 'seeing_is_believing/binary/remove_annotations'
25
+ RemoveAnnotations.call partially_cleaned_body, false, markers
26
+ end
27
+
28
+ def self.expression_wrapper(markers)
29
+ -> program, number_of_captures {
30
+ inspect_linenos = []
31
+ pp_linenos = []
32
+ Code.new(program).inline_comments.each do |c|
33
+ next unless c.text.start_with? markers[:value].sub(/\s+$/, '')
34
+ c.whitespace_col == 0 ? pp_linenos << c.line_number - 1
35
+ : inspect_linenos << c.line_number
36
+ end
37
+
38
+ InspectExpressions.call program,
39
+ number_of_captures,
40
+ before_all: -> {
41
+ # TODO: this is duplicated with the InspectExpressions class
42
+ number_of_captures_as_str = number_of_captures.inspect
43
+ number_of_captures_as_str = 'Float::INFINITY' if number_of_captures == Float::INFINITY
44
+ "begin; require 'pp'; $SiB.max_line_captures = #{number_of_captures_as_str}; $SiB.num_lines = #{program.lines.count}; "
45
+ },
46
+ after_each: -> line_number {
47
+ should_inspect = inspect_linenos.include?(line_number)
48
+ should_pp = pp_linenos.include?(line_number)
49
+ inspect = "$SiB.record_result(:inspect, #{line_number}, v)"
50
+ pp = "$SiB.record_result(:pp, #{line_number}, v) { PP.pp v, '', 74 }" # TODO: Is 74 the right value? Prob not, I think it's 80(default width) - 1(comment width) - 5(" => {"), but if I allow indented `# => `, then that would need to be less than 74 (idk if I actually do this or not, though :P)
51
+
52
+ if should_inspect && should_pp then ").tap { |v| #{inspect}; #{pp} }"
53
+ elsif should_inspect then ").tap { |v| #{inspect} }"
54
+ elsif should_pp then ").tap { |v| #{pp} }"
55
+ else ")"
56
+ end
57
+ }
58
+ }
59
+ end
60
+
61
+ def self.call(body, results, options)
62
+ new(body, results, options).call
63
+ end
64
+
65
+ def initialize(body, results, options={})
66
+ @options = options
67
+ @body = body
68
+ @results = results
69
+ end
70
+
71
+ # TODO: I think that this should respect the alignment strategy
72
+ # and we should just add a new alignment strategy for default xmpfilter style
73
+ def call
74
+ @new_body ||= begin
75
+ # TODO: doesn't currently realign output markers, do we want to do that?
76
+ require 'seeing_is_believing/binary' # defines the markers
77
+ require 'seeing_is_believing/binary/rewrite_comments'
78
+ require 'seeing_is_believing/binary/comment_formatter'
79
+ exception_lineno = @results.has_exception? ? @results.exception.line_number : -1
80
+ new_body = RewriteComments.call @body do |comment|
81
+ if !comment.text[value_regex]
82
+ [comment.whitespace, comment.text]
83
+ elsif comment.whitespace_col == 0
84
+ # TODO: check that having multiple mult-line output values here looks good (e.g. avdi's example in a loop)
85
+ result = @results[comment.line_number-1, :pp].map { |result| result.chomp }.join(', ')
86
+ comment_lines = result.each_line.map.with_index do |comment_line, result_offest|
87
+ if result_offest == 0
88
+ CommentFormatter.call(comment.whitespace_col, value_marker, comment_line.chomp, @options)
89
+ else
90
+ CommentFormatter.call(comment.whitespace_col, nextline_marker, comment_line.chomp, @options)
91
+ end
92
+ end
93
+ [comment.whitespace, comment_lines.join("\n")]
94
+ else
95
+ result = @results[comment.line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
96
+ [comment.whitespace, CommentFormatter.call(comment.text_col, value_marker, result, @options)]
97
+ end
98
+ end
99
+
100
+ # if exception_lineno == line_number
101
+ # if comment.text[value_regex] # has exception and comment
102
+ # # '# => # ~> exception...'
103
+ # [comment.whitespace, comment.text]
104
+ # else # exception, no comment
105
+ # # NORMAL EXCEPTION
106
+ # # result = @results[line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
107
+ # # CommentFormatter.call(line.size, value_marker, result, options)
108
+ # # [comment.whitespace, comment.text]
109
+ # end
110
+
111
+ require 'seeing_is_believing/binary/annotate_end_of_file'
112
+ AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
113
+
114
+ # What's w/ this debugger? maybe this should move higher?
115
+ @options[:debugger].context "OUTPUT"
116
+ new_body
117
+ end
118
+ end
119
+
120
+ def value_marker
121
+ @value_marker ||= @options[:markers][:value]
122
+ end
123
+
124
+ def nextline_marker
125
+ @xnextline_marker ||= @options[:markers][:nextline]
126
+ end
127
+
128
+ def value_regex
129
+ @value_regex ||= /\A#{value_marker.sub(/\s+$/, '')}/
130
+ end
131
+ end
132
+ end
133
+ end
@@ -1,7 +1,7 @@
1
1
  class SeeingIsBelieving
2
- class Binary
3
- # not sure I like this name, it formats comments that
4
- # show results e.g. "# => [1, 2, 3]"
2
+ module Binary
3
+ # not sure I like this name, it formats comments that show results
4
+ # e.g. "# => [1, 2, 3]"
5
5
  #
6
6
  # line_length is the length of the line this comment is being appended to
7
7
  #
@@ -15,13 +15,14 @@ class SeeingIsBelieving
15
15
  def initialize(line_length, separator, result, options)
16
16
  self.line_length = line_length
17
17
  self.separator = separator
18
- self.result = result.gsub "\n", '\n'
18
+ self.result = result
19
19
  self.options = options
20
20
  end
21
21
 
22
22
  def call
23
23
  @formatted ||= begin
24
- formatted = truncate "#{separator}#{result}", max_result_length
24
+ formatted = escape_non_printable result, chars_not_to_escape
25
+ formatted = truncate "#{separator}#{formatted}", max_result_length
25
26
  formatted = "#{' '*padding_length}#{formatted}"
26
27
  formatted = truncate formatted, max_line_length
27
28
  formatted = '' unless formatted.sub(/^ */, '').start_with? separator
@@ -57,6 +58,19 @@ class SeeingIsBelieving
57
58
  def ellipsify(string)
58
59
  string.sub(/.{0,3}$/) { |last_chars| '.' * last_chars.size }
59
60
  end
61
+
62
+ def chars_not_to_escape
63
+ options.fetch :dont_escape, []
64
+ end
65
+
66
+ def escape_non_printable(str, omissions)
67
+ str.each_char
68
+ .map { |char|
69
+ next char if 0x20 <= char.ord # above this has a printable representation
70
+ next char if omissions.include? char
71
+ char.inspect[1...-1]
72
+ }.join('')
73
+ end
60
74
  end
61
75
  end
62
76
  end
@@ -2,7 +2,7 @@ require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
4
  # spec/binary/comment_lines_spec.rb
5
- class Binary
5
+ module Binary
6
6
 
7
7
  # takes a body and a block
8
8
  # passes the block the line
@@ -1,7 +1,7 @@
1
1
  require 'seeing_is_believing/parser_helpers'
2
2
 
3
3
  class SeeingIsBelieving
4
- class Binary
4
+ module Binary
5
5
 
6
6
  # could possibly be sped up by just reflecting on the tokens instead of the whole ast
7
7
  #
@@ -0,0 +1,149 @@
1
+ # Debugger initialization happens here
2
+ require 'seeing_is_believing/debugger'
3
+
4
+ # Alignment decision happens here
5
+ require 'seeing_is_believing/binary/align_file'
6
+ require 'seeing_is_believing/binary/align_line'
7
+ require 'seeing_is_believing/binary/align_chunk'
8
+
9
+ # Evaluator decision happens here
10
+ require 'seeing_is_believing/evaluate_by_moving_files'
11
+ require 'seeing_is_believing/evaluate_with_eval_in'
12
+
13
+ # Annotator decision happens here
14
+ require 'seeing_is_believing/binary/annotate_every_line'
15
+ require 'seeing_is_believing/binary/annotate_xmpfilter_style'
16
+
17
+ class SeeingIsBelieving
18
+ module Binary
19
+ class InterpretFlags
20
+ def self.attr_predicate(name)
21
+ define_method("#{name}?") { predicates.fetch name }
22
+ end
23
+ attr_predicate :print_version
24
+ attr_predicate :inherit_exit_status
25
+ attr_predicate :result_as_json
26
+ attr_predicate :print_help
27
+ attr_predicate :print_cleaned
28
+ attr_predicate :provided_filename_dne
29
+ attr_predicate :file_is_on_stdin
30
+
31
+ def self.attr_attribute(name)
32
+ define_method(name) { attributes.fetch name }
33
+ end
34
+ attr_attribute :annotator
35
+ attr_attribute :help_screen
36
+ attr_attribute :debugger
37
+ attr_attribute :markers
38
+ attr_attribute :timeout
39
+ attr_attribute :shebang
40
+ attr_attribute :filename
41
+ attr_attribute :body
42
+ attr_attribute :annotator_options
43
+ attr_attribute :prepared_body
44
+ attr_attribute :lib_options
45
+ attr_attribute :errors
46
+
47
+ def initialize(flags, stdin, stdout)
48
+ # Some simple attributes
49
+ self.attributes = {}
50
+ attributes[:errors] = flags.fetch(:errors)
51
+ attributes[:markers] = flags.fetch(:markers) # TODO: Should probably object-ify these
52
+ attributes[:timeout] = flags.fetch(:timeout) # b/c binary prints this out in the error message TODO: rename seconds_until_timeout
53
+ attributes[:shebang] = flags.fetch(:shebang) # b/c binary uses this to validate syntax atm
54
+ attributes[:filename] = flags.fetch(:filename)
55
+
56
+ # All predicates
57
+ self.predicates = {}
58
+ predicates[:print_version] = flags.fetch(:version) # TODO: rename rhs to print_version ?
59
+ predicates[:inherit_exit_status] = flags.fetch(:inherit_exit_status)
60
+ predicates[:result_as_json] = flags.fetch(:result_as_json)
61
+ predicates[:print_help] = !!flags.fetch(:help)
62
+ predicates[:print_cleaned] = flags.fetch(:clean) # TODO: Better name on rhs
63
+ predicates[:provided_filename_dne] = !!(filename && !File.exist?(filename)) # TODO: Should this just be an error in errors table?
64
+ predicates[:file_is_on_stdin] = (!filename && !flags.fetch(:program_from_args))
65
+
66
+ # Polymorphism, y'all!
67
+ attributes[:annotator] = (flags.fetch(:xmpfilter_style) ? AnnotateXmpfilterStyle : AnnotateEveryLine)
68
+ attributes[:help_screen] = flags.fetch(:help) == 'help' ? flags.fetch(:short_help_screen) : flags.fetch(:long_help_screen)
69
+ attributes[:debugger] = flags.fetch(:debug) ? Debugger.new(stream: stdout, colour: true) : Debugger.new(stream: nil)
70
+ attributes[:body] = ((print_version? || print_help?) && String.new) ||
71
+ flags.fetch(:program_from_args) ||
72
+ (file_is_on_stdin? && stdin.read) ||
73
+ (File.read filename unless provided_filename_dne?) ||
74
+ String.new
75
+
76
+ # Attributes that depend on predicates
77
+ attributes[:prepared_body] = body && annotator.prepare_body(body, markers)
78
+
79
+ # The lib's options (passed to SeeingIsBelieving.new)
80
+ attributes[:lib_options] = {
81
+ evaluate_with: (flags.fetch(:safe) ? EvaluateWithEvalIn : EvaluateByMovingFiles),
82
+ filename: (flags.fetch(:as) || filename),
83
+ ruby_executable: shebang,
84
+ stdin: (file_is_on_stdin? ? '' : stdin),
85
+ require: (['seeing_is_believing/the_matrix'] + flags.fetch(:require)), # TODO: rename requires: files_to_require, or :requires or maybe :to_require
86
+ load_path: ([File.expand_path('../../..', __FILE__)] + flags.fetch(:load_path)),
87
+ encoding: flags.fetch(:encoding),
88
+ timeout: timeout,
89
+ debugger: debugger,
90
+ number_of_captures: flags.fetch(:number_of_captures), # TODO: Rename to max_number_of_captures
91
+ record_expressions: annotator.expression_wrapper(markers), # TODO: rename to wrap_expressions
92
+ }
93
+
94
+ # The annotator's options (passed to annotator.call)
95
+ attributes[:annotator_options] = {
96
+ alignment_strategy: extract_alignment_strategy(flags.fetch(:alignment_strategy), errors),
97
+ debugger: debugger,
98
+ markers: markers,
99
+ max_line_length: flags.fetch(:max_line_length),
100
+ max_result_length: flags.fetch(:max_result_length),
101
+ }
102
+
103
+ # Some error checking
104
+ if 1 < flags.fetch(:filenames).size
105
+ errors << "Can only have one filename, but had: #{flags.fetch(:filenames).map(&:inspect).join ', '}"
106
+ elsif filename && flags.fetch(:program_from_args)
107
+ errors << "You passed the program in an argument, but have also specified the filename #{filename.inspect}"
108
+ end
109
+ end
110
+
111
+ def inspect
112
+ inspected = "#<#{self.class.name.inspect}\n"
113
+ inspected << " --PREDICATES--\n"
114
+ predicates.each do |predicate, value|
115
+ inspected << inspect_line(sprintf " %-25s %p", predicate.to_s+"?", value)
116
+ end
117
+ inspected << " --ATTRIBUTES--\n"
118
+ attributes.each do |predicate, value|
119
+ inspected << inspect_line(sprintf " %-20s %p", predicate.to_s, value)
120
+ end
121
+ inspected << ">"
122
+ inspected
123
+ end
124
+
125
+ private
126
+
127
+ attr_accessor :predicates, :attributes
128
+
129
+ def extract_alignment_strategy(strategy_name, errors)
130
+ strategies = {'file' => AlignFile, 'chunk' => AlignChunk, 'line' => AlignLine}
131
+ if strategies[strategy_name]
132
+ strategies[strategy_name]
133
+ elsif strategy_name
134
+ errors << "alignment-strategy does not know #{strategy_name}, only knows: #{strategies.keys.join(', ')}"
135
+ else
136
+ errors << "alignment-strategy expected an alignment strategy as the following argument but did not see one"
137
+ end
138
+ end
139
+
140
+ def inspect_line(line)
141
+ if line.size < 78
142
+ line << "\n"
143
+ else
144
+ line[0, 75] << "...\n"
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end