report_print 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa071844b22c0289ddea4f370860f29eed4d25e7acafe22c4cbdc02577b240ed
4
- data.tar.gz: 550bcbe7efd3a01cecd72655ade451e6dd89dcfa5e5e66268f28bb872851a279
3
+ metadata.gz: d6bc69172badef1f367d722882bf9dfc8dcaffb66926811cc37ddebcffac68a8
4
+ data.tar.gz: 1fdb2ce8ed7441c34f6cc792480fc9b7ad614c0d26204cf237ada5c52f9969bc
5
5
  SHA512:
6
- metadata.gz: 0411f46d291c9799c7bb9c411f6c06528a20b649e8163a6d926bee83e2db4101bcc23d721f39facba30873de5d369070dd24b01ac5144e2ad32176dd17be9fbf
7
- data.tar.gz: c6f0e18ddc0e186035b4b737a4bbd0ccf553ba189d5fca5b271eda1126b1cb0cea0099e8a2c3091df5cd131fc67b428292d1b98c3f0003321496768769fd63c3
6
+ metadata.gz: b701bc0e9ac1896140001a39967d96ea4acc369fc7ae8a619a0e62035aa16ace37dcebcb6757abb5f0d3deb93be1e1f8381b31cc90c1e7aadd4a687cbcec0bec
7
+ data.tar.gz: 74c6fb521894a4f14591d5d0e23f1b941c04b5209fe9dfb880b35f84041a27c6f917aee484e6402ec555e466fd5a6e3fb582ba60c3ad1432b452f1385c7b4baa
data/README.md CHANGED
@@ -23,9 +23,33 @@ gem install report_print
23
23
  ```ruby
24
24
  require 'report_print'
25
25
 
26
- rp object
26
+ class Example
27
+ def initialize
28
+ @foo = { a: [1,2,3] }
29
+ @bar = Object.new
30
+ end
31
+ end
32
+
33
+ rp Example.new
27
34
  ```
28
35
 
