debt_ceiling 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b3cdc1008d893ea9057c63dd4834d2b801dfaff
4
- data.tar.gz: 8df6b9d644c3efc86e93b84426ef96bbd10cd105
3
+ metadata.gz: bea87b2ccec435c3759009586048f178a40d963f
4
+ data.tar.gz: 9538baf4d664690d4deb022db375f1a024471a2c
5
5
  SHA512:
6
- metadata.gz: 3cb2015140741a74a2a3a70f499cc26cd236b5da6d47280f4e8e0471b61761b458d587acc1ed7748dedbfba31e5e8c7b231655b1d7f2ef1cc56738aa611a4c5c
7
- data.tar.gz: 0748963438e02fd19db6646ab1ea71e08bf6d956931b5d0be120c31f150214c64711b5acc8ada8ff44324138b01090f05c2672041da078416c3124ddae24d0e2
6
+ metadata.gz: 9545baeb0a2fb3f1f5bb21d3642d3e63d7aae32a9124dce6bfd11a3536a8e177f5854c37917fda715439702d18f088b97a0134c75bc9d904685d497c84fe4a9f
7
+ data.tar.gz: ed0148e021b41fa1618abf56458068a7afa1d01ee959c316987ea5f4198170a812d5a15db9ac88bb1b50295f52a7055ced1130e401139087ac1ac20db569cf47
data/README.md CHANGED
@@ -7,11 +7,12 @@
7
7
  Main goal is to enforce a technical debt ceiling and tech debt reduction deadlines for your Ruby project programmatically via static analysis as part of your application's test suite. Eventually perhaps will aid in visualizing tech debt as a graph or graphs (breakind down debt into various categories and sources).
8
8
 
9
9
  Current features include:
