AXElements 0.6.0beta2 → 0.7.5

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 (107) hide show
  1. data/.yardopts +1 -2
  2. data/README.markdown +152 -88
  3. data/Rakefile +8 -103
  4. data/docs/Debugging.markdown +9 -2
  5. data/docs/KeyboardEvents.markdown +114 -49
  6. data/docs/Setting.markdown +1 -0
  7. data/docs/images/next_version.png +0 -0
  8. data/ext/accessibility/key_coder/extconf.rb +22 -0
  9. data/ext/accessibility/key_coder/key_coder.c +113 -0
  10. data/lib/AXElements.rb +2 -0
  11. data/lib/accessibility/core.rb +897 -0
  12. data/lib/accessibility/debug.rb +168 -0
  13. data/lib/accessibility/dsl.rb +697 -0
  14. data/lib/accessibility/enumerators.rb +104 -0
  15. data/lib/accessibility/errors.rb +32 -0
  16. data/lib/accessibility/factory.rb +153 -0
  17. data/lib/accessibility/graph.rb +150 -0
  18. data/lib/{ax_elements/inspector.rb → accessibility/pp_inspector.rb} +39 -28
  19. data/lib/accessibility/qualifier.rb +158 -0
  20. data/lib/accessibility/string.rb +494 -0
  21. data/lib/accessibility/translator.rb +178 -0
  22. data/lib/accessibility/version.rb +7 -0
  23. data/lib/accessibility.rb +79 -0
  24. data/lib/ax/application.rb +234 -0
  25. data/lib/{ax_elements/elements → ax}/button.rb +2 -0
  26. data/lib/ax/element.rb +518 -0
  27. data/lib/{ax_elements/elements → ax}/radio_button.rb +2 -0
  28. data/lib/ax/row.rb +37 -0
  29. data/lib/{ax_elements/elements → ax}/static_text.rb +2 -0
  30. data/lib/ax/systemwide.rb +86 -0
  31. data/lib/ax_elements/awesome_print.rb +25 -0
  32. data/lib/ax_elements/exception_workaround.rb +8 -0
  33. data/lib/ax_elements/nsarray_compat.rb +64 -0
  34. data/lib/ax_elements/vendor/inflection_data.rb +65 -0
  35. data/lib/ax_elements/vendor/inflections.rb +172 -0
  36. data/lib/ax_elements/vendor/inflector.rb +306 -0
  37. data/lib/ax_elements.rb +14 -25
  38. data/lib/minitest/ax_elements.rb +112 -12
  39. data/lib/mouse.rb +72 -46
  40. data/lib/rspec/expectations/ax_elements.rb +133 -6
  41. data/rakelib/doc.rake +13 -0
  42. data/rakelib/ext.rake +61 -0
  43. data/rakelib/gem.rake +28 -0
  44. data/rakelib/test.rake +53 -0
  45. data/test/helper.rb +11 -97
  46. data/test/integration/accessibility/test_core.rb +18 -0
  47. data/test/integration/accessibility/test_debug.rb +44 -0
  48. data/test/integration/accessibility/test_dsl.rb +225 -0
  49. data/test/integration/accessibility/test_enumerators.rb +122 -0
  50. data/test/integration/accessibility/test_errors.rb +38 -0
  51. data/test/integration/accessibility/test_notifications.rb +22 -0
  52. data/test/integration/accessibility/test_qualifier.rb +148 -0
  53. data/test/integration/ax/test_application.rb +56 -0
  54. data/test/integration/ax/test_element.rb +46 -0
  55. data/test/integration/ax/test_row.rb +23 -0
  56. data/test/integration/ax_elements/test_nsarray_compat.rb +43 -0
  57. data/test/integration/minitest/test_ax_elements.rb +98 -0
  58. data/test/integration/rspec/expectations/test_ax_elements.rb +58 -0
  59. data/test/integration/test_mouse.rb +35 -0
  60. data/test/sanity/accessibility/test_core.rb +553 -0
  61. data/test/sanity/accessibility/test_debug.rb +63 -0
  62. data/test/sanity/accessibility/test_dsl.rb +75 -0
  63. data/test/sanity/accessibility/test_errors.rb +10 -0
  64. data/test/sanity/accessibility/test_factory.rb +88 -0
  65. data/test/sanity/accessibility/test_pp_inspector.rb +110 -0
  66. data/test/sanity/accessibility/test_qualifier.rb +13 -0
  67. data/test/sanity/accessibility/test_string.rb +238 -0
  68. data/test/sanity/accessibility/test_translator.rb +145 -0
  69. data/test/sanity/ax/test_application.rb +90 -0
  70. data/test/sanity/ax/test_element.rb +80 -0
  71. data/test/sanity/ax/test_systemwide.rb +66 -0
  72. data/test/sanity/ax_elements/test_nsarray_compat.rb +16 -0
  73. data/test/sanity/ax_elements/test_nsobject_inspect.rb +11 -0
  74. data/test/sanity/minitest/test_ax_elements.rb +15 -0
  75. data/test/sanity/rspec/expectations/test_ax_elements.rb +12 -0
  76. data/test/sanity/test_ax_elements.rb +10 -0
  77. data/test/sanity/test_mouse.rb +19 -0
  78. metadata +111 -93
  79. data/LICENSE.txt +0 -25
  80. data/ext/key_coder/extconf.rb +0 -6
  81. data/ext/key_coder/key_coder.m +0 -77
  82. data/lib/ax_elements/accessibility/enumerators.rb +0 -104
  83. data/lib/ax_elements/accessibility/graph.rb +0 -118
  84. data/lib/ax_elements/accessibility/language.rb +0 -347
  85. data/lib/ax_elements/accessibility/qualifier.rb +0 -73
  86. data/lib/ax_elements/accessibility.rb +0 -166
  87. data/lib/ax_elements/core.rb +0 -541
  88. data/lib/ax_elements/element.rb +0 -593
  89. data/lib/ax_elements/elements/application.rb +0 -88
  90. data/lib/ax_elements/elements/row.rb +0 -30
  91. data/lib/ax_elements/elements/systemwide.rb +0 -46
  92. data/lib/ax_elements/macruby_extensions.rb +0 -255
  93. data/lib/ax_elements/notification.rb +0 -37
  94. data/lib/ax_elements/version.rb +0 -9
  95. data/test/elements/test_application.rb +0 -72
  96. data/test/elements/test_row.rb +0 -27
  97. data/test/elements/test_systemwide.rb +0 -38
  98. data/test/test_accessibility.rb +0 -127
  99. data/test/test_blankness.rb +0 -26
  100. data/test/test_core.rb +0 -448
  101. data/test/test_element.rb +0 -939
  102. data/test/test_enumerators.rb +0 -81
  103. data/test/test_inspector.rb +0 -130
  104. data/test/test_language.rb +0 -157
  105. data/test/test_macruby_extensions.rb +0 -303
  106. data/test/test_mouse.rb +0 -5
  107. data/test/test_search_semantics.rb +0 -143
