rubycritic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CONTRIBUTING.md +19 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +41 -0
  7. data/Rakefile +10 -0
  8. data/SPEC.md +58 -0
  9. data/bin/rubycritic +3 -0
  10. data/lib/rubycritic.rb +23 -0
  11. data/lib/rubycritic/analysers/config.reek +18 -0
  12. data/lib/rubycritic/analysers/flog.rb +20 -0
  13. data/lib/rubycritic/analysers/reek.rb +15 -0
  14. data/lib/rubycritic/cli.rb +25 -0
  15. data/lib/rubycritic/location.rb +40 -0
  16. data/lib/rubycritic/report_generators/assets/stylesheets/application.css +4 -0
  17. data/lib/rubycritic/report_generators/file_generator.rb +58 -0
  18. data/lib/rubycritic/report_generators/line_generator.rb +29 -0
  19. data/lib/rubycritic/report_generators/report_generator.rb +31 -0
  20. data/lib/rubycritic/report_generators/templates/file.html.erb +1 -0
  21. data/lib/rubycritic/report_generators/templates/layouts/application.html.erb +16 -0
  22. data/lib/rubycritic/report_generators/templates/line.html.erb +1 -0
  23. data/lib/rubycritic/report_generators/templates/smelly_line.html.erb +1 -0
  24. data/lib/rubycritic/smell.rb +32 -0
  25. data/lib/rubycritic/smell_adapters/flog.rb +41 -0
  26. data/lib/rubycritic/smell_adapters/reek.rb +35 -0
  27. data/lib/rubycritic/smells_aggregator.rb +29 -0
  28. data/lib/rubycritic/source_locator.rb +31 -0
  29. data/lib/rubycritic/version.rb +3 -0
  30. data/rubycritic.gemspec +31 -0
  31. data/test/lib/rubycritic/location_test.rb +37 -0
  32. data/test/lib/rubycritic/metric_adapters/flog_adapter_test.rb +25 -0
  33. data/test/lib/rubycritic/metric_adapters/reek_adapter_test.rb +34 -0
  34. data/test/lib/rubycritic/smell_test.rb +57 -0
  35. data/test/lib/rubycritic/smells_aggregator_test.rb +47 -0
  36. data/test/lib/rubycritic/source_locator_test.rb +44 -0
  37. data/test/lib/rubycritic/version_test.rb +7 -0
  38. data/test/samples/flog/smelly.rb +6 -0
  39. data/test/samples/location/dir1/file1.rb +0 -0
  40. data/test/samples/location/file0.rb +0 -0
  41. data/test/samples/reek/not_smelly.rb +7 -0
  42. data/test/samples/reek/smelly.rb +5 -0
  43. data/test/test_helper.rb +7 -0
  44. metadata +199 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e3254c84987373469b8c33271f81794a5495b7a1
4
+ data.tar.gz: 7face22a8c659ef7fb50c701e2ceaab5925c618a
5
+ SHA512:
6
+ metadata.gz: 3f00535b10889d81455894436c7b79d117a085745fcc6e886dfb9aabf7c53bcb47827d4d1d9ca1a6b596a46e2a4f9b4ca0494b555e7552321f8ac76d887e1671
7
+ data.tar.gz: 5fd94e011dd5c5ae34fab83e23d528ec618307ce801fecb87cdf1971be8b32df281ee1c4ea05e12d338ac483a74f84b7566f9fd0ff7943f46661af8bad859496
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,19 @@
1
+ Contributing
2
+ ============
3
+
4
+ 1. Fork the project.
5
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
6
+ 3. Stage partial-file changesets (`git -p`).
7
+ 4. Commit your changes (`git commit`).
8
+ Make exactly as many commits as you need.
9
+ Too many is confusing. Too few will result in epic diffs. Strive for just the right number.
10
+ Each commit should do one thing and one thing only.
11
+ Follow [these guidelines][1] when writing your commit messages.
12
+ 5. [Hide the sausage making][2]. Squash, split and reorder commits if necessary (`git rebase -i`).
13
+ For a more in-depth look at interactive rebasing, be sure to check [how to rewrite history][3].
14
+ 6. Push to the branch (`git push origin my-new-feature`).
15
+ 7. Create new Pull Request.
16
+
17
+ [1]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
18
+ [2]: http://sethrobertson.github.io/GitBestPractices/#sausage
19
+ [3]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rubycritic.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Guilherme Simoes
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ Ruby Critic
2
+ ===========
3
+
4
+ Ruby Critic is a tool that detects and reports smells in Ruby code.
5
+
6
+ Installation
7
+ ------------
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem "rubycritic"
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or just install it yourself:
22
+
23
+ ```bash
24
+ $ gem install rubycritic
25
+ ```
26
+
27
+ Usage
28
+ -----
29
+
30
+ Running `rubycritic` with no arguments will check all Ruby source files in the
31
+ current directory:
32
+
33
+ ```bash
34
+ $ rubycritic
35
+ ```
36
+
37
+ Alternatively you can pass `rubycritic` a list of files and directories to check:
38
+
39
+ ```bash
40
+ $ rubycritic app lib/foo.rb
41
+ ```
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.libs.push "test"
9
+ t.pattern = "test/**/*_test.rb"
10
+ end
data/SPEC.md ADDED
@@ -0,0 +1,58 @@
1
+ Ruby Critic
2
+ ===========
3
+
4
+ Ruby Critic is a tool that detects and reports smells in Ruby code.
5
+
6
+ Inspired by [RuboCop][1], [Rails Best Practices][2] and [Code Climate][3], Ruby Critic aims to better help you refactor your code. By making use of Ruby's rich ecosystem of code metrics tools, Ruby Critic generates high-quality visualizations and insightful code quality reports.
7
+
8
+ [1]: https://github.com/bbatsov/rubocop/
9
+ [2]: https://github.com/railsbp/rails_best_practices
10
+ [3]: https://codeclimate.com/
11
+
12
+ Installation
13
+ ------------
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem "rubycritic"
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ ```bash
24
+ $ bundle
25
+ ```
26
+
27
+ Or just install it yourself:
28
+
29
+ ```bash
30
+ $ gem install rubycritic
31
+ ```
32
+
33
+ Usage
34
+ -----
35
+
36
+ Running `rubycritic` with no arguments will check all Ruby source files in the
37
+ current directory:
38
+
39
+ ```bash
40
+ $ rubycritic
41
+ ```
42
+
43
+ Alternatively you can pass `rubycritic` a list of files and directories to check:
44
+
45
+ ```bash
46
+ $ rubycritic app lib/foo.rb
47
+ ```
48
+
49
+ Options
50
+ -------
51
+
52
+ To specify an output file for the results:
53
+
54
+ ```bash
55
+ $ rubycritic -o output_file
56
+ ```
57
+
58
+ The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `yaml` and `json`.
data/bin/rubycritic ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubycritic/cli"
data/lib/rubycritic.rb ADDED
@@ -0,0 +1,23 @@
1
+ require "rubycritic/source_locator"
2
+ require "rubycritic/analysers/reek"
3
+ require "rubycritic/smell_adapters/reek"
4
+ require "rubycritic/smells_aggregator"
5
+ require "rubycritic/report_generators/report_generator"
6
+
7
+ module Rubycritic
8
+
9
+ class Rubycritic
10
+ def initialize(paths)
11
+ @source = SourceLocator.new(paths)
12
+
13
+ analyser = Analyser::Reek.new(@source.paths)
14
+ smell_adapters = [ SmellAdapter::Reek.new(analyser) ]
15
+ @aggregator = SmellsAggregator.new(smell_adapters)
16
+ end
17
+
18
+ def critique
19
+ ReportGenerator.new(@source.pathnames, @aggregator.smelly_pathnames).generate_report
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,18 @@
1
+ ---
2
+ Attribute:
3
+ enabled: false
4
+ IrresponsibleModule:
5
+ enabled: false
6
+ UncommunicativeMethodName:
7
+ reject:
8
+ - !ruby/regexp /^[a-z]$/
9
+ - !ruby/regexp /[0-9]$/
10
+ UncommunicativeParameterName:
11
+ reject:
12
+ - !ruby/regexp /^.$/
13
+ - !ruby/regexp /[0-9]$/
14
+ - !ruby/regexp /^_/
15
+ UncommunicativeVariableName:
16
+ reject:
17
+ - !ruby/regexp /^.$/
18
+ - !ruby/regexp /[0-9]$/
@@ -0,0 +1,20 @@
1
+ require "flog"
2
+
3
+ module Rubycritic
4
+ module Analyser
5
+
6
+ class Flog < ::Flog
7
+ DEFAULT_OPTIONS = {
8
+ :all => true,
9
+ :continue => true,
10
+ :methods => true
11
+ }
12
+
13
+ def initialize(paths)
14
+ super(DEFAULT_OPTIONS)
15
+ flog(*paths)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ require "reek"
2
+
3
+ module Rubycritic
4
+ module Analyser
5
+
6
+ class Reek < ::Reek::Examiner
7
+ DEFAULT_CONFIG_FILES = [File.expand_path("../config.reek", __FILE__)]
8
+
9
+ def initialize(paths)
10
+ super(Array(paths), DEFAULT_CONFIG_FILES)
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require "optparse"
2
+ require "rubycritic"
3
+
4
+ module Rubycritic
5
+
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Usage: rubycritic [options] [paths]"
8
+
9
+ opts.on_tail("-v", "--version", "Show this version") do
10
+ require "rubycritic/version"
11
+ puts VERSION
12
+ exit 0
13
+ end
14
+
15
+ opts.on_tail("-h", "--help", "Show this message") do
16
+ puts opts
17
+ exit 0
18
+ end
19
+ end.parse!
20
+
21
+ ARGV << "." if ARGV.empty?
22
+ Rubycritic.new(ARGV).critique
23
+ exit 0
24
+
25
+ end
@@ -0,0 +1,40 @@
1
+ require "pathname"
2
+
3
+ module Rubycritic
4
+
5
+ class Location
6
+ attr_reader :pathname, :line
7
+
8
+ def initialize(path, line)
9
+ @pathname = Pathname.new(path)
10
+ @line = line
11
+ end
12
+
13
+ def path
14
+ @pathname.to_s
15
+ end
16
+
17
+ def file
18
+ @pathname.basename.to_s
19
+ end
20
+
21
+ def to_s
22
+ "#{path}:#{line}"
23
+ end
24
+
25
+ def ==(other)
26
+ self.class == other.class && state == other.state
27
+ end
28
+
29
+ def <=>(other)
30
+ state <=> other.state
31
+ end
32
+
33
+ protected
34
+
35
+ def state
36
+ [@pathname, @line]
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,4 @@
1
+ .smelly {
2
+ display: inline;
3
+ background-color: #FFC8C8;
4
+ }
@@ -0,0 +1,58 @@
1
+ require "erb"
2
+ require "rubycritic/report_generators/line_generator"
3
+
4
+ module Rubycritic
5
+
6
+ class FileGenerator
7
+ LINE_NUMBER_OFFSET = 1
8
+ REPORT_DIR = File.expand_path("tmp/rubycritic", Dir.getwd)
9
+ TEMPLATES_DIR = File.expand_path("../templates", __FILE__)
10
+ FILE_TEMPLATE = ERB.new(File.read(File.join(TEMPLATES_DIR, "file.html.erb")))
11
+ LAYOUT_TEMPLATE = ERB.new(File.read(File.join(TEMPLATES_DIR, "layouts", "application.html.erb")))
12
+
13
+ def initialize(pathname, smells)
14
+ @pathname = pathname
15
+ @smells = smells
16
+ end
17
+
18
+ def file_pathname
19
+ File.join(file_directory, file_name)
20
+ end
21
+
22
+ def file_directory
23
+ File.join(REPORT_DIR, File.dirname(@pathname))
24
+ end
25
+
26
+ def file_name
27
+ "#{@pathname.basename.sub_ext('')}.html"
28
+ end
29
+
30
+ def output
31
+ output ||= file_content
32
+ end
33
+
34
+ def stylesheet_path
35
+ File.join(REPORT_DIR, "assets/stylesheets/application.css")
36
+ end
37
+
38
+ def get_binding
39
+ binding
40
+ end
41
+
42
+ private
43
+
44
+ def file_content
45
+ file_code = ""
46
+ File.readlines(@pathname).each.with_index(LINE_NUMBER_OFFSET) do |line_text, line_number|
47
+ location = Location.new(@pathname, line_number)
48
+ line_smells = @smells.select { |smell| smell.located_in?(location) }
49
+ file_code << LineGenerator.new(line_text, line_number, line_smells).output
50
+ end
51
+
52
+ file_body = FILE_TEMPLATE.result(self.get_binding { file_code })
53
+
54
+ LAYOUT_TEMPLATE.result(self.get_binding { file_body })
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,29 @@
1
+ require "cgi"
2
+ require "erb"
3
+
4
+ module Rubycritic
5
+
6
+ class LineGenerator
7
+ LINE_NUMBER_PADDING = 3
8
+ TEMPLATES_DIR = File.expand_path("../templates", __FILE__)
9
+ NORMAL_TEMPLATE = ERB.new(File.read(File.join(TEMPLATES_DIR, "line.html.erb")))
10
+ SMELLY_TEMPLATE = ERB.new(File.read(File.join(TEMPLATES_DIR, "smelly_line.html.erb")))
11
+
12
+ def initialize(text, number, smells)
13
+ @text = CGI::escapeHTML(text.chomp)
14
+ @number = number.to_s.rjust(LINE_NUMBER_PADDING)
15
+ @smells = smells
16
+ @template =
17
+ if @smells.empty?
18
+ NORMAL_TEMPLATE
19
+ else
20
+ SMELLY_TEMPLATE
21
+ end
22
+ end
23
+
24
+ def output
25
+ @output ||= @template.result(binding).delete("\n") + "\n"
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,31 @@
1
+ require "fileutils"
2
+ require "rubycritic/report_generators/file_generator"
3
+
4
+ module Rubycritic
5
+
6
+ class ReportGenerator
7
+ ASSETS_DIR = File.expand_path("../assets", __FILE__)
8
+ REPORT_DIR = File.expand_path("tmp/rubycritic", Dir.getwd)
9
+
10
+ def initialize(source_pathnames, smelly_pathnames)
11
+ @source_pathnames = source_pathnames
12
+ @smelly_pathnames = smelly_pathnames
13
+ end
14
+
15
+ def generate_report
16
+ FileUtils.mkdir_p(REPORT_DIR)
17
+ FileUtils.cp_r(ASSETS_DIR, REPORT_DIR)
18
+
19
+ @source_pathnames.each do |pathname|
20
+ file_smells = @smelly_pathnames[pathname]
21
+ file_generator = FileGenerator.new(pathname, file_smells)
22
+
23
+ FileUtils.mkdir_p(file_generator.file_directory)
24
+ File.open(file_generator.file_pathname, "w+") do |report_file|
25
+ report_file.write(file_generator.output)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1 @@
1
+ <pre><code><%= yield %></code></pre>
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <title>RubyCritic</title>
7
+ <link href="<%= stylesheet_path %>" media="screen, projection, print" rel="stylesheet" type="text/css">
8
+ <meta name="description" content="">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1">
10
+ </head>
11
+ <body>
12
+ <h1>RubyCritic</h1>
13
+
14
+ <%= yield %>
15
+ </body>
16
+ </html>
@@ -0,0 +1 @@
1
+ <%= @number %>. <%= @text %>
@@ -0,0 +1 @@
1
+ <div class='smelly'><%= @number %>. <%= @text %> # <% @smells.each { |smell| %><%= smell %><% } %></div>
@@ -0,0 +1,32 @@
1
+ require "virtus"
2
+ require "rubycritic/location"
3
+
4
+ module Rubycritic
5
+
6
+ class Smell
7
+ include Virtus.model
8
+
9
+ attribute :context
10
+ attribute :locations
11
+ attribute :message
12
+ attribute :score
13
+ attribute :type
14
+
15
+ def pathnames
16
+ @pathnames ||= locations.map(&:pathname).uniq
17
+ end
18
+
19
+ def located_in?(other_location)
20
+ locations.any? { |location| location == other_location }
21
+ end
22
+
23
+ def <=>(other)
24
+ locations <=> other.locations
25
+ end
26
+
27
+ def to_s
28
+ "(#{type}) #{context} #{message}"
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,41 @@
1
+ require "rubycritic/smell"
2
+
3
+ module Rubycritic
4
+ module SmellAdapter
5
+
6
+ class Flog
7
+ def initialize(flog)
8
+ @flog = flog
9
+ end
10
+
11
+ def smells
12
+ smells = []
13
+ @flog.each_by_score do |class_method, score|
14
+ smells << create_smell(class_method, score)
15
+ end
16
+ smells
17
+ end
18
+
19
+ private
20
+
21
+ def create_smell(context, score)
22
+ location = method_location(context)
23
+ message = "has a complexity of #{score.round}"
24
+ Smell.new(
25
+ :locations => [location],
26
+ :context => context,
27
+ :message => message,
28
+ :score => score,
29
+ :type => "Complexity"
30
+ )
31
+ end
32
+
33
+ def method_location(context)
34
+ line = @flog.method_locations[context]
35
+ file_path, file_line = line.split(':')
36
+ Location.new(file_path, file_line)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ require "rubycritic/smell"
2
+
3
+ module Rubycritic
4
+ module SmellAdapter
5
+
6
+ class Reek
7
+ def initialize(reek)
8
+ @reek = reek
9
+ end
10
+
11
+ def smells
12
+ @reek.smells.map do |smell|
13
+ create_smell(smell)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def create_smell(smell)
20
+ locations = smell_locations(smell.source, smell.lines)
21
+ message = smell.message
22
+ context = smell.context
23
+ type = smell.subclass
24
+ Smell.new(:locations => locations, :context => context, :message => message, :type => type)
25
+ end
26
+
27
+ def smell_locations(file_path, file_lines)
28
+ file_lines.uniq.map do |file_line|
29
+ Location.new(file_path, file_line)
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module Rubycritic
2
+
3
+ class SmellsAggregator
4
+ def initialize(smell_adapters)
5
+ @smell_adapters = smell_adapters
6
+ end
7
+
8
+ def smells
9
+ @smells ||= @smell_adapters.map(&:smells).flatten.sort
10
+ end
11
+
12
+ def smelly_pathnames
13
+ @smelly_pathnames ||= pathnames_to_files_with_smells
14
+ end
15
+
16
+ private
17
+
18
+ def pathnames_to_files_with_smells
19
+ pathnames = Hash.new { |hash, key| hash[key] = [] }
20
+ smells.each do |smell|
21
+ smell.pathnames.each do |path|
22
+ pathnames[path] << smell
23
+ end
24
+ end
25
+ pathnames
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,31 @@
1
+ module Rubycritic
2
+
3
+ class SourceLocator
4
+ RUBY_FILES = File.join("**", "*.rb")
5
+
6
+ def initialize(dirs)
7
+ @dirs = dirs
8
+ end
9
+
10
+ def pathnames
11
+ @pathnames ||= expand_paths
12
+ end
13
+
14
+ def paths
15
+ @paths ||= pathnames.map(&:to_s)
16
+ end
17
+
18
+ private
19
+
20
+ def expand_paths
21
+ @dirs.map do |path|
22
+ if File.directory?(path)
23
+ Pathname.glob(RUBY_FILES)
24
+ elsif File.exists?(path)
25
+ Pathname.new(path)
26
+ end
27
+ end.flatten.compact.sort
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,3 @@
1
+ module Rubycritic
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "rubycritic/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rubycritic"
8
+ spec.version = Rubycritic::VERSION
9
+ spec.authors = ["Guilherme Simoes"]
10
+ spec.email = ["guilherme.rdems@gmail.com"]
11
+ spec.description = <<-EOF
12
+ Ruby Critic is a tool that detects and reports smells in Ruby classes, modules and methods.
13
+ EOF
14
+ spec.summary = "Ruby code smell detector"
15
+ spec.homepage = "https://github.com/GuilhermeSimoes/rubycritic"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files`.split("\n")
19
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ spec.test_files = `git ls-files -- test/*`.split("\n")
21
+ spec.require_path = "lib"
22
+
23
+ spec.add_runtime_dependency "virtus", "~> 1.0"
24
+ spec.add_runtime_dependency "flog", "4.2.0"
25
+ spec.add_runtime_dependency "reek", "1.3.6"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "minitest", "~> 5.3"
30
+ spec.add_development_dependency "mocha", "~> 1.0"
31
+ end
@@ -0,0 +1,37 @@
1
+ require "test_helper"
2
+ require "rubycritic/location"
3
+
4
+ describe Rubycritic::Location do
5
+ describe "attribute readers" do
6
+ before do
7
+ @path = "./foo"
8
+ @line = 42
9
+ @location = Rubycritic::Location.new(@path, @line)
10
+ end
11
+
12
+ it "has a file path" do
13
+ @location.path.must_equal @path
14
+ end
15
+
16
+ it "has a file name" do
17
+ @location.file.must_equal "foo"
18
+ end
19
+
20
+ it "has a line number" do
21
+ @location.line.must_equal @line
22
+ end
23
+ end
24
+
25
+ it "is comparable" do
26
+ location1 = Rubycritic::Location.new("./foo", 42)
27
+ location2 = Rubycritic::Location.new("./foo", 42)
28
+ location1.must_equal location2
29
+ end
30
+
31
+ it "is sortable" do
32
+ location1 = Rubycritic::Location.new("./foo", 42)
33
+ location2 = Rubycritic::Location.new("./bar", 23)
34
+ location3 = Rubycritic::Location.new("./bar", 16)
35
+ [location1, location2, location3].sort.must_equal [location3, location2, location1]
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ require "test_helper"
2
+ require "rubycritic/analysers/flog"
3
+ require "rubycritic/smell_adapters/flog"
4
+
5
+ describe Rubycritic::SmellAdapter::Flog do
6
+ before do
7
+ @sample_path = "test/samples/flog/smelly.rb"
8
+ flog = Rubycritic::Analyser::Flog.new([@sample_path])
9
+ @adapter = Rubycritic::SmellAdapter::Flog.new(flog)
10
+ end
11
+
12
+ it "detects smells" do
13
+ @adapter.smells.wont_be_empty
14
+ end
15
+
16
+ it "has smells with messages" do
17
+ smell = @adapter.smells.first
18
+ smell.message.must_equal "has a complexity of 8"
19
+ end
20
+
21
+ it "has smells with scores" do
22
+ smell = @adapter.smells.first
23
+ smell.score.must_be_kind_of Numeric
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ require "test_helper"
2
+ require "rubycritic/analysers/reek"
3
+ require "rubycritic/smell_adapters/reek"
4
+
5
+ describe Rubycritic::SmellAdapter::Reek do
6
+ context "when analysing a smelly file" do
7
+ before do
8
+ @sample_path = "test/samples/reek/smelly.rb"
9
+ reek = Rubycritic::Analyser::Reek.new([@sample_path])
10
+ @adapter = Rubycritic::SmellAdapter::Reek.new(reek)
11
+ end
12
+
13
+ it "detects smells" do
14
+ @adapter.smells.wont_be_empty
15
+ end
16
+
17
+ it "has smells with messages" do
18
+ smell = @adapter.smells.first
19
+ smell.message.must_equal "has boolean parameter 'reek'"
20
+ end
21
+ end
22
+
23
+ context "when analysing a file with smells ignored in config.reek" do
24
+ before do
25
+ sample_path = "test/samples/reek/not_smelly.rb"
26
+ reek = Rubycritic::Analyser::Reek.new(sample_path)
27
+ @adapter = Rubycritic::SmellAdapter::Reek.new(reek)
28
+ end
29
+
30
+ it "does not detect smells" do
31
+ @adapter.smells.must_be_empty
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,57 @@
1
+ require "test_helper"
2
+ require "rubycritic/smell"
3
+
4
+ describe Rubycritic::Smell do
5
+ it "has a context reader" do
6
+ context = "#bar"
7
+ smell = Rubycritic::Smell.new(:context => context)
8
+ smell.context.must_equal context
9
+ end
10
+
11
+ it "has a locations reader" do
12
+ location = Rubycritic::Location.new("./foo", "42")
13
+ smell = Rubycritic::Smell.new(:locations => [location])
14
+ smell.locations.must_equal [location]
15
+ end
16
+
17
+ it "has a pathnames reader" do
18
+ path = Pathname.new("./foo")
19
+ location1 = Rubycritic::Location.new("./foo", "42")
20
+ location2 = Rubycritic::Location.new("./foo", "23")
21
+ smell = Rubycritic::Smell.new(:locations => [location1, location2])
22
+ smell.pathnames.must_equal [path]
23
+ end
24
+
25
+ it "has a message reader" do
26
+ message = "This smells"
27
+ smell = Rubycritic::Smell.new(:message => message)
28
+ smell.message.must_equal message
29
+ end
30
+
31
+ it "has a score reader" do
32
+ score = 0
33
+ smell = Rubycritic::Smell.new(:score => score)
34
+ smell.score.must_equal score
35
+ end
36
+
37
+ it "has a type reader" do
38
+ type = :complexity
39
+ smell = Rubycritic::Smell.new(:type => type)
40
+ smell.type.must_equal type
41
+ end
42
+
43
+ it "is sortable" do
44
+ location1 = Rubycritic::Location.new("./foo", 42)
45
+ location2 = Rubycritic::Location.new("./bar", 23)
46
+ location3 = Rubycritic::Location.new("./bar", 16)
47
+ [location1, location2, location3].sort.must_equal [location3, location2, location1]
48
+ end
49
+
50
+ describe "#located_in?" do
51
+ it "returns true if the smell has a location that matches the location passed as argument" do
52
+ location = Rubycritic::Location.new("./foo", "42")
53
+ smell = Rubycritic::Smell.new(:locations => [location])
54
+ smell.located_in?(location).must_equal true
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,47 @@
1
+ require "test_helper"
2
+ require "rubycritic/smells_aggregator"
3
+
4
+ describe Rubycritic::SmellsAggregator do
5
+ context "when when analysing smelly files" do
6
+ before do
7
+ @location1 = Rubycritic::Location.new("./foo", "42")
8
+ @location2 = Rubycritic::Location.new("./bar", "23")
9
+ @location3 = Rubycritic::Location.new("./bar", "16")
10
+ @smell1 = Rubycritic::Smell.new(:locations => [@location1])
11
+ @smell2 = Rubycritic::Smell.new(:locations => [@location2, @location3])
12
+ @smell_adapters = [stub(:smells => [@smell1]), stub(:smells => [@smell2])]
13
+ end
14
+
15
+ describe "#smells" do
16
+ it "returns the smells found in those files" do
17
+ smells = [@smell2, @smell1]
18
+ Rubycritic::SmellsAggregator.new(@smell_adapters).smells.must_equal smells
19
+ end
20
+ end
21
+
22
+ describe "#smelly_pathnames" do
23
+ it "returns the files where smells were found" do
24
+ smelly_pathnames = {@location1.pathname => [@smell1], @location2.pathname => [@smell2]}
25
+ Rubycritic::SmellsAggregator.new(@smell_adapters).smelly_pathnames.must_equal smelly_pathnames
26
+ end
27
+ end
28
+ end
29
+
30
+ context "when analysing files with no smells" do
31
+ before do
32
+ @smell_adapters = [stub(:smells => [])]
33
+ end
34
+
35
+ describe "#smells" do
36
+ it "returns an empty array" do
37
+ Rubycritic::SmellsAggregator.new(@smell_adapters).smells.must_equal []
38
+ end
39
+ end
40
+
41
+ describe "#smelly_pathnames" do
42
+ it "returns an empty hash" do
43
+ Rubycritic::SmellsAggregator.new(@smell_adapters).smelly_pathnames.must_equal({})
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ require "test_helper"
2
+ require "rubycritic/source_locator"
3
+
4
+ describe Rubycritic::SourceLocator do
5
+ before do
6
+ @original_dir = Dir.pwd
7
+ Dir.chdir("test/samples/location")
8
+ end
9
+
10
+ describe "#paths" do
11
+ it "finds a single path" do
12
+ paths = ["file0.rb"]
13
+ Rubycritic::SourceLocator.new(paths).paths.must_equal paths
14
+ end
15
+
16
+ it "finds multiple paths" do
17
+ paths = ["dir1/file1.rb", "file0.rb"]
18
+ Rubycritic::SourceLocator.new(paths).paths.must_equal paths
19
+ end
20
+
21
+ it "finds all the paths" do
22
+ paths = ["dir1/file1.rb", "file0.rb"]
23
+ Rubycritic::SourceLocator.new(["."]).paths.must_equal paths
24
+ end
25
+
26
+ it "deals with non-existent paths" do
27
+ paths = ["non_existent_dir1/non_existent_file1.rb", "non_existent_file0.rb"]
28
+ Rubycritic::SourceLocator.new(paths).paths.must_equal []
29
+ end
30
+ end
31
+
32
+ describe "#pathnames" do
33
+ it "finds a single path" do
34
+ path = "file0.rb"
35
+ paths = [path]
36
+ result = [Pathname.new(path)]
37
+ Rubycritic::SourceLocator.new(paths).pathnames.must_equal result
38
+ end
39
+ end
40
+
41
+ after do
42
+ Dir.chdir(@original_dir)
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ describe "Rubycritic version" do
4
+ it "is defined" do
5
+ Rubycritic::VERSION.wont_be_nil
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ class AllTheMethods
2
+ def method_missing(method, *args, &block)
3
+ define_method(method) { "I did not exist, but now I do." }
4
+ send(method)
5
+ end
6
+ end
File without changes
File without changes
@@ -0,0 +1,7 @@
1
+ class Perfumed
2
+ attr_reader :perfume
3
+
4
+ def ignoresRubyStyle(oneParameter)
5
+ oneVariable = oneParameter
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class Theon
2
+ def reeks?(reek = true)
3
+ reek
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "minitest/autorun"
2
+ require "minitest/pride"
3
+ require "mocha/setup"
4
+
5
+ def context(*args, &block)
6
+ describe(*args, &block)
7
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubycritic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Guilherme Simoes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: virtus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: flog
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 4.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 4.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: reek
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.6
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.6
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mocha
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ description: |2
112
+ Ruby Critic is a tool that detects and reports smells in Ruby classes, modules and methods.
113
+ email:
114
+ - guilherme.rdems@gmail.com
115
+ executables:
116
+ - rubycritic
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - CONTRIBUTING.md
122
+ - Gemfile
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - SPEC.md
127
+ - bin/rubycritic
128
+ - lib/rubycritic.rb
129
+ - lib/rubycritic/analysers/config.reek
130
+ - lib/rubycritic/analysers/flog.rb
131
+ - lib/rubycritic/analysers/reek.rb
132
+ - lib/rubycritic/cli.rb
133
+ - lib/rubycritic/location.rb
134
+ - lib/rubycritic/report_generators/assets/stylesheets/application.css
135
+ - lib/rubycritic/report_generators/file_generator.rb
136
+ - lib/rubycritic/report_generators/line_generator.rb
137
+ - lib/rubycritic/report_generators/report_generator.rb
138
+ - lib/rubycritic/report_generators/templates/file.html.erb
139
+ - lib/rubycritic/report_generators/templates/layouts/application.html.erb
140
+ - lib/rubycritic/report_generators/templates/line.html.erb
141
+ - lib/rubycritic/report_generators/templates/smelly_line.html.erb
142
+ - lib/rubycritic/smell.rb
143
+ - lib/rubycritic/smell_adapters/flog.rb
144
+ - lib/rubycritic/smell_adapters/reek.rb
145
+ - lib/rubycritic/smells_aggregator.rb
146
+ - lib/rubycritic/source_locator.rb
147
+ - lib/rubycritic/version.rb
148
+ - rubycritic.gemspec
149
+ - test/lib/rubycritic/location_test.rb
150
+ - test/lib/rubycritic/metric_adapters/flog_adapter_test.rb
151
+ - test/lib/rubycritic/metric_adapters/reek_adapter_test.rb
152
+ - test/lib/rubycritic/smell_test.rb
153
+ - test/lib/rubycritic/smells_aggregator_test.rb
154
+ - test/lib/rubycritic/source_locator_test.rb
155
+ - test/lib/rubycritic/version_test.rb
156
+ - test/samples/flog/smelly.rb
157
+ - test/samples/location/dir1/file1.rb
158
+ - test/samples/location/file0.rb
159
+ - test/samples/reek/not_smelly.rb
160
+ - test/samples/reek/smelly.rb
161
+ - test/test_helper.rb
162
+ homepage: https://github.com/GuilhermeSimoes/rubycritic
163
+ licenses:
164
+ - MIT
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.1.11
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: Ruby code smell detector
186
+ test_files:
187
+ - test/lib/rubycritic/location_test.rb
188
+ - test/lib/rubycritic/metric_adapters/flog_adapter_test.rb
189
+ - test/lib/rubycritic/metric_adapters/reek_adapter_test.rb
190
+ - test/lib/rubycritic/smell_test.rb
191
+ - test/lib/rubycritic/smells_aggregator_test.rb
192
+ - test/lib/rubycritic/source_locator_test.rb
193
+ - test/lib/rubycritic/version_test.rb
194
+ - test/samples/flog/smelly.rb
195
+ - test/samples/location/dir1/file1.rb
196
+ - test/samples/location/file0.rb
197
+ - test/samples/reek/not_smelly.rb
198
+ - test/samples/reek/smelly.rb
199
+ - test/test_helper.rb