debt_ceiling 0.1.1 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OWNlZGJlMzY1M2EyZTg5ZDJmNDhlM2RiYzc5MGVlZmYyZmQ0YWU4ZQ==
5
- data.tar.gz: !binary |-
6
- ZDdmOWM0NWE1NmVkOGQ4NWIyNTFkZjFiNTA0NWNmMTQ2OWRiZDQ1NQ==
2
+ SHA1:
3
+ metadata.gz: 50413717fff100682e4d62f350b6b0625c92f5bf
4
+ data.tar.gz: 17ad97252626d36a70b221725fd7213962951dd6
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OWJhMDhhMTM3ZDAyNjBlNDg4OThhYjhhYWQ5ZjA3MTZlODUyYjg2OGNjNjA3
10
- YzEyY2Y1OGQ2YTllNzU2NDJhY2VlZjhkNzFjNjEyZDgxOTY5MWQzYzZlZWUw
11
- NTFlNmMyZjFkZTkwNzI4NjY4Yjg4M2M3MDc1NGUyNGJmMTJmYjI=
12
- data.tar.gz: !binary |-
13
- NzI3ZGNhMzJlOTBiNDY2MDNmOTBlM2I3OWQxYjQ2MWFiZTAwMjEyMzIyOWJh
14
- ZDc5ZWFlZDZhNWNmYmY1M2MwMzc0NTdmZjUxYjkxMTFmM2IyY2Q4ZjcwZDU4
15
- YTlmZWJhZmIzYjcxYmM1YTZjNDk1NDUzNGU2M2U2ZTQ4ZjAxM2U=
6
+ metadata.gz: 9cb9b3b28fa5aa7f3b09e832b7b279412181d71157e7ac4cbd9f8bc4e5fb2d3d7cc759a76806aad8193bb9f997e15d9ec732a617d65619ff620081fd6131d4ae
7
+ data.tar.gz: 76afab03d0406fe9a1988ec25e648629afea95702e9356645073253b325d00a28b6cde6ea74169fd36dcc43805a97c6216e1cbeb14a97f5695083b24190da677
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  Gemfile.lock
2
- .debt_ceiling_definition
2
+ tmp/*
3
+ coverage/*
data/.travis.yml CHANGED
@@ -4,3 +4,6 @@ rvm:
4
4
  - jruby-19mode # JRuby in 1.9 mode
5
5
  - 2.1.1
6
6
  - rbx-2.2
7
+ addons:
8
+ code_climate:
9
+ repo_token: cd7df1b0973b1b3323ce8b2f09fe9c087010115889fed6
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source :rubygems
1
+ source "https://rubygems.org"
2
2
  gemspec
data/debt_ceiling.gemspec CHANGED
@@ -23,11 +23,12 @@ Gem::Specification.new do |s|
23
23
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
24
24
  s.require_paths = ['lib']
25
25
 
26
- s.add_runtime_dependency 'rubycritic', '~> 1.1.1'
26
+ s.add_runtime_dependency 'rubycritic', '~> 1.4'
27
27
  s.add_runtime_dependency 'chronic', '~> 0.10'
28
- s.add_runtime_dependency 'configurations', '~> 2.0.0.pre'
28
+ s.add_runtime_dependency 'configurations', '~> 2.0'
29
29
  s.add_development_dependency 'pry', '~> 0.10'
30
30
  s.add_development_dependency 'rake', '~> 10.3'
31
31
  s.add_development_dependency 'rspec', '~> 3.1'
32
32
  s.add_development_dependency 'coveralls', '~> 0.7'
33
+ s.add_development_dependency 'codeclimate-test-reporter', '~> 0.4'
33
34
  end
@@ -4,19 +4,38 @@ DebtCeiling.configure do |c|
4
4
  #exceeding this target will fail after the reduction date (parsed by Chronic)
5
5
  c.reduction_target = 100
6
6
  c.reduction_date = 'Jan 1 2015'
7
- #set the multipliers per line of code in a file with each letter grade, these are the current defaults
8
- c.grade_points = { a: 0, b: 10, c: 20, d: 40, f: 100 }
7
+ #Max debt to allow in any one file/module before failing tests
8
+ c.max_debt_per_module = 200
9
+
10
+ #ABOVE config ^ effect what amount of debt where and when can fail your test suite
11
+ # none of the above have any default settings
12
+
13
+ #BELOW config v effect how debt is calculated
14
+ # most of these have 'sane' default settings, look at code and/or docs for details
15
+
16
+ #set the base cost per file for each letter grade, these are the current defaults
17
+ c.grade_points = { a: 0, b: 3, c: 13, d: 55, f: 144 }
18
+ #default multiplier for debt factors from flog, flay, reek and method count
19
+ c.complexity_multiplier = 0.5
20
+ c.method_count_multiplier = 0.5
21
+ c.smells_multiplier = 3
22
+ c.duplication_multiplier = 1.5
23
+ c.ideal_max_line_count = 100
24
+ c.cost_per_line_over_ideal = 1
9
25
  #load custom debt calculations (see examples/debt.rb) from this path
10
26
  c.extension_path = "./debt.rb"
11
- #below two both use same mechanic, todo just assumes capital TODO as string, cost_per_todo defaults to 0
27
+ #below two both use same mechanic, cost_per_todo just assumes capital
28
+ #TODO as string, cost_per_todo defaults to 0
12
29
  c.cost_per_todo = 50
13
30
  c.deprecated_reference_pairs = { 'DEPRECATED_API' => 20}
14
- #manually assign debt to code sections with these or with default "TECH DEBT", as a comment like #TECH DEBT +50
31
+ #manually assign arbitrary case specific amount of debt to code sections
32
+ #with specified terms or with default "TECH DEBT", as a comment like #TECH DEBT +50
15
33
  c.manual_callouts += ["REFACTOR THIS", "HORRIBLE HACK"]
16
34
  #only count debt scores for files/folders matching these strings (converted to regexes)
17
35
  c.whitelist = %w(app lib)
18
36
  #or
19
- #exclude debt scores for files/folders matching these strings (commented as mutually exclusive)
37
+ #exclude debt scores for files/folders matching these strings
38
+ #(commented out here as white/black lists are mutually exclusive)
20
39
  #c.blacklist = %w(config version debt_ceiling.rb)
21
40
  end
22
41
 
data/lib/debt_ceiling.rb CHANGED
@@ -1,53 +1,82 @@
1
1
  require 'configurations'
2
2
  require 'chronic'
3
+ require 'rubycritic'
4
+ require 'rubycritic/cli/application'
5
+ require 'ostruct'
3
6
  require_relative 'debt_ceiling/accounting'
7
+ require_relative 'debt_ceiling/custom_debt_analysis'
4
8
  require_relative 'debt_ceiling/debt'
9
+ require_relative 'debt_ceiling/compatibility'
10
+ require_relative 'debt_ceiling/file_attributes'
5
11
 
6
12
  module DebtCeiling
7
13
  include Configurations
8
14
  extend Forwardable
9
15
  extend self
10
16
 
11
- attr_reader :debt
12
- def_delegator :configuration, :extension_path
13
- def_delegator :configuration, :blacklist
14
- def_delegator :configuration, :whitelist
15
- def_delegator :configuration, :cost_per_todo
16
- def_delegator :configuration, :deprecated_reference_pairs
17
- def_delegator :configuration, :manual_callouts
18
- def_delegator :configuration, :grade_points
19
- def_delegator :configuration, :reduction_date
20
- def_delegator :configuration, :reduction_target
21
- def_delegator :configuration, :debt_ceiling
22
-
23
- configuration_defaults do |c|
24
- c.extension_path = "#{Dir.pwd}/debt.rb"
25
- c.blacklist = []
26
- c.whitelist = []
27
- c.deprecated_reference_pairs = {}
28
- c.manual_callouts = ['TECH DEBT']
29
- c.grade_points = { a: 0, b: 10, c: 20, d: 40, f: 100 }
17
+ attr_reader :total_debt, :accounting_result
18
+
19
+ def_delegators :configuration, :extension_path, :blacklist, :whitelist,
20
+ :cost_per_todo, :deprecated_reference_pairs, :manual_callouts,
21
+ :grade_points, :reduction_date, :reduction_target, :debt_ceiling,
22
+ :max_debt_per_module, :non_grade_scoring, :complexity_multiplier ,
23
+ :method_count_multiplier, :smells_multiplier, :duplication_multiplier,
24
+ :ideal_max_line_count, :cost_per_line_over_ideal
25
+
26
+ configuration_defaults do |config|
27
+ config.extension_path = "#{Dir.pwd}/debt.rb"
28
+ config.blacklist = []
29
+ config.whitelist = []
30
+ config.deprecated_reference_pairs = {}
31
+ config.manual_callouts = ['TECH DEBT']
32
+ config.grade_points = { a: 0, b: 3, c: 13, d: 55, f: 144 }
33
+ config.complexity_multiplier = 0.5
34
+ config.method_count_multiplier = 0.5
35
+ config.smells_multiplier = 3
36
+ config.duplication_multiplier = 1.5
37
+ config.ideal_max_line_count = 100
38
+ config.cost_per_line_over_ideal = 1
39
+ #smells are pretty valid/fixable, complexity and method count
40
+ #may be inherent/way to improve smells
41
+ end
42
+
43
+
44
+ def calculate(dir = '.', opts={preconfigured: false})
45
+ @total_debt = accounting_result(dir, opts).total_debt
46
+ fail_test if failed_condition?
47
+ total_debt
48
+ end
49
+
50
+ def accounting_result(dir = '.', opts={preconfigured: false})
51
+ @accounting_result ||= begin
52
+ load_configuration unless @loaded || opts[:preconfigured]
53
+ Accounting.calculate(dir)
54
+ end
30
55
  end
31
56
 
32
- def load_configuration
33
- if File.exist?(Dir.pwd + '/.debt_ceiling.rb')
34
- load(Dir.pwd + '/.debt_ceiling.rb')
35
- elsif File.exist?(Dir.home + '/.debt_ceiling.rb')
36
- load(Dir.home + '/.debt_ceiling.rb')
57
+ def load_configuration(config_file_name=".debt_ceiling.rb")
58
+ pwd = Dir.pwd
59
+ home = Dir.home
60
+ if File.exist?("#{pwd}/#{config_file_name}")
61
+ load("#{pwd}/#{config_file_name}")
62
+ elsif File.exist?("#{home}/#{config_file_name}")
63
+ load("#{home}/#{config_file_name}")
37
64
  else
38
- puts "No .debt_ceiling.rb configuration file detected in #{Dir.pwd} or ~/, using defaults"
65
+ puts "No #{config_file_name} configuration file detected in #{pwd} or ~/, using defaults"
39
66
  end
40
67
 
41
68
  load extension_path if extension_path && File.exist?(extension_path)
42
69
  @loaded = true
43
70
  end
44
71
 
45
- def calculate(dir = '.', opts={preconfigured: false})
46
- load_configuration unless @loaded || opts[:preconfigured]
47
- @debt = DebtCeiling::Accounting.calculate(dir)
48
- evaluate
72
+ def clear
73
+ @accounting_result = nil
74
+ Accounting.clear
49
75
  end
50
76
 
77
+ private
78
+
79
+
51
80
  def blacklist_matching(matchers)
52
81
  @blacklist = matchers.map { |matcher| Regexp.new(matcher) }
53
82
  end
@@ -61,15 +90,21 @@ module DebtCeiling
61
90
  deprecated_reference_pairs[string] = value
62
91
  end
63
92
 
93
+ def failed_condition?
94
+ exceeded_total_limit? || missed_target? || max_debt_per_module_exceeded?
95
+ end
64
96
 
65
- def evaluate
66
- if debt_ceiling && debt_ceiling <= debt
67
- fail_test
68
- elsif reduction_target && reduction_target <= debt &&
97
+ def exceeded_total_limit?
98
+ debt_ceiling && debt_ceiling <= total_debt
99
+ end
100
+
101
+ def missed_target?
102
+ reduction_target && reduction_target <= total_debt &&
69
103
  Time.now > Chronic.parse(reduction_date)
70
- fail_test
71
- end
72
- debt
104
+ end
105
+
106
+ def max_debt_per_module_exceeded?
107
+ max_debt_per_module && max_debt_per_module <= accounting_result.max_debt.to_i
73
108
  end
74
109
 
75
110
  def fail_test
@@ -1,47 +1,57 @@
1
- require 'rubycritic'
2
- require 'ostruct'
3
1
  module DebtCeiling
4
2
  class Accounting
5
3
  DebtCeilingExceeded = Class.new(StandardError)
6
4
  TargetDeadlineMissed = Class.new(StandardError)
7
5
  class << self
6
+ attr_reader :result, :path
8
7
  def calculate(path)
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(:+)
13
- puts "Current total tech debt: #{total_debt}"
14
- puts "Largest source of debt is: #{max_debt.name} at #{max_debt.to_i}"
15
- total_debt
16
- end
17
-
18
- def construct_debts(modules)
19
- modules.map do |mod|
20
- path = mod.path
21
- file_attributes = OpenStruct.new
22
- file_attributes.linecount = `wc -l #{path}`.match(/\d+/)[0].to_i
23
- file_attributes.path = path
24
- file_attributes.analysed_module = mod
25
- file_attributes.source_code = File.read(path)
26
- Debt.new(file_attributes)
27
- end
8
+ @path = path
9
+ print_results
10
+ result
11
+ end
12
+
13
+ def result
14
+ @result ||= result_from_analysed_modules(construct_rubycritic_modules(path))
15
+ end
16
+
17
+ def clear
18
+ @result = nil
19
+ end
20
+
21
+ def result_from_analysed_modules(analysed_modules)
22
+ analysis = OpenStruct.new
23
+ analysis.debts = analysed_modules.map {|mod| Debt.new(FileAttributes.new(mod)) }
24
+ analysis.max_debt = analysis.debts.max_by(&:to_i)
25
+ analysis.total_debt = analysis.debts.map(&:to_i).reduce(:+)
26
+ analysis
27
+ end
28
+
29
+ def print_results
30
+ puts <<-RESULTS
31
+ Current total tech debt: #{result.total_debt}
32
+ Largest source of debt is: #{max_debt.name} at #{max_debt.to_i}
33
+ The rubycritic grade for that debt is: #{max_debt.letter_grade}
34
+ The flog complexity for that debt is: #{max_debt.analysed_module.complexity}
35
+ Flay suspects #{max_debt.analysed_module.duplication.to_i} areas of code duplication
36
+ There are #{method_count} methods and #{smell_count} smell(s) from reek.
37
+ The file is #{max_debt.linecount} lines long.
38
+ RESULTS
39
+ end
40
+
41
+ def max_debt
42
+ result.max_debt
43
+ end
44
+
45
+ def method_count
46
+ max_debt.analysed_module.methods_count
47
+ end
48
+
49
+ def smell_count
50
+ max_debt.analysed_module.smells.count
28
51
  end
29
52
 
30
53
  def construct_rubycritic_modules(path)
31
- if ENV['FULL_ANALYSIS']
32
- Rubycritic::Orchestrator.new.critique([path])
33
- else
34
- # temporarily use Rubycritic internals until they provide an API
35
- require 'rubycritic/modules_initializer'
36
- require 'rubycritic/analysers/complexity'
37
- require 'rubycritic/analysers/smells/flay'
38
-
39
- modules = Rubycritic::ModulesInitializer.init([path])
40
- [Rubycritic::Analyser::Complexity, Rubycritic::Analyser::FlaySmells].each do |analyser|
41
- analyser.new(modules).run
42
- end
43
- modules
44
- end
54
+ Rubycritic.create(mode: :ci, format: :json, paths: Array(path)).critique
45
55
  end
46
56
  end
47
57
  end
@@ -0,0 +1,7 @@
1
+ #rubycritic 1.4 uses to_h method, which is not present in ruby 1.9, so until fixed
2
+ #this is needed for compatibility
3
+ class Hash
4
+ def to_h
5
+ to_hash
6
+ end
7
+ end
@@ -0,0 +1,51 @@
1
+ module DebtCeiling
2
+ module CustomDebtAnalysis
3
+
4
+ def external_measure_debt
5
+ public_send(:measure_debt) if self.respond_to?(:measure_debt)
6
+ end
7
+
8
+ def external_augmented_debt
9
+ (public_send(:augment_debt) if respond_to?(:augment_debt)).to_i
10
+ end
11
+
12
+ def debt_from_source_code_rules
13
+ manual_callout_debt +
14
+ text_match_debt('TODO', DebtCeiling.cost_per_todo) +
15
+ deprecated_reference_debt
16
+ end
17
+
18
+ def text_match_debt(string, cost)
19
+ source_code.scan(string).count * cost.to_i
20
+ end
21
+
22
+ def manual_callout_debt
23
+ DebtCeiling.manual_callouts.reduce(0) do |sum, callout|
24
+ sum + debt_from_callout(callout)
25
+ end
26
+ end
27
+
28
+ def deprecated_reference_debt
29
+ DebtCeiling.deprecated_reference_pairs
30
+ .reduce(0) {|accum, (string, value)| accum + text_match_debt(string, value.to_i) }
31
+ end
32
+
33
+ def debt_from_callout(callout)
34
+ source_code.each_line.reduce(0) do |sum, line|
35
+ match_data = line.match(Regexp.new(callout + '.*'))
36
+ string = match_data.to_s.split(callout).last
37
+ amount = string.match(/\d+/).to_s if string
38
+ sum + amount.to_i
39
+ end
40
+ end
41
+
42
+ def valid_debt?
43
+ black_empty = DebtCeiling.blacklist.empty?
44
+ white_empty = DebtCeiling.whitelist.empty?
45
+ fail DoNotWhitelistAndBlacklistSimulateneously unless black_empty || white_empty
46
+ (black_empty && white_empty) ||
47
+ (black_empty && self.class.whitelist_includes?(self)) ||
48
+ (white_empty && !self.class.blacklist_includes?(self))
49
+ end
50
+ end
51
+ end
@@ -2,10 +2,12 @@ require 'forwardable'
2
2
  module DebtCeiling
3
3
  class Debt
4
4
  extend Forwardable
5
+ include CustomDebtAnalysis
5
6
  DoNotWhitelistAndBlacklistSimulateneously = Class.new(StandardError)
6
7
 
7
- attr_reader :file_attributes
8
8
  def_delegators :file_attributes, :path, :analysed_module, :module_name, :linecount, :source_code
9
+ def_delegators :non_grade_scoring, :complexity_multiplier, :duplication_multiplier, :smells_multiplier,
10
+ :method_count_multiplier, :ideal_max_line_count, :cost_per_line_over_ideal
9
11
  def_delegator :analysed_module, :rating
10
12
  attr_accessor :debt_amount
11
13
  def_delegator :debt_amount, :to_i
@@ -15,69 +17,68 @@ module DebtCeiling
15
17
  default_measure_debt if valid_debt?
16
18
  end
17
19
 
20
+ def name
21
+ file_attributes.analysed_module.name || path.to_s.split('/').last
22
+ end
23
+
24
+ def +(other)
25
+ to_i + other.to_i
26
+ end
27
+
28
+ def letter_grade
29
+ rating.to_s.downcase.to_sym
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :file_attributes
35
+
18
36
  def default_measure_debt
19
- cost = public_send(:measure_debt) if self.respond_to?(:measure_debt)
20
-
21
- unless cost
22
- cost = public_send(:augment_debt) if respond_to?(:augment_debt)
23
- cost = cost.to_i
24
- letter_grade = rating.to_s.downcase
25
- cost_per_line = DebtCeiling.configuration.grade_points[letter_grade.to_sym]
26
- cost += file_attributes.linecount * cost_per_line
27
- cost += debt_from_source_code_rules
28
- end
29
- self.debt_amount = cost
37
+ self.debt_amount = external_measure_debt || internal_measure_debt
30
38
  end
31
39
 
32
- def debt_from_source_code_rules
33
- manual_callout_debt +
34
- text_match_debt('TODO', DebtCeiling.cost_per_todo) +
35
- DebtCeiling.deprecated_reference_pairs
36
- .reduce(0) {|accum, (string, value)| accum + text_match_debt(string, value.to_i) }
40
+ def internal_measure_debt
41
+ external_augmented_debt +
42
+ cost_from_static_analysis_points +
43
+ debt_from_source_code_rules
37
44
  end
38
45
 
39
- def text_match_debt(string, cost)
40
- source_code.scan(string).count * cost.to_i
46
+ def cost_from_static_analysis_points
47
+ DebtCeiling.grade_points[letter_grade] + cost_from_non_grade_scoring
41
48
  end
42
49
 
43
- def manual_callout_debt
44
- DebtCeiling.manual_callouts.reduce(0) do |sum, callout|
45
- sum + debt_from_callout(callout)
46
- end
50
+ def cost_from_non_grade_scoring
51
+ flog_flay_debt + method_count_debt + smells_debt + line_count_debt
47
52
  end
48
53
 
49
- def debt_from_callout(callout)
50
- source_code.each_line.reduce(0) do |sum, line|
51
- match_data = line.match(Regexp.new(callout + '.*'))
52
- string = match_data.to_s.split(callout).last
53
- amount = string.match(/\d+/).to_s if string
54
- sum + amount.to_i
55
- end
54
+ def smells_debt
55
+ analysed_module.smells.map(&:cost).inject(0, :+) * smells_multiplier
56
56
  end
57
57
 
58
- def valid_debt?
59
- black_empty = DebtCeiling.blacklist.empty?
60
- white_empty = DebtCeiling.whitelist.empty?
61
- fail DoNotWhitelistAndBlacklistSimulateneously unless black_empty || white_empty
62
- (black_empty && white_empty) ||
63
- (black_empty && self.class.whitelist_includes?(self)) ||
64
- (white_empty && !self.class.blacklist_includes?(self))
58
+ def method_count_debt
59
+ analysed_module.methods_count * method_count_multiplier
65
60
  end
66
61
 
67
- def self.whitelist_includes?(debt)
68
- DebtCeiling.whitelist.find { |filename| debt.path.match filename }
62
+ def flog_flay_debt
63
+ analysed_module.complexity * complexity_multiplier +
64
+ analysed_module.duplication * duplication_multiplier
69
65
  end
70
66
 
71
- def self.blacklist_includes?(debt)
72
- DebtCeiling.blacklist.find { |filename| debt.path.match filename }
67
+ def line_count_debt
68
+ excess_lines = linecount - ideal_max_line_count
69
+ excess_lines > 0 ? excess_lines * cost_per_line_over_ideal : 0
73
70
  end
74
71
 
75
- def name
76
- file_attributes.analysed_module.name || path.to_s.split('/').last
72
+ def non_grade_scoring
73
+ DebtCeiling
77
74
  end
78
75
 
79
- def +(other)
80
- to_i + other.to_i
76
+ def self.whitelist_includes?(debt)
77
+ DebtCeiling.whitelist.find { |filename| debt.path.match filename }
78
+ end
79
+
80
+ def self.blacklist_includes?(debt)
81
+ DebtCeiling.blacklist.find { |filename| debt.path.match filename }
81
82
  end
82
83
  end
83
84
  end
@@ -0,0 +1,10 @@
1
+ class FileAttributes
2
+ attr_reader :linecount, :path, :analysed_module, :source_code
3
+ def initialize(analysed_file)
4
+ file_path = analysed_file.path
5
+ @linecount = `wc -l #{file_path}`.match(/\d+/)[0].to_i
6
+ @path = file_path
7
+ @analysed_module = analysed_file
8
+ @source_code = File.read(file_path)
9
+ end
10
+ end
@@ -1,3 +1,3 @@
1
1
  module DebtCeiling
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -11,7 +11,13 @@ describe DebtCeiling do
11
11
  it 'has failing exit status when target debt reduction is missed' do
12
12
  DebtCeiling.configure {|c| c.reduction_target =0; c.reduction_date = Time.now.to_s }
13
13
  expect(DebtCeiling.debt_ceiling).to eq(nil)
14
- expect { DebtCeiling.calculate('.', preconfigured: true) }.to raise_error
14
+ expect { DebtCeiling.calculate('.', preconfigured: true) }.to raise_error(SystemExit)
15
+ end
16
+
17
+ it 'has failing exit status when max debt per modile is exceeded' do
18
+ DebtCeiling.configure {|c| c.max_debt_per_module =5 }
19
+ expect(DebtCeiling.debt_ceiling).to eq(nil)
20
+ expect { DebtCeiling.calculate('.', preconfigured: true) }.to raise_error(SystemExit)
15
21
  end
16
22
 
17
23
  it 'returns quantity of total debt' do
@@ -33,4 +39,9 @@ describe DebtCeiling do
33
39
  expect(DebtCeiling.calculate('spec/support/manual_example.rb')).to be 150 # hardcoded in example file
34
40
  end
35
41
 
42
+ it 'assigns debt for file length over ideal file size' do
43
+ DebtCeiling.configure {|c| c.ideal_max_line_count = 10; c.cost_per_line_over_ideal = 100 }
44
+ expect(DebtCeiling.calculate('spec/support/long_file_example.rb', preconfigured: true)).to be 300 # hardcoded 13 lines long example file
45
+ end
46
+
36
47
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,17 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
1
3
  require 'coveralls'
2
4
  Coveralls.wear!
5
+
3
6
  RSpec.configure do |config|
4
7
  config.before { allow($stdout).to receive(:puts) }
5
- config.after(:each) { DebtCeiling.configure {|c| c.debt_ceiling = nil; c.reduction_target = nil; c.reduction_date = nil } }
8
+ config.after(:each) { DebtCeiling.configure {|c| c.debt_ceiling = nil; c.reduction_target = nil; c.reduction_date = nil } ; DebtCeiling.clear }
9
+ config.after(:all) do
10
+ DebtCeiling.configure do |c|
11
+ c.whitelist = %w(app lib)
12
+ c.max_debt_per_module = 150
13
+ c.debt_ceiling = 300
14
+ end
15
+ DebtCeiling.calculate('.')
16
+ end
6
17
  end
@@ -0,0 +1,14 @@
1
+
2
+ module LongFileExample
3
+ def ends_on_line_13
4
+ "I'm not really long but I'm longer than configured to be allowd"
5
+
6
+
7
+
8
+ "blah blah"
9
+
10
+
11
+
12
+ "that's how long this file is, supposedly, though accounts differ"
13
+ end
14
+ end
metadata CHANGED
@@ -1,116 +1,129 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debt_ceiling
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Glusman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-09 00:00:00.000000000 Z
11
+ date: 2015-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubycritic
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.1.1
19
+ version: '1.4'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.1.1
26
+ version: '1.4'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: chronic
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0.10'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.10'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: configurations
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 2.0.0.pre
47
+ version: '2.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 2.0.0.pre
54
+ version: '2.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0.10'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.10'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '10.3'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '10.3'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ~>
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '3.1'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ~>
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.1'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: coveralls
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ~>
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0.7'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ~>
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.7'
111
- description: ! ' Get a grip on your technical debt
112
-
113
- '
111
+ - !ruby/object:Gem::Dependency
112
+ name: codeclimate-test-reporter
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.4'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.4'
125
+ description: |2
126
+ Get a grip on your technical debt
114
127
  email:
115
128
  - bglusman@shutterstock.com
116
129
  executables:
@@ -119,8 +132,8 @@ extensions:
119
132
  - Rakefile
120
133
  extra_rdoc_files: []
121
134
  files:
122
- - .gitignore
123
- - .travis.yml
135
+ - ".gitignore"
136
+ - ".travis.yml"
124
137
  - Gemfile
125
138
  - LICENSE
126
139
  - MIT-LICENSE.md
@@ -132,10 +145,14 @@ files:
132
145
  - examples/debt.rb.example
133
146
  - lib/debt_ceiling.rb
134
147
  - lib/debt_ceiling/accounting.rb
148
+ - lib/debt_ceiling/compatibility.rb
149
+ - lib/debt_ceiling/custom_debt_analysis.rb
135
150
  - lib/debt_ceiling/debt.rb
151
+ - lib/debt_ceiling/file_attributes.rb
136
152
  - lib/debt_ceiling/version.rb
137
153
  - spec/debt_ceiling_spec.rb
138
154
  - spec/spec_helper.rb
155
+ - spec/support/long_file_example.rb
139
156
  - spec/support/manual_example.rb
140
157
  - spec/support/todo_example.rb
141
158
  homepage: https://github.com/bglusman/debt_ceiling
@@ -147,19 +164,18 @@ require_paths:
147
164
  - lib
148
165
  required_ruby_version: !ruby/object:Gem::Requirement
149
166
  requirements:
150
- - - ! '>='
167
+ - - ">="
151
168
  - !ruby/object:Gem::Version
152
169
  version: '0'
153
170
  required_rubygems_version: !ruby/object:Gem::Requirement
154
171
  requirements:
155
- - - ! '>='
172
+ - - ">="
156
173
  - !ruby/object:Gem::Version
157
174
  version: '0'
158
175
  requirements: []
159
176
  rubyforge_project: debt_ceiling
160
- rubygems_version: 2.4.5
177
+ rubygems_version: 2.4.3
161
178
  signing_key:
162
179
  specification_version: 4
163
180
  summary: DebtCeiling helps you track Tech Debt
164
181
  test_files: []
165
- has_rdoc: