inch 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +335 -3
  4. data/Rakefile +8 -0
  5. data/TODOS.md +12 -0
  6. data/bin/inch +17 -0
  7. data/inch.gemspec +7 -2
  8. data/lib/inch.rb +6 -1
  9. data/lib/inch/cli.rb +24 -0
  10. data/lib/inch/cli/arguments.rb +45 -0
  11. data/lib/inch/cli/command.rb +29 -0
  12. data/lib/inch/cli/command/base.rb +62 -0
  13. data/lib/inch/cli/command/base_list.rb +75 -0
  14. data/lib/inch/cli/command/base_object.rb +40 -0
  15. data/lib/inch/cli/command/console.rb +22 -0
  16. data/lib/inch/cli/command/inspect.rb +20 -0
  17. data/lib/inch/cli/command/list.rb +25 -0
  18. data/lib/inch/cli/command/options/base.rb +137 -0
  19. data/lib/inch/cli/command/options/base_list.rb +84 -0
  20. data/lib/inch/cli/command/options/base_object.rb +47 -0
  21. data/lib/inch/cli/command/options/console.rb +26 -0
  22. data/lib/inch/cli/command/options/inspect.rb +25 -0
  23. data/lib/inch/cli/command/options/list.rb +30 -0
  24. data/lib/inch/cli/command/options/show.rb +27 -0
  25. data/lib/inch/cli/command/options/stats.rb +20 -0
  26. data/lib/inch/cli/command/options/suggest.rb +61 -0
  27. data/lib/inch/cli/command/output/base.rb +32 -0
  28. data/lib/inch/cli/command/output/console.rb +45 -0
  29. data/lib/inch/cli/command/output/inspect.rb +129 -0
  30. data/lib/inch/cli/command/output/list.rb +87 -0
  31. data/lib/inch/cli/command/output/show.rb +79 -0
  32. data/lib/inch/cli/command/output/stats.rb +111 -0
  33. data/lib/inch/cli/command/output/suggest.rb +104 -0
  34. data/lib/inch/cli/command/show.rb +20 -0
  35. data/lib/inch/cli/command/stats.rb +20 -0
  36. data/lib/inch/cli/command/suggest.rb +104 -0
  37. data/lib/inch/cli/command_parser.rb +82 -0
  38. data/lib/inch/cli/sparkline_helper.rb +31 -0
  39. data/lib/inch/cli/trace_helper.rb +42 -0
  40. data/lib/inch/cli/yardopts_helper.rb +49 -0
  41. data/lib/inch/code_object.rb +8 -0
  42. data/lib/inch/code_object/docstring.rb +97 -0
  43. data/lib/inch/code_object/nodoc_helper.rb +84 -0
  44. data/lib/inch/code_object/proxy.rb +37 -0
  45. data/lib/inch/code_object/proxy/base.rb +194 -0
  46. data/lib/inch/code_object/proxy/class_object.rb +9 -0
  47. data/lib/inch/code_object/proxy/constant_object.rb +8 -0
  48. data/lib/inch/code_object/proxy/method_object.rb +118 -0
  49. data/lib/inch/code_object/proxy/method_parameter_object.rb +81 -0
  50. data/lib/inch/code_object/proxy/module_object.rb +8 -0
  51. data/lib/inch/code_object/proxy/namespace_object.rb +38 -0
  52. data/lib/inch/core_ext.rb +2 -0
  53. data/lib/inch/core_ext/string.rb +3 -0
  54. data/lib/inch/core_ext/yard.rb +4 -0
  55. data/lib/inch/evaluation.rb +35 -0
  56. data/lib/inch/evaluation/base.rb +60 -0
  57. data/lib/inch/evaluation/class_object.rb +6 -0
  58. data/lib/inch/evaluation/constant_object.rb +34 -0
  59. data/lib/inch/evaluation/file.rb +66 -0
  60. data/lib/inch/evaluation/method_object.rb +127 -0
  61. data/lib/inch/evaluation/module_object.rb +6 -0
  62. data/lib/inch/evaluation/namespace_object.rb +94 -0
  63. data/lib/inch/evaluation/role/base.rb +49 -0
  64. data/lib/inch/evaluation/role/constant.rb +43 -0
  65. data/lib/inch/evaluation/role/method.rb +60 -0
  66. data/lib/inch/evaluation/role/method_parameter.rb +46 -0
  67. data/lib/inch/evaluation/role/missing.rb +20 -0
  68. data/lib/inch/evaluation/role/namespace.rb +58 -0
  69. data/lib/inch/evaluation/role/object.rb +64 -0
  70. data/lib/inch/evaluation/score_range.rb +26 -0
  71. data/lib/inch/rake.rb +1 -0
  72. data/lib/inch/rake/suggest.rb +26 -0
  73. data/lib/inch/source_parser.rb +36 -0
  74. data/lib/inch/version.rb +1 -1
  75. data/test/fixtures/code_examples/lib/foo.rb +87 -0
  76. data/test/fixtures/readme/lib/foo.rb +17 -0
  77. data/test/fixtures/simple/README +25 -0
  78. data/test/fixtures/simple/lib/broken.rb +10 -0
  79. data/test/fixtures/simple/lib/foo.rb +214 -0
  80. data/test/fixtures/simple/lib/role_methods.rb +78 -0
  81. data/test/fixtures/simple/lib/role_namespaces.rb +68 -0
  82. data/test/fixtures/visibility/lib/foo.rb +18 -0
  83. data/test/fixtures/yardopts/.yardopts +1 -0
  84. data/test/fixtures/yardopts/foo/bar.rb +6 -0
  85. data/test/inch/cli/arguments_test.rb +70 -0
  86. data/test/inch/cli/command/console_test.rb +59 -0
  87. data/test/inch/cli/command/inspect_test.rb +59 -0
  88. data/test/inch/cli/command/list_test.rb +61 -0
  89. data/test/inch/cli/command/show_test.rb +59 -0
  90. data/test/inch/cli/command/stats_test.rb +57 -0
  91. data/test/inch/cli/command/suggest_test.rb +57 -0
  92. data/test/inch/cli/command_parser_test.rb +33 -0
  93. data/test/inch/cli/yardopts_helper_test.rb +84 -0
  94. data/test/inch/code_object/docstring_test.rb +204 -0
  95. data/test/inch/code_object/nodoc_helper_test.rb +38 -0
  96. data/test/inch/code_object/proxy_test.rb +188 -0
  97. data/test/inch/source_parser_test.rb +23 -0
  98. data/test/integration/stats_options_test.rb +34 -0
  99. data/test/integration/visibility_options_test.rb +79 -0
  100. data/test/test_helper.rb +21 -0
  101. metadata +184 -7
