rubycritic 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +593 -0
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +7 -0
  5. data/CONTRIBUTING.md +17 -15
  6. data/README.md +15 -4
  7. data/Rakefile +9 -6
  8. data/bin/rubycritic +2 -2
  9. data/lib/rubycritic.rb +21 -1
  10. data/lib/rubycritic/analysers/churn.rb +4 -2
  11. data/lib/rubycritic/analysers/helpers/ast_node.rb +4 -8
  12. data/lib/rubycritic/analysers/helpers/methods_counter.rb +2 -5
  13. data/lib/rubycritic/analysers/helpers/modules_locator.rb +2 -5
  14. data/lib/rubycritic/analysers/helpers/parser.rb +12 -0
  15. data/lib/rubycritic/analysers/helpers/reek.rb +6 -2
  16. data/lib/rubycritic/analysers/smells/reek.rb +1 -1
  17. data/lib/rubycritic/analysers_runner.rb +13 -7
  18. data/lib/rubycritic/cli/application.rb +24 -0
  19. data/lib/rubycritic/cli/options.rb +57 -0
  20. data/lib/rubycritic/commands/ci.rb +28 -0
  21. data/lib/rubycritic/commands/default.rb +30 -0
  22. data/lib/rubycritic/commands/help.rb +13 -0
  23. data/lib/rubycritic/commands/version.rb +11 -0
  24. data/lib/rubycritic/configuration.rb +19 -14
  25. data/lib/rubycritic/core/analysed_module.rb +1 -1
  26. data/lib/rubycritic/core/smell.rb +1 -1
  27. data/lib/rubycritic/report_generators/base.rb +1 -1
  28. data/lib/rubycritic/report_generators/line.rb +1 -1
  29. data/lib/rubycritic/report_generators/smells_index.rb +1 -0
  30. data/lib/rubycritic/report_generators/templates/code_file.html.erb +1 -1
  31. data/lib/rubycritic/report_generators/templates/smells_index.html.erb +6 -2
  32. data/lib/rubycritic/report_generators/templates/smelly_line.html.erb +1 -1
  33. data/lib/rubycritic/report_generators/view_helpers.rb +4 -4
  34. data/lib/rubycritic/reporters/base.rb +1 -1
  35. data/lib/rubycritic/revision_comparator.rb +14 -14
  36. data/lib/rubycritic/source_control_systems/base.rb +9 -32
  37. data/lib/rubycritic/source_control_systems/double.rb +6 -7
  38. data/lib/rubycritic/source_control_systems/git.rb +10 -16
  39. data/lib/rubycritic/source_control_systems/mercurial.rb +29 -0
  40. data/lib/rubycritic/source_locator.rb +4 -5
  41. data/lib/rubycritic/version.rb +1 -1
  42. data/rubycritic.gemspec +4 -3
  43. data/test/lib/rubycritic/analysers/churn_test.rb +5 -5
  44. data/test/lib/rubycritic/analysers/smells/flay_test.rb +1 -1
  45. data/test/lib/rubycritic/configuration_test.rb +8 -7
  46. data/test/lib/rubycritic/core/analysed_module_test.rb +2 -2
  47. data/test/lib/rubycritic/core/smell_test.rb +2 -2
  48. data/test/lib/rubycritic/source_control_systems/{source_control_system_test.rb → base_test.rb} +1 -1
  49. data/test/lib/rubycritic/source_control_systems/double_test.rb +11 -0
  50. data/test/lib/rubycritic/source_control_systems/git_test.rb +13 -0
  51. data/test/lib/rubycritic/source_control_systems/interfaces/basic.rb +7 -0
  52. data/test/lib/rubycritic/source_control_systems/interfaces/time_travel.rb +7 -0
  53. data/test/lib/rubycritic/source_control_systems/mercurial_test.rb +11 -0
  54. data/test/lib/rubycritic/source_locator_test.rb +8 -2
  55. data/test/test_helper.rb +29 -0
  56. metadata +49 -13
  57. data/lib/rubycritic/cli.rb +0 -46
  58. data/lib/rubycritic/modules_initializer.rb +0 -14
  59. data/lib/rubycritic/orchestrator.rb +0 -23
@@ -0,0 +1,30 @@
1
+ require "rubycritic/source_control_systems/base"
2
+ require "rubycritic/analysers_runner"
3
+ require "rubycritic/revision_comparator"
4
+ require "rubycritic/reporters/main"
5
+
6
+ module Rubycritic
7
+ module Command
8
+ class Default
9
+ def initialize(options)
10
+ @paths = options[:paths]
11
+ Config.source_control_system = SourceControlSystem::Base.create
12
+ end
13
+
14
+ def execute
15
+ critique(@paths)
16
+ report
17
+ end
18
+
19
+ def critique(paths)
20
+ @analysed_modules = AnalysersRunner.new(paths).run
21
+ RevisionComparator.new(paths).set_statuses(@analysed_modules)
22
+ end
23
+
24
+ def report
25
+ report_location = Reporter::Main.new(@analysed_modules).generate_report
26
+ puts "New critique at #{report_location}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Rubycritic
2
+ module Command
3
+ class Help
4
+ def initialize(options)
5
+ @options = options
6
+ end
7
+
8
+ def execute
9
+ puts @options.help_text
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require "rubycritic/version"
2
+
3
+ module Rubycritic
4
+ module Command
5
+ class Version
6
+ def execute
7
+ puts "RubyCritic #{VERSION}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,24 +1,29 @@
1
- require "pathname"
2
-
3
1
  module Rubycritic
4
- def self.configuration
5
- @configuration ||= Configuration.new
6
- end
7
-
8
2
  class Configuration
9
3
  attr_reader :root
4
+ attr_accessor :source_control_system, :mode
10
5
 
11
- def initialize
12
- self.root = "tmp/rubycritic"
6
+ def set(options)
7
+ self.mode = options[:mode] || :default
8
+ self.root = options[:root] || "tmp/rubycritic"
13
9
  end
14
10
 
15
11
  def root=(path)
16
- @root =
17
- if Pathname(path).relative?
18
- File.expand_path(path)
19
- else
20
- path
21
- end
12
+ @root = File.expand_path(path)
13
+ end
14
+ end
15
+
16
+ module Config
17
+ def self.configuration
18
+ @configuration ||= Configuration.new
19
+ end
20
+
21
+ def self.set(options = {})
22
+ configuration.set(options)
23
+ end
24
+
25
+ def self.method_missing(method, *args, &block)
26
+ configuration.public_send(method, *args, &block)
22
27
  end
23
28
  end
24
29
  end
@@ -35,7 +35,7 @@ module Rubycritic
35
35
  end
36
36
  end
37
37
 
38
- def has_smells?
38
+ def smells?
39
39
  !smells.empty?
40
40
  end
41
41
 
@@ -18,7 +18,7 @@ module Rubycritic
18
18
  locations.any? { |location| location == other_location }
19
19
  end
20
20
 
21
- def has_multiple_locations?
21
+ def multiple_locations?
22
22
  locations.length > 1
23
23
  end
24
24
 
@@ -38,7 +38,7 @@ module Rubycritic
38
38
  private
39
39
 
40
40
  def root_directory
41
- @root_directory ||= Pathname.new(::Rubycritic.configuration.root)
41
+ @root_directory ||= Pathname.new(Config.root)
42
42
  end
43
43
 
44
44
  def get_binding
@@ -12,7 +12,7 @@ module Rubycritic
12
12
 
13
13
  def initialize(file_directory, text, smells)
14
14
  @file_directory = file_directory
15
- @text = CGI::escapeHTML(text.chomp)
15
+ @text = CGI.escapeHTML(text.chomp)
16
16
  @smells = smells
17
17
  @template =
18
18
  if @smells.empty?
@@ -9,6 +9,7 @@ module Rubycritic
9
9
  def initialize(analysed_modules)
10
10
  @smells = analysed_modules.flat_map(&:smells).uniq
11
11
  @analysed_module_names = analysed_module_names(analysed_modules)
12
+ @show_status = (Config.mode == :default)
12
13
  end
13
14
 
14
15
  def file_name
@@ -28,7 +28,7 @@
28
28
  </div>
29
29
  </div>
30
30
 
31
- <% if @analysed_module.has_smells? %>
31
+ <% if @analysed_module.smells? %>
32
32
  <button id="js-toggle-smells" class="smells-toggle-button button">Toggle Smells</button>
33
33
  <% end %>
34
34
  </div>
@@ -3,7 +3,9 @@
3
3
  <tr>
4
4
  <th class="first-cell">Smell</th>
5
5
  <th>Locations</th>
6
- <th class="centered-cell last-cell" title="Code smells introduced recently are marked as new. There's still time before they are committed!">Status</th>
6
+ <% if @show_status %>
7
+ <th class="centered-cell last-cell" title="Code smells introduced recently are marked as new. There's still time before they are committed!">Status</th>
8
+ <% end %>
7
9
  </tr>
8
10
  </thead>
9
11
  <tbody>
@@ -15,7 +17,9 @@
15
17
  <a href="<%= smell_location_path(location) %>"><%= @analysed_module_names[location.pathname] %></a>
16
18
  <% end %>
17
19
  </td>
18
- <td class="centered-cell last-cell"><span class="status-<%= smell.status %> circled-text circle"><%= smell.status %></span></td>
20
+ <% if @show_status %>
21
+ <td class="centered-cell last-cell"><span class="status-<%= smell.status %> circled-text circle"><%= smell.status %></span></td>
22
+ <% end %>
19
23
  </tr>
20
24
  <% end %>
21
25
  </tbody>
@@ -4,7 +4,7 @@
4
4
  <% @smells.each do |smell| %>
5
5
  <li class="smell <%= smell.status %>">
6
6
  <span class="description"><%= smell %>
7
- <% if smell.has_multiple_locations? %>
7
+ <% if smell.multiple_locations? %>
8
8
  <% smell.locations.each_with_index do |location, index| %>
9
9
  <a href="<%= smell_location_path(location) %>" class="js-smell-location"><%= index %></a>
10
10
  <% end %>
@@ -14,11 +14,11 @@ module Rubycritic
14
14
  end
15
15
 
16
16
  def asset_path(file)
17
- relative_path(root_directory + "assets" + file)
17
+ relative_path("assets/#{file}")
18
18
  end
19
19
 
20
20
  def file_path(file)
21
- relative_path(root_directory + file)
21
+ relative_path(file)
22
22
  end
23
23
 
24
24
  def smell_location_path(location)
@@ -27,8 +27,8 @@ module Rubycritic
27
27
 
28
28
  private
29
29
 
30
- def relative_path(pathname)
31
- pathname.relative_path_from(file_directory)
30
+ def relative_path(file)
31
+ (root_directory + file).relative_path_from(file_directory)
32
32
  end
33
33
 
34
34
  def file_directory
@@ -16,7 +16,7 @@ module Rubycritic
16
16
  end
17
17
 
18
18
  def copy_assets_to_report_directory
19
- FileUtils.cp_r(ASSETS_DIR, ::Rubycritic.configuration.root)
19
+ FileUtils.cp_r(ASSETS_DIR, Config.root)
20
20
  end
21
21
  end
22
22
 
@@ -1,5 +1,4 @@
1
1
  require "rubycritic/serializer"
2
- require "rubycritic/modules_initializer"
3
2
  require "rubycritic/analysers_runner"
4
3
  require "rubycritic/smells_status_setter"
5
4
  require "rubycritic/version"
@@ -10,17 +9,18 @@ module Rubycritic
10
9
  class RevisionComparator
11
10
  SNAPSHOTS_DIR_NAME = "snapshots"
12
11
 
13
- def initialize(analysed_modules, source_control_system, paths)
14
- @analysed_modules_now = analysed_modules
15
- @source_control_system = source_control_system
12
+ def initialize(paths)
16
13
  @paths = paths
17
14
  end
18
15
 
19
- def set_statuses
20
- SmellsStatusSetter.set(
21
- analysed_modules_before.flat_map(&:smells),
22
- @analysed_modules_now.flat_map(&:smells)
23
- )
16
+ def set_statuses(analysed_modules_now)
17
+ if Config.source_control_system.revision?
18
+ SmellsStatusSetter.set(
19
+ analysed_modules_before.flat_map(&:smells),
20
+ analysed_modules_now.flat_map(&:smells)
21
+ )
22
+ end
23
+ analysed_modules_now
24
24
  end
25
25
 
26
26
  private
@@ -30,9 +30,9 @@ module Rubycritic
30
30
  if File.file?(revision_file)
31
31
  serializer.load
32
32
  else
33
- analysed_modules = ModulesInitializer.init(@paths)
34
- @source_control_system.travel_to_head do
35
- AnalysersRunner.new(analysed_modules, @source_control_system).run
33
+ analysed_modules = nil
34
+ Config.source_control_system.travel_to_head do
35
+ analysed_modules = AnalysersRunner.new(@paths).run
36
36
  end
37
37
  serializer.dump(analysed_modules)
38
38
  analysed_modules
@@ -41,11 +41,11 @@ module Rubycritic
41
41
 
42
42
  def revision_file
43
43
  @revision_file ||= File.join(
44
- ::Rubycritic.configuration.root,
44
+ Config.root,
45
45
  SNAPSHOTS_DIR_NAME,
46
46
  VERSION,
47
47
  Digest::MD5.hexdigest(Marshal.dump(@paths)),
48
- @source_control_system.head_reference
48
+ Config.source_control_system.head_reference
49
49
  )
50
50
  end
51
51
  end
@@ -10,46 +10,22 @@ module Rubycritic
10
10
  @@systems << self
11
11
  end
12
12
 
13
+ def self.systems
14
+ @@systems
15
+ end
16
+
13
17
  def self.create
14
- supported_system = systems.detect(&:supported?)
18
+ supported_system = systems.find(&:supported?)
15
19
  if supported_system
16
20
  supported_system.new
17
21
  else
18
- puts "Rubycritic requires a #{system_names} repository."
22
+ puts "RubyCritic can provide more feedback if you use a #{connected_system_names} repository."
19
23
  Double.new
20
24
  end
21
25
  end
22
26
 
23
- def self.systems
24
- @@systems
25
- end
26
-
27
- def self.system_names
28
- systems.join(", ")
29
- end
30
-
31
- def self.supported?
32
- raise NotImplementedError.new("The #{self.class} class must implement the #{__method__} method.")
33
- end
34
-
35
- def has_revision?
36
- raise NotImplementedError.new("The #{self.class} class must implement the #{__method__} method.")
37
- end
38
-
39
- def head_reference
40
- raise NotImplementedError.new("The #{self.class} class must implement the #{__method__} method.")
41
- end
42
-
43
- def travel_to_head
44
- raise NotImplementedError.new("The #{self.class} class must implement the #{__method__} method.")
45
- end
46
-
47
- def revisions_count(path)
48
- raise NotImplementedError.new("The #{self.class} class must implement the #{__method__} method.")
49
- end
50
-
51
- def date_of_last_commit(path)
52
- raise NotImplementedError.new("The #{self.class} class must implement the #{__method__} method.")
27
+ def self.connected_system_names
28
+ "#{systems[0...-1].join(', ')} or #{systems[-1]}"
53
29
  end
54
30
  end
55
31
 
@@ -58,3 +34,4 @@ end
58
34
 
59
35
  require "rubycritic/source_control_systems/double"
60
36
  require "rubycritic/source_control_systems/git"
37
+ require "rubycritic/source_control_systems/mercurial"
@@ -1,18 +1,17 @@
1
1
  module Rubycritic
2
2
  module SourceControlSystem
3
-
4
3
  class Double < Base
5
- def has_revision?
6
- false
7
- end
8
-
9
- def revisions_count(path)
4
+ def revisions_count(_)
10
5
  "N/A"
11
6
  end
12
7
 
13
- def date_of_last_commit(path)
8
+ def date_of_last_commit(_)
14
9
  nil
15
10
  end
11
+
12
+ def revision?
13
+ false
14
+ end
16
15
  end
17
16
 
18
17
  end
@@ -12,7 +12,15 @@ module Rubycritic
12
12
  "Git"
13
13
  end
14
14
 
15
- def has_revision?
15
+ def revisions_count(path)
16
+ `git log --follow --format=%h #{path.shellescape}`.count("\n")
17
+ end
18
+
19
+ def date_of_last_commit(path)
20
+ `git log -1 --date=iso --format=%ad #{path.shellescape}`.chomp!
21
+ end
22
+
23
+ def revision?
16
24
  head_reference && $?.success?
17
25
  end
18
26
 
@@ -27,31 +35,17 @@ module Rubycritic
27
35
  travel_to_original_state if stash_successful
28
36
  end
29
37
 
30
- def revisions_count(path)
31
- `git log --follow --format=oneline #{path.shellescape}`.count("\n")
32
- end
33
-
34
- def date_of_last_commit(path)
35
- `git log -1 --date=iso --format=%ad #{path.shellescape}`.chomp!
36
- end
37
-
38
38
  private
39
39
 
40
40
  def stash_changes
41
- return false if everything_commmited?
42
-
43
41
  stashes_count_before = stashes_count
44
42
  `git stash`
45
43
  stashes_count_after = stashes_count
46
44
  stashes_count_after > stashes_count_before
47
45
  end
48
46
 
49
- def everything_commmited?
50
- `git status --porcelain`.empty?
51
- end
52
-
53
47
  def stashes_count
54
- `git stash list`.count("\n")
48
+ `git stash list --format=%h`.count("\n")
55
49
  end
56
50
 
57
51
  def travel_to_original_state
@@ -0,0 +1,29 @@
1
+ module Rubycritic
2
+ module SourceControlSystem
3
+
4
+ class Mercurial < Base
5
+ register_system
6
+
7
+ def self.supported?
8
+ `hg verify 2>&1` && $?.success?
9
+ end
10
+
11
+ def self.to_s
12
+ "Mercurial"
13
+ end
14
+
15
+ def revisions_count(path)
16
+ `hg log #{path.shellescape} --template '1'`.size
17
+ end
18
+
19
+ def date_of_last_commit(path)
20
+ `hg log #{path.shellescape} --template '{date|isodate}' --limit 1`.chomp
21
+ end
22
+
23
+ def revision?
24
+ false
25
+ end
26
+ end
27
+
28
+ end
29
+ end