36
+ Output:
37
+
38
+ ```text
39
+ Example 0x378
40
+ @foo = {
41
+ a: [
42
+ 1,
43
+ 2,
44
+ 3
45
+ ]
46
+ }
47
+ @bar = Object 0x3a8
48
+ end
49
+ ```
50
+
51
+ See the [full documentation](https://benoithiller.github.io/report_print/) for more details.
52
+
29
53
  ## Development
30
54
 
31
55
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/doc/.document ADDED
@@ -0,0 +1,2 @@
1
+ *.md
2
+ ../lib
@@ -0,0 +1,150 @@
1
+ # Customization
2
+
3
+ To set up customized printing for your own classes, you need to define the `report_print` instance method.
4
+
5
+ When you then go to print the object, the method you defined will be called passing in a ReportPrint::Printer instance that you can use to print things.
6
+
7
+ The printer is composed of the following 4 methods:
8
+
9
+ * ReportPrint::Printer#multiline
10
+ * ReportPrint::Printer#inline
11
+ * ReportPrint::Printer#write
12
+ * ReportPrint::Printer#rp
13
+
14
+ The `multiline` and `inline` methods modify the behaviour of the `write` method to enable you to specify how elements are joined and lines are separated.
15
+
16
+ The `rp` method simply calls the `report_print` method of the specified object passing in the printer.
17
+
18
+ ## `multiline`
19
+
20
+ The `multiline` method creates a new multiline context and executes the provided block within it, resetting the printer context back at the end of the block.
21
+
22
+ Inside a multiline context each call to `write` will write its output on a new line indented as per the current indent level.
23
+
24
+ If you specify a `separator` for the multiline context, then that will be written before the newline on every line except the first line of the multiline context.
25
+
26
+ At the end of a multiline context the content of `after` is written into the outer context, unless `after_empty` is false and nothing was written inside the context.
27
+
28
+ Example:
29
+
30
+ ```
31
+ class ::Array < ::Object
32
+ def report_print(rp)
33
+ rp.write("[")
34
+ rp.multiline(after: "]", separator: ",") do
35
+ to_a.each do |item|
36
+ rp.rp(item)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ ```
42
+
43
+ In this example we also see the outline of the common pattern intended for when you are putting delimiters around a block. You write the opening token manually, but rely on the `after:` keyword argument to write the closing token.
44
+
45
+ The value passed to `after:` will be written without any separator before it. This pattern allows us to do the above safely without wrapping things in an extra `inline` block to prevent a separator being placed before the `"]"`.
46
+
47
+ ## `inline`
48
+
49
+ The `inline` method creates a new inline context and executes the provided block within it, resetting the printer context back at the end of the block.
50
+
51
+ When an inline context is placed within a multiline context we will say that it defines a `line`.
52
+
53
+ For each line the first call to `write` behaves the same as one in the containing multiline context.
54
+
55
+ Within an inline context the `inline_separator` defines a sequence to be written between two writes in that context.
56
+
57
+ When inline contexts are nested the rules are applied such that all of the writes within a child context are treated as one write for the purposes of joining.
58
+
59
+ Example:
60
+
61
+ ```
62
+ rp.inline("+") do
63
+ rp.inline("-") do
64
+ rp.write(1)
65
+ end
66
+ rp.inline("/") do
67
+ rp.write(2)
68
+ end
69
+ end
70
+
71
+ # => "1+2"
72
+
73
+ rp.inline("+") do
74
+ rp.inline("-") do
75
+ rp.write(1)
76
+ rp.write(2)
77
+ end
78
+ rp.inline("/") do
79
+ rp.write(3)
80
+ rp.write(4)
81
+ end
82
+ end
83
+
84
+ # => "1-2+3/4"
85
+ ```
86
+
87
+ ## Writing Defensively
88
+
89
+ When creating a `report_print` method it is very important to be explicit about how you want the text to be written.
90
+
91
+ **You cannot safely assume anything about the outer context.**
92
+
93
+ Thus if you want items to be rendered on the same line you always need to enclose them in an `inline` context. Same goes for multiple lines and a multiline context.
94
+
95
+ Example:
96
+
97
+ ```
98
+ # Bad: assumes that it is in an inline context with a blank inline_separator
99
+ def report_print(rp)
100
+ rp.write("Item")
101
+ rp.write("[")
102
+ rp.multiline(after: "]", separator: ",") do
103
+ each do |sub_item|
104
+ rp.rp(sub_item)
105
+ end
106
+ end
107
+ end
108
+
109
+ # Good: sets up an inline context
110
+ def report_print(rp)
111
+ rp.inline("") do
112
+ rp.write("Item")
113
+ rp.write("[")
114
+ rp.multiline(after: "]", separator: ",") do
115
+ each do |sub_item|
116
+ rp.rp(sub_item)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ ```
122
+
123
+ ## Only use Separators for Separators
124
+
125
+ Separators are only written between two writes, so you probably don't want to use them for some things that feel like separators but don't have that same property.
126
+
127
+ For example:
128
+
129
+ ```
130
+ class Assignment
131
+ # Bad: if @rhs is empty we probably still want to render an =.
132
+ # Otherwise users won't be able to tell that it is an assignment being
133
+ # printed.
134
+ def report_print(rp)
135
+ rp.inline(" = ") do
136
+ rp.rp(@lhs)
137
+ rp.rp(@rhs) if @rhs
138
+ end
139
+ end
140
+
141
+ # Better
142
+ def report_print(rp)
143
+ rp.inline(" ") do
144
+ rp.rp(@lhs)
145
+ rp.write("=")
146
+ rp.rp(@rhs) if @rhs
147
+ end
148
+ end
149
+ end
150
+ ```
data/doc/Formatters.md ADDED
@@ -0,0 +1,185 @@
1
+ # Formatters
2
+
3
+ The following formatter implementation are provided by default.
4
+
5
+ They are implemented by adding the `report_print` method onto the specified class, and so will be inherited as expected and can be overwritten as needed.
6
+
7
+ ## Simple Values
8
+
9
+ A number of simple value types are configured to produce colorized versions of their `inspect` output.
10
+
11
+ rp :symbols
12
+ rp "strings"
13
+ rp /regex/
14
+
15
+ # All Numeric types
16
+ rp 1
17
+ rp 0.5
18
+ rp Float::INFINITY
19
+ rp 1i * 1i
20
+ rp Rational(2, 3)
21
+
22
+ # Constants
23
+ rp true
24
+ rp false
25
+ rp nil
26
+
27
+ Output:
28
+
29
+ <pre>
30
+ <span style="color:var(--code-blue)">:symbols<span style="color:var(--code-green)">
31
+ "strings"<span style="color:var(--code-orange)">
32
+ /regex/<span style="color:var(--code-purple)">
33
+ 1
34
+ 0.5
35
+ Infinity
36
+ (-1+0i)
37
+ (2/3)<span style="color:var(--code-blue)">
38
+ true
39
+ false
40
+ nil</span>
41
+ </pre>
42
+
43
+ ## `Object`
44
+
45
+ class A
46
+ def initialize
47
+ @name = self.class.name
48
+ end
49
+ end
50
+
51
+ class B < A
52
+ def initialize
53
+ super
54
+ @field = A.new
55
+ end
56
+ end
57
+
58
+ rp B.new
59
+
60
+ Output:
61
+
62
+ <pre>
63
+ <span style="color:var(--code-orange)">B</span> <span style="color:var(--code-gray)">0x2b0</span>
64
+ <span style="color:var(--code-cyan)">@name</span> = <span style="color:var(--code-green)">"B"</span>
65
+ <span style="color:var(--code-cyan)">@field</span> = <span style="color:var(--code-orange)">A</span> <span style="color:var(--code-gray)">0x2b8</span>
66
+ <span style="color:var(--code-cyan)">@name</span> = <span style="color:var(--code-green)">"A"</span>
67
+ <span style="color:var(--code-blue)">end
68
+ end</span>
69
+ </pre>
70
+
71
+ ## `Module`
72
+
73
+ rp Class
74
+
75
+ Output:
76
+
77
+ <pre>
78
+ <span style="color:var(--code-orange)">Class</span>
79
+ </pre>
80
+
81
+
82
+ ## `Array`
83
+
84
+ rp [1, "two", [[]]]
85
+
86
+ Output:
87
+
88
+ <pre>
89
+ [
90
+ <span style="color:var(--code-purple)">1</span>,
91
+ <span style="color:var(--code-green)">"two"</span>,
92
+ [
93
+ []
94
+ ]
95
+ ]</span>
96
+ </pre>
97
+
98
+ ## `Hash`
99
+
100
+ rp({
101
+ "one" => 1,
102
+ two: {
103
+ [1, 2] => {}
104
+ }
105
+ })
106
+
107
+ Output:
108
+
109
+ <pre>
110
+ {
111
+ <span style="color:var(--code-green)">"one"</span> => <span style="color:var(--code-purple)">1</span>,
112
+ <span style="color:var(--code-cyan)">two:</span> {
113
+ [
114
+ <span style="color:var(--code-purple)">1</span>,
115
+ <span style="color:var(--code-purple)">2</span>
116
+ ] => {}
117
+ }
118
+ }</span>
119
+ </pre>
120
+
121
+ ## `Set`
122
+
123
+ rp Set[1, "thing", Set[]]
124
+
125
+ Output:
126
+
127
+ <pre>
128
+ <span style="color:var(--code-orange)">Set</span>[
129
+ <span style="color:var(--code-purple)">1</span>,
130
+ <span style="color:var(--code-green)">"thing"</span>,
131
+ <span style="color:var(--code-orange)">Set</span>[]
132
+ ]</span>
133
+ </pre>
134
+
135
+ ## `Data`
136
+
137
+ Direction = Data.define(:x, :y)
138
+ Velocity = Data.define(:speed, :direction)
139
+
140
+ rp Velocity[100, Direction[0.6, 0.8]]
141
+
142
+ Output:
143
+
144
+ <pre>
145
+ <span style="color:var(--code-blue)">Velocity</span>[
146
+ <span style="color:var(--code-cyan)">speed</span>: <span style="color:var(--code-purple)">100</span>,
147
+ <span style="color:var(--code-cyan)">direction</span>: <span style="color:var(--code-blue)">Direction</span>[
148
+ <span style="color:var(--code-cyan)">x</span>: <span style="color:var(--code-purple)">0.6</span>,
149
+ <span style="color:var(--code-cyan)">y</span>: <span style="color:var(--code-purple)">0.8</span>
150
+ ]
151
+ ]</span>
152
+ </pre>
153
+
154
+ ## `Struct`
155
+
156
+ Player = Struct.new(:health, :mana)
157
+
158
+ rp Player.new(100, 200)
159
+
160
+ Output:
161
+
162
+ <pre>
163
+ <span style="color:var(--code-orange)">Player</span>( <span style="color:var(--code-gray)">0x298</span>
164
+ <span style="color:var(--code-cyan)">health</span>: <span style="color:var(--code-purple)">100</span>,
165
+ <span style="color:var(--code-cyan)">mana</span>: <span style="color:var(--code-purple)">200</span>
166
+ )</span>
167
+ </pre>
168
+
169
+ Note that the object id is included for `Struct` but not for `Data` as the latter is a flyweight while the former is essentially an ordinary class.
170
+
171
+ ## `Range` and `ArithmeticSequence`
172
+
173
+ rp(1..10)
174
+ rp(..10)
175
+ rp((1...10).step(2))
176
+ rp("a".."z")
177
+
178
+ Output:
179
+
180
+ <pre>
181
+ (<span style="color:var(--code-purple)">1</span>..<span style="color:var(--code-purple)">10</span>)
182
+ (..<span style="color:var(--code-purple)">10</span>)
183
+ (<span style="color:var(--code-purple)">1</span>...<span style="color:var(--code-purple)">10</span>).step(<span style="color:var(--code-purple)">2</span>)
184
+ (<span style="color:var(--code-green)">"a"</span>..<span style="color:var(--code-green)">"z"</span>)</span>
185
+ </pre>
data/doc/overview.md ADDED
@@ -0,0 +1,84 @@
1
+ A hybrid between PrettyPrint and AwesomePrint/AmazingPrint. Providing you both a stylish readable default, and the ability to customize it as needed.
2
+
3
+ The name ReportPrint comes from the desire to output detailed reports of the program state at a given point in time, rather than merely inspecting an object.
4
+
5
+ ## Usage
6
+
7
+ require 'report_print'
8
+
9
+ class Example
10
+ def initialize
11
+ @foo = { a: [1,2,3] }
12
+ @bar = Object.new
13
+ end
14
+ end
15
+
16
+ rp Example.new
17
+
18
+ Output:
19
+
20
+ <pre>
21
+ <span style="color:var(--code-orange)">Example</span> <span style="color:var(--code-gray)">0x2a0</span>
22
+ <span style="color:var(--code-cyan)">@foo</span> = {
23
+ <span style="color:var(--code-cyan)">a:</span> [
24
+ <span style="color:var(--code-purple)">1</span>,
25
+ <span style="color:var(--code-purple)">2</span>,
26
+ <span style="color:var(--code-purple)">3</span>
27
+ ]
28
+ }
29
+ <span style="color:var(--code-cyan)">@bar</span> = <span style="color:var(--code-orange)">Object</span> <span style="color:var(--code-gray)">0x2a8<span style="color:var(--code-blue)">
30
+ end</span>
31
+ </pre>
32
+
33
+ See ReportPrint::Dsl#rp for the documentation of the global `rp` method.
34
+
35
+ ## Features
36
+
37
+ ReportPrint comes with custom printers for the following standard library classes:
38
+
39
+ * `Object`
40
+ * `Module`
41
+ * `Array`
42
+ * `Hash`
43
+ * `Set`
44
+ * `Data`
45
+ * `Struct`
46
+
47
+ In addition it will produce colorized `inspect` output for the following classes:
48
+
49
+ * `Symbol`
50
+ * `String`
51
+ * `Numeric`
52
+ * `TrueClass`
53
+ * `FalseClass`
54
+ * `NilClass`
55
+
56
+ See the page on Formatters for examples of each.
57
+
58
+ ### Customization
59
+
60
+ Similarly to PrettyPrint you can define the `report_print` method on a class to customize the rendering of instances of that class.
61
+
62
+ class Production
63
+ attr_accessor :left, :right
64
+
65
+ def report_print(rp)
66
+ rp.inline(" ") do
67
+ rp.rp(left)
68
+ rp.write("=>")
69
+ right.each do |token|
70
+ rp.rp(token)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ See Customization for a guide on how to use the various methods provided by the `rp` object above.
77
+
78
+ See ReportPrint::Printer for the API documentation of printer object.
79
+
80
+ ### Design
81
+
82
+ The printed output intentionally avoids the standard inspect format of `#<Object:0x000078c7430fd6c8>` in favour of relying primarily on line breaks and indentation to delimit the start and end of objects.
83
+
84
+ Similarly a conscious choice was made not to attempt any kind of midline alignment like `ap` does. Midline alignment may sometimes be pretty but it pays a substantial readability penalty for it.
@@ -0,0 +1,5 @@
1
+ A hybrid between PrettyPrint and AwesomePrint/AmazingPrint. Providing you both a stylish readable default, and the ability to customize it as needed.
2
+
3
+ The name ReportPrint comes from the desire to output detailed reports of the program state at a given point in time, rather than merely inspecting an object.
4
+
5
+ After requiring simply use the global `rp` method to print objects like you do with `pp` or `ap`.
data/lib/.document ADDED
@@ -0,0 +1,3 @@
1
+ report_print/printer.rb
2
+ report_print/dsl.rb
3
+ report_print/api.rb
@@ -0,0 +1,61 @@
1
+ module ReportPrint
2
+ ##
3
+ # These are the extensions that are included into the Module class that
4
+ # enabled you to customize the behaviour of ReportPrint for your own classes.
5
+ module Api
6
+ ##
7
+ # Defines options used by the printer when printing instances of the
8
+ # current class.
9
+ #
10
+ # The currently supported options are:
11
+ #
12
+ # * `detect_cycles:` whether instances of this class should display just a
13
+ # header and id when printed multiple times by the same report print
14
+ # command.
15
+ # <br>
16
+ # Defaults to `true` except for in the case of the simple value types and
17
+ # Data classes.
18
+ # * `color:` the color value to apply when using #report_print_inspect!.
19
+ # <br>
20
+ # Defaults to `nil`.
21
+ #
22
+ # #### Example Usage
23
+ #
24
+ # ```
25
+ # class WithColor
26
+ # report_print_options(color: :bright_blue)
27
+ # report_print_inspect!
28
+ # end
29
+ #
30
+ # class CustomContainer
31
+ # report_print_options(detect_cycles: false)
32
+ # end
33
+ # ```
34
+ #
35
+ # #### Option Loading
36
+ #
37
+ # Setting any value to `nil` means: take the global value if it is defined.
38
+ #
39
+ # When ReportPrint fetches options for a given class it first fetches the
40
+ # superclass options (recursively), then it merges the options for the
41
+ # current class on top.
42
+ #
43
+ # After which it compacts the options (removing the nils) and merges them
44
+ # on top of any global options. Where the global options are currently
45
+ # specified by calling `ReportPrint.report_print_options`.
46
+ def report_print_options(**kwargs)
47
+ @report_print_options = (@report_print_options || {}).merge(kwargs)
48
+ end
49
+
50
+ ##
51
+ # Helper that defines a `report_print` method which calls `inspect` and
52
+ # applies the `color:` specified in #report_print_options.
53
+ def report_print_inspect!
54
+ define_method(:report_print) do |rp|
55
+ rp.write(inspect, color: rp.options_for(self)[:color])
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ Module.prepend(ReportPrint::Api)
@@ -1,47 +1,114 @@
1
1
  using ReportPrint::Refinements
2
2
 
3
- class ::Object < ::BasicObject
3
+ class ::Module < ::Object
4
4
  def report_print(rp)
5
- rp.unless_seen(self) do
6
- rp.write_header(self)
7
- rp.multiline(after: rp.color("end", :bright_blue), after_empty: false) do
8
- rp.write_instance_variables(self)
9
- end
10
- end
5
+ rp.write(short_class_name, color: :bright_yellow)
11
6
  end
12
7
  end
13
8
 
14
- class ::Module < ::Object
9
+ class ::Object < ::BasicObject
10
+ report_print_options(detect_cycles: true)
11
+
15
12
  def report_print(rp)
16
- rp.write(name.short_class_name)
13
+ rp.write_header(self)
14
+ rp.multiline(after: rp.color("end", :bright_blue), after_empty: false) do
15
+ rp.write_instance_variables(self)
16
+ end
17
+ end
18
+
19
+ def report_print_cycle(rp)
20
+ rp.write_header(self)
17
21
  end
18
22
  end
19
23
 
20
24
  class ::Symbol < ::Object
21
- report_print_inspect!(color: :bright_blue)
25
+ report_print_options(color: :bright_blue, detect_cycles: false)
26
+ report_print_inspect!
22
27
  end
23
28
 
24
29
  class ::TrueClass < ::Object
25
- report_print_inspect!(color: :bright_blue)
30
+ report_print_options(color: :bright_blue, detect_cycles: false)
31
+ report_print_inspect!
26
32
  end
27
33
 
28
34
  class ::FalseClass < ::Object
29
- report_print_inspect!(color: :bright_blue)
35
+ report_print_options(color: :bright_blue, detect_cycles: false)
36
+ report_print_inspect!
30
37
  end
31
38
 
32
39
  class ::Numeric < ::Object
33
- report_print_inspect!(color: :bright_magenta)
40
+ report_print_options(color: :bright_magenta, detect_cycles: false)
41
+ report_print_inspect!
34
42
  end
35
43
 
36
44
  class ::String < ::Object
37
- report_print_inspect!(color: :bright_green)
45
+ report_print_options(color: :bright_green, detect_cycles: false)
46
+ report_print_inspect!
38
47
  end
39
48
 
40
49
  class ::NilClass < ::Object
41
- report_print_inspect!(color: :bright_blue)
50
+ report_print_options(color: :bright_blue, detect_cycles: false)
51
+ report_print_inspect!
52
+ end
53
+
54
+ class ::Regexp < ::Object
55
+ report_print_options(color: :yellow, detect_cycles: false)
56
+ report_print_inspect!
57
+ end
58
+
59
+ class ::Range < ::Object
60
+ report_print_options(detect_cycles: false)
61
+
62
+ def report_print(rp)
63
+ first = self.first rescue nil
64
+ last = self.last rescue nil
65
+
66
+ rp.inline("") do
67
+ rp.write("(")
68
+ unless first.nil?
69
+ rp.rp(first)
70
+ end
71
+ rp.write("..")
72
+ if exclude_end?
73
+ rp.write(".")
74
+ end
75
+ unless last.nil?
76
+ rp.rp(last)
77
+ end
78
+ rp.write(")")
79
+ end
80
+ end
81
+ end
82
+
83
+ class ::Enumerator::ArithmeticSequence ::Enumerator
84
+ report_print_options(detect_cycles: false)
85
+
86
+ def report_print(rp)
87
+ rp.inline("") do
88
+ rp.write("(")
89
+ unless self.begin.nil?
90
+ rp.rp(self.begin)
91
+ end
92
+ rp.write("..")
93
+ if exclude_end?
94
+ rp.write(".")
95
+ end
96
+ unless self.end.nil?
97
+ rp.rp(self.end)
98
+ end
99
+ rp.write(")")
100
+ unless step == 1
101
+ rp.write(".step(")
102
+ rp.rp(step)
103
+ rp.write(")")
104
+ end
105
+ end
106
+ end
42
107
  end
43
108
 
44
109
  class ::Array < ::Object
110
+ report_print_options(detect_cycles: false)
111
+
45
112
  def report_print(rp)
46
113
  rp.write("[")
47
114
  rp.multiline(after: "]", separator: ",") do
@@ -53,6 +120,8 @@ class ::Array < ::Object
53
120
  end
54
121
 
55
122
  class ::Hash < ::Object
123
+ report_print_options(detect_cycles: false)
124
+
56
125
  def report_print(rp)
57
126
  rp.write("{")
58
127
  rp.multiline(after: "}", separator: ",") do
@@ -74,6 +143,8 @@ class ::Hash < ::Object
74
143
  end
75
144
 
76
145
  class ::Set < ::Object
146
+ report_print_options(detect_cycles: false)
147
+
77
148
  def report_print(rp)
78
149
  rp.inline("") do
79
150
  rp.write("Set", color: :yellow)
@@ -88,6 +159,8 @@ class ::Set < ::Object
88
159
  end
89
160
 
90
161
  class ::Data < ::Object
162
+ report_print_options(detect_cycles: false)
163
+
91
164
  def report_print(rp)
92
165
  name = self.class.short_class_name
93
166
  rp.inline("") do
@@ -96,8 +169,11 @@ class ::Data < ::Object
96
169
  end
97
170
  rp.multiline(after: "]", separator: ",") do
98
171
  self.to_h.each do |name, value|
99
- rp.inline(": ") do
100
- rp.write(name, color: :bright_cyan)
172
+ rp.inline(" ") do
173
+ rp.inline("") do
174
+ rp.write(name, color: :bright_cyan)
175
+ rp.write(":")
176
+ end
101
177
  rp.rp(value)
102
178
  end
103
179
  end
@@ -19,3 +19,5 @@ module ReportPrint
19
19
  end
20
20
  end
21
21
  end
22
+
23
+ Kernel.prepend(ReportPrint::Dsl)
@@ -13,16 +13,12 @@ module ReportPrint
13
13
  :next_inline_separator,
14
14
  :after,
15
15
  :start_of_line,
16
- :start_of_block
16
+ :start_of_block,
17
+ :start_of_output
17
18
  ) do