@@ -0,0 +1,104 @@
1
+ ##
2
+ # Namespace for enumerators used to navigate accessibility hierarchies.
3
+ module Accessibility::Enumerators
4
+
5
+ ##
6
+ # Enumerator for visiting each element in a UI hierarchy in breadth
7
+ # first order.
8
+ class BreadthFirst
9
+ include Enumerable
10
+
11
+ # @param [AX::Element]
12
+ def initialize root
13
+ @root = root
14
+ end
15
+
16
+ ##
17
+ # Semi-lazily iterate through the tree.
18
+ #
19
+ # @yieldparam [AX::Element]
20
+ def each
21
+ # @todo Lazy-wrap element refs, might make things a bit faster
22
+ # for fat trees; what is impact on thin trees?
23
+ # @todo See if we can implement the method in a single loop
24
+ queue = [@root]
25
+ until queue.empty?
26
+ queue.shift.attribute(:children).each do |x|
27
+ queue << x if x.attributes.include? :children
28
+ yield x
29
+ end
30
+ end
31
+ end
32
+
33
+ ##
34
+ # @note Explicitly defined so that escaping at the first found element
35
+ # actually works. Since only a single `break` is called when an
36
+ # item is found it does not fully escape the method. Technically,
37
+ # we need to do this with other 'escape-early' iteraters, but
38
+ # they aren't being used...yet.
39
+ #
40
+ # Override `Enumerable#find` for performance reasons.
41
+ def find
42
+ each { |x| return x if yield x }
43
+ end
44
+
45
+ end
46
+
47
+ ##
48
+ # Enumerator for visitng each element in a UI hierarchy in
49
+ # depth first order.
50
+ class DepthFirst
51
+ include Enumerable
52
+
53
+ # @param [AX::Element]
54
+ def initialize root
55
+ @root = root
56
+ end
57
+
58
+ # @yieldparam [AX::Element]
59
+ def each
60
+ stack = @root.attribute(:children)
61
+ until stack.empty?
62
+ current = stack.shift
63
+ yield current
64
+ if current.attributes.include? :children
65
+ # need to reverse it since child ordering seems to matter in practice
66
+ stack.unshift *current.attribute(:children)
67
+ end
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Walk the UI element tree and yield both the element and the level
73
+ # that the element is at relative to the root.
74
+ #
75
+ # @yieldparam [AX::Element]
76
+ # @yieldparam [Number]
77
+ def each_with_level &block
78
+ # @todo A bit of a hack that I would like to fix one day...
79
+ @root.attribute(:children).each do |element|
80
+ recursive_each_with_level element, 1, block
81
+ end
82
+ end
83
+
84
+
85
+ private
86
+
87
+ ##
88
+ # Recursive implementation of a depth first iterator.
89
+ #
90
+ # @param [AX::Element]
91
+ # @param [Number]
92
+ # @param [#call]
93
+ def recursive_each_with_level element, depth, block
94
+ block.call element, depth
95
+ if element.respond_to? :children
96
+ element.attribute(:children).each do |x|
97
+ recursive_each_with_level x, depth + 1, block
98
+ end
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,32 @@
1
+ require 'accessibility/debug'
2
+
3
+ ##
4
+ # Error raised when an implicit search fails to return a result.
5
+ class Accessibility::SearchFailure < NoMethodError
6
+
7
+ def initialize searcher, searchee, filters
8
+ filters = {} unless filters.kind_of? Hash
9
+ msg = "Could not find `#{pp_searchee searchee, filters}` "
10
+ msg << "as a child of #{searcher.class}\n"
11
+ msg << "Element Path:\n\t" << path_to(searcher)
12
+ # @todo Consider turning this on by default
13
+ msg << "\nSubtree:\n\t" << debug(searcher) if Accessibility::Debug.on?
14
+ super msg
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def pp_searchee searchee, filters
21
+ Accessibility::Qualifier.new(searchee, filters).describe
22
+ end
23
+
24
+ def path_to element
25
+ Accessibility::Debug.path(element).map! { |x| x.inspect }.join("\n\t")
26
+ end
27
+
28
+ def debug searcher
29
+ Accessibility::Debug.text_subtree(searcher)
30
+ end
31
+
32
+ end
@@ -0,0 +1,153 @@
1
+ require 'accessibility/core'
2
+ require 'accessibility/translator'
3
+
4
+ ##
5
+ # Namespace container for all the accessibility objects.
6
+ module AX; end
7
+
8
+ ##
9
+ # Mixin made for processing low level data from AXAPI methods.
10
+ module Accessibility::Factory
11
+
12
+ ##
13
+ # Processes any given data from an AXAPI method and wraps it if
14
+ # needed. Meant for taking a return value from {Accessibility::Core#attr:for:}
15
+ # and friends.
16
+ #
17
+ # Generally, used to process an `AXValue` into a `CGPoint` or an
18
+ # `AXUIElementRef` into some kind of {AX::Element} object.
19
+ def process value
20
+ return nil if value.nil? # CFGetTypeID(nil) crashes runtime
21
+ case CFGetTypeID(value)
22
+ when ARRAY_TYPE then process_array value
23
+ when REF_TYPE then process_element value
24
+ else
25
+ value
26
+ end
27
+ end
28
+
29
+
30
+ private
31
+
32
+ ##
33
+ # @private
34
+ #
35
+ # Reference to the singleton instance of the translator.
36
+ #
37
+ # @return [Accessibility::Translator]
38
+ TRANSLATOR = Accessibility::Translator.instance
39
+
40
+ ##
41
+ # @private
42
+ #
43
+ # Type ID for `AXUIElementRef` objects.
44
+ #
45
+ # @return [Number]
46
+ REF_TYPE = AXUIElementGetTypeID()
47
+
48
+ ##
49
+ # @private
50
+ #
51
+ # Type ID for `CFArrayRef` objects.
52
+ #
53
+ # @return [Number]
54
+ ARRAY_TYPE = CFArrayGetTypeID()
55
+
56
+ ##
57
+ # @todo Should we handle cases where a subrole has a value of
58
+ # 'Unknown'? What is the performance impact?
59
+ #
60
+ # Takes an `AXUIElementRef` and gives you some kind of wrapped
61
+ # accessibility object.
62
+ #
63
+ # Some code paths have been unrolled for efficiency. Don't hate player,
64
+ # hate the game.
65
+ #
66
+ # @param [AXUIElementRef]
67
+ # @return [AX::Element]
68
+ def process_element ref
69
+ role = TRANSLATOR.unprefix ref.role
70
+ attrs = ref.attributes
71
+ klass = if attrs.include? KAXSubroleAttribute
72
+ subrole = ref.subrole
73
+ # Some objects claim to have a subrole but return nil
74
+ if subrole
75
+ class_for TRANSLATOR.unprefix(subrole), and: role
76
+ else
77
+ class_for role
78
+ end
79
+ else
80
+ class_for role
81
+ end
82
+ klass.new ref
83
+ end
84
+
85
+ ##
86
+ # We assume a homogeneous array and only wrap element arrays right now.
87
+ #
88
+ # @return [Array]
89
+ def process_array vals
90
+ return vals if vals.empty?
91
+ return vals if CFGetTypeID(vals.first) != REF_TYPE
92
+ return vals.map { |val| process_element val }
93
+ end
94
+
95
+ ##
96
+ # @todo Consider using {AX.const_missing} instead.
97
+ #
98
+ # Find the class for a given role. If the class does not exist it will
99
+ # be created on demand.
100
+ #
101
+ # @param [#to_s]
102
+ # @return [Class]
103
+ def class_for role
104
+ if AX.const_defined? role, false
105
+ AX.const_get role
106
+ else
107
+ create_class role
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Find the class for a given subrole and role. If the class does not
113
+ # exist it will be created on demand.
114
+ #
115
+ # @param [#to_s]
116
+ # @param [#to_s]
117
+ # @return [Class]
118
+ def class_for subrole, and: role
119
+ # @todo it would be nice if we didn't have to lookup twice
120
+ if AX.const_defined? subrole, false
121
+ AX.const_get subrole
122
+ else
123
+ create_class subrole, with_superclass: role
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Create a new class in the {AX} namespace that has {AX::Element}
129
+ # as the superclass.
130
+ #
131
+ # @param [#to_s]
132
+ # @return [Class]
133
+ def create_class name
134
+ klass = Class.new AX::Element
135
+ AX.const_set name, klass
136
+ end
137
+
138
+ ##
139
+ # Create a new class in the {AX} namesapce that has the given
140
+ # `superklass` as the superclass..
141
+ #
142
+ # @param [#to_s] name
143
+ # @param [#to_s] superklass
144
+ # @return [Class]
145
+ def create_class name, with_superclass: superklass
146
+ unless AX.const_defined? superklass, false
147
+ create_class superklass
148
+ end
149
+ klass = Class.new AX.const_get(superklass)
150
+ AX.const_set name, klass
151
+ end
152
+
153
+ end
@@ -0,0 +1,150 @@
1
+ ##
2
+ # DOT graph generator for AXElements. It can generate the digraph code
3
+ # for a UI subtree. That code can then be given to GraphViz to generate
4
+ # an image for the graph.
5
+ #
6
+ # You can learn more about generating graphs in the
7
+ # {file:docs/Debugging.markdown Debugging} tutorial.
8
+ class Accessibility::Graph
9
+
10
+ ##
11
+ # @todo Graphs could be a lot nicer looking. That is, nodes could be much
12
+ # more easily identifiable, by allowing different classes to tell
13
+ # the node more about itself. A mixin module/protocol should
14
+ # probably be created, just as with the inspector mixin, and added
15
+ # to abstract base and overridden as needed in subclasses. In this
16
+ # way, an object can be more specific about what shape it is, how
17
+ # it is coloured, etc.
18
+ # Reference: http://www.graphviz.org/doc/info/attrs.html
19
+ #
20
+ # A node in the UI hierarchy. Used by {Accessibility::Graph} in order
21
+ # to build Graphviz DOT graphs.
22
+ class Node
23
+
24
+ # @return [String]
25
+ attr_reader :id
26
+
27
+ # @return [AX::Element]
28
+ attr_reader :element
29
+
30
+ # @param [AX::Element]
31
+ def initialize element
32
+ @element = element
33
+ @id = "element_#{element.object_id}"
34
+ end
35
+
36
+ # @return [String]
37
+ def to_dot
38
+ "#{@id} #{identifier} #{shape}"
39
+ end
40
+
41
+
42
+ private
43
+
44
+ EMPTY_STRING = ''
45
+ NAMESPACE = '::'
46
+
47
+ def identifier
48
+ klass = @element.class.to_s.split(NAMESPACE).last
49
+ ident = @element.pp_identifier
50
+ ident.gsub! /"/, '\"'
51
+ "[label = \"#{klass}#{ident}\"]"
52
+ end
53
+
54
+ def shape
55
+ @element.actions.empty? ? OVAL : BOX
56
+ end
57
+
58
+ def enabled
59
+ FILL if @element.respond_to?(:enabled) && !@element.enabled?
60
+ end
61
+
62
+ def focus
63
+ BOLD if @element.respond_to?(:focused) && @element.focused?
64
+ end
65
+
66
+ OVAL = '[shape = oval]'
67
+ BOX = '[shape = box]'
68
+ BOLD = '[style = bold]'
69
+ FILL = '[style = filled] [color = "grey"]'
70
+ end
71
+
72
+ ##
73
+ # An edge in the UI hierarchy. Used by {Accessibility::Graph} in order
74
+ # to build Graphviz DOT graphs.
75
+ class Edge
76
+
77
+ ##
78
+ # The style of arrowhead to use
79
+ #
80
+ # @return [String]
81
+ attr_accessor :style
82
+
83
+ # @param [Accessibility::Graph::Node]
84
+ # @param [Accessibility::Graph::Node]
85
+ def initialize head, tail
86
+ @head = head
87
+ @tail = tail
88
+ end
89
+
90
+ # @return [String]
91
+ def to_dot
92
+ arrow = style ? style : 'normal'
93
+ "#{@head.id} -> #{@tail.id} [arrowhead = #{arrow}]"
94
+ end
95
+
96
+ end
97
+
98
+
99
+ ##
100
+ # List of nodes in the UI hierarchy.
101
+ #
102
+ # @return [Array<Accessibility::Graph::Node>]
103
+ attr_reader :nodes
104
+
105
+ ##
106
+ # List of edges in the graph.
107
+ #
108
+ # @return [Array<Accessibility::Graph::Edge>]
109
+ attr_reader :edges
110
+
111
+ # @param [AX::Element]
112
+ def initialize root
113
+ root_node = Node.new(root)
114
+ @nodes = [root_node]
115
+ @edges = []
116
+
117
+ # exploit the ordering of a breadth-first enumeration to simplify
118
+ # the creation of edges for the graph. This only works because
119
+ # the UI hiearchy is a simple tree.
120
+ @edge_queue = Array.new(root.size_of(:children), root_node)
121
+ end
122
+
123
+ ##
124
+ # Construct the list of nodes and edges for the graph.
125
+ #
126
+ # The secret sauce is that we create an edge queue to exploit the
127
+ # breadth first ordering of the enumerator, which makes building the
128
+ # edges very easy.
129
+ def build!
130
+ Accessibility::Enumerators::BreadthFirst.new(nodes.last.element).each do |element|
131
+ nodes << node = Node.new(element)
132
+ edges << Edge.new(node, @edge_queue.shift)
133
+ @edge_queue.concat Array.new(element.size_of(:children), node)
134
+ end
135
+ end
136
+
137
+ ##
138
+ # Generate the `dot` graph code. You should take this string and
139
+ # feed it to the `dot` program to have it generate the graph.
140
+ #
141
+ # @return [String]
142
+ def to_dot
143
+ graph = "digraph {\n"
144
+ graph << nodes.map(&:to_dot).join("\n")
145
+ graph << "\n\n"
146
+ graph << edges.map(&:to_dot).join("\n")
147
+ graph << "\n}\n"
148
+ end
149
+
150
+ end
@@ -12,27 +12,16 @@
12
12
  #
13
13
  module Accessibility::PPInspector
14
14
 
15
-
16
- protected
17
-
18
- ##
19
- # Added for backwards compatability with Snow Leopard.
20
- #
21
- # @return [String]
22
- KAXIdentifierAttribute = 'AXIdentifier'.freeze
23
-
24
15
  ##
25
- # @todo I feel a bit bad about having such a large method that has
26
- # some inefficiencies.
27
- #
28
16
  # Create an identifier for `self` using various attributes that should
29
17
  # make it very easy to identify the element.
30
18
  #
31
19
  # @return [String]
32
20
  def pp_identifier
33
- # use or lack of use of #inspect is intentional for visual effect
21
+ # @todo Break this method up into chunks
22
+ # @note use, or lack of use, of #inspect is intentional for visual effect
34
23
 
35
- if attributes.include? KAXValueAttribute
24
+ if attributes.include? :value
36
25
  val = attribute :value
37
26
  if val.kind_of? NSString
38
27
  return " #{val.inspect}" unless val.empty?
@@ -42,27 +31,27 @@ module Accessibility::PPInspector
42
31
  end
43
32
  end
44
33
 
45
- if attributes.include? KAXTitleAttribute
34
+ if attributes.include? :title
46
35
  val = attribute(:title)
47
36
  return " #{val.inspect}" if val && !val.empty?
48
37
  end
49
38
 
50
- if attributes.include? KAXTitleUIElementAttribute
39
+ if attributes.include? :title_ui_element
51
40
  val = attribute :title_ui_element
52
- return BUFFER + val.inspect if val
41
+ return " #{val.inspect}" if val
53
42
  end
54
43
 
55
- if attributes.include? KAXDescriptionAttribute
44
+ if attributes.include? :description
56
45
  val = attribute(:description).to_s
57
- return BUFFER + val unless val.empty?
46
+ return " #{val}" unless val.empty?
58
47
  end
59
48
 
60
- if attributes.include? KAXIdentifierAttribute
49
+ if attributes.include? :identifier
61
50
  return " id=#{attribute(:identifier)}"
62
51
  end
63
52
 
64
53
  # @todo should we have other fallbacks?
65
- return ::EMPTY_STRING
54
+ return EMPTY_STRING
66
55
  end
67
56
 
68
57
  ##
@@ -75,7 +64,7 @@ module Accessibility::PPInspector
75
64
  if position
76
65
  " (#{position.x}, #{position.y})"
77
66
  else
78
- ::EMPTY_STRING
67
+ EMPTY_STRING
79
68
  end
80
69
  end
81
70
 
@@ -89,9 +78,9 @@ module Accessibility::PPInspector
89
78
  if child_count > 1
