cute_print 0.3.0 → 0.4.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -1
  3. data/README.md +42 -1
  4. data/VERSION +1 -1
  5. data/cute_print.gemspec +25 -4
  6. data/features/call_chain.feature +6 -5
  7. data/features/configuring/configure_position_format.feature +1 -2
  8. data/features/inspect/core.feature +18 -0
  9. data/features/inspect/inspect_with_location.feature +1 -1
  10. data/features/support/env.rb +3 -3
  11. data/features/support/helpers/example.rb +11 -13
  12. data/features/support/helpers/example_runner.rb +24 -0
  13. data/features/support/helpers/fork_example_runner.rb +37 -0
  14. data/features/support/helpers/lib_path.rb +7 -0
  15. data/features/support/helpers/shell_example_runner.rb +26 -0
  16. data/features/support/step_definitions.rb +0 -6
  17. data/lib/cute_print/core.rb +3 -0
  18. data/lib/cute_print/core_ext.rb +2 -0
  19. data/lib/cute_print/cute_print.rb +83 -0
  20. data/lib/cute_print/format/inspect.rb +12 -0
  21. data/lib/cute_print/format/pretty_print.rb +17 -0
  22. data/lib/cute_print/format.rb +2 -0
  23. data/lib/cute_print/formatter.rb +15 -81
  24. data/lib/cute_print/inline_labeler.rb +36 -0
  25. data/lib/cute_print/labeler.rb +71 -0
  26. data/lib/cute_print/location.rb +4 -10
  27. data/lib/cute_print/location_label/filename.rb +19 -0
  28. data/lib/cute_print/location_label/path.rb +19 -0
  29. data/lib/cute_print/location_label.rb +23 -0
  30. data/lib/cute_print/mixin.rb +13 -12
  31. data/lib/cute_print/outline_labeler.rb +38 -0
  32. data/lib/cute_print/printer.rb +4 -36
  33. data/lib/cute_print/source_label.rb +26 -0
  34. data/lib/cute_print.rb +1 -25
  35. data/spec/format/inspect_spec.rb +20 -0
  36. data/spec/format/pretty_print_spec.rb +36 -0
  37. data/spec/inline_labeler_spec.rb +39 -0
  38. data/spec/labeler_spec.rb +97 -0
  39. data/spec/outline_labeler_spec.rb +37 -0
  40. metadata +24 -3
  41. data/lib/cute_print/default_printer.rb +0 -14
@@ -1,107 +1,56 @@
1
1
  require "pp"
2
2
  require "stringio"
3
3
 
4
+ require_relative "format"
5
+ require_relative "labeler"
4
6
  require_relative "location"
5
- require_relative "ruby_parser"
7
+ require_relative "location_label"
8
+ require_relative "source_label"
6
9
 
7
10
  module CutePrint
8
11
  # @api private
9
12
  class Formatter
10
13
 
11
14
  DEFAULT_WIDTH = 79
12
- DEFAULT_MAX_INLINE_LABEL_WIDTH = 40
13
15
 
14
16
  def initialize(opts = {})
15
- @inspector = :inspect
16
17
  @method = opts.fetch(:method)
17
18
  @out = opts.fetch(:out)
18
19
  @block = opts.fetch(:block, nil)
19
20
  @values = opts.fetch(:values, [])
20
21
  @width = opts.fetch(:width, DEFAULT_WIDTH)
21
- @max_inline_label_width = opts.fetch(:max_inline_label_width,
22
- DEFAULT_MAX_INLINE_LABEL_WIDTH)
23
22
  if @block && !@values.empty?
24
23
  raise ArgumentError, "arguments and block are mutually exclusive"
25
24
  end
25
+ @location_label = nil
26
26
  end
27
27
 
28
28
  def write
29
- if labeled_values.empty? && !label.empty?
29
+ if values.empty? && !label.empty?
30
30
  write_line label.chomp(": ")
31
31
  else
