rubycritic 0.0.1

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.
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