rubycritic 1.1.1 → 1.2.0

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