32
- labeled_values.each do |lines|
33
- write_lines lines
34
- end
35
- end
36
- end
37
-
38
- def inspected_values
39
- values.map do |value|
40
- @inspector.call(value, value_width)
41
- end
42
- end
43
-
44
- def labeled_values
45
- if label_width > @max_inline_label_width
46
- outline_labeled_values
47
- else
48
- inline_labeled_values
49
- end
50
- end
51
-
52
- def inline_labeled_values
53
- labeled_values = inspected_values.map do |lines|
54
- lines.map.with_index do |line, i|
55
- if i == 0
56
- label + line
57
- else
58
- indent + line
59
- end
60
- end
61
- end
62
- end
63
-
64
- def outline_labeled_values
65
- inspected_values.map do |lines|
66
- [label.rstrip] + lines.map do |line|
67
- " " + line
32
+ values.each do |value|
33
+ labeler = Labeler.new(@format, @width, label, value)
34
+ write_lines labeler.labeled
68
35
  end
69
36
  end
70
37
  end
71
38
 
72
39
  def with_location(format_key)
73
- @location_format = LOCATION_FORMATS.fetch(format_key) {
74
- raise ArgumentError, "Unknown location format: #{format_key.inspect}"
75
- }
76
- @location = Location.find
40
+ location = Location.find
41
+ @location_label = LocationLabel.make(format_key, location)
77
42
  end
78
43
 
79
44
  def inspect
80
- @inspector = ->(value, value_width) do
81
- [value.inspect]
82
- end
45
+ @format = Format::Inspect.new
83
46
  end
84
47
 
85
48
  def pretty_print
86
- @inspector = ->(value, value_width) do
87
- out = StringIO.new
88
- PP.pp(value, out, value_width)
89
- out.string.lines
90
- end
49
+ @format = Format::PrettyPrint.new
91
50
  end
92
51
 
93
52
  private
94
53
 
95
- LOCATION_FORMATS = {
96
- filename: "%<filename>s:%<line_number>d: ",
97
- path: "%<path>s:%<line_number>d: ",
98
- }
99
- private_constant :LOCATION_FORMATS
100
-
101
- def value_width
102
- @width - label_width
103
- end
104
-
105
54
  def values
106
55
  if @block
107
56
  [@block.call]
@@ -125,27 +74,12 @@ module CutePrint
125
74
  @label ||= make_label
126
75
  end
127
76
 
128
- def label_width
129
- label.size
130
- end
131
-
132
- def indent
133
- " " * label_width
134
- end
135
-
136
77
  def make_label
137
78
  [
138
- (@location.format(@location_format) if @location),
139
- ("#{block_code} is " if @block),
79
+ (@location_label.to_s if @location_label),
80
+ (SourceLabel.new(@block, @method) if @block),
140
81
  ].compact.join
141
82
  end
142
83
 
143
- def block_code
144
- ruby_parser = RubyParser.from_block(@block)
145
- parsed_code = ruby_parser.parse
146
- method_call = parsed_code.first_call_to_method(@method)
147
- method_call.block.to_ruby
148
- end
149
-
150
84
  end
151
85
  end
@@ -0,0 +1,36 @@
1
+ module CutePrint
2
+ # @api private
3
+ class InlineLabeler
4
+
5
+ def self.label(formatter, width, label, value)
6
+ new(formatter, width, label, value).labeled
7
+ end
8
+
9
+ def initialize(formatter, width, label, value)
10
+ @formatter = formatter
11
+ @width = width
12
+ @label = label
13
+ @value = value
14
+ end
15
+
16
+ def labeled
17
+ [@label.rstrip + "\n"] + indented_lines
18
+ end
19
+
20
+ private
21
+
22
+ INDENT = ' '
23
+ private_constant :INDENT
24
+
25
+ def indented_lines
26
+ lines.map do |line|
27
+ INDENT + line
28
+ end
29
+ end
30
+
31
+ def lines
32
+ @formatter.format(@width - INDENT.size, @value)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,71 @@
1
+ require_relative "inline_labeler"
2
+ require_relative "outline_labeler"
3
+
4
+ module CutePrint
5
+ # @api private
6
+ class Labeler
7
+
8
+ def self.label(format, width, label, value)
9
+ new(format, width, label, value).labeled
10
+ end
11
+
12
+ def initialize(format, width, label, value)
13
+ @format = format
14
+ @width = width
15
+ @label = label
16
+ @value = value
17
+ end
18
+
19
+ def labeled
20
+ # Optimization: Avoid the computation of "outlined", and the
21
+ # elaborate comparison, in many cases.
22
+ return outlined if outlined_fits_on_one_line?
23
+ inlined_fits = lines_fit_horizontally?(inlined)
24
+ outlined_fits = lines_fit_horizontally?(outlined)
25
+ if inlined_fits && outlined_fits
26
+ [inlined, outlined].min_by do |lines|
27
+ [
28
+ lines.size,
29
+ lines.equal?(outlined) ? 0 : 1,
30
+ ]
31
+ end
32
+ elsif inlined_fits && !outlined_fits
33
+ inlined
34
+ elsif !inlined_fits && outlined_fits
35
+ outlined
36
+ else # neither fits
37
+ [inlined, outlined].min_by do |lines|
38
+ [
39
+ lines.map(&:size).min,
40
+ lines.size,
41
+ ]
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def lines_fit_horizontally?(lines)
49
+ lines.all? do |line|
50
+ line_fits_horizontally?(line)
51
+ end
52
+ end
53
+
54
+ def line_fits_horizontally?(line)
55
+ line.chomp.size <= @width
56
+ end
57
+
58
+ def outlined_fits_on_one_line?
59
+ outlined.size == 1 && line_fits_horizontally?(outlined.first)
60
+ end
61
+
62
+ def outlined
63
+ @outlined ||= OutlineLabeler.label(@format, @width, @label, @value)
64
+ end
65
+
66
+ def inlined
67
+ @inlined ||= InlineLabeler.label(@format, @width, @label, @value)
68
+ end
69
+
70
+ end
71
+ end
@@ -1,6 +1,7 @@
1
1
  require_relative "finds_foreign_caller"