18
- # Ruby type checking can't really handle this type of dynamic programming
19
- # very well yet, so we just skip type checking here as there is only the
20
- # one short function.
21
- # steep:ignore:start
22
19
  def inline?
23
20
  mode == :inline
24
21
  end
25
- # steep:ignore:end
26
22
  end
27
23
 
28
24
  ##
@@ -32,10 +28,7 @@ module ReportPrint
32
28
  def initialize(output = $>, color: :auto)
33
29
  @output = output
34
30
  if color == :auto
35
- # Ignore block performing runtime type check.
36
- # steep:ignore:start
37
31
  color = output.respond_to?(:tty?) && output.tty?
38
- # steep:ignore:end
39
32
  end
40
33
  @rainbow = Rainbow::Wrapper.new(color)
41
34
 
@@ -47,9 +40,12 @@ module ReportPrint
47
40
  inline_separator: "",
48
41
  next_inline_separator: nil,
49
42
  after: nil,
50
- start_of_line: false,
51
- start_of_block: true
43
+ start_of_line: true,
44
+ start_of_block: true,
45
+ start_of_output: true
52
46
  ]
47
+
48
+ @options = ScopedOptions.new(ReportPrint.report_print_options)
53
49
  end
54
50
 
55
51
  ##
@@ -60,7 +56,14 @@ module ReportPrint
60
56
  def rp(object)
