debt_ceiling 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: