inch 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +170 -56
  3. data/TODOS.md +0 -6
  4. data/config/defaults.rb +82 -0
  5. data/lib/inch/cli/command/base.rb +9 -0
  6. data/lib/inch/cli/command/base_list.rb +8 -8
  7. data/lib/inch/cli/command/base_object.rb +1 -1
  8. data/lib/inch/cli/command/list.rb +6 -1
  9. data/lib/inch/cli/command/options/base.rb +1 -1
  10. data/lib/inch/cli/command/options/suggest.rb +3 -1
  11. data/lib/inch/cli/command/output/list.rb +6 -6
  12. data/lib/inch/cli/command/output/show.rb +3 -3
  13. data/lib/inch/cli/command/output/stats.rb +52 -33
  14. data/lib/inch/cli/command/output/suggest.rb +7 -7
  15. data/lib/inch/cli/command/show.rb +5 -0
  16. data/lib/inch/cli/command/stats.rb +6 -1
  17. data/lib/inch/cli/command/suggest.rb +31 -12
  18. data/lib/inch/cli/command.rb +7 -14
  19. data/lib/inch/cli/command_parser.rb +2 -9
  20. data/lib/inch/cli/sparkline_helper.rb +13 -13
  21. data/lib/inch/cli/weighted_list.rb +85 -0
  22. data/lib/inch/cli.rb +2 -1
  23. data/lib/inch/code_object/nodoc_helper.rb +2 -0
  24. data/lib/inch/code_object/proxy/base.rb +36 -13
  25. data/lib/inch/code_object/proxy/method_object.rb +29 -0
  26. data/lib/inch/code_object/proxy/namespace_object.rb +6 -0
  27. data/lib/inch/config.rb +30 -37
  28. data/lib/inch/evaluation/base.rb +53 -1
  29. data/lib/inch/evaluation/constant_object.rb +8 -11
  30. data/lib/inch/evaluation/file.rb +1 -1
  31. data/lib/inch/evaluation/grade.rb +41 -0
  32. data/lib/inch/evaluation/grade_list.rb +32 -0
  33. data/lib/inch/evaluation/method_object.rb +10 -51
  34. data/lib/inch/evaluation/namespace_object.rb +8 -49
  35. data/lib/inch/evaluation/{criteria.rb → object_schema.rb} +12 -22
  36. data/lib/inch/evaluation/read_write_methods.rb +21 -0
  37. data/lib/inch/evaluation/role/method.rb +8 -0
  38. data/lib/inch/evaluation.rb +5 -2
  39. data/lib/inch/version.rb +1 -1
  40. data/lib/inch.rb +3 -1
  41. data/test/fixtures/simple/lib/broken.rb +15 -0
  42. data/test/fixtures/simple/lib/role_methods.rb +15 -0
  43. data/test/inch/cli/weighted_list_test.rb +55 -0
  44. data/test/inch/code_object/proxy/method_object_test.rb +42 -0
  45. data/test/integration/stats_options_test.rb +1 -1
  46. metadata +11 -5
  47. data/lib/inch/evaluation/score_range.rb +0 -38
@@ -16,10 +16,10 @@ module Inch
16
16
  [203, 203, 204, 204, 205, 206, 207]
17
17
  ].flatten.map { |s| :"color#{s}" }
18
18
 
19
- def initialize(options, objects, ranges)
19
+ def initialize(options, objects, grade_lists)
20
20
  @options = options
21
21
  @objects = objects
22
- @ranges = ranges
22
+ @grade_lists = grade_lists
23
23
 
24
24
  method("display_#{@options.format}").call
25
25
  end
@@ -27,19 +27,23 @@ module Inch
27
27
  private
28
28
 
29
29
  def display_text
30
- display_text_grades
31
- display_text_priorities
30
+ print_grades
31
+ print_grades_by_priority
32
+ print_priorities
32
33
  puts
33
34
  puts 'Try `--format json|yaml` for raw numbers.'.dark
34
35
  end
35
36
 
36
- def display_text_grades
37
- sparkline = ranges_sparkline(@ranges).to_s(' ')
37
+ def print_grades
38
+ sparkline = grade_lists_sparkline(@grade_lists).to_s(' ')
38
39
  puts
39
40
  puts 'Grade distribution: (undocumented, C, B, A)'
40
41
  puts
41
42
  puts " Overall: #{sparkline} #{objects.size.to_s.rjust(5)} objects"
42
43
  puts
44
+ end
45
+
46
+ def print_grades_by_priority
43
47
  puts 'Grade distribution by priority:'
44
48
  puts
45
49
  PRIORITY_MAP.each do |priority_range, arrow|
@@ -51,25 +55,29 @@ module Inch
51
55
  end
52
56
  end
53
57
 
54
- def display_text_priorities
58
+ def print_grade_list(grade_list)
59
+ list = grade_list.objects.map(&:priority)
60
+
61
+ priorities = {}
62
+ (-7..7).each do |key|
63
+ priorities[key.to_s] = list.select { |p| p == key }.size
64
+ end
65
+
66
+ sparkline = Sparkr::Sparkline.new(priorities.values)
67
+ sparkline.format do |tick, count, index|
68
+ tick.color( PRIORITY_COLORS[index] )
69
+ end
70
+ puts " #{grade_list.grade}: " + sparkline.to_s(' ') +
71
+ " #{grade_list.objects.size.to_s.rjust(5)} objects"
72
+ puts
73
+ end
74
+
75
+ def print_priorities
55
76
  puts "Priority distribution in grades: (low to high)"
56
77
  puts
57
78
  puts " #{PRIORITY_MAP.values.reverse.join(' ')}"
58
- @ranges.reverse.each do |range|
59
- list = range.objects.map(&:priority)
60
-
61
- priorities = {}
62
- (-7..7).each do |key|
63
- priorities[key.to_s] = list.select { |p| p == key }.size
64
- end
65
-
66
- sparkline = Sparkr::Sparkline.new(priorities.values)
67
- sparkline.format do |tick, count, index|
68
- tick.color( PRIORITY_COLORS[index] )
69
- end
70
- puts " #{range.grade}: " + sparkline.to_s(' ') +
71
- " #{range.objects.size.to_s.rjust(5)} objects"
72
- puts
79
+ @grade_lists.reverse.each do |grade_list|
80
+ print_grade_list(grade_list)
73
81
  end
74
82
  end
75
83
 
@@ -82,25 +90,36 @@ module Inch
82
90
  end
83
91
 
84
92
  def stats_hash
85
- hash = {}
93
+ {
94
+ 'grade_lists' => __grade_lists,
95
+ 'scores' => __scores,
96
+ 'priorities' => __priorities
97
+ }
98
+ end
86
99
 
87
- hash['ranges'] = {}
88
- @ranges.each do |r|
89
- hash['ranges'][r.grade.to_s] = r.objects.size
100
+ def __grade_lists
101
+ hash = {}
102
+ @grade_lists.each do |r|
103
+ hash[r.grade.to_s] = r.objects.size
90
104
  end
105
+ hash
106
+ end
91
107
 
92
- hash['scores'] = {}
108
+ def __scores
109
+ hash = {}
93
110
  @objects.sort_by(&:score).each do |o|
94
- hash['scores'][o.score.to_i] ||= 0
95
- hash['scores'][o.score.to_i] += 1
111
+ hash[o.score.to_i] ||= 0
112
+ hash[o.score.to_i] += 1
96
113
  end
114
+ hash
115
+ end
97
116
 
98
- hash['priorities'] = {}
117
+ def __priorities
118
+ hash = {}
99
119
  @objects.sort_by(&:priority).each do |o|
100
- hash['priorities'][o.priority.to_i] ||= 0
101
- hash['priorities'][o.priority.to_i] += 1
120
+ hash[o.priority.to_i] ||= 0
121
+ hash[o.priority.to_i] += 1
102
122
  end
103
-
104
123
  hash
105
124
  end
106
125
 
@@ -20,13 +20,13 @@ module Inch
20
20
  # @param options [Command::Options::Suggest]
21
21
  # @param objects_to_display [Array<CodeObject::Proxy::Base>]
22
22
  # @param relevant_objects [Array<CodeObject::Proxy::Base>] the objects meeting the criteria defined in +options+
23
- # @param ranges [Array<Evaluation::ScoreRange>]
23
+ # @param grade_lists [Array<Evaluation::GradeList>]
24
24
  # @param files [Array<Evaluation::File>]
25
- def initialize(options, objects_to_display, relevant_objects, ranges, files)
25
+ def initialize(options, objects_to_display, relevant_objects, grade_lists, files)
26
26
  @options = options
27
27
  @objects = objects_to_display
28
28
  @relevant_objects = relevant_objects
29
- @ranges = ranges
29
+ @grade_lists = grade_lists
30
30
  @files = files
31
31
 
32
32
  if objects.empty?
@@ -70,11 +70,11 @@ module Inch
70
70
 
71
71
  def display_list
72
72
  @options.grades_to_display.map do |grade|
73
- r = range(grade)
73
+ r = grade_list(grade)
74
74
  grade_objects = objects.select { |o| o.grade == r.grade }
75
75
  unless grade_objects.empty?
76
76
  trace
77
- trace_header(RANGE_LABELS[r.grade], r.color, r.bg_color)
77
+ trace_header(RANGE_LABELS[r.grade.to_sym], r.color, r.bg_color)
78
78
  grade_objects.each do |o|
79
79
  grade = o.grade.to_s.ljust(2).color(r.color)
80
80
  priority = o.priority
@@ -94,8 +94,8 @@ module Inch
94
94
  end.compact
95
95
  end
96
96
 
97
- def range(grade)
98
- @ranges.detect { |r| r.grade == grade }
97
+ def grade_list(grade_symbol)
98
+ @grade_lists.detect { |r| r.grade.to_sym == grade_symbol }
99
99
  end
100
100
  end
101
101
  end
@@ -1,7 +1,12 @@
1
+ require_relative 'options/show'
2
+ require_relative 'output/show'
3
+
1
4
  module Inch
2
5
  module CLI
3
6
  module Command
4
7
  class Show < BaseObject
8
+ register_command_as :show
9
+
5
10
  def description
6
11
  'Shows an object with its results'
7
12
  end
@@ -1,7 +1,12 @@
1
+ require_relative 'options/stats'
2
+ require_relative 'output/stats'
3
+
1
4
  module Inch
2
5
  module CLI
3
6
  module Command
4
7
  class Stats < List
8
+ register_command_as :stats
9
+
5
10
  def description
6
11
  'Lists all objects with their results'
7
12
  end
@@ -12,7 +17,7 @@ module Inch
12
17
 
13
18
  def run(*args)
14
19
  prepare_list(*args)
15
- Output::Stats.new(@options, objects, @ranges)
20
+ Output::Stats.new(@options, objects, @grade_lists)
16
21
  end
17
22
  end
18
23
  end
@@ -1,7 +1,12 @@
1
+ require_relative 'options/suggest'
2
+ require_relative 'output/suggest'
3
+
1
4
  module Inch
2
5
  module CLI
3
6
  module Command
4
7
  class Suggest < List
8
+ register_command_as :suggest, true
9
+
5
10
  def description
6
11
  'Suggests some objects to be doucmented (better)'
7
12
  end
@@ -17,29 +22,43 @@ module Inch
17
22
  # @return [void]
18
23
  def run(*args)
19
24
  prepare_list(*args)
20
- Output::Suggest.new(@options, objects_to_display, relevant_objects, @ranges, files)
25
+ Output::Suggest.new(@options, objects_to_display, relevant_objects, @grade_lists, files)
26
+ end
27
+
28
+ private
29
+
30
+ # @return [Fixnum] how many objects should be displayed in the output
31
+ def object_count
32
+ @options.object_count
33
+ end
34
+
35
+ # @return [Array<Fixnum>]
36
+ # how many objects of each grade should be displayed in the output
37
+ def object_list_counts
38
+ @options.grade_weights.map { |w| w * object_count }
21
39
  end
22
40
 
41
+ # @return [Array] the objects that should be displayed in the output
23
42
  def objects_to_display
24
43
  @objects_to_display ||= filter_objects_to_display
25
44
  end
26
45
 
46
+ # @return [Array] the objects that should be displayed in the output
27
47
  def filter_objects_to_display
28
- list = []
48
+ grade_list = []
29
49
  @options.grades_to_display.map do |grade|
30
- r = range(grade)
50
+ r = grade_list(grade)
31
51
  arr = select_by_priority(r.objects, @options.object_min_priority)
32
52
  arr = arr.select { |o| o.score <= @options.object_max_score }
33
- list.concat arr
53
+ grade_list << arr
34
54
  end
35
55
 
36
- list = sort_by_priority(list)
56
+ weighted_list = WeightedList.new(grade_list, object_list_counts)
57
+
58
+ list = sort_by_priority(weighted_list.to_a.flatten)
37
59
 
38
- if list.size > @options.object_count
39
- list = list[0...@options.object_count]
40
- elsif list.size < @options.object_count
41
- # should we add objects with lower priority to fill out the
42
- # requested count?
60
+ if list.size > object_count
61
+ list = list[0...object_count]
43
62
  end
44
63
  list
45
64
  end
@@ -78,8 +97,8 @@ module Inch
78
97
  objects.map(&:grade).uniq
79
98
  end
80
99
 
81
- def range(grade)
82
- @ranges.detect { |r| r.grade == grade }
100
+ def grade_list(grade_symbol)
101
+ @grade_lists.detect { |r| r.grade.to_sym == grade_symbol }
83
102
  end
84
103
 
85
104
  def relevant_objects
@@ -2,22 +2,15 @@ require_relative 'command/base'
2
2
  require_relative 'command/base_list'
3
3
  require_relative 'command/base_object'
4
4
 
5
- require_relative 'command/list'
6
- require_relative 'command/show'
7
- require_relative 'command/stats'
8
- require_relative 'command/suggest'
9
-
10
5
  require_relative 'command/options/base'
11
6
  require_relative 'command/options/base_list'
12
7
  require_relative 'command/options/base_object'
13
8
 
14
- require_relative 'command/options/list'
15
- require_relative 'command/options/show'
16
- require_relative 'command/options/stats'
17
- require_relative 'command/options/suggest'
18
-
19
9
  require_relative 'command/output/base'
20
- require_relative 'command/output/list'
21
- require_relative 'command/output/show'
22
- require_relative 'command/output/stats'
23
- require_relative 'command/output/suggest'
10
+
11
+ require_relative 'command_parser'
12
+
13
+ require_relative 'command/list'
14
+ require_relative 'command/show'
15
+ require_relative 'command/stats'
16
+ require_relative 'command/suggest'
@@ -43,15 +43,8 @@ module Inch
43
43
  attr_accessor :default_command
44
44
  end
45
45
 
46
- self.commands = {
47
- :list => Command::List,
48
- :show => Command::Show,
49
- :stats => Command::Stats,
50
- :suggest => Command::Suggest,
51
- }
52
-
53
- self.default_command = :suggest
54
-
46
+ self.commands = {}
47
+
55
48
  # Convenience method to create a new CommandParser and call {#run}
56
49
  # @return (see #run)
57
50
  def self.run(*args)
@@ -1,28 +1,28 @@
1
1
  module Inch
2
2
  module CLI
3
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
4
+ def grade_lists_sparkline(_grade_lists)
5
+ grade_lists = _grade_lists.reverse
6
+ list = grade_lists.map { |r| r.objects.size }
7
+ __sparkline(list, grade_lists)
12
8
  end
13
9
 
14
10
  def grades_sparkline(objects)
15
11
  grades = {}
16
12
  objects.each do |o|
17
- grades[o.grade] ||= 0
18
- grades[o.grade] += 1
13
+ grades[o.grade.to_sym] ||= 0
14
+ grades[o.grade.to_sym] += 1
19
15
  end
20
- ranges = Evaluation.new_score_ranges.reverse
21
- order = ranges.map(&:grade)
16
+ grade_lists = Evaluation.new_grade_lists.reverse
17
+ order = grade_lists.map(&:to_sym)
22
18
  list = order.map { |g| grades[g] }
19
+ __sparkline(list, grade_lists)
20
+ end
21
+
22
+ def __sparkline(list, grade_lists)
23
23
  sparkline = Sparkr::Sparkline.new(list)
24
24
  sparkline.format do |tick, count, index|
25
- t = tick.color(ranges[index].color)
25
+ t = tick.color(grade_lists[index].color)
26
26
  index == 0 ? t + ' ' : t
27
27
  end
28
28
  end
@@ -0,0 +1,85 @@
1
+ module Inch
2
+ module CLI
3
+ class WeightedList
4
+ # Trims down a list of object lists to given sizes.
5
+ # If there are not enough objects in any particular tier,
6
+ # they are filled up from the tier below/above.
7
+ #
8
+ # @example
9
+ #
10
+ # list = [
11
+ # [:B1, :B2],
12
+ # [:C1, :C2, :C3, :C4, :C5, :C6, :C7, :C8, :C9, :C10],
13
+ # [:U1, :U2, :U3, :U4, :U5, :U6, :U7, :U8, :U9, :U10]
14
+ # ]
15
+ # counts = [4, 8, 8]
16
+ #
17
+ # This means there should be 4 +:B+s, 8 +:C+s, and 8 +:U+s in the
18
+ # resulting list. But we only have 2 +:B+s, so the result is filled up
19
+ # with +:U+s:
20
+ #
21
+ # WeightedList.new(list, counts).to_a
22
+ # # => [
23
+ # [:B1, :B2],
24
+ # [:C1, :C2, :C3, :C4, :C5, :C6, :C7, :C8],
25
+ # [:U1, :U2, :U3, :U4, :U5, :U6, :U7, :U8, :U9, :U10]
26
+ # ]
27
+ #
28
+ # @param list_of_lists [Array<Array>]
29
+ # @param counts [Array<Fixnum>]
30
+ def initialize(list_of_lists, counts)
31
+ @original = list_of_lists
32
+ @max_counts = counts
33
+ compute_list
34
+ end
35
+
36
+ # @return [Array]
37
+ def to_a
38
+ @list
39
+ end
40
+
41
+ private
42
+
43
+ def compute_list
44
+ @list = init_empty_list(@original.size)
45
+ still_missing = fill_list_with_predefined_sizes
46
+
47
+ # all sublists are now filled up to their max capacity but we might
48
+ # have missed some objects (+still_missing+), because there weren't
49
+ # enough
50
+
51
+ @original.reverse_each.with_index do |sublist, rear_index|
52
+ if still_missing > 0
53
+ index = @original.size - rear_index - 1
54
+ present_count = @max_counts[index]
55
+ not_yet_in_list = sublist[present_count..-1]
56
+ if not_yet_in_list
57
+ put_in_list = not_yet_in_list[0...still_missing]
58
+ @list[index].concat put_in_list
59
+ still_missing -= put_in_list.size
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def init_empty_list(sublist_count)
66
+ list = []
67
+ sublist_count.times { list << [] }
68
+ list
69
+ end
70
+
71
+ def fill_list_with_predefined_sizes
72
+ missing = 0
73
+ @original.each_with_index do |sublist, index|
74
+ target_count = @max_counts[index]
75
+ @list[index].concat sublist[0...target_count]
76
+
77
+ if sublist.size < target_count
78
+ missing += target_count - sublist.size
79
+ end
80
+ end
81
+ missing
82
+ end
83
+ end
84
+ end
85
+ end
data/lib/inch/cli.rb CHANGED
@@ -17,11 +17,12 @@ module Inch
17
17
  end
18
18
 
19
19
  require_relative 'cli/arguments'
20
+ require_relative 'cli/weighted_list'
20
21
  require_relative 'cli/sparkline_helper'
21
22
  require_relative 'cli/trace_helper'
22
23
  require_relative 'cli/yardopts_helper'
24
+
23
25
  require_relative 'cli/command'
24
- require_relative 'cli/command_parser'
25
26
 
26
27
  console_rb = File.join(File.dirname(__FILE__), 'cli', 'command', 'console.rb')
27
28
  if File.exists?(console_rb)
@@ -85,6 +85,8 @@ module Inch
85
85
  def files
86
86
  object.files
87
87
  rescue YARD::CodeObjects::ProxyMethodError
88
+ # this error is raised by YARD
89
+ # see broken.rb in test fixtures
88
90
  []
89
91
  end
90
92
 
@@ -9,10 +9,10 @@ module Inch
9
9
  include NodocHelper
10
10
 
11
11
  # @return [YARD::CodeObjects::Base] the actual (YARD) code object
12
- attr_accessor :object
12
+ attr_reader :object
13
13
 
14
14
  # @return [Symbol]
15
- # when objects are assigned to ScoreRanges, this grade is set to
15
+ # when objects are assigned to GradeLists, this grade is set to
16
16
  # enable easier querying for objects of a certain grade
17
17
  attr_writer :grade
18
18
 
@@ -27,7 +27,7 @@ module Inch
27
27
 
28
28
  # @param object [YARD::CodeObjects::Base] the actual (YARD) code object
29
29
  def initialize(object)
30
- self.object = object
30
+ @object = object
31
31
  end
32
32
 
33
33
  def api_tag?
@@ -35,7 +35,14 @@ module Inch
35
35
  end
36
36
 
37
37
  def api_tag
38
- object.tag(:api) || (parent && parent.api_tag)
38
+ tag(:api) || (parent && parent.api_tag)
39
+ end
40
+
41
+ # To be overridden
42
+ # @see Proxy::NamespaceObject
43
+ # @return [CodeObject::Proxy::Base,nil] the child inside the current object or +nil+
44
+ def child(name)
45
+ nil
39
46
  end
40
47
 
41
48
  # To be overridden
@@ -65,8 +72,8 @@ module Inch
65
72
 
66
73
  # @return [Symbol]
67
74
  def grade
68
- @grade ||= Evaluation.new_score_ranges.detect { |range|
69
- range.range.include?(score)
75
+ @grade ||= Evaluation.new_grade_lists.detect { |range|
76
+ range.scores.include?(score)
70
77
  }.grade
71
78
  end
72
79
 
@@ -75,7 +82,7 @@ module Inch
75
82
  end
76
83
 
77
84
  def has_code_example?
78
- !object.tags(:example).empty? ||
85
+ !tags(:example).empty? ||
79
86
  docstring.contains_code_example?
80
87
  end
81
88
 
@@ -84,10 +91,10 @@ module Inch
84
91
  end
85
92
 
86
93
  def has_multiple_code_examples?
87
- if object.tags(:example).size > 1 || docstring.code_examples.size > 1
94
+ if tags(:example).size > 1 || docstring.code_examples.size > 1
88
95
  true
89
96
  else
90
- if tag = object.tag(:example)
97
+ if tag = tag(:example)
91
98
  multi_code_examples?(tag.text)
92
99
  elsif text = docstring.code_examples.first
93
100
  multi_code_examples?(text)
@@ -148,7 +155,11 @@ module Inch
148
155
  # @return [Boolean]
149
156
  # +true+ if the object or its parent is tagged as @private
150
157
  def private_tag?
151
- !object.tag(:private).nil? || (parent && parent.private_tag?)
158
+ !private_tag.nil?
159
+ end
160
+
161
+ def private_tag
162
+ tag(:private) || (parent && parent.private_tag)
152
163
  end
153
164
 
154
165
  def private_api_tag?
@@ -165,13 +176,13 @@ module Inch
165
176
 
166
177
  # @return [Boolean] +true+ if the object has no documentation at all
167
178
  def undocumented?
168
- docstring.empty? && object.tags.empty?
179
+ docstring.empty? && tags.empty?
169
180
  end
170
181
 
171
182
  # @return [Array]
172
183
  # YARD tags that are not already covered by other wrapper methods
173
184
  def unconsidered_tags
174
- @unconsidered_tags ||= object.tags.reject do |tag|
185
+ @unconsidered_tags ||= tags.reject do |tag|
175
186
  CONSIDERED_YARD_TAGS.include?(tag.tag_name)
176
187
  end
177
188
  end
@@ -180,11 +191,23 @@ module Inch
180
191
  "#<#{self.class.to_s}: #{path}>"
181
192
  end
182
193
 
183
- private
194
+ protected
184
195
 
185
196
  def multi_code_examples?(text)
186
197
  text.scan(/\b(#{Regexp.escape(name)})[^_0-9\!\?]/m).size > 1
187
198
  end
199
+
200
+ def tag(name)
201
+ tags(name).first
202
+ end
203
+
204
+ def tags(name = nil)
205
+ object.tags(name)
206
+ rescue YARD::CodeObjects::ProxyMethodError
207
+ # this error is raised by YARD
208
+ # see broken.rb in test fixtures
209
+ []
210
+ end
188
211
  end
189
212
  end
190
213
  end
@@ -16,6 +16,20 @@ module Inch
16
16
  name =~ /\!$/
17
17
  end
18
18
 
19
+ def getter?
20
+ attr_info = object.attr_info || {}
21
+ read_info = attr_info[:read]
22
+ if read_info
23
+ read_info.path == path
24
+ else
25
+ parent.child(:"#{name}=")
26
+ end
27
+ end
28
+
29
+ def has_doc?
30
+ super && !implicit_docstring?
31
+ end
32
+
19
33
  def has_parameters?
20
34
  !parameters.empty?
21
35
  end
@@ -68,6 +82,10 @@ module Inch
68
82
  (return_tag && !return_tag.text.empty?) || docstring.describes_return?
69
83
  end
70
84
 
85
+ def setter?
86
+ name =~ /\=$/ && parameters.size == 1
87
+ end
88
+
71
89
  def questioning_name?
72
90
  name =~ /\?$/
73
91
  end
@@ -109,6 +127,17 @@ module Inch
109
127
  lines.reverse
110
128
  end
111
129
 
130
+ def implicit_docstring?
131
+ if getter?
132
+ docstring == "Returns the value of attribute #{name}"
133
+ elsif setter?
134
+ basename = name.to_s.gsub(/(\=)$/, '')
135
+ docstring == "Sets the attribute #{basename}"
136
+ else
137
+ false
138
+ end
139
+ end
140
+
112
141
  def signature_parameter_names
113
142
  object.parameters.map(&:first)
114
143
  end
@@ -4,6 +4,12 @@ module Inch
4
4
  # a namespace object can have methods and other namespace objects
5
5
  # inside itself (e.g. classes and modules)
6
6
  class NamespaceObject < Base
7
+ def child(name)
8
+ if children
9
+ children.detect { |child| child.name == name }
10
+ end
11
+ end
12
+
7
13
  def children
8
14
  object.children.map do |o|
9
15
  Proxy.for(o)