61
57
  case object
62
58
  when Object
63
- object.report_print(self)
59
+ if @seen.include?(object.__id__)
60
+ object.report_print_cycle(self)
61
+ else
62
+ if options_for(object)[:detect_cycles]
63
+ @seen.add(object.__id__)
64
+ end
65
+ object.report_print(self)
66
+ end
64
67
  else
65
68
  write("BasicObject", color: :bright_yellow)
66
69
  end
@@ -120,7 +123,8 @@ module ReportPrint
120
123
  if @state.inline?
121
124
  set_state(
122
125
  start_of_line: @state.start_of_line && result.start_of_line,
123
- start_of_block: @state.start_of_block && result.start_of_block
126
+ start_of_block: @state.start_of_block && result.start_of_block,
127
+ start_of_output: @state.start_of_output && result.start_of_output
124
128
  )
125
129
  if @state.next_inline_separator != result.next_inline_separator
126
130
  # If the inner inline state is requesting a separator, then that
@@ -129,7 +133,8 @@ module ReportPrint
129
133
  end
130
134
  else
131
135
  set_state(
132
- start_of_block: @state.start_of_block && result.start_of_block
136
+ start_of_block: @state.start_of_block && result.start_of_block,
137
+ start_of_output: @state.start_of_output && result.start_of_output
133
138
  )
