dkubb-yardstick 0.0.1
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.
- data/.document +5 -0
- data/.gitignore +8 -0
- data/LICENSE +20 -0
- data/README.rdoc +16 -0
- data/Rakefile +24 -0
- data/VERSION +1 -0
- data/bin/yardstick +7 -0
- data/lib/yardstick/autoload.rb +7 -0
- data/lib/yardstick/cli.rb +78 -0
- data/lib/yardstick/core_ext/object.rb +13 -0
- data/lib/yardstick/measurable.rb +95 -0
- data/lib/yardstick/measurement.rb +176 -0
- data/lib/yardstick/method.rb +106 -0
- data/lib/yardstick/processor.rb +67 -0
- data/lib/yardstick/yard_ext.rb +25 -0
- data/lib/yardstick.rb +52 -0
- data/spec/public/yardstick/measurement_spec.rb +196 -0
- data/spec/public/yardstick/method_spec.rb +6 -0
- data/spec/public/yardstick_spec.rb +6 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/metrics.rake +1 -0
- data/tasks/rdoc.rake +15 -0
- data/tasks/spec.rake +75 -0
- metadata +90 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Dan Kubb
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
= yardstick
|
2
|
+
|
3
|
+
Yardstick is a tool that verifies YARD coverage of ruby code.
|
4
|
+
|
5
|
+
It will measure the source and provide feedback on what is missing from
|
6
|
+
the documentation and what can be improved.
|
7
|
+
|
8
|
+
== Usage
|
9
|
+
|
10
|
+
From the command-line:
|
11
|
+
|
12
|
+
yardstick lib/**/*.rb
|
13
|
+
|
14
|
+
== Copyright
|
15
|
+
|
16
|
+
Copyright (c) 2009 Dan Kubb. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
Pathname.glob('tasks/**/*.rake').each { |task| load task.expand_path }
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
|
10
|
+
Jeweler::Tasks.new do |gem|
|
11
|
+
gem.name = 'yardstick'
|
12
|
+
gem.summary = 'A tool for verifying YARD documentation coverage'
|
13
|
+
gem.email = 'dan.kubb@gmail.com'
|
14
|
+
gem.homepage = 'http://github.com/dkubb/yardstick'
|
15
|
+
gem.authors = [ 'Dan Kubb' ]
|
16
|
+
gem.rubyforge_project = 'yardstick'
|
17
|
+
|
18
|
+
gem.add_dependency('yard', '~>0.2')
|
19
|
+
end
|
20
|
+
|
21
|
+
Jeweler::RubyforgeTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts 'Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler'
|
24
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/yardstick
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Yardstick
|
4
|
+
class CLI
|
5
|
+
|
6
|
+
# Parse the command line options, and run the command
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Yardstick::CLI.run(%w[ article.rb ]) # => [ Measurement ]
|
10
|
+
#
|
11
|
+
# @param [Array] *args
|
12
|
+
# arguments passed in from the command line
|
13
|
+
#
|
14
|
+
# @return [Array(Measurement)]
|
15
|
+
# the measurement for each file
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def self.run(*args)
|
19
|
+
Yardstick.measure(*parse_options(args)).each do |measurement|
|
20
|
+
measurement.warn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Parse the options provided from the command-line
|
25
|
+
#
|
26
|
+
# @param [Array<String>] args
|
27
|
+
# the command-line options
|
28
|
+
#
|
29
|
+
# @return [Array(Array<String>, Hash)]
|
30
|
+
# the list of files, and options parsed from the command-line
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def self.parse_options(args)
|
34
|
+
args << '--help' if args.empty?
|
35
|
+
options = {}
|
36
|
+
option_parser(options).parse!(args)
|
37
|
+
[ args, options ]
|
38
|
+
rescue OptionParser::InvalidOption => error
|
39
|
+
display_exit(error.message << "\n" << args.inspect)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return an OptionParser instance for the command-line app
|
43
|
+
#
|
44
|
+
# @param [Hash] options
|
45
|
+
# the options to set when parsing the command-line arguments
|
46
|
+
#
|
47
|
+
# @return [OptionParser]
|
48
|
+
# the option parser instance
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
def self.option_parser(options)
|
52
|
+
opts = OptionParser.new
|
53
|
+
opts.on_tail('-v', '--version', 'print version information and exit') { display_exit("#{opts.program_name} #{Yardstick::VERSION}") }
|
54
|
+
opts.on_tail('-h', '--help', 'display this help and exit') { display_exit(opts) }
|
55
|
+
opts
|
56
|
+
end
|
57
|
+
|
58
|
+
# Display a message and exit
|
59
|
+
#
|
60
|
+
# @param [#to_str] message
|
61
|
+
# the message to display
|
62
|
+
#
|
63
|
+
# @return [undefined]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def self.display_exit(message)
|
67
|
+
puts message.to_str
|
68
|
+
exit
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
private :option_parser
|
73
|
+
private :parse_options
|
74
|
+
private :display_exit
|
75
|
+
end
|
76
|
+
|
77
|
+
end # module CLI
|
78
|
+
end # module Yardstick
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Yardstick
|
2
|
+
module Measurable
|
3
|
+
include Measurement::UtilityMethods
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
# List of measurement types for this class
|
8
|
+
#
|
9
|
+
# @return [Array<Array(String, Symbol)>]
|
10
|
+
# the measurements for this class
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def measurements
|
14
|
+
@measurements ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Set the description for the measurement
|
18
|
+
#
|
19
|
+
# @param [#to_str] description
|
20
|
+
# the measurement description
|
21
|
+
#
|
22
|
+
# @yield []
|
23
|
+
# the measurement to perform
|
24
|
+
#
|
25
|
+
# @yieldreturn [Boolean]
|
26
|
+
# return true if successful, false if not
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
def measurement(description, &block)
|
32
|
+
measurements << [ description, block ]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Include the class or module with measurable class methods
|
38
|
+
#
|
39
|
+
# @param [Module] mod
|
40
|
+
# the module to include within
|
41
|
+
#
|
42
|
+
# @return [undefined]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def included(mod)
|
46
|
+
mod.extend(ClassMethods)
|
47
|
+
copy_measurements(mod)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Extend the docstring meta class with measurable class methods
|
51
|
+
#
|
52
|
+
# @param [YARD::Docstring] docstring
|
53
|
+
# the docstring to extend
|
54
|
+
#
|
55
|
+
# @return [undefined]
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def extended(docstring)
|
59
|
+
meta_class = docstring.meta_class
|
60
|
+
meta_class.extend(ClassMethods)
|
61
|
+
copy_measurements(meta_class)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Copy measurements from the ancestor to the descendant
|
65
|
+
#
|
66
|
+
# @param [Module] descendant
|
67
|
+
# the descendant module or class
|
68
|
+
#
|
69
|
+
# @return [undefined]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
def copy_measurements(descendant)
|
73
|
+
descendant.measurements.concat(measurements).uniq!
|
74
|
+
end
|
75
|
+
end # module ClassMethods
|
76
|
+
|
77
|
+
extend ClassMethods
|
78
|
+
|
79
|
+
# Return a list of measurements for this docstring instance
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# docstring.measure # => [ Measurement ]
|
83
|
+
#
|
84
|
+
# @return [Array<Measurement>]
|
85
|
+
# a collection of measurements
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
def measure
|
89
|
+
meta_class.measurements.map do |(description, block)|
|
90
|
+
Measurement.new(description, self, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end # module Measurable
|
95
|
+
end # module Yardstick
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module Yardstick
|
2
|
+
class Measurement
|
3
|
+
|
4
|
+
# Return a Measurement instance
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# measurement = Measurement.new('The description', docstring, :successful_method)
|
8
|
+
#
|
9
|
+
# @param [#to_str] description
|
10
|
+
# the measurement description
|
11
|
+
# @param [YARD::Docstring] docstring
|
12
|
+
# the docstring to measure
|
13
|
+
#
|
14
|
+
# @yield []
|
15
|
+
# the measurement to perform
|
16
|
+
#
|
17
|
+
# @return [Yardstick::Measurement]
|
18
|
+
# the measurement instance
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
def initialize(description, docstring, &block)
|
22
|
+
@description = description.to_str
|
23
|
+
@docstring = docstring
|
24
|
+
@block = block
|
25
|
+
@result = measure
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return true if the measurement was successful
|
29
|
+
#
|
30
|
+
# @example Measurement successful
|
31
|
+
# measurement.ok? # => true
|
32
|
+
#
|
33
|
+
# @example Measurement unsuccessful
|
34
|
+
# measurement.ok? # => false
|
35
|
+
#
|
36
|
+
# @return [Boolean]
|
37
|
+
# true if the measurement was successful, false if not
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def ok?
|
41
|
+
@result == true || skip?
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return true if the measurement was skipped
|
45
|
+
#
|
46
|
+
# @example Measurement skipped
|
47
|
+
# measurement.skip? # => true
|
48
|
+
#
|
49
|
+
# @example Measurement not skipped
|
50
|
+
# measurement.skip? # => false
|
51
|
+
#
|
52
|
+
# @return [Boolean]
|
53
|
+
# true if the measurement was skipped, false if not
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def skip?
|
57
|
+
@result == :skip
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return true if the measurement is not implemented
|
61
|
+
#
|
62
|
+
# @example Measurement not implemented
|
63
|
+
# measurement.todo? # => true
|
64
|
+
#
|
65
|
+
# @example Measurement implemented
|
66
|
+
# measurement.todo? # => false
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
# true if the measurement is not implemented, false if not
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
def todo?
|
73
|
+
@result == :todo
|
74
|
+
end
|
75
|
+
|
76
|
+
# Warns the results the measurement if it was not successful
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# measurement.warn # (outputs results if not successful)
|
80
|
+
#
|
81
|
+
# @return [undefined]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
def warn
|
85
|
+
unless ok?
|
86
|
+
Kernel.warn("#{file}:#{line}: #{path}: #{@description}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Measure the docstring using the method provided to the constructor
|
93
|
+
#
|
94
|
+
# @return [Boolean, Symbol]
|
95
|
+
# true if the test is successful, false if not
|
96
|
+
# :todo if the test is not implemented
|
97
|
+
# :skip if the test does not apply
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
def measure
|
101
|
+
catch :measurement do
|
102
|
+
@docstring.instance_eval(&@block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# The code object for the docstring
|
107
|
+
#
|
108
|
+
# @return [YARD::CodeObjects::Base]
|
109
|
+
# the code object
|
110
|
+
#
|
111
|
+
# @api private
|
112
|
+
def object
|
113
|
+
@docstring.object
|
114
|
+
end
|
115
|
+
|
116
|
+
# The filename for the code
|
117
|
+
#
|
118
|
+
# @return [Pathname]
|
119
|
+
# the filename
|
120
|
+
#
|
121
|
+
# @api private
|
122
|
+
def file
|
123
|
+
Pathname(object.file)
|
124
|
+
end
|
125
|
+
|
126
|
+
# The line number for the code
|
127
|
+
#
|
128
|
+
# @return [Integer]
|
129
|
+
# the line number
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def line
|
133
|
+
object.line
|
134
|
+
end
|
135
|
+
|
136
|
+
# The class and method name for the code
|
137
|
+
#
|
138
|
+
# @return [String]
|
139
|
+
# the class and method name
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
def path
|
143
|
+
object.path.sub(/\A::/, '')
|
144
|
+
end
|
145
|
+
|
146
|
+
module UtilityMethods #:nodoc:
|
147
|
+
private
|
148
|
+
|
149
|
+
# Throw a :skip measurement message to the caller
|
150
|
+
#
|
151
|
+
# This method allows you to short-circuit measurement methods when
|
152
|
+
# the measurement does not apply due to specific object states.
|
153
|
+
#
|
154
|
+
# @return [undefined]
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def skip
|
158
|
+
throw :measurement, :skip
|
159
|
+
end
|
160
|
+
|
161
|
+
# Throw a :todo measurement message to the caller
|
162
|
+
#
|
163
|
+
# This method allows you to short-circuit measurement methods when
|
164
|
+
# the measurement is not implemented.
|
165
|
+
#
|
166
|
+
# @return [undefined]
|
167
|
+
#
|
168
|
+
# @api private
|
169
|
+
def todo
|
170
|
+
throw :measurement, :todo
|
171
|
+
end
|
172
|
+
|
173
|
+
end # module UtilityMethods
|
174
|
+
|
175
|
+
end # class Measurement
|
176
|
+
end # module Yardstick
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Yardstick
|
2
|
+
module Method
|
3
|
+
include Measurable
|
4
|
+
|
5
|
+
measurement 'The method summary should be specified' do
|
6
|
+
skip if has_tag?('see')
|
7
|
+
summary != ''
|
8
|
+
end
|
9
|
+
|
10
|
+
measurement 'The method summary should be less than 80 characters in length' do
|
11
|
+
summary.length <= 78
|
12
|
+
end
|
13
|
+
|
14
|
+
measurement 'The method summary should be a single line' do
|
15
|
+
!summary.include?("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
measurement 'The method summary should not end in a period' do
|
19
|
+
summary[0, -1] != '.'
|
20
|
+
end
|
21
|
+
|
22
|
+
measurement 'The public/semipublic method should have an example specified' do
|
23
|
+
skip unless api?(%w[ public semipublic ]) && tag_types('return') != %w[ undefined ]
|
24
|
+
has_tag?('example')
|
25
|
+
end
|
26
|
+
|
27
|
+
measurement 'The @api tag should be specified' do
|
28
|
+
has_tag?('api')
|
29
|
+
end
|
30
|
+
|
31
|
+
measurement 'The @api tag must be either public, semipublic or private' do
|
32
|
+
%w[ public semipublic private ].include?(tag_text('api'))
|
33
|
+
end
|
34
|
+
|
35
|
+
measurement 'A method with public visibility must have an @api tag of public, semipublic or private' do
|
36
|
+
skip unless visibility == :public
|
37
|
+
api?(%w[ public semipublic private ])
|
38
|
+
end
|
39
|
+
|
40
|
+
measurement 'A method with protected visibility must have an @api tag of semipublic or private' do
|
41
|
+
skip unless visibility == :protected
|
42
|
+
api?(%w[ semipublic private ])
|
43
|
+
end
|
44
|
+
|
45
|
+
measurement 'A method with private visibility must have an @api tag of private' do
|
46
|
+
skip unless visibility == :private
|
47
|
+
api?(%w[ private ])
|
48
|
+
end
|
49
|
+
|
50
|
+
measurement 'The @return tag should be specified' do
|
51
|
+
has_tag?('return')
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# The text for a specified tag
|
57
|
+
#
|
58
|
+
# @param [String] tag_name
|
59
|
+
# the name of the tag
|
60
|
+
#
|
61
|
+
# @return [String, nil]
|
62
|
+
# the tag text if the tag exists
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def tag_text(tag_name)
|
66
|
+
tag(tag_name).text if has_tag?(tag_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# The types for a specified tag
|
70
|
+
#
|
71
|
+
# @param [String] tag_name
|
72
|
+
# the name of the tag
|
73
|
+
#
|
74
|
+
# @return [Array<String>, nil]
|
75
|
+
# a collection of tag types if the tag exists
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def tag_types(tag_name)
|
79
|
+
tag(tag_name).types if has_tag?(tag_name)
|
80
|
+
end
|
81
|
+
|
82
|
+
# The method visibility: public, protected or private
|
83
|
+
#
|
84
|
+
# @return [Symbol]
|
85
|
+
# the visibility of the method
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def visibility
|
89
|
+
object.visibility
|
90
|
+
end
|
91
|
+
|
92
|
+
# Check if the method API type matches
|
93
|
+
#
|
94
|
+
# @param [Array<String>] types
|
95
|
+
# a collection of API types
|
96
|
+
#
|
97
|
+
# @return [Boolean]
|
98
|
+
# true if the API type matches
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
def api?(types)
|
102
|
+
types.include?(tag_text('api'))
|
103
|
+
end
|
104
|
+
|
105
|
+
end # module Method
|
106
|
+
end # module Yardstick
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Yardstick
|
2
|
+
class Processor
|
3
|
+
|
4
|
+
# Measure files provided
|
5
|
+
#
|
6
|
+
# @param [Array<#to_str>] files
|
7
|
+
# the files to measure
|
8
|
+
#
|
9
|
+
# @return [Array<Measurement>]
|
10
|
+
# a collection of measurements
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def self.process_files(files)
|
14
|
+
YARD.parse(Pathname.glob(files).map { |file| file.to_str })
|
15
|
+
measure_method_objects(method_objects)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Measure string provided
|
19
|
+
#
|
20
|
+
# @param [#to_str] string
|
21
|
+
# the string to measure
|
22
|
+
#
|
23
|
+
# @return [Array<Measurement>]
|
24
|
+
# a collection of measurements
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def self.process_string(string)
|
28
|
+
YARD.parse_string(string.to_str)
|
29
|
+
measure_method_objects(method_objects)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return method objects in YARD registry
|
33
|
+
#
|
34
|
+
# @return [Array<YARD::CodeObjects::MethodObject>]
|
35
|
+
# a collection of method objects
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
def self.method_objects
|
39
|
+
YARD::Registry.all(:method).sort_by do |method_object|
|
40
|
+
[ method_object.file, method_object.line ]
|
41
|
+
end
|
42
|
+
ensure
|
43
|
+
YARD::Registry.clear
|
44
|
+
end
|
45
|
+
|
46
|
+
# Measure the method objects provided
|
47
|
+
#
|
48
|
+
# @param [Array<YARD::CodeObjects::MethodObject>] method_objects
|
49
|
+
# a collection of method objects
|
50
|
+
#
|
51
|
+
# @return [Array<Measurement>]
|
52
|
+
# a collection of measurements
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
def self.measure_method_objects(method_objects)
|
56
|
+
method_objects.map do |method_object|
|
57
|
+
method_object.docstring.measure
|
58
|
+
end.flatten
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
private :method_objects
|
63
|
+
private :measure_method_objects
|
64
|
+
end
|
65
|
+
|
66
|
+
end # class Processor
|
67
|
+
end # module Yardstick
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module YARD #:nodoc: all
|
2
|
+
module CodeObjects
|
3
|
+
class MethodObject
|
4
|
+
|
5
|
+
# Return the docstring associated with the method
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# method_object.docstring # => YARD::Docstring instance
|
9
|
+
#
|
10
|
+
# @return [YARD::Docstring]
|
11
|
+
# the docstring for this method
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def docstring
|
15
|
+
# TODO: update to use super() once reek does not flag it as
|
16
|
+
# a utility method
|
17
|
+
@docstring.extend(Yardstick::Method)
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO: create an object to wrap tags, and extend each tag object with
|
21
|
+
# the matching module, if one exists under Yardstick::Tag::*
|
22
|
+
|
23
|
+
end # class MethodObject
|
24
|
+
end # module CodeObjects
|
25
|
+
end # module YARD
|
data/lib/yardstick.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yard'
|
3
|
+
|
4
|
+
module Yardstick
|
5
|
+
VERSION = '0.0.1'.freeze
|
6
|
+
ROOT = Pathname(__FILE__).dirname.parent.expand_path.freeze
|
7
|
+
|
8
|
+
# Measure a list of files
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Yardstick.measure('article.rb') # => [ Measurement ]
|
12
|
+
#
|
13
|
+
# @param [Array<#to_str>, #to_str] files
|
14
|
+
# optional list of paths to measure
|
15
|
+
# @param [Hash] options
|
16
|
+
# optional configuration
|
17
|
+
#
|
18
|
+
# @return [Array<Measurement>]
|
19
|
+
# the measurements for each file
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def self.measure(files = 'lib/**/*.rb', options = {})
|
23
|
+
Yardstick::Processor.process_files(files)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Measure a string of code and YARD documentation
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# string = "def my_method; end"
|
30
|
+
#
|
31
|
+
# Yardstick.measure_string(string) # => [ Measurement ]
|
32
|
+
#
|
33
|
+
# @param [#to_str] string
|
34
|
+
# the string to measure
|
35
|
+
# @param [Hash] options
|
36
|
+
# optional configuration
|
37
|
+
#
|
38
|
+
# @return [Array<Measurement>]
|
39
|
+
# the measurements for the string
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
def self.measure_string(string, options = {})
|
43
|
+
Yardstick::Processor.process_string(string)
|
44
|
+
end
|
45
|
+
|
46
|
+
end # module Yardstick
|
47
|
+
|
48
|
+
$LOAD_PATH << Yardstick::ROOT + 'lib'
|
49
|
+
|
50
|
+
require 'yardstick/core_ext/object'
|
51
|
+
require 'yardstick/yard_ext'
|
52
|
+
require 'yardstick/autoload'
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.expand_path.join('..', '..', 'spec_helper')
|
3
|
+
|
4
|
+
shared_examples_for 'measurement is successful' do
|
5
|
+
before :all do
|
6
|
+
@measurement = Yardstick::Measurement.new('successful', @docstring) { true }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
shared_examples_for 'measurement is skipped' do
|
11
|
+
before :all do
|
12
|
+
@measurement = Yardstick::Measurement.new('skipped', @docstring) { skip }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
shared_examples_for 'measurement is not successful' do
|
17
|
+
before :all do
|
18
|
+
@measurement = Yardstick::Measurement.new('not successful', @docstring) { false }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_examples_for 'measurement is not implemented' do
|
23
|
+
before :all do
|
24
|
+
@measurement = Yardstick::Measurement.new('not implemented', @docstring) { todo }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
shared_examples_for 'measurement is warned' do
|
29
|
+
before :all do
|
30
|
+
$stderr = StringIO.new
|
31
|
+
@response = @measurement.warn
|
32
|
+
$stderr.rewind
|
33
|
+
@output = $stderr.read
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return nil' do
|
37
|
+
@response.should be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Yardstick::Measurement do
|
42
|
+
before :all do
|
43
|
+
YARD.parse_string('def test; end')
|
44
|
+
@docstring = YARD::Registry.all(:method).first.docstring
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '.new' do
|
48
|
+
before :all do
|
49
|
+
@response = Yardstick::Measurement.new('test measurement', @docstring) { true }
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should return a Measurement' do
|
53
|
+
@response.should be_kind_of(Yardstick::Measurement)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#ok?' do
|
58
|
+
describe 'when the measurement is successful' do
|
59
|
+
it_should_behave_like 'measurement is successful'
|
60
|
+
|
61
|
+
it 'should return true' do
|
62
|
+
@measurement.ok?.should be_true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'when the measurement is skipped' do
|
67
|
+
it_should_behave_like 'measurement is skipped'
|
68
|
+
|
69
|
+
it 'should return true' do
|
70
|
+
@measurement.ok?.should be_true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'when the measurement is not successful' do
|
75
|
+
it_should_behave_like 'measurement is not successful'
|
76
|
+
|
77
|
+
it 'should return false' do
|
78
|
+
@measurement.ok?.should be_false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'when the measurement is not implemented' do
|
83
|
+
it_should_behave_like 'measurement is not implemented'
|
84
|
+
|
85
|
+
it 'should return false' do
|
86
|
+
@measurement.ok?.should be_false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#skip?' do
|
92
|
+
describe 'when the measurement is successful' do
|
93
|
+
it_should_behave_like 'measurement is successful'
|
94
|
+
|
95
|
+
it 'should return false' do
|
96
|
+
@measurement.skip?.should be_false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'when the measurement is skipped' do
|
101
|
+
it_should_behave_like 'measurement is skipped'
|
102
|
+
|
103
|
+
it 'should return true' do
|
104
|
+
@measurement.skip?.should be_true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe 'when the measurement is not successful' do
|
109
|
+
it_should_behave_like 'measurement is not successful'
|
110
|
+
|
111
|
+
it 'should return false' do
|
112
|
+
@measurement.skip?.should be_false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'when the measurement is not implemented' do
|
117
|
+
it_should_behave_like 'measurement is not implemented'
|
118
|
+
|
119
|
+
it 'should return false' do
|
120
|
+
@measurement.skip?.should be_false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '#todo?' do
|
126
|
+
describe 'when the measurement is successful' do
|
127
|
+
it_should_behave_like 'measurement is successful'
|
128
|
+
|
129
|
+
it 'should return false' do
|
130
|
+
@measurement.todo?.should be_false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'when the measurement is skipped' do
|
135
|
+
it_should_behave_like 'measurement is skipped'
|
136
|
+
|
137
|
+
it 'should return false' do
|
138
|
+
@measurement.todo?.should be_false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'when the measurement is not successful' do
|
143
|
+
it_should_behave_like 'measurement is not successful'
|
144
|
+
|
145
|
+
it 'should return false' do
|
146
|
+
@measurement.todo?.should be_false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe 'when the measurement is not implemented' do
|
151
|
+
it_should_behave_like 'measurement is not implemented'
|
152
|
+
|
153
|
+
it 'should return true' do
|
154
|
+
@measurement.todo?.should be_true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#warn' do
|
160
|
+
describe 'when the measurement is successful' do
|
161
|
+
it_should_behave_like 'measurement is successful'
|
162
|
+
it_should_behave_like 'measurement is warned'
|
163
|
+
|
164
|
+
it 'should not output a warning' do
|
165
|
+
@output.should == ''
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe 'when the measurement is skipped' do
|
170
|
+
it_should_behave_like 'measurement is skipped'
|
171
|
+
it_should_behave_like 'measurement is warned'
|
172
|
+
|
173
|
+
it 'should not output a warning' do
|
174
|
+
@output.should == ''
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'when the measurement is not successful' do
|
179
|
+
it_should_behave_like 'measurement is not successful'
|
180
|
+
it_should_behave_like 'measurement is warned'
|
181
|
+
|
182
|
+
it 'should output a warning' do
|
183
|
+
@output.should == "(stdin):1: #test: not successful\n"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe 'when the measurement is not implemented' do
|
188
|
+
it_should_behave_like 'measurement is not implemented'
|
189
|
+
it_should_behave_like 'measurement is warned'
|
190
|
+
|
191
|
+
it 'should output a warning' do
|
192
|
+
@output.should == "(stdin):1: #test: not implemented\n"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
data/spec/rcov.opts
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
data/tasks/metrics.rake
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'metric_fu'
|
data/tasks/rdoc.rake
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rake/rdoctask'
|
2
|
+
|
3
|
+
Rake::RDocTask.new do |rdoc|
|
4
|
+
version = if File.exist?('VERSION.yml')
|
5
|
+
config = YAML.load(File.read('VERSION.yml'))
|
6
|
+
"#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
7
|
+
else
|
8
|
+
''
|
9
|
+
end
|
10
|
+
|
11
|
+
rdoc.rdoc_dir = 'rdoc'
|
12
|
+
rdoc.title = "yardstick #{version}"
|
13
|
+
rdoc.rdoc_files.include('README*')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
data/tasks/spec.rake
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
require 'spec/rake/verify_rcov'
|
3
|
+
|
4
|
+
spec_defaults = lambda do |spec|
|
5
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
6
|
+
spec.libs << 'lib' << 'spec'
|
7
|
+
spec.spec_opts << '--options' << 'spec/spec.opts'
|
8
|
+
end
|
9
|
+
|
10
|
+
Spec::Rake::SpecTask.new(:spec, &spec_defaults)
|
11
|
+
|
12
|
+
Spec::Rake::SpecTask.new(:rcov) do |rcov|
|
13
|
+
spec_defaults.call(rcov)
|
14
|
+
rcov.rcov = true
|
15
|
+
rcov.rcov_opts << File.readlines('spec/rcov.opts').map { |line| line.strip }
|
16
|
+
end
|
17
|
+
|
18
|
+
RCov::VerifyTask.new(:verify_rcov => :rcov) do |rcov|
|
19
|
+
rcov.threshold = 94.73
|
20
|
+
end
|
21
|
+
|
22
|
+
# original code by Ashley Moran:
|
23
|
+
# http://aviewfromafar.net/2007/11/1/rake-task-for-heckling-your-specs
|
24
|
+
desc 'Heckle each module and class'
|
25
|
+
task :heckle => :verify_rcov do
|
26
|
+
root_module = 'Yardstick'
|
27
|
+
spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
|
29
|
+
current_module = nil
|
30
|
+
current_method = nil
|
31
|
+
|
32
|
+
heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
|
33
|
+
unhandled_mutations = 0
|
34
|
+
|
35
|
+
IO.popen("spec --heckle #{root_module} #{spec_files}") do |pipe|
|
36
|
+
while line = pipe.gets
|
37
|
+
case line = line.chomp
|
38
|
+
when /\A\*\*\*\s+(#{root_module}(?:::)?(?:\w+(?:::)?)*)#(\w+)\b/
|
39
|
+
current_module, current_method = $1, $2
|
40
|
+
when "The following mutations didn't cause test failures:"
|
41
|
+
heckle_caught_modules[current_module] << current_method
|
42
|
+
when '+++ mutation'
|
43
|
+
unhandled_mutations += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
puts line
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if unhandled_mutations > 0
|
51
|
+
error_message_lines = [ "*************\n" ]
|
52
|
+
|
53
|
+
error_message_lines << "Heckle found #{unhandled_mutations} " \
|
54
|
+
"mutation#{"s" unless unhandled_mutations == 1} " \
|
55
|
+
"that didn't cause spec violations\n"
|
56
|
+
|
57
|
+
heckle_caught_modules.each do |mod, methods|
|
58
|
+
error_message_lines << "#{mod} contains the following " \
|
59
|
+
'poorly-specified methods:'
|
60
|
+
methods.each do |method|
|
61
|
+
error_message_lines << " - #{method}"
|
62
|
+
end
|
63
|
+
error_message_lines << ''
|
64
|
+
end
|
65
|
+
|
66
|
+
error_message_lines << 'Get your act together and come back ' \
|
67
|
+
'when your specs are doing their job!'
|
68
|
+
|
69
|
+
raise error_message_lines.join("\n")
|
70
|
+
else
|
71
|
+
puts 'Well done! Your code withstood a heckling.'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
task :default => :spec
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dkubb-yardstick
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Kubb
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-22 00:00:00 -07:00
|
13
|
+
default_executable: yardstick
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: yard
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0.2"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: dan.kubb@gmail.com
|
27
|
+
executables:
|
28
|
+
- yardstick
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- bin/yardstick
|
42
|
+
- lib/yardstick.rb
|
43
|
+
- lib/yardstick/autoload.rb
|
44
|
+
- lib/yardstick/cli.rb
|
45
|
+
- lib/yardstick/core_ext/object.rb
|
46
|
+
- lib/yardstick/measurable.rb
|
47
|
+
- lib/yardstick/measurement.rb
|
48
|
+
- lib/yardstick/method.rb
|
49
|
+
- lib/yardstick/processor.rb
|
50
|
+
- lib/yardstick/yard_ext.rb
|
51
|
+
- spec/public/yardstick/measurement_spec.rb
|
52
|
+
- spec/public/yardstick/method_spec.rb
|
53
|
+
- spec/public/yardstick_spec.rb
|
54
|
+
- spec/rcov.opts
|
55
|
+
- spec/spec.opts
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
- tasks/metrics.rake
|
58
|
+
- tasks/rdoc.rake
|
59
|
+
- tasks/spec.rake
|
60
|
+
has_rdoc: false
|
61
|
+
homepage: http://github.com/dkubb/yardstick
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --charset=UTF-8
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project: yardstick
|
82
|
+
rubygems_version: 1.2.0
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: A tool for verifying YARD documentation coverage
|
86
|
+
test_files:
|
87
|
+
- spec/public/yardstick/measurement_spec.rb
|
88
|
+
- spec/public/yardstick/method_spec.rb
|
89
|
+
- spec/public/yardstick_spec.rb
|
90
|
+
- spec/spec_helper.rb
|