2
2
 
3
3
  module CutePrint
4
+ # @api private
4
5
  class Location
5
6
 
6
7
  extend FindsForeignCaller
@@ -11,21 +12,14 @@ module CutePrint
11
12
  new(path, line_number)
12
13
  end
13
14
 
15
+ attr_reader :path
16
+ attr_reader :line_number
17
+
14
18
  def initialize(path, line_number)
15
19
  @path = path
16
20
  @line_number = line_number
17
21
  end
18
22
 
19
- def format(template)
20
- template % {
21
- path: @path,
22
- filename: filename,
23
- line_number: @line_number,
24
- }
25
- end
26
-
27
- private
28
-
29
23
  def filename
30
24
  File.basename(@path)
31
25
  end
@@ -0,0 +1,19 @@
1
+ module CutePrint
2
+ class LocationLabel
3
+ # @api private
4
+ class Filename
5
+
6
+ def initialize(location)
7
+ @location = location
8
+ end
9
+
10
+ def to_s
11
+ "%s:%d: " % [
12
+ @location.filename,
13
+ @location.line_number,
14
+ ]
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module CutePrint
2
+ class LocationLabel
3
+ # @api private
4
+ class Path
5
+
6
+ def initialize(location)
7
+ @location = location
8
+ end
9
+
10
+ def to_s
11
+ "%s:%d: " % [
12
+ @location.path,
13
+ @location.line_number,
14
+ ]
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "location_label/filename"
2
+ require_relative "location_label/path"
3
+
4
+ module CutePrint
5
+ # @api private
6
+ class LocationLabel
7
+
8
+ FORMATS = {
9
+ filename: Filename,
10
+ path: Path,
11
+ }
12
+ private_constant :FORMATS
13
+
14
+ def self.make(format, location)
15
+ klass = FORMATS[format]
16
+ unless klass
17
+ raise ArgumentError, "Unknown location format: #{format.inspect}"
18
+ end
19
+ klass.new(location)
20
+ end
21
+
22
+ end
23
+ end
@@ -1,4 +1,3 @@
1
- require_relative "default_printer"
2
1
  require_relative "printer"
3
2
 
4
3
  module CutePrint
@@ -9,24 +8,24 @@ module CutePrint
9
8
  # the module itself is not.
10
9
  module Mixin
11
10
 
12
- # @see Printer#q
11
+ # @see CutePrint#q
13
12
  def q(*args, &block)
14
- CutePrint::DefaultPrinter.printer.q(*args, &block)
13
+ CutePrint.q(*args, &block)
15
14
  end
16
15
 
17
- # @see Printer#ql
16
+ # @see CutePrint#ql
18
17
  def ql(*args, &block)
19
- CutePrint::DefaultPrinter.printer.ql(*args, &block)
18
+ CutePrint.ql(*args, &block)
20
19
  end
21
20
 
22
- # @see Printer#qq
21
+ # @see CutePrint#qq
23
22
  def qq(*args, &block)
24
- CutePrint::DefaultPrinter.printer.qq(*args, &block)
23
+ CutePrint.qq(*args, &block)
25
24
  end
26
25
 
27
- # @see Printer#qql
26
+ # @see CutePrint#qql
28
27
  def qql(*args, &block)
29
- CutePrint::DefaultPrinter.printer.qql(*args, &block)
28
+ CutePrint.qql(*args, &block)
30
29
  end
31
30
 
32
31
  # Debug a call chain by printing self and then returning self.
@@ -36,7 +35,7 @@ module CutePrint
36
35
  self
37
36
  end
38
37
 
39
- # Debug a call chain by printing self, with source position, and
38
+ # Debug a call chain by printing self, with source location, and
40
39
  # then returning self.
41
40
  # @return [Object] self
42
41
  def tapql
@@ -46,13 +45,15 @@ module CutePrint
46
45
 
47
46
  # Debug a call chain by pretty-printing self and then returning
48
47
  # self.
48
+ # @return [Object] self
49
49
  def tapqq
50
50
  qq self
51
51
  self
52
52
  end
53
53
 
54
- # Debug a call chain by printing the source location,
55
- # pretty-printing self, and then returning self.
54
+ # Debug a call chain by pretty-printing self, with source
55
+ # location, and then returning self.
56
+ # @return [Object] self
56
57
  def tapqql
57
58
  qql self
58
59
  self
@@ -0,0 +1,38 @@
1
+ module CutePrint
2
+ # @api private
3
+ class OutlineLabeler
4
+
5
+ def self.label(formatter, width, label, value)
6
+ new(formatter, width, label, value).labeled
7
+ end
8
+
9
+ def initialize(formatter, width, label, value)
10
+ @formatter = formatter
11
+ @width = width
12
+ @label = label
13
+ @value = value
14
+ end
15
+
16
+ def labeled
17
+ [labeled_first_line] + indented_remaining_lines
18
+ end
19
+
20
+ private
21
+
22
+ def labeled_first_line
23
+ @label + lines.first
24
+ end
25
+
26
+ def indented_remaining_lines
27
+ indent = ' ' * @label.size
28
+ lines.to_a[1..-1].map do |line|
29
+ indent + line
30
+ end
31
+ end
32
+
33
+ def lines
34
+ @lines ||= @formatter.format(@width - @label.size, @value)
35
+ end
36
+
37
+ end
38
+ end
@@ -36,15 +36,7 @@ module CutePrint
36
36
  @location_format = :filename
37
37
  end
38
38
 
39
- # Inspect and write one or more objects.
40
- #
41
- # If called without a block, prints the inspected arguments, one
42
- # on a line.
43
- #
44
- # If called with a block, prints the source code of the block and
45
- # the inspected result of the block.
46
- #
47
- # @return nil
39
+ # @see CutePrint.q
48
40
  def q(*values, &block)
49
41
  formatter = Formatter.new(
50
42
  method: __method__,
@@ -57,15 +49,7 @@ module CutePrint
57
49
  nil
58
50
  end
59
51
 
60
- # Inspect and write one or more objects, with source location.
61
- #
62
- # If called without a block, prints the inspected arguments, one
63
- # on a line.
64
- #
65
- # If called with a block, prints the source code of the block and
66
- # the inspected result of the block.
67
- #
68
- # @return nil
52
+ # @see CutePrint.ql
69
53
  def ql(*values, &block)
70
54
  formatter = Formatter.new(
71
55
  method: __method__,
@@ -78,15 +62,7 @@ module CutePrint
78
62
  nil
79
63
  end
80
64
 
81
- # Pretty-print and write one or more objects.
82
- #
83
- # If called without a block, pretty-prints the pretty-printed
84
- # arguments, one on a line.
85
- #
86
- # If called with a block, prints the source code of the block and
87
- # pretty-prints the result of the block.
88
- #
89
- # @return nil
65
+ # @see CutePrint.qq
90
66
  def qq(*values, &block)
91
67
  formatter = Formatter.new(
92
68
  method: __method__,
@@ -98,15 +74,7 @@ module CutePrint
98
74
  nil
99
75
  end
100
76
 
101
- # Pretty-print and write one or more objects, with source location.
102
- #
103
- # If called without a block, pretty-prints the arguments, one on a
104
- # line.
105
- #
106
- # If called with a block, prints the source code of the block and
107
- # pretty-prints the result of the block.
108
- #
109
- # @return nil
77
+ # @see CutePrint.qql
110
78
  def qql(*values, &block)
111
79
  formatter = Formatter.new(
112
80
  method: __method__,
@@ -0,0 +1,26 @@
1
+ require_relative "ruby_parser"
2
+
3
+ module CutePrint
4
+ # @api private
5
+ class SourceLabel
6
+
7
+ def initialize(block, method)
8
+ @block = block
9
+ @method = method
10
+ end
11
+
12
+ def to_s
13
+ "#{block_code} is "
14
+ end
15
+
16
+ private
17
+
18
+ def block_code
19
+ ruby_parser = RubyParser.from_block(@block)
20
+ parsed_code = ruby_parser.parse
21
+ method_call = parsed_code.first_call_to_method(@method)
22
+ method_call.block.to_ruby
23
+ end
24
+
25
+ end
26
+ end
data/lib/cute_print.rb CHANGED
@@ -1,26 +1,2 @@
1
- require_relative "cute_print/configure"
1
+ require_relative "cute_print/core"
2
2
  require_relative "cute_print/core_ext"
3
- require_relative "cute_print/default_printer"
4
-
5
- # Like Kernel#p, only fancier. For example, this code:
6
- #
7
- # require 'cute_print'
8
- # q { 1 + 2 }
9
- #
10
- # prints this to $stderr:
11
- #
12
- # (1 + 2) is 3
13
- module CutePrint
14
-
15
- # Configure the library. For example:
16
- #
17
- # CutePrint.configure do |c|
18
- # c.out = $stdout
19
- # end
20
- #
21
- # @yieldparam config [Configure]
22
- def self.configure(&block)
23
- Configure.new(DefaultPrinter.printer, &block)
24
- end
25
-
26
- end
@@ -0,0 +1,20 @@
1
+ require_relative "../spec_helper"
2
+
3
+ require "cute_print/format/inspect"
4
+
5
+ module CutePrint
6
+ module Format
7
+ describe Inspect do
8
+
9
+ let(:width) { 80 }
10
+ let(:value) { (1..5).to_a }
11
+ subject { Inspect.new.format(width, value) }
12
+ specify do
13
+ expect(subject).to eq [
14
+ "[1, 2, 3, 4, 5]\n",
15
+ ]
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ require_relative "../spec_helper"
2
+
3
+ require "cute_print/format/pretty_print"
4
+
5
+ module CutePrint
6
+ module Format
7
+ describe PrettyPrint do
8
+
9
+ let(:value) { (1..5).to_a }
10
+ subject { PrettyPrint.new.format(width, value).to_a }
11
+
12
+ context "fits on one line" do
13
+ let(:width) { 80 }
14
+ specify do
15
+ expect(subject).to eq [
16
+ "[1, 2, 3, 4, 5]\n",
17
+ ]
18
+ end
19
+ end
20
+
21
+ context "needs multiple lines" do
22
+ let(:width) { 5 }
23
+ specify do
24
+ expect(subject).to eq [
25
+ "[1,\n",
26
+ " 2,\n",
27
+ " 3,\n",
28
+ " 4,\n",
29
+ " 5]\n",
30
+ ]
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require_relative "spec_helper"
2
+
3
+ require "cute_print/inline_labeler"
4
+
5
+ module CutePrint
6
+ describe InlineLabeler do
7
+
8
+ let(:label) { "foo.rb:1: " }
9
+ let(:value) { [1, 2, 3, 4, 5] }
10
+ subject { InlineLabeler.label(formatter, width, label, value) }
11
+
12
+ context "single line" do
13
+ let(:formatter) { Format::Inspect.new }
14
+ let(:width) { 80 }
15
+ specify do
16
+ expect(subject).to eq [
17
+ "foo.rb:1:\n",
18
+ " [1, 2, 3, 4, 5]\n",
19
+ ]
20
+ end
21
+ end
22
+
23
+ context "multiple lines" do
24
+ let(:formatter) { Format::PrettyPrint.new }
25
+ let(:width) { 4 }
26
+ specify do
27
+ expect(subject).to eq [
28
+ "foo.rb:1:\n",
29
+ " [1,\n",
30
+ " 2,\n",
31
+ " 3,\n",
32
+ " 4,\n",
33
+ " 5]\n",
34
+ ]
35
+ end
36
+ end
37
+
38
+ end
39
+ end