134
139
  end
135
140
  end
@@ -167,7 +172,8 @@ module ReportPrint
167
172
  )
168
173
 
169
174
  set_state(
170
- start_of_block: @state.start_of_block && result.start_of_block
175
+ start_of_block: @state.start_of_block && result.start_of_block,
176
+ start_of_output: @state.start_of_output && result.start_of_output
171
177
  )
172
178
 
173
179
  did_write = !result.start_of_block
@@ -205,7 +211,7 @@ module ReportPrint
205
211
  # first concatenated into a single string, as in `IO#write`. Meaning no
206
212
  # separators or line breaks will be rendered between them.
207
213
  def write(*strings, color: nil)
208
- if @state.start_of_line
214
+ if @state.start_of_line && !@state.start_of_output
209
215
  unless @state.start_of_block
210
216
  @output.write(@state.separator)
211
217
  end
@@ -226,12 +232,14 @@ module ReportPrint
226
232
  set_state(
227
233
  start_of_line: false,
228
234
  start_of_block: false,
235
+ start_of_output: false,
229
236
  next_inline_separator: @state.inline_separator
230
237
  )
231
238
  else
232
239
  set_state(
233
240
  start_of_block: false,
234
- start_of_line: true
241
+ start_of_line: true,
242
+ start_of_output: false
235
243
  )
236
244
  end
237
245
  end
@@ -309,38 +317,17 @@ module ReportPrint
309
317
  end
310
318
  end
311
319
 
312
- def unless_seen(object)
313
- if @seen.include?(object.__id__)
314
- write_header(object)
315
- else
316
- @seen.add(object.__id__)
317
- yield
318
- end
320
+ ##
321
+ # Fetch the options which are set for the class of `object`.
322
+ #
323
+ # See Api#report_print_options for more details about the available
324
+ # options.
325
+ def options_for(object)
326
+ @options.options_for(object.class)
319
327
  end
320
328
 
321
329
  private
322
330
 
323
- def write_after(value)
324
- if @state.start_of_line
325
- break_line
326
- end
327
-
328
- @output.write(value)
329
-
330
- if @state.inline?
331
- set_state(
332
- start_of_line: false,
333
- start_of_block: false,
334
- next_inline_separator: @state.inline_separator
335
- )
336
- else
337
- set_state(
338
- start_of_block: false,
339
- start_of_line: true
340
- )
341
- end
342
- end
343
-
344
331
  def break_line
345
332
  @output.write("\n")
346
333
  @output.write(" " * @state.indent)
@@ -1,14 +1,5 @@
1
1
  module ReportPrint
2
2
  module Refinements
3
- refine Object.singleton_class do
4
- def report_print_inspect!(color: nil)
5
- define_method(:report_print) do |rp|
6
- # @type self: Object
7
- rp.write(inspect, color:)
8
- end
9
- end
10
- end
11
-
12
3
  refine Module do
13
4
  def short_class_name
14
5
  name.sub(/^.*::/, "")
@@ -0,0 +1,21 @@
1
+ module ReportPrint
2
+ class ScopedOptions
3
+ def initialize(global_options)
4
+ @global_options = global_options
5
+ @cache = {
6
+ Object => Object.report_print_options,
7
+ BasicObject => {}
8
+ }
9
+ end
10
+
11
+ def options_for(klass)
12
+ @global_options.merge(self_options_for(klass).compact)
13
+ end
14
+
15
+ private def self_options_for(klass)
16
+ @cache[klass] ||= (
17
+ self_options_for(klass.superclass).merge(klass.report_print_options)
18
+ )
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module ReportPrint
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/report_print.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require_relative "report_print/version"
2
2
  require_relative "report_print/refinements"
3
+ require_relative "report_print/api"
4
+ require_relative "report_print/scoped_options"
3
5
  require_relative "report_print/printer"
4
6
  require_relative "report_print/core_extensions"
5
7
  require_relative "report_print/dsl"