90
79
  " #{child_count} children"
91
80
  elsif child_count == 1
92
- ' 1 child'
81
+ ONE_CHILD
93
82
  else # there are some odd edge cases
94
- ::EMPTY_STRING
83
+ EMPTY_STRING
95
84
  end
96
85
  end
97
86
 
@@ -103,7 +92,7 @@ module Accessibility::PPInspector
103
92
  # @param [Symbol]
104
93
  # @return [String]
105
94
  def pp_checkbox attr
106
- " #{attr}[#{attribute(attr) ? '✔' : '✘'}]"
95
+ " #{attr}[#{attribute(attr) ? CHECKMARK : CROSS }]"
107
96
  end
108
97
 
109
98
 
@@ -112,10 +101,32 @@ module Accessibility::PPInspector
112
101
  ##
113
102
  # @private
114
103
  #
115
- # A string with a single space, used as a buffer. This is a
116
- # performance hack.
104
+ # Constant string used by {#pp_checkbox}.
105
+ #
106
+ # @return [String]
107
+ CHECKMARK = '✔'
108
+
109
+ ##
110
+ # @private
111
+ #
112
+ # Constant string used by {#pp_checkbox}.
113
+ #
114
+ # @return [String]
115
+ CROSS = '✘'
116
+
117
+ ##
118
+ # @private
119
+ #
120
+ # Constant string used by {#pp_children}.
117
121
  #
118
122
  # @return [String]
119
- BUFFER = ' '.freeze
123
+ ONE_CHILD = ' 1 child'
120
124
 
125
+ ##
126
+ # @private
127
+ #
128
+ # Constant used all over the place.
129
+ #
130
+ # @return [String]
131
+ EMPTY_STRING = ''
121
132
  end