debt_ceiling 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +8 -0
- data/Rakefile +11 -0
- data/bin/debt_ceiling +24 -0
- data/debt_ceiling.gemspec +35 -0
- data/examples/.debt_ceiling.example +36 -0
- data/examples/debt.rb.example +19 -0
- data/lib/debt_ceiling/debt.rb +36 -0
- data/lib/debt_ceiling/enforcement.rb +25 -0
- data/lib/debt_ceiling/version.rb +3 -0
- data/lib/debt_ceiling.rb +54 -0
- data/test/debt_ceiling_test.rb +5 -0
- data/test/test_helper.rb +3 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bb989ae976353da509970f25093e1ae303ec8b6f
|
4
|
+
data.tar.gz: a473a4ba56137d62578d03522e1519f11ab4d982
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d4243bb0c4569c7d42d37de59102d36dc29211647f087295fe4b5807f5493c416203f54734a295a744b8b0adc65672263dedabaa2f38594ea4f67e214e6f288
|
7
|
+
data.tar.gz: a4820d63a6f400e833c7015d85751aa539a58ae2c89d979d087b17eb350f397b1c2382cba9105cd5c8297b1d4b7adcb02d1e9929127c9038dad905c77cf04e74
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Brian Glusman
|
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.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#DebtCeiling
|
2
|
+
|
3
|
+
###
|
4
|
+
Work in progress, trying to use some automatic heuristic plus manual mechanisms to help visibility and tracking of technical debt.
|
5
|
+
|
6
|
+
Current plan is to configure/customize the weight given to heuristic grade
|
7
|
+
based first on a simple DSL in a .debt_ceiling file in the project's home directory, and if additional customization is desired, pass a path to
|
8
|
+
`extension_file_path` command in the DSL file to a file defining DebtCeiling::Debt like the one in examples directory, and replace/augment it's methods with your own additional calculation per file.
|
data/Rakefile
ADDED
data/bin/debt_ceiling
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/debt_ceiling"
|
4
|
+
if File.exists?(Dir.pwd + '/.debt_ceiling')
|
5
|
+
File.open(Dir.pwd + "/.debt_ceiling") {|f| DebtCeiling.module_eval(f.read)}
|
6
|
+
elsif File.exists?(Dir.home + '/.debt_ceiling')
|
7
|
+
File.open(Dir.home + '/.debt_ceiling') {|f| DebtCeiling.module_eval(f.read)}
|
8
|
+
else
|
9
|
+
puts "
|
10
|
+
***NOTICE:***
|
11
|
+
Using example .debt_ceiling file, which can be copied and customized from
|
12
|
+
#{File.dirname(File.dirname(`gem which debt_ceiling`.chop)) + '/examples/.debt_ceiling.example'}\n
|
13
|
+
place .debt_ceiling file in project directory and/or home directories, debt_ceiling looks
|
14
|
+
at pwd first for ./debt_ceiling and then at ~/.debt_ceiling and defaults to example
|
15
|
+
if neither is found\n\n"
|
16
|
+
|
17
|
+
File.open(File.dirname(File.dirname(`gem which debt_ceiling`.chop)) +
|
18
|
+
'/examples/.debt_ceiling.example') {|f| DebtCeiling.module_eval(f.read)}
|
19
|
+
end
|
20
|
+
|
21
|
+
extension_path = DebtCeiling.current_extension_file_path
|
22
|
+
load extension_path if File.exists?(extension_path)
|
23
|
+
|
24
|
+
DebtCeiling.enforce(ARGV[0] ? ARGV[0] : ".")
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$:.unshift lib unless $:.include?(lib)
|
4
|
+
require "debt_ceiling/version"
|
5
|
+
|
6
|
+
# module ::Gem
|
7
|
+
# post_install {|gem_installer| puts 'in hook'; DebtCeiling.post_install(gem_installer) }
|
8
|
+
# end
|
9
|
+
|
10
|
+
Gem::Specification.new do |s|
|
11
|
+
s.name = "debt_ceiling"
|
12
|
+
s.version = DebtCeiling::VERSION
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.authors = ["Brian Glusman"]
|
15
|
+
s.email = ["bglusman@shutterstock.com"]
|
16
|
+
s.homepage = "https://github.com/bglusman/DebtCeiling"
|
17
|
+
s.summary = "DebtCeiling helps you track Tech Debt"
|
18
|
+
s.rubyforge_project = "debt_ceiling"
|
19
|
+
s.extensions = ["Rakefile"]
|
20
|
+
|
21
|
+
s.description = <<-DESC
|
22
|
+
Quantify and add visibility/history to (some half-baked standin for) Technical Debt
|
23
|
+
DESC
|
24
|
+
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
|
31
|
+
s.add_runtime_dependency "rubycritic", "~> 1.1.1"
|
32
|
+
s.add_runtime_dependency "pry"
|
33
|
+
s.add_development_dependency "rake"
|
34
|
+
s.add_development_dependency "minitest"
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#TODO example gradings
|
2
|
+
# debt_ceiling 15000
|
3
|
+
|
4
|
+
b_cost_per_line 10
|
5
|
+
c_cost_per_line 20
|
6
|
+
d_cost_per_line 40
|
7
|
+
f_cost_per_line 100
|
8
|
+
# todo_cost 500
|
9
|
+
|
10
|
+
# debt_per_reference_to blah
|
11
|
+
|
12
|
+
# points_per_regex_match REGEX, points
|
13
|
+
|
14
|
+
#rubocop/cane integration debt for style violations
|
15
|
+
|
16
|
+
#every line over x ideal file size is y points of debt
|
17
|
+
|
18
|
+
#whitelist/blacklist API: TODO
|
19
|
+
|
20
|
+
#multipliers for important files
|
21
|
+
|
22
|
+
#ditch/augment module eval and use below or something
|
23
|
+
|
24
|
+
# class DebtRule
|
25
|
+
# def measure_file(filename, metrics)
|
26
|
+
# cost = 0
|
27
|
+
|
28
|
+
# cost += 0 if metrics.rubycritic.grade == :a
|
29
|
+
# cost += 10 if metrics.rubycritic.grade == :b
|
30
|
+
|
31
|
+
# churn = measure_churn(filename)
|
32
|
+
# cost += whatever if churn > something
|
33
|
+
# end
|
34
|
+
|
35
|
+
# def measure_churn...
|
36
|
+
# end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module DebtCeiling
|
2
|
+
class Debt
|
3
|
+
#replace the DebtCeiling cost calculation per file with your own
|
4
|
+
|
5
|
+
|
6
|
+
def measure_debt
|
7
|
+
#calculcate entire cost based on #file_attributes
|
8
|
+
#or return false/nil to use defaults if you want to conditionally
|
9
|
+
#override default cost calculation
|
10
|
+
end
|
11
|
+
|
12
|
+
#OR do your own calcuation first, and add defaults on top of that
|
13
|
+
def augment_custom_debt
|
14
|
+
#output additional cost beyond defaults based on #file_attributes
|
15
|
+
end
|
16
|
+
|
17
|
+
#augment_custom_debt is only called if measure_debt is undefined or returns nil/false
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DebtCeiling
|
2
|
+
class Debt
|
3
|
+
attr_reader :file_attributes
|
4
|
+
attr_accessor :debt_amount
|
5
|
+
def initialize(file_attributes)
|
6
|
+
@file_attributes = file_attributes
|
7
|
+
default_measure_debt
|
8
|
+
end
|
9
|
+
|
10
|
+
def default_measure_debt
|
11
|
+
if self.respond_to?(:measure_file)
|
12
|
+
cost = self.public_send(:measure_debt)
|
13
|
+
end
|
14
|
+
if !cost
|
15
|
+
cost = if self.respond_to?(:augment_custom_debt)
|
16
|
+
self.public_send(:augment_custom_debt) || 0
|
17
|
+
else
|
18
|
+
0
|
19
|
+
end
|
20
|
+
letter_grade = file_attributes.analyzed_module.rating.to_s.downcase
|
21
|
+
cost_per_line = DebtCeiling.public_send("#{letter_grade}_current_cost_per_line")
|
22
|
+
cost += file_attributes.linecount * cost_per_line
|
23
|
+
end
|
24
|
+
self.debt_amount = cost
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_i
|
28
|
+
debt_amount
|
29
|
+
end
|
30
|
+
|
31
|
+
def +(other)
|
32
|
+
self.to_i + other.to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubycritic'
|
2
|
+
require 'ostruct'
|
3
|
+
module DebtCeiling
|
4
|
+
class Enforcement
|
5
|
+
class << self
|
6
|
+
attr_reader :rules
|
7
|
+
def add(rule)
|
8
|
+
@rules ||= []
|
9
|
+
@rules << rule
|
10
|
+
end
|
11
|
+
|
12
|
+
def process_directory(path, output=:destructured)
|
13
|
+
modules = Rubycritic::Orchestrator.new.critique([path])
|
14
|
+
debts = modules.map do |mod|
|
15
|
+
file_attributes = OpenStruct.new
|
16
|
+
file_attributes.linecount = `wc -l #{mod.path}`.match(/\d+/)[0].to_i
|
17
|
+
file_attributes.path = mod.path
|
18
|
+
file_attributes.analyzed_module = mod
|
19
|
+
debt_rule = Debt.new(file_attributes)
|
20
|
+
end
|
21
|
+
puts "Current total tech debt: #{debts.map(&:to_i).reduce(&:+)}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/debt_ceiling.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
require_relative 'debt_ceiling/enforcement'
|
3
|
+
require_relative 'debt_ceiling/debt'
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
module DebtCeiling
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def debt_ceiling(pattern, message=nil, options={})
|
11
|
+
if pattern.kind_of?(String)
|
12
|
+
rule = Rule.new(pattern, message, options)
|
13
|
+
DebtCeiling::Enforcement.add(rule)
|
14
|
+
else
|
15
|
+
DebtCeiling::Enforcement.add(self.send(pattern))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def enforce(dir=".")
|
20
|
+
DebtCeiling::Enforcement.process_directory(dir, :stdout)
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_files(files)
|
24
|
+
files.reduce(error: false, output:"") do |results, file|
|
25
|
+
error, output = DebtCeiling::Enforcement.process_file(file)
|
26
|
+
results[:error] ||= error
|
27
|
+
results[:output] += output
|
28
|
+
results
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
@extension_file_path = "#{Dir.pwd}/debt_rule.rb"
|
33
|
+
def extension_file_path(path)
|
34
|
+
@extension_file_path = path
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_extension_file_path
|
38
|
+
@extension_file_path
|
39
|
+
end
|
40
|
+
|
41
|
+
GRADES = [:a, :b, :c, :d, :f]
|
42
|
+
GRADE_DEFAULTS = [0, 10, 20, 40, 100]
|
43
|
+
GRADE_MAP = GRADES.zip(0..4).to_h
|
44
|
+
GRADES.each do |grade|
|
45
|
+
default_index = GRADE_MAP[grade]
|
46
|
+
instance_variable_set "@#{grade}_cost_per_line", GRADE_DEFAULTS[default_index]
|
47
|
+
define_method("#{grade}_current_cost_per_line") do
|
48
|
+
instance_variable_get "@#{grade}_cost_per_line"
|
49
|
+
end
|
50
|
+
define_method("#{grade}_cost_per_line") do |value| #def set methods, no =
|
51
|
+
instance_variable_set "@#{grade}_cost_per_line", value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: debt_ceiling
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Glusman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubycritic
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.1.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: |2
|
70
|
+
Quantify and add visibility/history to (some half-baked standin for) Technical Debt
|
71
|
+
email:
|
72
|
+
- bglusman@shutterstock.com
|
73
|
+
executables:
|
74
|
+
- debt_ceiling
|
75
|
+
extensions:
|
76
|
+
- Rakefile
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- ".gitignore"
|
80
|
+
- Gemfile
|
81
|
+
- LICENSE
|
82
|
+
- README.md
|
83
|
+
- Rakefile
|
84
|
+
- bin/debt_ceiling
|
85
|
+
- debt_ceiling.gemspec
|
86
|
+
- examples/.debt_ceiling.example
|
87
|
+
- examples/debt.rb.example
|
88
|
+
- lib/debt_ceiling.rb
|
89
|
+
- lib/debt_ceiling/debt.rb
|
90
|
+
- lib/debt_ceiling/enforcement.rb
|
91
|
+
- lib/debt_ceiling/version.rb
|
92
|
+
- test/debt_ceiling_test.rb
|
93
|
+
- test/test_helper.rb
|
94
|
+
homepage: https://github.com/bglusman/DebtCeiling
|
95
|
+
licenses: []
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project: debt_ceiling
|
113
|
+
rubygems_version: 2.2.2
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: DebtCeiling helps you track Tech Debt
|
117
|
+
test_files: []
|