6
8
 
7
- module ReportPrint; end
8
-
9
- # :enddoc:
10
- Kernel.prepend(ReportPrint::Dsl)
9
+ module ReportPrint
10
+ @report_print_options = {}
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: report_print
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benoit Hiller
@@ -23,10 +23,12 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '3.1'
26
- description: 'A hybrid between PrettyPrint and AwesomePrint/AmazingPrint. Providing
27
- you both a stylish readable default, and the ability to customize it as needed.
26
+ description: |
27
+ A hybrid between PrettyPrint and AwesomePrint/AmazingPrint. Providing you both a stylish readable default, and the ability to customize it as needed.
28
28
 
29
- '
29
+ The name ReportPrint comes from the desire to output detailed reports of the program state at a given point in time, rather than merely inspecting an object.
30
+
31
+ After requiring simply use the global `rp` method to print objects like you do with `pp` or `ap`.
30
32
  email:
31
33
  - benoit.hiller@gmail.com
32
34
  executables: []
@@ -35,18 +37,26 @@ extra_rdoc_files: []
35
37
  files:
36
38
  - LICENSE
37
39
  - README.md
40
+ - doc/.document
41
+ - doc/Customization.md
42
+ - doc/Formatters.md
43
+ - doc/overview.md
44
+ - gem_description.rdoc
45
+ - lib/.document
38
46
  - lib/report_print.rb
47
+ - lib/report_print/api.rb
39
48
  - lib/report_print/core_extensions.rb
40
49
  - lib/report_print/dsl.rb
41
50
  - lib/report_print/printer.rb
42
51
  - lib/report_print/refinements.rb
52
+ - lib/report_print/scoped_options.rb
43
53
  - lib/report_print/version.rb
44
- homepage: https://github.com/BenoitHiller/report_print
54
+ homepage: https://benoithiller.github.io/report_print/
45
55
  licenses:
46
56
  - MIT-0
47
57
  metadata:
48
58
  allowed_push_host: https://rubygems.org
49
- homepage_uri: https://github.com/BenoitHiller/report_print
59
+ homepage_uri: https://benoithiller.github.io/report_print/
50
60
  source_code_uri: https://github.com/BenoitHiller/report_print
51
61
  rdoc_options: []
52
62
  require_paths: