inch 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,87 @@
1
+ module Inch
2
+ module CLI
3
+ module Command
4
+ module Output
5
+ class List < Base
6
+ attr_reader :objects
7
+
8
+ PER_RANGE = 10
9
+
10
+ def initialize(options, objects, ranges)
11
+ @options = options
12
+ @objects = objects
13
+ @ranges = ranges
14
+ @omitted = 0
15
+
16
+ display_list
17
+ end
18
+
19
+ private
20
+
21
+ def display_list
22
+ @ranges.each do |range|
23
+ if range.objects.empty?
24
+ # pass
25
+ else
26
+ trace
27
+ trace_header(range.description, range.color, range.bg_color)
28
+ display_range(range)
29
+ end
30
+ end
31
+
32
+ if @omitted > 0
33
+ trace
34
+ trace "This output omitted #{@omitted} objects. ".dark +
35
+ "Use `--all` to display all objects.".dark
36
+ end
37
+ end
38
+
39
+ def display_range(range)
40
+ display_count = @options.full ? range.objects.size : PER_RANGE
41
+ list = range.objects[0...display_count]
42
+ list.each do |o|
43
+ echo range.color, result(o, range.color)
44
+ end
45
+
46
+ display_omitted_hint(range, display_count)
47
+ end
48
+
49
+ def display_omitted_hint(range, display_count)
50
+ omitted = range.objects.size - display_count
51
+ if omitted > 0
52
+ @omitted += omitted
53
+ echo range.color, "... (omitting #{omitted} objects)".dark
54
+ end
55
+ end
56
+
57
+ def echo(color, msg)
58
+ trace edged(color, msg)
59
+ end
60
+
61
+ def result(object, color)
62
+ if @options.numbers
63
+ result_numbers(object, color)
64
+ else
65
+ result_grades(object, color)
66
+ end
67
+ end
68
+
69
+ def result_grades(object, color)
70
+ grade = object.grade.to_s
71
+ grade = grade.ljust(2).color(color)
72
+ priority = object.priority
73
+ " #{grade} #{priority_arrow(priority, color)} #{object.path}"
74
+ end
75
+
76
+ def result_numbers(object, color)
77
+ score = object.score.to_i.to_s
78
+ score = score.rjust(3).color(color)
79
+ priority = object.priority
80
+ "#{score} #{priority} #{object.path}"
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,79 @@
1
+ module Inch
2
+ module CLI
3
+ module Command
4
+ module Output
5
+ class Show < Base
6
+ attr_reader :objects
7
+
8
+ COLOR = :color132
9
+ BG_COLOR = :color138
10
+ LJUST = 20
11
+
12
+ def initialize(options, objects)
13
+ @options = options
14
+ @objects = objects
15
+
16
+ display_objects
17
+ end
18
+
19
+ private
20
+
21
+ def display_objects
22
+ objects.each do |o|
23
+ print_object(o)
24
+ end
25
+ end
26
+
27
+ def print_object(o)
28
+ trace
29
+ trace_header(o.path, COLOR, BG_COLOR)
30
+
31
+ print_file_info(o)
32
+ print_grade_info(o)
33
+ print_roles_info(o)
34
+
35
+ end
36
+
37
+ def print_file_info(o)
38
+ o.files.each do |f|
39
+ echo "-> #{f[0]}:#{f[1]}".color(COLOR)
40
+ end
41
+ echo separator
42
+ end
43
+
44
+ def print_grade_info(o)
45
+ echo "Grade: #{grade(o.score)}".rjust(5)
46
+ echo separator
47
+ end
48
+
49
+ def print_roles_info(o)
50
+ if o.roles.empty?
51
+ echo "No roles assigned.".dark
52
+ else
53
+ o.roles.each_with_index do |role, index|
54
+ if role.suggestion
55
+ echo "+".color(COLOR) + " #{role.suggestion}"
56
+ end
57
+ end
58
+ end
59
+ echo separator
60
+ end
61
+
62
+ def echo(msg = "")
63
+ trace edged(COLOR, msg)
64
+ end
65
+
66
+ def separator
67
+ "-".color(COLOR) * (CLI::COLUMNS - 2)
68
+ end
69
+
70
+ def grade(score)
71
+ ranges ||= Evaluation.new_score_ranges
72
+ r = ranges.detect { |r| r.range.include?(score) }
73
+ "#{r.grade} - #{r.description}".color(r.color)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,111 @@
1
+ require 'json'
2
+ require 'yaml'
3
+
4
+ module Inch
5
+ module CLI
6
+ module Command
7
+ module Output
8
+ class Stats < Base
9
+ include SparklineHelper
10
+
11
+ attr_reader :objects
12
+
13
+ PRIORITY_COLORS = [
14
+ [213,212,211,210,210,209,209],
15
+ [177],
16
+ [203, 203, 204, 204, 205, 206, 207]
17
+ ].flatten.map { |s| :"color#{s}" }
18
+
19
+ def initialize(options, objects, ranges)
20
+ @options = options
21
+ @objects = objects
22
+ @ranges = ranges
23
+
24
+ method("display_#{@options.format}").call
25
+ end
26
+
27
+ private
28
+
29
+ def display_text
30
+ display_text_grades
31
+ display_text_priorities
32
+ puts
33
+ puts 'Try `--format json|yaml` for raw numbers.'.dark
34
+ end
35
+
36
+ def display_text_grades
37
+ sparkline = ranges_sparkline(@ranges).to_s(' ')
38
+ puts
39
+ puts 'Grade distribution: (undocumented, C, B, A)'
40
+ puts
41
+ puts " Overall: #{sparkline} #{objects.size.to_s.rjust(5)} objects"
42
+ puts
43
+ puts 'Grade distribution by priority:'
44
+ puts
45
+ PRIORITY_MAP.each do |priority_range, arrow|
46
+ list = objects.select { |o| priority_range.include?(o.priority) }
47
+ sparkline = grades_sparkline(list).to_s(' ')
48
+ puts " #{arrow} #{sparkline} " +
49
+ "#{list.size.to_s.rjust(5)} objects"
50
+ puts
51
+ end
52
+ end
53
+
54
+ def display_text_priorities
55
+ puts "Priority distribution in grades: (low to high)"
56
+ puts
57
+ 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
73
+ end
74
+ end
75
+
76
+ def display_json
77
+ puts JSON.pretty_generate(stats_hash)
78
+ end
79
+
80
+ def display_yaml
81
+ puts YAML.dump(stats_hash)
82
+ end
83
+
84
+ def stats_hash
85
+ hash = {}
86
+
87
+ hash['ranges'] = {}
88
+ @ranges.each do |r|
89
+ hash['ranges'][r.grade.to_s] = r.objects.size
90
+ end
91
+
92
+ hash['scores'] = {}
93
+ @objects.sort_by(&:score).each do |o|
94
+ hash['scores'][o.score.to_i] ||= 0
95
+ hash['scores'][o.score.to_i] += 1
96
+ end
97
+
98
+ hash['priorities'] = {}
99
+ @objects.sort_by(&:priority).each do |o|
100
+ hash['priorities'][o.priority.to_i] ||= 0
101
+ hash['priorities'][o.priority.to_i] += 1
102
+ end
103
+
104
+ hash
105
+ end
106
+
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,104 @@
1
+ require 'sparkr'
2
+
3
+ module Inch
4
+ module CLI
5
+ module Command
6
+ module Output
7
+ class Suggest < Base
8
+ include SparklineHelper
9
+
10
+ attr_reader :objects, :files
11
+
12
+ FILE_COLOR = :dark # TODO: store all colors somewhere
13
+ RANGE_LABELS = {
14
+ :A => "Nearly perfect:",
15
+ :B => "Properly documented, could be improved:",
16
+ :C => "Not properly documented:",
17
+ :U => "Undocumented:",
18
+ }
19
+
20
+ # @param options [Command::Options::Suggest]
21
+ # @param objects_to_display [Array<CodeObject::Proxy::Base>]
22
+ # @param relevant_objects [Array<CodeObject::Proxy::Base>] the objects meeting the criteria defined in +options+
23
+ # @param ranges [Array<Evaluation::ScoreRange>]
24
+ # @param files [Array<Evaluation::File>]
25
+ def initialize(options, objects_to_display, relevant_objects, ranges, files)
26
+ @options = options
27
+ @objects = objects_to_display
28
+ @relevant_objects = relevant_objects
29
+ @ranges = ranges
30
+ @files = files
31
+
32
+ if objects.empty?
33
+ # TODO: show hint
34
+ else
35
+ display_list
36
+ display_files
37
+ display_distribution
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def display_distribution
44
+ sparkline = grades_sparkline(@relevant_objects).to_s(' ')
45
+ puts "Grade distribution (undocumented, C, B, A): " + sparkline
46
+ puts
47
+ puts priority_filter_hint
48
+ end
49
+
50
+ def priority_filter_hint
51
+ arrows = min_priority_arrows
52
+ pretext = if @options.pedantic
53
+ "Considering priority objects: #{arrows}"
54
+ else
55
+ "Only considering priority objects: #{arrows}"
56
+ end
57
+ "#{pretext} (use `--help` for options).".dark
58
+ end
59
+
60
+ def display_files
61
+ trace
62
+ trace "You might want to look at these files:"
63
+ trace
64
+
65
+ files.each do |f|
66
+ trace edged(FILE_COLOR, f.path.color(FILE_COLOR))
67
+ end
68
+ trace
69
+ end
70
+
71
+ def display_list
72
+ @options.grades_to_display.map do |grade|
73
+ r = range(grade)
74
+ grade_objects = objects.select { |o| o.grade == r.grade }
75
+ unless grade_objects.empty?
76
+ trace
77
+ trace_header(RANGE_LABELS[r.grade], r.color, r.bg_color)
78
+ grade_objects.each do |o|
79
+ grade = o.grade.to_s.ljust(2).color(r.color)
80
+ priority = o.priority
81
+ trace edged(r.color, " #{grade} #{priority_arrow(priority, r.color)} #{o.path}")
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def min_priority_arrows
88
+ priority_arrows_gte(@options.object_min_priority).join(' ')
89
+ end
90
+
91
+ def priority_arrows_gte(min_priority)
92
+ PRIORITY_MAP.map do |range, str|
93
+ str if range.min >= min_priority
94
+ end.compact
95
+ end
96
+
97
+ def range(grade)
98
+ @ranges.detect { |r| r.grade == grade }
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,20 @@
1
+ module Inch
2
+ module CLI
3
+ module Command
4
+ class Show < BaseObject
5
+ def description
6
+ 'Shows an object with its results'
7
+ end
8
+
9
+ def usage
10
+ 'Usage: inch show [paths] OBJECT_NAME [[OBJECT_NAME2] ...] [options]'
11
+ end
12
+
13
+ def run(*args)
14
+ prepare_objects(*args)
15
+ Output::Show.new(@options, objects)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Inch
2
+ module CLI
3
+ module Command
4
+ class Stats < List
5
+ def description
6
+ 'Lists all objects with their results'
7
+ end
8
+
9
+ def usage
10
+ 'Usage: inch stats [paths] [options]'
11
+ end
12
+
13
+ def run(*args)
14
+ prepare_list(*args)
15
+ Output::Stats.new(@options, objects, @ranges)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,104 @@
1
+ module Inch
2
+ module CLI
3
+ module Command
4
+ class Suggest < List
5
+ def description
6
+ 'Suggests some objects to be doucmented (better)'
7
+ end
8
+
9
+ def usage
10
+ 'Usage: inch suggest [paths] [options]'
11
+ end
12
+
13
+ # Runs the commandline utility, parsing arguments and displaying a
14
+ # list of objects
15
+ #
16
+ # @param [Array<String>] args the list of arguments.
17
+ # @return [void]
18
+ def run(*args)
19
+ prepare_list(*args)
20
+ Output::Suggest.new(@options, objects_to_display, relevant_objects, @ranges, files)
21
+ end
22
+
23
+ def objects_to_display
24
+ @objects_to_display ||= filter_objects_to_display
25
+ end
26
+
27
+ def filter_objects_to_display
28
+ list = []
29
+ @options.grades_to_display.map do |grade|
30
+ r = range(grade)
31
+ arr = select_by_priority(r.objects, @options.object_min_priority)
32
+ arr = arr.select { |o| o.score <= @options.object_max_score }
33
+ list.concat arr
34
+ end
35
+
36
+ list = sort_by_priority(list)
37
+
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?
43
+ end
44
+ list
45
+ end
46
+
47
+ def files
48
+ list = files_sorted_by_importance
49
+ how_many = @options.file_count || list.size
50
+ list[0...how_many]
51
+ end
52
+
53
+ def files_sorted_by_importance
54
+ list = all_filenames(relevant_objects).uniq.map do |filename|
55
+ f = Evaluation::File.for(filename, relevant_objects)
56
+ end
57
+
58
+ list = list.select do |f|
59
+ relevant_grades.include?(f.grade) &&
60
+ relevant_priorities.include?(f.priority)
61
+ end
62
+
63
+ sort_by_priority(list)
64
+ end
65
+
66
+ def all_filenames(objects)
67
+ objects.map do |o|
68
+ o.files.map(&:first)
69
+ end.flatten
70
+ end
71
+
72
+ # Returns the unique grades assigned to objects
73
+ #
74
+ # grades # => [:A, :B, :C, :U]
75
+ #
76
+ # @return [Array<Symbol>]
77
+ def grades
78
+ objects.map(&:grade).uniq
79
+ end
80
+
81
+ def range(grade)
82
+ @ranges.detect { |r| r.grade == grade }
83
+ end
84
+
85
+ def relevant_objects
86
+ select_by_priority(objects, @options.object_min_priority)
87
+ end
88
+
89
+ def relevant_grades
90
+ grades.size >= 2 ? grades[-2..-1] : [grades.last].compact
91
+ end
92
+
93
+ def relevant_priorities
94
+ (@options.object_min_priority..99)
95
+ end
96
+
97
+ def select_by_priority(list, min_priority)
98
+ list.select { |o| o.priority >= min_priority }
99
+ end
100
+
101
+ end
102
+ end
103
+ end
104
+ end