10
- * configuring points per [RubyCritic](https://github.com/whitesmith/rubycritic) grade per file line
10
+ * configuring points per [RubyCritic](https://github.com/whitesmith/rubycritic) grade per file line (add FULL_ANALYSIS=true for a lengthier analysis by RubyCritic including churn and more code smells, but same grading logic, made available for use by hooks)
11
11
  * Whitelisting/blacklisting files by matching path/filename
12
12
  * Modifying or replacing default calculation on a per file basis
13
13
  * Reporting the single greatest source of debt based on your definitions
14
14
  * Reporting total debt for the git repo based on your definitions
15
+ * Adding cost for TODOs or deprecated references you specify (see .debt_ceiling.example)
15
16
  * Running the binary from a test suite to fail if debt ceiling is exceeded
16
17
  * Running the binary from a test suite to fail if debt deadline is missed
17
18
 
@@ -49,7 +50,7 @@ As mentioned/linked above, additional customization is supported.
49
50
 
50
51
  As shown in example file, pass a path to `extension_file_path` command pointing to a file defining DebtCeiling::Debt like the one in examples directory, and define its methods for your own additional calculation per file.
51
52
 
52
- Right now it lacks all tests... feel free to open a PR!
53
+ Right now it lacks all tests... feel free to open a PR!
53
54
 
54
55
  I'll try and add test coverage where it makes sense as API matures.
55
56
 
@@ -57,7 +58,7 @@ I'll try and add test coverage where it makes sense as API matures.
57
58
 
58
59
  todo_cost 500 #cost per comment matching /TODO/
59
60
 
60
- debt_per_reference_to deprecated_regex, 500 # help transition away from deprecated APIs
61
+ debt_per_reference_to deprecated_regex, 500 # help transition away from deprecated APIs
61
62
 
62
63
  points_per_regex_match REGEX, 500 # seems like an alias for above maybe, nix?
63
64
 
@@ -70,7 +71,9 @@ every line over x ideal file size is y points of debt
70
71
 
71
72
  multipliers for important files
72
73
 
73
- include one of the JS complexity/debt analysis libraries below, or another if anyone had another suggestion:
74
+ visualization/history of debt would be nice, but unclear how to best support... one possibility is running it against each commit in a repo, and using git-notes to add score data (and some metadata perhaps?) to store it for comparing/graphing, and for comparing branches etc. optionally configured could do this for every commit that doesn't already have a note attached, or for which the note's metadata/version is out of sync with current definitions.
75
+
76
+ include one of the JS complexity/debt analysis libraries below, or another if anyone had another suggestion:
74
77
 
75
78
  * https://github.com/es-analysis/plato
76
79
 
data/Rakefile CHANGED
@@ -1,10 +1,5 @@
1
1
  #!/usr/bin/env rake
2
2
 
3
- if Regexp.new('RUBYARCHDIR') === ARGV[0]
4
- require_relative "lib/debt_ceiling/post_install"
5
- DebtCeiling::PostInstall.new(ARGV[0])
6
- end
7
-
8
3
  task :default => 'test'
9
4
  task :test do
10
5
  sh "ruby test/debt_ceiling_test.rb"
@@ -12,8 +12,14 @@ f_cost_per_line 100
12
12
  #load custom debt calculations (see examples/debt.rb) from this path
13
13
  extension_file_path "./debt.rb"
14
14
 
15
- #only count debt scores for files matching these strings (converted to regexes)
15
+ #below two both same mechanic, todo just assumes capital TODO as string
16
+ cost_per_todo 50
17
+
18
+ debt_per_reference_to 'DEPRECATED_API', 20
19
+
20
+
21
+ #only count debt scores for file paths matching these strings (converted to regexes)
16
22
  whitelist_matching %w(app lib)
17
23
  #or
18
24
  #exclude debt scores for files matching these strings (commented as mutually exclusive)
19
- #blacklist_matching %w(schema.rb routes.rb)
25
+ #blacklist_matching %w(schema.rb routes.rb)
@@ -6,32 +6,42 @@ module DebtCeiling
6
6
  TargetDeadlineMissed = Class.new(StandardError)
7
7
  class << self
8
8
  def calculate(path)
9
- #temporarily use Rubycritic internals until they provide an API
10
- require "rubycritic/modules_initializer"
11
- require "rubycritic/analysers/complexity"
12
- require "rubycritic/analysers/smells/flay"
13
-
14
- analysed_modules = Rubycritic::ModulesInitializer.init([path])
15
- [Rubycritic::Analyser::Complexity, Rubycritic::Analyser::FlaySmells].each do |analyser|
16
- analyser.new(analysed_modules).run
17
- end
18
- debts = construct_debts(analysed_modules)
19
- max_debt = debts.max_by(&:to_i)
20
- total_debt = debts.map(&:to_i).reduce(&:+)
9
+ analysed_modules = construct_rubycritic_modules(path)
10
+ debts = construct_debts(analysed_modules)
11
+ max_debt = debts.max_by(&:to_i)
12
+ total_debt = debts.map(&:to_i).reduce(:+)
21
13
  puts "Current total tech debt: #{total_debt}"
22
- puts "Largest source of debt is: #{max_debt.file_attributes.analysed_module.name} at #{max_debt.to_i}"
14
+ puts "Largest source of debt is: #{max_debt.name} at #{max_debt.to_i}"
23
15
  total_debt
24
16
  end
25
17
 
26
18
  def construct_debts(modules)
27
- modules.map do |mod|
19
+ modules.map do |mod|
28
20
  file_attributes = OpenStruct.new
29
21
  file_attributes.linecount = `wc -l #{mod.path}`.match(/\d+/)[0].to_i
30
22
  file_attributes.path = mod.path
31
23
  file_attributes.analysed_module = mod
24
+ file_attributes.source_code = File.read(mod.path)
32
25
  debt_rule = Debt.new(file_attributes)
33
26
  end
34
27
  end
28
+
29
+ def construct_rubycritic_modules(path)
30
+ if ENV['FULL_ANALYSIS']
31
+ Rubycritic::Orchestrator.new.critique([path])
32
+ else
33
+ #temporarily use Rubycritic internals until they provide an API
34
+ require "rubycritic/modules_initializer"
35
+ require "rubycritic/analysers/complexity"
36
+ require "rubycritic/analysers/smells/flay"
37
+
38
+ modules = Rubycritic::ModulesInitializer.init([path])
39
+ [Rubycritic::Analyser::Complexity, Rubycritic::Analyser::FlaySmells].each do |analyser|
40
+ analyser.new(modules).run
41
+ end
42
+ modules
43
+ end
44
+ end
35
45
  end
36
46
  end
37
47
  end
@@ -2,7 +2,7 @@ module DebtCeiling
2
2
  class Debt
3
3
  DoNotWhitelistAndBlacklistSimulateneously = Class.new(StandardError)
4
4
 
5
- attr_reader :file_attributes, :path, :analysed_module, :module_name, :linecount
5
+ attr_reader :file_attributes, :path, :analysed_module, :module_name, :linecount, :source_code
6
6
  attr_accessor :debt_amount
7
7
  def initialize(file_attributes)
8
8
  @file_attributes = file_attributes
@@ -10,6 +10,7 @@ module DebtCeiling
10
10
  @analysed_module = file_attributes.analysed_module
11
11
  @module_name = file_attributes.name
12
12
  @linecount = file_attributes.linecount
13
+ @source_code = file_attributes.source_code
13
14
  default_measure_debt if valid_debt?
14
15
  end
15
16
 
@@ -26,10 +27,21 @@ module DebtCeiling
26
27
  letter_grade = file_attributes.analysed_module.rating.to_s.downcase
27
28
  cost_per_line = DebtCeiling.public_send("#{letter_grade}_current_cost_per_line")
28
29
  cost += file_attributes.linecount * cost_per_line
30
+ cost += debt_from_source_code_rules
29
31
  end
30
32
  self.debt_amount = cost
31
33
  end
32
34
 
35
+ def debt_from_source_code_rules
36
+ text_match_debt('TODO', DebtCeiling.current_cost_per_todo) +
37
+ DebtCeiling.deprecated_reference_pairs.map {|string, value|
38
+ text_match_debt(string, value.to_i) }.inject(&:+).to_i
39
+ end
40
+
41
+ def text_match_debt(string, cost)
42
+ source_code.scan(string).count * cost
43
+ end
44
+
33
45
  def valid_debt?
34
46
  black_empty = DebtCeiling.blacklist.empty?
35
47
  white_empty = DebtCeiling.whitelist.empty?
@@ -47,6 +59,10 @@ module DebtCeiling
47
59
  DebtCeiling.blacklist.detect {|filename| filename.match debt.path }
48
60
  end
49
61
 
62
+ def name
63
+ file_attributes.analysed_module.name || path.to_s.split('/').last
64
+ end
65
+
50
66
  def to_i
51
67
  debt_amount.to_i
52
68
  end
@@ -1,3 +1,3 @@
1
1
  module DebtCeiling
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.6"
3
3
  end
data/lib/debt_ceiling.rb CHANGED
@@ -42,6 +42,14 @@ module DebtCeiling
42
42
  @ceiling_amount = value
43
43
  end
44
44
 
45
+ def cost_per_todo(value)
46
+ @current_cost_per_todo = value.to_i
47
+ end
48
+
49
+ def debt_per_reference_to(string, value)
50
+ deprecated_reference_pairs[string] = value
51
+ end
52
+
45
53
  def debt_reduction_target_and_date(target_value, date_to_parse)
46
54
  @reduction_target = target_value
47
55
  @reduction_date = Chronic.parse(date_to_parse)
@@ -50,7 +58,7 @@ module DebtCeiling
50
58
  def evaluate
51
59
  if ceiling_amount && ceiling_amount <= debt
52
60
  fail_test
53
- elsif reduction_target && reduction_target <= debt &&
61
+ elsif reduction_target && reduction_target <= debt &&
54
62
  Time.now > reduction_date
55
63
  fail_test
56
64
  end
@@ -60,9 +68,12 @@ module DebtCeiling
60
68
  Kernel.exit 1
61
69
  end
62
70
 
63
- attr_reader :blacklist, :whitelist, :ceiling_amount, :reduction_date, :reduction_target, :debt
71
+ attr_reader :blacklist, :whitelist, :ceiling_amount, :reduction_date, :reduction_target,
72
+ :debt, :current_cost_per_todo, :deprecated_reference_pairs
64
73
  @blacklist = []
65
74
  @whitelist = []
75
+ @current_cost_per_todo = 0
76
+ @deprecated_reference_pairs = {}
66
77
 
67
78
  GRADE_MAP = {a: 0, b: 10, c: 20, d: 40, f: 100} #arbitrary default grades for now
68
79
  GRADE_MAP.keys.each do |grade|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debt_ceiling
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Glusman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-20 00:00:00.000000000 Z
11
+ date: 2014-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubycritic