fukuzatsu 0.10.1 → 1.0.0

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: 98e183078ac1f80b93a9f0c432ff70a5a75fb156
4
- data.tar.gz: 9ff83590fd2e21f4dab4100db0bb32dc51d09468
3
+ metadata.gz: d1fc37f05fcd183b24dbe2ed09a7d48b5214d5cb
4
+ data.tar.gz: fd03679df8338b84b667fb9c084fdc5466c442b3
5
5
  SHA512:
6
- metadata.gz: 361c28387778b0fa9211c4401dc9d6ebfd1ad491a0a1c6857a0c57ccc32b052d3b4ec81fefbd30ca02a0304800120b89ff6781ae3188c5de8e553cb834fbcc8b
7
- data.tar.gz: e8d4b595508e6fd5db7d3f057dc2325d3d1c7c694729f6caf8b09fcf6c29f8cbc79d5ab5513df4a57f6daf5944a42d15295af2c8cfb00ec8e8db4c6fb6eeec39
6
+ metadata.gz: 0e96102937e251544859c12e3bd403d07fb405d1509c4f46369122aeebb2eeea0037aece27e8c8f78d11ef07b9452ece1e6a2925d0962eaa5a8d5281c3021978
7
+ data.tar.gz: 453b198fd14b8eabd98ac67a9375fdcfd2f41ae64254ed0ab488718e80beb11d54660a34a8c12d280a4d0509a848933406b5c1ae0453fcff2a667ce4cccb45f7
@@ -1,5 +1,6 @@
1
- ODE
2
- ## Version 0.4
1
+ # CODE OF CONDUCT
2
+
3
+ This project has adopted version 0.4 of the [Contributor Covenant](https://github.com/bantik/contributor_covenant/).
3
4
 
4
5
  As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
5
6
 
data/README.md CHANGED
@@ -1,16 +1,13 @@
1
1
  # Fukuzatsu
2
2
 
3
- *Note: this gem is a work in progress and should not be considered production-ready until version 1.0*
4
-
5
3
  Fukuzatsu ("complexity") is a tool for measuring code complexity in Ruby class files. Its analysis
6
- generates relative complexity figures similar to the results of cyclomatic complexity algorithms.
4
+ generates relative complexity figures similar to the results of cyclomatic complexity algorithms.
7
5
  (You can learn more about cyclomatic complexity at http://en.wikipedia.org/wiki/Cyclomatic_complexity)
8
6
 
9
- Why should you care about this kind of complexity? More complex code tends to attract bugs and to
7
+ Why should you care about this kind of complexity? More complex code tends to attract bugs and to
10
8
  increase the friction around extending features or refactoring code.
11
9
 
12
- It was inspired by Saikuro, written by Zev Blut.
13
- Fukuzatsu was created by [Coraline Ada Ehmke](http://where.coraline.codes/) with invaluable assistance from [Mike Ziwisky](https://github.com/mziwisky).
10
+ Fukuzatsu was created by [Coraline Ada Ehmke](http://where.coraline.codes/) with invaluable assistance from [Mike Ziwisky](https://github.com/mziwisky). It was inspired by Saikuro, written by Zev Blut.
14
11
 
15
12
  ## Screenshots
16
13
 
@@ -22,7 +22,9 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency "poro_plus"
23
23
  spec.add_dependency "haml"
24
24
  spec.add_dependency "parser"
25
+ spec.add_dependency "rainbow"
25
26
  spec.add_dependency "rouge"
27
+ spec.add_dependency "terminal-table"
26
28
  spec.add_dependency "thor"
27
29
 
28
30
  spec.add_development_dependency "bundler", "~> 1.6"
@@ -13,6 +13,7 @@ require_relative "fukuzatsu/formatters/text"
13
13
  require_relative "fukuzatsu/line_of_code"
14
14
  require_relative "fukuzatsu/parsed_file"
15
15
  require_relative "fukuzatsu/parsed_method"
16
+ require_relative "fukuzatsu/parser"
16
17
  require_relative "fukuzatsu/version"
17
18
 
18
19
  module Fukuzatsu
@@ -53,10 +53,18 @@ class Analyzer
53
53
  if node.type == :def || node.type == :defs || node.type == :class
54
54
  name = node.loc.name
55
55
  expression = node.loc.expression
56
+ type = case(node.type)
57
+ when :defs
58
+ :class
59
+ when :def
60
+ :instance
61
+ when :class
62
+ :none
63
+ end
56
64
  methods << ParsedMethod.new(
57
65
  name: content[name.begin_pos..name.end_pos - 1],
58
66
  content: content[expression.begin_pos..expression.end_pos - 1],
59
- type: [:defs, :class].include?(node.type) ? :class : :instance
67
+ type: type
60
68
  )
61
69
  end
62
70
  node.children.each do |child|
@@ -13,29 +13,19 @@ module Fukuzatsu
13
13
  method_option :threshold, :type => :numeric, :default => 0, :aliases => "-t"
14
14
 
15
15
  def check(path="./")
16
- file_list(path).each{ |path_to_file| parse(path_to_file) }
17
- handle_index(summaries)
18
- report
16
+ parser = Fukuzatsu::Parser.new(
17
+ path,
18
+ formatter,
19
+ options['threshold']
20
+ )
21
+ parser.parse_files
22
+ parser.report
19
23
  end
20
24
 
21
25
  default_task :check
22
26
 
23
- attr_accessor :summaries, :last_file
24
-
25
27
  private
26
28
 
27
- def complexities
28
- summaries.to_a.map{|s| s[:complexity]}
29
- end
30
-
31
- def file_list(start_file)
32
- if File.directory?(start_file)
33
- return Dir.glob(File.join(start_file, "**", "*.rb"))
34
- else
35
- return [start_file]
36
- end
37
- end
38
-
39
29
  def formatter
40
30
  case options['format']
41
31
  when 'html'
@@ -47,41 +37,6 @@ module Fukuzatsu
47
37
  end
48
38
  end
49
39
 
50
- def handle_index(file_summary)
51
- return unless options['format'] == 'html'
52
- index = Formatters::HtmlIndex.new(file_summary)
53
- self.last_file = File.join(index.output_path, index.filename)
54
- index.export
55
- end
56
-
57
- def parse(path_to_file, options={})
58
- file = ParsedFile.new(path_to_file: path_to_file)
59
- parser = formatter.new(file, file.source)
60
- parser.export
61
- self.summaries ||= []
62
- self.summaries << file.summary.merge(results_file: parser.path_to_results)
63
- last_file = file
64
- end
65
-
66
- def report
67
- unless options['format'] == "text"
68
- puts "Results written to:"
69
- puts last_file.present? && "#{last_file}" || results_files.join("\r\n")
70
- end
71
- report_complexity
72
- end
73
-
74
- def report_complexity
75
- return if options['threshold'].to_i == 0
76
- return if complexities.max.to_i <= options['threshold']
77
- puts "Maximum complexity of #{complexities.max} exceeds #{options['threshold']} threshold!"
78
- exit 1
79
- end
80
-
81
- def results_files
82
- summaries.map{|s| s[:results_file]}
83
- end
84
-
85
40
  end
86
41
 
87
42
  end
@@ -5,27 +5,21 @@ module Formatters
5
5
  def self.included(klass)
6
6
  klass.send(:attr_accessor, :file)
7
7
  klass.send(:attr_accessor, :source)
8
+ klass.send(:attr_accessor, :output_directory)
8
9
  end
9
10
 
10
- def initialize(file, source="")
11
+ def initialize(file, output_directory, source="")
11
12
  self.file = file
12
13
  self.source = source
14
+ self.output_directory = output_directory
13
15
  end
14
16
 
15
- def content
16
- [header, rows, footer].flatten.join("\r\n")
17
- end
18
-
19
- def columns
20
- ["class", "method", "complexity"]
21
- end
22
-
23
- def root_path
24
- "doc/fukuzatsu"
17
+ def filename
18
+ File.basename(self.file.path_to_file) + file_extension
25
19
  end
26
20
 
27
21
  def output_path
28
- output_path = File.dirname(File.join(root_path, self.file.path_to_file))
22
+ output_path = File.dirname(File.join(self.output_directory, self.file.path_to_file))
29
23
  FileUtils.mkpath(output_path)
30
24
  output_path
31
25
  end
@@ -34,22 +28,6 @@ module Formatters
34
28
  File.join(output_path, filename)
35
29
  end
36
30
 
37
- def filename
38
- File.basename(self.file.path_to_file) + file_extension
39
- end
40
-
41
- def file_extension
42
- ""
43
- end
44
-
45
- def export
46
- begin
47
- File.open(path_to_results, 'w') {|outfile| outfile.write(content)}
48
- rescue Exception => e
49
- puts "Unable to write output: #{e} #{e.backtrace}"
50
- end
51
- end
52
-
53
31
  end
54
32
 
55
33
  end
@@ -4,24 +4,40 @@ module Formatters
4
4
 
5
5
  include Formatters::Base
6
6
 
7
- def header
8
- columns.join(',')
7
+ def self.has_index?
8
+ false
9
9
  end
10
10
 
11
- def rows
12
- file.methods.inject([]) do |a, method|
13
- a << "#{file.class_name},#{method.prefix}#{method.name},#{method.complexity}"
14
- a
15
- end.join("\r\n")
11
+ def self.writes_to_file_system?
12
+ true
16
13
  end
17
14
 
18
- def footer
15
+ def content
16
+ rows + "\r\n"
17
+ end
18
+
19
+ def export
20
+ begin
21
+ File.open(path_to_results, 'a') {|outfile| outfile.write(content)}
22
+ rescue Exception => e
23
+ puts "Unable to write output: #{e} #{e.backtrace}"
24
+ end
19
25
  end
20
26
 
21
27
  def file_extension
22
28
  ".csv"
23
29
  end
24
30
 
31
+ def path_to_results
32
+ File.join(output_directory, "results#{file_extension}")
33
+ end
34
+
35
+ def rows
36
+ file.methods.map do |method|
37
+ "#{file.path_to_file},#{file.class_name},#{method.prefix}#{method.name},#{method.complexity}"
38
+ end.join("\r\n")
39
+ end
40
+
25
41
  end
26
42
 
27
43
  end
@@ -6,8 +6,20 @@ module Formatters
6
6
 
7
7
  include Formatters::Base
8
8
 
9
- def header
10
- columns.map{|col| "<th>#{col.titleize}</th>"}.join("\r\n")
9
+ def self.has_index?
10
+ true
11
+ end
12
+
13
+ def self.writes_to_file_system?
14
+ true
15
+ end
16
+
17
+ def self.index_class
18
+ Formatters::HtmlIndex
19
+ end
20
+
21
+ def columns
22
+ ["class", "method", "complexity"]
11
23
  end
12
24
 
13
25
  def content
@@ -25,10 +37,22 @@ module Formatters
25
37
  )
26
38
  end
27
39
 
40
+ def export
41
+ begin
42
+ File.open(path_to_results, 'w') {|outfile| outfile.write(content)}
43
+ rescue Exception => e
44
+ puts "Unable to write output: #{e} #{e.backtrace}"
45
+ end
46
+ end
47
+
28
48
  def formatter
29
49
  Rouge::Formatters::HTML.new(line_numbers: true)
30
50
  end
31
51
 
52
+ def header
53
+ columns.map{|col| "<th>#{col.titleize}</th>"}.join("\r\n")
54
+ end
55
+
32
56
  def lexer
33
57
  lexer = Rouge::Lexers::Ruby.new
34
58
  end
@@ -54,9 +78,6 @@ module Formatters
54
78
  end.join("\r\n")
55
79
  end
56
80
 
57
- def footer
58
- end
59
-
60
81
  def file_extension
61
82
  ".htm"
62
83
  end
@@ -4,19 +4,11 @@ module Formatters
4
4
 
5
5
  include Formatters::Base
6
6
 
7
- attr_accessor :file_summary
7
+ attr_reader :file_summary, :output_directory
8
8
 
9
- def initialize(file_summary)
10
- self.file_summary = file_summary
11
- end
12
-
13
- def filename
14
- "index.htm"
15
- end
16
-
17
- def output_path
18
- FileUtils.mkpath(root_path)
19
- root_path
9
+ def initialize(file_summary, output_directory)
10
+ @file_summary = file_summary
11
+ @output_directory = output_directory
20
12
  end
21
13
 
22
14
  def content
@@ -29,6 +21,23 @@ module Formatters
29
21
  )
30
22
  end
31
23
 
24
+ def export
25
+ begin
26
+ File.open(path_to_results, 'w') {|outfile| outfile.write(content)}
27
+ rescue Exception => e
28
+ puts "Unable to write output: #{e} #{e.backtrace}"
29
+ end
30
+ end
31
+
32
+ def filename
33
+ "index.htm"
34
+ end
35
+
36
+ def output_path
37
+ FileUtils.mkpath(self.output_directory)
38
+ self.output_directory
39
+ end
40
+
32
41
  def output_template
33
42
  File.read(File.dirname(__FILE__) + "/templates/index.html.haml")
34
43
  end
@@ -1,26 +1,50 @@
1
1
  module Formatters
2
2
 
3
+ require 'terminal-table'
4
+ require 'rainbow/ext/string'
5
+
3
6
  class Text
4
7
 
5
8
  include Formatters::Base
6
9
 
10
+ def self.has_index?
11
+ false
12
+ end
13
+
14
+ def self.writes_to_file_system?
15
+ false
16
+ end
17
+
18
+ def color_for(row)
19
+ return :green if row.complexity == 0
20
+ return :yellow if row.complexity <= file.average_complexity
21
+ return :red if row.complexity > file.average_complexity
22
+ return :white
23
+ end
24
+
7
25
  def header
8
- "#{file.class_name}\t\t#{file.complexity}"
26
+ ["Class/Module", "Method", "Complexity"]
9
27
  end
10
28
 
11
29
  def export
12
- puts content
30
+ return if rows.empty?
31
+ table = Terminal::Table.new(
32
+ title: file.path_to_file.color(:white),
33
+ headings: header,
34
+ rows: rows,
35
+ style: {width: 80}
36
+ )
37
+ table.align_column(3, :right)
38
+ puts table
13
39
  end
14
40
 
15
41
  def rows
16
42
  file.methods.map do |method|
17
- "#{file.class_name}\t#{method.prefix}#{method.name}\t#{method.complexity}"
43
+ color = color_for(method)
44
+ ["#{file.class_name}".color(color), "#{method.name}".color(color), "#{method.complexity}".color(color)]
18
45
  end
19
46
  end
20
47
 
21
- def footer
22
- end
23
-
24
48
  end
25
49
 
26
50
  end
@@ -18,6 +18,10 @@ class ParsedFile
18
18
  @analyzer ||= Analyzer.new(content)
19
19
  end
20
20
 
21
+ def average_complexity
22
+ methods.map(&:complexity).reduce(:+) / methods.count.to_f
23
+ end
24
+
21
25
  def complexity
22
26
  @complexity ||= analyzer.complexity
23
27
  end
@@ -40,7 +44,6 @@ class ParsedFile
40
44
 
41
45
  def summary
42
46
  {
43
- results_file: self.path_to_results,
44
47
  path_to_file: self.path_to_file,
45
48
  source: source,
46
49
  class_name: self.class_name,
@@ -8,8 +8,15 @@ class ParsedMethod
8
8
  @complexity ||= analyzer.complexity
9
9
  end
10
10
 
11
+ def name
12
+ return "" if self.type == :none
13
+ "#{prefix}#{@name}"
14
+ end
15
+
11
16
  def prefix
12
- self.type.to_s == 'class' ? "." : "#"
17
+ return "." if self.type == :class
18
+ return "#" if self.type == :instance
19
+ return "*"
13
20
  end
14
21
 
15
22
  def analyzer
@@ -0,0 +1,68 @@
1
+ require 'fileutils'
2
+
3
+ module Fukuzatsu
4
+
5
+ class Parser
6
+
7
+ attr_reader :start_path, :parsed_files
8
+ attr_reader :threshold, :formatter
9
+
10
+ OUTPUT_DIRECTORY = "doc/fukuzatsu"
11
+
12
+ def initialize(path, formatter, threshold=0)
13
+ @start_path = path
14
+ @formatter = formatter
15
+ @threshold = threshold
16
+ reset_output_directory
17
+ end
18
+
19
+ def parse_files
20
+ @parsed_files = source_files.map{ |path_to_file| parse_source_file(path_to_file) }
21
+ end
22
+
23
+ def report
24
+ self.parsed_files.each{ |file| formatter.new(file, OUTPUT_DIRECTORY, file.source).export }
25
+ write_report_index
26
+ report_complexity
27
+ end
28
+
29
+ private
30
+
31
+ def reset_output_directory
32
+ begin
33
+ FileUtils.remove_dir(OUTPUT_DIRECTORY)
34
+ rescue Errno::ENOENT
35
+ end
36
+ FileUtils.mkpath(OUTPUT_DIRECTORY)
37
+ end
38
+
39
+ def source_files
40
+ if File.directory?(start_path)
41
+ return Dir.glob(File.join(start_path, "**", "*.rb"))
42
+ else
43
+ return [start_path]
44
+ end
45
+ end
46
+
47
+ def parse_source_file(path_to_file, options={})
48
+ ParsedFile.new(path_to_file: path_to_file)
49
+ end
50
+
51
+ def report_complexity
52
+ return if self.threshold == 0
53
+ complexities = self.parsed_files.map(&:complexity)
54
+ return if complexities.max.to_i <= self.threshold
55
+ puts "Maximum complexity of #{complexities.max} exceeds #{options['threshold']} threshold!"
56
+ exit 1
57
+ end
58
+
59
+ def write_report_index
60
+ return unless self.formatter.writes_to_file_system?
61
+ puts "Results written to #{OUTPUT_DIRECTORY} "
62
+ return unless self.formatter.has_index?
63
+ formatter.index_class.new(parsed_files.map(&:summary), OUTPUT_DIRECTORY).export
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -1,3 +1,3 @@
1
1
  module Fukuzatsu
2
- VERSION = "0.10.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fukuzatsu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bantik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-22 00:00:00.000000000 Z
11
+ date: 2014-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ephemeral
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rainbow
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rouge
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: terminal-table
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: thor
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -183,6 +211,7 @@ files:
183
211
  - lib/fukuzatsu/line_of_code.rb
184
212
  - lib/fukuzatsu/parsed_file.rb
185
213
  - lib/fukuzatsu/parsed_method.rb
214
+ - lib/fukuzatsu/parser.rb
186
215
  - lib/fukuzatsu/version.rb
187
216
  - spec/analyzer_spec.rb
188
217
  - spec/cli_spec.rb