inch 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +335 -3
- data/Rakefile +8 -0
- data/TODOS.md +12 -0
- data/bin/inch +17 -0
- data/inch.gemspec +7 -2
- data/lib/inch.rb +6 -1
- data/lib/inch/cli.rb +24 -0
- data/lib/inch/cli/arguments.rb +45 -0
- data/lib/inch/cli/command.rb +29 -0
- data/lib/inch/cli/command/base.rb +62 -0
- data/lib/inch/cli/command/base_list.rb +75 -0
- data/lib/inch/cli/command/base_object.rb +40 -0
- data/lib/inch/cli/command/console.rb +22 -0
- data/lib/inch/cli/command/inspect.rb +20 -0
- data/lib/inch/cli/command/list.rb +25 -0
- data/lib/inch/cli/command/options/base.rb +137 -0
- data/lib/inch/cli/command/options/base_list.rb +84 -0
- data/lib/inch/cli/command/options/base_object.rb +47 -0
- data/lib/inch/cli/command/options/console.rb +26 -0
- data/lib/inch/cli/command/options/inspect.rb +25 -0
- data/lib/inch/cli/command/options/list.rb +30 -0
- data/lib/inch/cli/command/options/show.rb +27 -0
- data/lib/inch/cli/command/options/stats.rb +20 -0
- data/lib/inch/cli/command/options/suggest.rb +61 -0
- data/lib/inch/cli/command/output/base.rb +32 -0
- data/lib/inch/cli/command/output/console.rb +45 -0
- data/lib/inch/cli/command/output/inspect.rb +129 -0
- data/lib/inch/cli/command/output/list.rb +87 -0
- data/lib/inch/cli/command/output/show.rb +79 -0
- data/lib/inch/cli/command/output/stats.rb +111 -0
- data/lib/inch/cli/command/output/suggest.rb +104 -0
- data/lib/inch/cli/command/show.rb +20 -0
- data/lib/inch/cli/command/stats.rb +20 -0
- data/lib/inch/cli/command/suggest.rb +104 -0
- data/lib/inch/cli/command_parser.rb +82 -0
- data/lib/inch/cli/sparkline_helper.rb +31 -0
- data/lib/inch/cli/trace_helper.rb +42 -0
- data/lib/inch/cli/yardopts_helper.rb +49 -0
- data/lib/inch/code_object.rb +8 -0
- data/lib/inch/code_object/docstring.rb +97 -0
- data/lib/inch/code_object/nodoc_helper.rb +84 -0
- data/lib/inch/code_object/proxy.rb +37 -0
- data/lib/inch/code_object/proxy/base.rb +194 -0
- data/lib/inch/code_object/proxy/class_object.rb +9 -0
- data/lib/inch/code_object/proxy/constant_object.rb +8 -0
- data/lib/inch/code_object/proxy/method_object.rb +118 -0
- data/lib/inch/code_object/proxy/method_parameter_object.rb +81 -0
- data/lib/inch/code_object/proxy/module_object.rb +8 -0
- data/lib/inch/code_object/proxy/namespace_object.rb +38 -0
- data/lib/inch/core_ext.rb +2 -0
- data/lib/inch/core_ext/string.rb +3 -0
- data/lib/inch/core_ext/yard.rb +4 -0
- data/lib/inch/evaluation.rb +35 -0
- data/lib/inch/evaluation/base.rb +60 -0
- data/lib/inch/evaluation/class_object.rb +6 -0
- data/lib/inch/evaluation/constant_object.rb +34 -0
- data/lib/inch/evaluation/file.rb +66 -0
- data/lib/inch/evaluation/method_object.rb +127 -0
- data/lib/inch/evaluation/module_object.rb +6 -0
- data/lib/inch/evaluation/namespace_object.rb +94 -0
- data/lib/inch/evaluation/role/base.rb +49 -0
- data/lib/inch/evaluation/role/constant.rb +43 -0
- data/lib/inch/evaluation/role/method.rb +60 -0
- data/lib/inch/evaluation/role/method_parameter.rb +46 -0
- data/lib/inch/evaluation/role/missing.rb +20 -0
- data/lib/inch/evaluation/role/namespace.rb +58 -0
- data/lib/inch/evaluation/role/object.rb +64 -0
- data/lib/inch/evaluation/score_range.rb +26 -0
- data/lib/inch/rake.rb +1 -0
- data/lib/inch/rake/suggest.rb +26 -0
- data/lib/inch/source_parser.rb +36 -0
- data/lib/inch/version.rb +1 -1
- data/test/fixtures/code_examples/lib/foo.rb +87 -0
- data/test/fixtures/readme/lib/foo.rb +17 -0
- data/test/fixtures/simple/README +25 -0
- data/test/fixtures/simple/lib/broken.rb +10 -0
- data/test/fixtures/simple/lib/foo.rb +214 -0
- data/test/fixtures/simple/lib/role_methods.rb +78 -0
- data/test/fixtures/simple/lib/role_namespaces.rb +68 -0
- data/test/fixtures/visibility/lib/foo.rb +18 -0
- data/test/fixtures/yardopts/.yardopts +1 -0
- data/test/fixtures/yardopts/foo/bar.rb +6 -0
- data/test/inch/cli/arguments_test.rb +70 -0
- data/test/inch/cli/command/console_test.rb +59 -0
- data/test/inch/cli/command/inspect_test.rb +59 -0
- data/test/inch/cli/command/list_test.rb +61 -0
- data/test/inch/cli/command/show_test.rb +59 -0
- data/test/inch/cli/command/stats_test.rb +57 -0
- data/test/inch/cli/command/suggest_test.rb +57 -0
- data/test/inch/cli/command_parser_test.rb +33 -0
- data/test/inch/cli/yardopts_helper_test.rb +84 -0
- data/test/inch/code_object/docstring_test.rb +204 -0
- data/test/inch/code_object/nodoc_helper_test.rb +38 -0
- data/test/inch/code_object/proxy_test.rb +188 -0
- data/test/inch/source_parser_test.rb +23 -0
- data/test/integration/stats_options_test.rb +34 -0
- data/test/integration/visibility_options_test.rb +79 -0
- data/test/test_helper.rb +21 -0
- metadata +184 -7
@@ -0,0 +1,118 @@
|
|
1
|
+
module Inch
|
2
|
+
module CodeObject
|
3
|
+
module Proxy
|
4
|
+
class MethodObject < Base
|
5
|
+
def comment_and_abbrev_source
|
6
|
+
comments.join('') + abbrev_source
|
7
|
+
end
|
8
|
+
|
9
|
+
def bang_name?
|
10
|
+
name =~ /\!$/
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_parameters?
|
14
|
+
!parameters.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
MANY_PARAMETERS_THRESHOLD = 3
|
18
|
+
def has_many_parameters?
|
19
|
+
parameters.size > MANY_PARAMETERS_THRESHOLD
|
20
|
+
end
|
21
|
+
|
22
|
+
MANY_LINES_THRESHOLD = 20
|
23
|
+
def has_many_lines?
|
24
|
+
# for now, this includes the 'def' line and comments
|
25
|
+
if source = object.source
|
26
|
+
size = source.lines.count
|
27
|
+
size > MANY_LINES_THRESHOLD
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def method?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def parameters
|
38
|
+
@parameters ||= all_parameter_names.map do |name|
|
39
|
+
in_signature = signature_parameter_names.include?(name)
|
40
|
+
tag = parameter_tag(name)
|
41
|
+
MethodParameterObject.new(self, name, tag, in_signature)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def parameter(name)
|
46
|
+
parameters.detect { |p| p.name == name.to_s }
|
47
|
+
end
|
48
|
+
|
49
|
+
def overridden?
|
50
|
+
!!object.overridden_method
|
51
|
+
end
|
52
|
+
|
53
|
+
def overridden_method
|
54
|
+
@overridden_method ||= Proxy.for(object.overridden_method)
|
55
|
+
end
|
56
|
+
|
57
|
+
def return_mentioned?
|
58
|
+
!!return_tag || docstring.mentions_return?
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def all_parameter_names
|
64
|
+
names = signature_parameter_names
|
65
|
+
names.concat parameter_tags.map(&:name)
|
66
|
+
names.compact.uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
def abbrev_source
|
70
|
+
lines = object.source.to_s.lines
|
71
|
+
if lines.size >= 5
|
72
|
+
indent = lines[1].scan(/^(\s+)/).flatten.join('')
|
73
|
+
lines = lines[0..1] +
|
74
|
+
["#{indent}# ... snip ...\n"] +
|
75
|
+
lines[-2..-1]
|
76
|
+
end
|
77
|
+
lines.join('')
|
78
|
+
end
|
79
|
+
|
80
|
+
def comments
|
81
|
+
@comments ||= files.map do |(filename, line_no)|
|
82
|
+
get_lines_up_while(filename, line_no - 1) do |line|
|
83
|
+
line =~ /^\s*#/
|
84
|
+
end.flatten.join('')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_lines_up_while(filename, line_no, &block)
|
89
|
+
lines = []
|
90
|
+
line = get_line_no(filename, line_no)
|
91
|
+
if yield(line) && line_no > 0
|
92
|
+
lines << line.gsub(/^(\s+)/, '')
|
93
|
+
lines << get_lines_up_while(filename, line_no - 1, &block)
|
94
|
+
end
|
95
|
+
lines.reverse
|
96
|
+
end
|
97
|
+
|
98
|
+
def signature_parameter_names
|
99
|
+
object.parameters.map(&:first)
|
100
|
+
end
|
101
|
+
|
102
|
+
def parameter_tag(param_name)
|
103
|
+
parameter_tags.detect do |tag|
|
104
|
+
tag.name == param_name
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def parameter_tags
|
109
|
+
object.tags(:param)
|
110
|
+
end
|
111
|
+
|
112
|
+
def return_tag
|
113
|
+
object.tags(:return).first
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Inch
|
2
|
+
module CodeObject
|
3
|
+
module Proxy
|
4
|
+
class MethodParameterObject
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
# @param method [Inch::CodeObject::Proxy::MethodObject] the method the parameter belongs_to
|
8
|
+
# @param name [String] the name of the parameter
|
9
|
+
# @param tag [YARD::Tags::Tag] the Tag object for the parameter
|
10
|
+
# @param in_signature [Boolean] +true+ if the method's signature contains the parameter
|
11
|
+
def initialize(method, name, tag, in_signature)
|
12
|
+
@method = method
|
13
|
+
@name = name
|
14
|
+
@tag = tag
|
15
|
+
@in_signature = in_signature
|
16
|
+
end
|
17
|
+
|
18
|
+
BAD_NAME_EXCEPTIONS = %w(id)
|
19
|
+
BAD_NAME_THRESHOLD = 3
|
20
|
+
def bad_name?
|
21
|
+
return false if BAD_NAME_EXCEPTIONS.include?(name)
|
22
|
+
name.size < BAD_NAME_THRESHOLD || name =~ /[0-9]$/
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] +true+ if the parameter is a block
|
26
|
+
def block?
|
27
|
+
name =~ /^\&/
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Boolean] +true+ if an additional description given?
|
31
|
+
def described?
|
32
|
+
described_by_tag? || described_by_docstring?
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Boolean] +true+ if the parameter is mentioned in the docs
|
36
|
+
def mentioned?
|
37
|
+
!!@tag || mentioned_by_docstring?
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Boolean] +true+ if the parameter is a splat argument
|
41
|
+
def splat?
|
42
|
+
name =~ /^\*/
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Boolean] +true+ if the type of the parameter is defined
|
46
|
+
def typed?
|
47
|
+
@tag && @tag.types && !@tag.types.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Boolean] +true+ if the parameter is mentioned in the docs, but not present in the method's signature
|
51
|
+
def wrongly_mentioned?
|
52
|
+
mentioned? && !@in_signature
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def described_by_tag?
|
58
|
+
@tag && !@tag.text.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def described_by_docstring?
|
62
|
+
if @method.docstring.describes_parameter?(name)
|
63
|
+
true
|
64
|
+
else
|
65
|
+
unsplatted = name.gsub(/^[\&\*]/, '')
|
66
|
+
@method.docstring.describes_parameter?(unsplatted)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def mentioned_by_docstring?
|
71
|
+
if @method.docstring.mentions_parameter?(name)
|
72
|
+
true
|
73
|
+
else
|
74
|
+
unsplatted = name.gsub(/^[\&\*]/, '')
|
75
|
+
@method.docstring.mentions_parameter?(unsplatted)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Inch
|
2
|
+
module CodeObject
|
3
|
+
module Proxy
|
4
|
+
# a namespace object can have methods and other namespace objects
|
5
|
+
# inside itself (e.g. classes and modules)
|
6
|
+
class NamespaceObject < Base
|
7
|
+
def children
|
8
|
+
object.children.map do |o|
|
9
|
+
Proxy.for(o)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
MANY_ATTRIBUTES_THRESHOLD = 5
|
14
|
+
def has_many_attributes?
|
15
|
+
n = object.class_attributes.size + object.instance_attributes.size
|
16
|
+
n > MANY_ATTRIBUTES_THRESHOLD
|
17
|
+
end
|
18
|
+
|
19
|
+
MANY_CHILDREN_THRESHOLD = 20
|
20
|
+
def has_many_children?
|
21
|
+
children.size > MANY_CHILDREN_THRESHOLD
|
22
|
+
end
|
23
|
+
|
24
|
+
def namespace?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def no_methods?
|
29
|
+
!children.any?(&:method?)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pure_namespace?
|
33
|
+
children.all?(&:namespace?)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Inch
|
2
|
+
module Evaluation
|
3
|
+
def self.for(code_object)
|
4
|
+
class_for(code_object).new(code_object)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def self.class_for(code_object)
|
10
|
+
class_name = code_object.class.to_s.split('::').last
|
11
|
+
eval(class_name)
|
12
|
+
rescue
|
13
|
+
Base
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative 'evaluation/file'
|
19
|
+
|
20
|
+
require_relative 'evaluation/score_range'
|
21
|
+
|
22
|
+
require_relative 'evaluation/role/base'
|
23
|
+
require_relative 'evaluation/role/missing'
|
24
|
+
require_relative 'evaluation/role/object'
|
25
|
+
require_relative 'evaluation/role/method'
|
26
|
+
require_relative 'evaluation/role/method_parameter'
|
27
|
+
require_relative 'evaluation/role/namespace'
|
28
|
+
require_relative 'evaluation/role/constant'
|
29
|
+
|
30
|
+
require_relative 'evaluation/base'
|
31
|
+
require_relative 'evaluation/namespace_object'
|
32
|
+
require_relative 'evaluation/class_object'
|
33
|
+
require_relative 'evaluation/constant_object'
|
34
|
+
require_relative 'evaluation/method_object'
|
35
|
+
require_relative 'evaluation/module_object'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Inch
|
2
|
+
module Evaluation
|
3
|
+
class Base
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
MIN_SCORE = 0
|
7
|
+
MAX_SCORE = 100
|
8
|
+
|
9
|
+
TAGGED_SCORE = 20 # assigned per unconsidered tag
|
10
|
+
|
11
|
+
|
12
|
+
attr_accessor :object
|
13
|
+
attr_reader :min_score, :max_score
|
14
|
+
|
15
|
+
def initialize(object)
|
16
|
+
self.object = object
|
17
|
+
@roles = []
|
18
|
+
evaluate
|
19
|
+
end
|
20
|
+
|
21
|
+
def evaluate
|
22
|
+
end
|
23
|
+
|
24
|
+
def max_score
|
25
|
+
arr = @roles.map(&:max_score).compact
|
26
|
+
[MAX_SCORE].concat(arr).min
|
27
|
+
end
|
28
|
+
|
29
|
+
def min_score
|
30
|
+
arr = @roles.map(&:min_score).compact
|
31
|
+
[MIN_SCORE].concat(arr).max
|
32
|
+
end
|
33
|
+
|
34
|
+
def score
|
35
|
+
value = @roles.inject(0) { |sum,r| sum + r.score.to_f }
|
36
|
+
if value < min_score
|
37
|
+
min_score
|
38
|
+
elsif value > max_score
|
39
|
+
max_score
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def priority
|
46
|
+
@roles.inject(0) { |sum,r| sum + r.priority.to_i }
|
47
|
+
end
|
48
|
+
|
49
|
+
def roles
|
50
|
+
@roles
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def add_role(role)
|
56
|
+
@roles << role
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Inch
|
2
|
+
module Evaluation
|
3
|
+
class ConstantObject < Base
|
4
|
+
DOC_SCORE = MAX_SCORE
|
5
|
+
|
6
|
+
def evaluate
|
7
|
+
if object.has_doc?
|
8
|
+
add_role Role::Constant::WithDoc.new(object, DOC_SCORE)
|
9
|
+
else
|
10
|
+
add_role Role::Constant::WithoutDoc.new(object, DOC_SCORE)
|
11
|
+
end
|
12
|
+
if object.nodoc?
|
13
|
+
add_role Role::Constant::TaggedAsNodoc.new(object)
|
14
|
+
end
|
15
|
+
if object.has_unconsidered_tags?
|
16
|
+
count = object.unconsidered_tags.size
|
17
|
+
add_role Role::Object::Tagged.new(object, TAGGED_SCORE * count)
|
18
|
+
end
|
19
|
+
if object.in_root?
|
20
|
+
add_role Role::Constant::InRoot.new(object)
|
21
|
+
end
|
22
|
+
if object.public?
|
23
|
+
add_role Role::Constant::Public.new(object)
|
24
|
+
end
|
25
|
+
if object.protected?
|
26
|
+
add_role Role::Constant::Protected.new(object)
|
27
|
+
end
|
28
|
+
if object.private?
|
29
|
+
add_role Role::Constant::Private.new(object)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Inch
|
2
|
+
module Evaluation
|
3
|
+
class File
|
4
|
+
attr_accessor :filename, :objects
|
5
|
+
|
6
|
+
def initialize(filename, objects)
|
7
|
+
self.filename = filename
|
8
|
+
self.objects = objects.select do |o|
|
9
|
+
o.filename == filename
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# @note added to be compatible with code objects
|
14
|
+
def path
|
15
|
+
filename
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# grade, priority, and score are not meant to be displayed in the CLI
|
20
|
+
# they are just for internal evaluation purposes
|
21
|
+
#
|
22
|
+
|
23
|
+
def grade
|
24
|
+
median(grades.sort)
|
25
|
+
end
|
26
|
+
|
27
|
+
def priority
|
28
|
+
median(priorities.sort)
|
29
|
+
end
|
30
|
+
|
31
|
+
def score
|
32
|
+
objects.select(&:undocumented?).size
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def grades
|
38
|
+
objects.map(&:grade)
|
39
|
+
end
|
40
|
+
|
41
|
+
def priorities
|
42
|
+
objects.map(&:priority)
|
43
|
+
end
|
44
|
+
|
45
|
+
def scores
|
46
|
+
objects.map(&:score)
|
47
|
+
end
|
48
|
+
|
49
|
+
def median(sorted_list)
|
50
|
+
index = (sorted_list.size / 2).round
|
51
|
+
sorted_list[index]
|
52
|
+
end
|
53
|
+
|
54
|
+
class << self
|
55
|
+
def for(filename, objects)
|
56
|
+
@cache ||= {}
|
57
|
+
if file = @cache[filename]
|
58
|
+
file
|
59
|
+
else
|
60
|
+
@cache[filename] = new(filename, objects)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|