@@ -0,0 +1,82 @@
1
+ # This was adapted from https://github.com/lsegal/yard/blob/master/lib/yard/cli/command_parser.rb
2
+ module Inch
3
+ module CLI
4
+ # This class parses a command name out of the +inch+ CLI command and calls
5
+ # that command in the form:
6
+ #
7
+ # $ inch command_name [options]
8
+ #
9
+ # If no command or arguments are specified, or if the arguments immediately
10
+ # begin with a +--opt+ (not +--help+), the {default_command} will be used
11
+ # (which itself defaults to +:doc+).
12
+ #
13
+ class CommandParser
14
+ include TraceHelper
15
+
16
+ class << self
17
+ # @return [Hash{Symbol => Command}] the mapping of command names to
18
+ # command classes to parse the user command.
19
+ attr_accessor :commands
20
+
21
+ # @return [Symbol] the default command name to use when no options
22
+ # are specified or
23
+ attr_accessor :default_command
24
+ end
25
+
26
+ self.commands = {
27
+ :console => Command::Console,
28
+ :inspect => Command::Inspect,
29
+ :list => Command::List,
30
+ :show => Command::Show,
31
+ :stats => Command::Stats,
32
+ :suggest => Command::Suggest,
33
+ }
34
+
35
+ self.default_command = :suggest
36
+
37
+ # Convenience method to create a new CommandParser and call {#run}
38
+ # @return (see #run)
39
+ def self.run(*args)
40
+ new.run(*args)
41
+ end
42
+
43
+ def initialize
44
+ #log.show_backtraces = false
45
+ end
46
+
47
+ # Runs the {Command} object matching the command name of the first
48
+ # argument.
49
+ # @return [void]
50
+ def run(*args)
51
+ unless ['--help', '-h'].include?(args.join)
52
+ if args.size == 0 || args.first =~ /^-/
53
+ command_name = self.class.default_command
54
+ else
55
+ command_name = args.first.to_sym
56
+ args.shift
57
+ end
58
+ if commands.has_key?(command_name)
59
+ return commands[command_name].run(*args)
60
+ end
61
+ end
62
+ list_commands
63
+ end
64
+
65
+ private
66
+
67
+ def commands
68
+ self.class.commands
69
+ end
70
+
71
+ def list_commands
72
+ trace "Usage: inch <command> [options]"
73
+ trace
74
+ trace "Commands:"
75
+ commands.keys.sort_by {|k| k.to_s }.each do |command_name|
76
+ command = commands[command_name].new
77
+ trace " %-8s %s" % [command_name, command.description]
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ module Inch
2
+ module CLI
3
+ module SparklineHelper
4
+ def ranges_sparkline(_ranges)
5
+ ranges = _ranges.reverse
6
+ list = ranges.map { |r| r.objects.size }
7
+ sparkline = Sparkr::Sparkline.new(list)
8
+ sparkline.format do |tick, count, index|
9
+ t = tick.color(ranges[index].color)
10
+ index == 0 ? t + ' ' : t
11
+ end
12
+ end
13
+
14
+ def grades_sparkline(objects)
15
+ grades = {}
16
+ objects.each do |o|
17
+ grades[o.grade] ||= 0
18
+ grades[o.grade] += 1
19
+ end
20
+ ranges = Evaluation.new_score_ranges.reverse
21
+ order = ranges.map(&:grade)
22
+ list = order.map { |g| grades[g] }
23
+ sparkline = Sparkr::Sparkline.new(list)
24
+ sparkline.format do |tick, count, index|
25
+ t = tick.color(ranges[index].color)
26
+ index == 0 ? t + ' ' : t
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ module Inch
2
+ module CLI
3
+ module TraceHelper
4
+ def debug(msg)
5
+ return unless ENV['DEBUG']
6
+ msg.to_s.lines.each do |line|
7
+ trace edged :dark, line.gsub(/\n$/,'').dark
8
+ end
9
+ end
10
+
11
+ # Writes the given +text+ to stdout
12
+ # @param text [String]
13
+ # @return [void]
14
+ def trace(text = "")
15
+ puts text
16
+ end
17
+
18
+ def trace_header(text, color, bg_color = nil)
19
+ trace header(text, color, bg_color)
20
+ trace if !use_color?
21
+ end
22
+
23
+ private
24
+
25
+ def edged(color, msg, edge = "┃ ")
26
+ edge.color(color) + msg
27
+ end
28
+
29
+ def header(text, color, bg_color = nil)
30
+ bg_color ||= "intense_#{color}"
31
+ bar = " #{text}".ljust(CLI::COLUMNS-1)
32
+ .on_color(bg_color).color(:color16)
33
+ "#".color(color).on_color(color) + bar
34
+ end
35
+
36
+ def use_color?
37
+ Term::ANSIColor::coloring?
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ module Inch
2
+ module CLI
3
+ module YardoptsHelper
4
+ # @return [Array<String>] list of Ruby source files to process
5
+ attr_accessor :yard_files
6
+
7
+ # @return [Array<String>] list of excluded paths (regexp matches)
8
+ attr_accessor :excluded
9
+
10
+ VALID_YARD_SWITCHES = %w(--private --no-private --protected --no-public
11
+ --plugin --load --safe --yardopts --no-yardopts --document --no-document)
12
+
13
+ # Parses the option and gracefully handles invalid switches
14
+ #
15
+ # @param [OptionParser] opts the option parser object
16
+ # @param [Array<String>] args the arguments passed from input. This
17
+ # array will be modified.
18
+ # @return [void]
19
+ def parse_yardopts_options(opts, args)
20
+ wrapper = YardoptsWrapper.new()
21
+
22
+ dupped_args = args.dup
23
+ dupped_args.delete("--help")
24
+ dupped_args.delete_if do |arg|
25
+ arg =~ /^\-/ && !VALID_YARD_SWITCHES.include?(arg)
26
+ end
27
+
28
+ debug "Sending args to YARD:\n" \
29
+ " args: #{dupped_args}"
30
+
31
+ wrapper.parse_arguments(*dupped_args)
32
+
33
+ self.yard_files = wrapper.files
34
+ self.excluded = wrapper.excluded
35
+ end
36
+
37
+ def yardopts_options(opts)
38
+ wrapper = YardoptsWrapper.new()
39
+ wrapper.add_yardoc_options(opts)
40
+ end
41
+
42
+ class YardoptsWrapper < YARD::CLI::Yardoc
43
+ def add_yardoc_options(opts)
44
+ yardopts_options(opts)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ module Inch
2
+ module CodeObject
3
+ end
4
+ end
5
+
6
+ require_relative 'code_object/docstring'
7
+ require_relative 'code_object/nodoc_helper'
8
+ require_relative 'code_object/proxy'
@@ -0,0 +1,97 @@
1
+ module Inch
2
+ module CodeObject
3
+ class Docstring
4
+ def initialize(text)
5
+ @text = text.to_s
6
+ end
7
+
8
+ def empty?
9
+ @text.strip.empty?
10
+ end
11
+
12
+ def contains_code_example?
13
+ !code_examples.empty?
14
+ end
15
+
16
+ def code_examples
17
+ @code_examples ||= parse_code_examples
18
+ end
19
+
20
+ def describes_parameter?(name)
21
+ describe_parameter_regexps(name).any? do |pattern|
22
+ @text.index(pattern)
23
+ end
24
+ end
25
+
26
+ def mentions_parameter?(name)
27
+ mention_parameter_regexps(name).any? do |pattern|
28
+ @text.index(pattern)
29
+ end
30
+ end
31
+
32
+ def mentions_return?
33
+ @text.lines.last =~ /^Returns\ /
34
+ end
35
+
36
+ def parse_code_examples
37
+ code_examples = []
38
+ example = nil
39
+ @text.lines.each_with_index do |line, index|
40
+ if line =~/^\s*+$/
41
+ code_examples << example if example
42
+ example = []
43
+ elsif line =~/^\ {2,}\S+/
44
+ example << line if example
45
+ else
46
+ code_examples << example if example
47
+ example = nil
48
+ end
49
+ end
50
+ code_examples << example if example
51
+ code_examples.delete_if(&:empty?).map(&:join)
52
+ end
53
+
54
+ private
55
+
56
+ def mention_parameter_patterns(name)
57
+ [
58
+ "+#{name}+",
59
+ "+#{name}+::",
60
+ "<tt>#{name}</tt>",
61
+ "<tt>#{name}</tt>::",
62
+ "#{name}::",
63
+ /^#{name}\ \-\ /
64
+ ]
65
+ end
66
+
67
+ def describe_parameter_extra_regexps(name)
68
+ [
69
+ "#{name}::",
70
+ "+#{name}+::",
71
+ "<tt>#{name}</tt>::",
72
+ ].map do |pattern|
73
+ r = pattern.is_a?(Regexp) ? pattern : Regexp.escape(pattern)
74
+ /#{r}\n\ {2,}.+/m
75
+ end
76
+ end
77
+
78
+ def describe_parameter_regexps(name)
79
+ mention_parameter_patterns(name).map do |pattern|
80
+ r = pattern.is_a?(Regexp) ? pattern : Regexp.escape(pattern)
81
+ /^#{r}\s?\S+/
82
+ end + describe_parameter_extra_regexps(name)
83
+ end
84
+
85
+ def mention_parameter_regexps(name)
86
+ mention_parameter_patterns(name).map do |pattern|
87
+ if pattern.is_a?(Regexp)
88
+ pattern
89
+ else
90
+ r = Regexp.escape(pattern)
91
+ /\W#{r}\W/
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,84 @@
1
+ module Inch
2
+ module CodeObject
3
+ module NodocHelper
4
+
5
+ # Returns true if the code object is somehow marked not to be
6
+ # documented.
7
+ #
8
+ # @note Doesnot recognize ":startdoc:" and ":stopdoc:"
9
+ #
10
+ def nodoc?
11
+ object.tag(:private) || nodoc_comment?
12
+ end
13
+
14
+ NO_DOC_REGEX = /#\s*\:nodoc\:/
15
+ NO_DOC_ALL_REGEX = /#\s*\:nodoc\:\s*all/
16
+ DOC_REGEX = /#\s*\:doc\:/
17
+
18
+ def nodoc_comment?
19
+ explicit_nodoc_comment? || implicit_nodoc_comment?
20
+ end
21
+
22
+ def explicit_nodoc_comment?
23
+ declarations.any? { |str| str =~ NO_DOC_REGEX }
24
+ end
25
+
26
+ def explicit_nodoc_all_comment?
27
+ declarations.any? { |str| str =~ NO_DOC_ALL_REGEX }
28
+ end
29
+
30
+ def explicit_doc_comment?
31
+ declarations.any? { |str| str =~ DOC_REGEX }
32
+ end
33
+
34
+ def implicit_nodoc_all_comment?
35
+ if parent
36
+ parent.explicit_nodoc_all_comment? ||
37
+ parent.implicit_nodoc_all_comment?
38
+ end
39
+ end
40
+
41
+ def implicit_nodoc_comment?
42
+ return false if explicit_doc_comment?
43
+
44
+ if parent
45
+ return false if parent.explicit_doc_comment?
46
+
47
+ if namespace?
48
+ if parent.explicit_nodoc_all_comment?
49
+ return true
50
+ else
51
+ return parent.implicit_nodoc_all_comment?
52
+ end
53
+ else
54
+ if parent.explicit_nodoc_comment?
55
+ return true
56
+ else
57
+ return parent.implicit_nodoc_all_comment?
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def declarations
64
+ @declarations ||= files.map do |(filename, line_no)|
65
+ get_line_no(filename, line_no)
66
+ end
67
+ end
68
+
69
+ def files
70
+ object.files
71
+ rescue YARD::CodeObjects::ProxyMethodError
72
+ []
73
+ end
74
+
75
+ def get_line_no(filename, n)
76
+ f = File.open(filename)
77
+ n.times{f.gets}
78
+ result = $_
79
+ f.close
80
+ result
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ module Inch
2
+ module CodeObject
3
+ module Proxy
4
+ class << self
5
+ def for(code_object)
6
+ @cache ||= {}
7
+ if proxy_object = @cache[cache_key(code_object)]
8
+ proxy_object
9
+ else
10
+ @cache[cache_key(code_object)] = class_for(code_object).new(code_object)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def class_for(code_object)
17
+ class_name = code_object.class.to_s.split('::').last
18
+ eval("::Inch::CodeObject::Proxy::#{class_name}")
19
+ rescue
20
+ Base
21
+ end
22
+
23
+ def cache_key(o)
24
+ o.path
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ require_relative 'proxy/base'
32
+ require_relative 'proxy/namespace_object'
33
+ require_relative 'proxy/class_object'
34
+ require_relative 'proxy/constant_object'
35
+ require_relative 'proxy/method_object'
36
+ require_relative 'proxy/method_parameter_object'
37
+ require_relative 'proxy/module_object'
@@ -0,0 +1,194 @@
1
+ require 'forwardable'
2
+
3
+ module Inch
4
+ module CodeObject
5
+ module Proxy
6
+ class Base
7
+ extend Forwardable
8
+ include NodocHelper
9
+
10
+ # the actual (YARD) code object
11
+ attr_accessor :object
12
+
13
+ # @return [Symbol]
14
+ # when objects are assigned to ScoreRanges, this grade is set to
15
+ # enable easier querying for objects of a certain grade
16
+ attr_writer :grade
17
+
18
+ # Tags considered by wrapper methods like {#has_code_example?}
19
+ CONSIDERED_YARD_TAGS = %w(example param private return)
20
+
21
+ # convenient shortcuts to (YARD) code object
22
+ def_delegators :object, :type, :path, :name, :namespace, :source, :source_type, :signature, :group, :dynamic, :visibility, :docstring
23
+
24
+ # convenient shortcuts to evalution object
25
+ def_delegators :evaluation, :score, :roles, :priority
26
+
27
+ def initialize(object)
28
+ self.object = object
29
+ end
30
+
31
+ # To be overridden
32
+ # @see Proxy::NamespaceObject
33
+ # @return [Array,nil] the children of the current object or +nil+
34
+ def children
35
+ nil
36
+ end
37
+
38
+ def docstring
39
+ @docstring ||= Docstring.new(object.docstring)
40
+ end
41
+
42
+ def evaluation
43
+ @evaluation ||= Evaluation.for(self)
44
+ end
45
+
46
+ # Returns the name of the file where the object is declared first
47
+ # @return [String] a filename
48
+ def filename
49
+ # just checking the first file (which is the file where an object
50
+ # is first declared)
51
+ files.size > 0 ? files[0][0] : nil
52
+ end
53
+
54
+ def grade
55
+ @grade ||= Evaluation.new_score_ranges.detect { |range|
56
+ range.range.include?(score)
57
+ }.grade
58
+ end
59
+
60
+ def has_alias?
61
+ !object.aliases.empty?
62
+ end
63
+
64
+ def has_code_example?
65
+ !object.tags(:example).empty? ||
66
+ docstring.contains_code_example?
67
+ end
68
+
69
+ def has_doc?
70
+ !docstring.empty?
71
+ end
72
+
73
+ def has_multiple_code_examples?
74
+ if object.tags(:example).size > 1 || docstring.code_examples.size > 1
75
+ true
76
+ else
77
+ if tag = object.tag(:example)
78
+ multi_code_examples?(tag.text)
79
+ elsif text = docstring.code_examples.first
80
+ multi_code_examples?(text)
81
+ else
82
+ false
83
+ end
84
+ end
85
+ end
86
+
87
+ def has_unconsidered_tags?
88
+ !unconsidered_tags.empty?
89
+ end
90
+
91
+ def in_root?
92
+ depth == 1
93
+ end
94
+
95
+ # The depth of the following is 4:
96
+ #
97
+ # Foo::Bar::Baz#initialize
98
+ # ^ ^ ^ ^
99
+ # 1 << 2 << 3 << 4
100
+ #
101
+ # +depth+ answers the question "how many layers of code objects are
102
+ # above this one?"
103
+ #
104
+ # @note top-level counts, that's why Foo has depth 1!
105
+ #
106
+ # @param i [Fixnum] a counter for recursive method calls
107
+ # @return [Fixnum] the depth of the object in terms of namespace
108
+ def depth(i = 0)
109
+ if parent
110
+ parent.depth(i+1)
111
+ else
112
+ i
113
+ end
114
+ end
115
+
116
+ # In the following example, the height of +Foo+ is 3
117
+ # (the height of the top-level is 4):
118
+ #
119
+ # Foo::Bar::Baz#initialize
120
+ # ^ ^ ^ ^
121
+ # 0 >> 1 >> 2 >> 3
122
+ #
123
+ # +height+ answers the question "how many layers of code objects are
124
+ # underneath this one?"
125
+ #
126
+ # @param i [Fixnum] a counter for recursive method calls
127
+ # @return [Fixnum] the height of the object in terms of namespace
128
+ def height(i = 0)
129
+ if children && !children.empty?
130
+ children.map do |child|
131
+ child.height(i+1)
132
+ end.max
133
+ else
134
+ i
135
+ end
136
+ end
137
+
138
+ # @return [Boolean] +true+ if the object represents a method
139
+ def method?
140
+ false
141
+ end
142
+
143
+ # @return [Boolean] +true+ if the object represents a namespace
144
+ def namespace?
145
+ false
146
+ end
147
+
148
+ # @return [Array,nil] the parent of the current object or +nil+
149
+ def parent
150
+ Proxy.for(object.parent) if object.parent
151
+ end
152
+
153
+ def private?
154
+ visibility == :private
155
+ end
156
+
157
+ def private_tag?
158
+ !!object.tag(:private)
159
+ end
160
+
161
+ def protected?
162
+ visibility == :protected
163
+ end
164
+
165
+ def public?
166
+ visibility == :public
167
+ end
168
+
169
+ # @return [Boolean] +true+ if the object has no documentation at all
170
+ def undocumented?
171
+ docstring.empty? && object.tags.empty?
172
+ end
173
+
174
+ # @return [Array]
175
+ # YARD tags that are not already covered by other wrapper methods
176
+ def unconsidered_tags
177
+ @unconsidered_tags ||= object.tags.reject do |tag|
178
+ CONSIDERED_YARD_TAGS.include?(tag.tag_name)
179
+ end
180
+ end
181
+
182
+ def inspect
183
+ "#<#{self.class.to_s}: #{path}>"
184
+ end
185
+
186
+ private
187
+
188
+ def multi_code_examples?(text)
189
+ text =~ /\b#{Regexp.escape(name)}[^_0-9\!\?]/
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end