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,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