rubocop_director 0.1.0 โ†’ 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9495c42ab7429abd8ad0cf4c31074079fba3419b9cccc56a9ab51fdd66ae3c49
4
- data.tar.gz: e634bc7e5f50acedb08be3c5dbb2f2e8ea03cbda5ef9694f89e87c5943b16a8d
3
+ metadata.gz: 06b576908b0f9b2cf1470350c38e53c7339caca11fb7abca712909feac9508f7
4
+ data.tar.gz: 2087b4e9a5002c5a2c2294a573426def90556ec98866c07421b67355d928fc24
5
5
  SHA512:
6
- metadata.gz: 5ad6d7874bae6bc6bab004da66c96b777a24bec8f50457a7dc46aabb7b5e349bc49d52d2866e4f06c14c2ad71e9840d03c9fcc088d1d237061f80acfef3735f8
7
- data.tar.gz: 8b1aeda0559907b4696f47b77a069f605816e7d25b99318bb36e54288511c859350d144ddc8469c40e8c347afb0c2c16cbd0cd956d02200974703b9decbb04f4
6
+ metadata.gz: c798e0ac5e7b7b53339bc0ec001d5165f4bcbe794952f0a297b70cba7ca5cba1fdffd3ea88395e177c72e5d43bc19afe4f56e02e7b77994e56f4e698d77b0dc1
7
+ data.tar.gz: d46b526303bd4930b5a4b4e9d63054d08ae472ed813bb1fbf366ccd08c18f873a8f1e9b1b8cbb7b357abce89face540b22ca66bf406db56f9130a5878d646479
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
- ## [Unreleased]
1
+ # Change log
2
2
 
3
- ## [0.1.0] - 2023-05-08
3
+ ## master
4
4
 
5
- - Initial release
5
+ ## 0.2.0 (2023-06-03)
6
+
7
+ - [PR #13](https://github.com/DmitryTsepelev/rubocop_director/pull/13) Prettify output formatter and change stats calculation algorithm ([@DmitryTsepelev][])
8
+ - [PR #9](https://github.com/DmitryTsepelev/rubocop_director/pull/9) Removed sed dependency: stats generated using temp config ([@samarthkathal][])
9
+ - [PR #8](https://github.com/DmitryTsepelev/rubocop_director/pull/8) Reorder plan flow to run expensive check first ([@DmitryTsepelev][])
10
+ - [PR #7](https://github.com/DmitryTsepelev/rubocop_director/pull/7) GenerateConfig will a prompt to overwrite when config already exists ([@samarthkathal][])
11
+
12
+ ## 0.1.0 (2023-05-08)
13
+
14
+ - Initial version ([@DmitryTsepelev][])
15
+
16
+ [@DmitryTsepelev]: https://github.com/DmitryTsepelev
17
+ [@samarthkathal]: https://github.com/samarthkathal
data/README.md CHANGED
@@ -6,7 +6,6 @@ A commandโ€“line utility for refactoring planning. It uses `.rubocop_todo.yml` a
6
6
 
7
7
  Prerequisites:
8
8
 
9
- - `sed`;
10
9
  - `git` repo;
11
10
  - generated `.rubocop_todo.yml`.
12
11
 
@@ -44,21 +43,24 @@ As a result you'll get something like this:
44
43
  ๐Ÿ’ก Checking git history since 1995-01-01 to find hot files...
45
44
  ๐Ÿ’ก๐ŸŽฅ Running rubocop to get the list of offences to fix...
46
45
  ๐Ÿ’ก๐ŸŽฅ๐ŸŽฌ Calculating a list of files to refactor...
47
- --------------------
48
- spec/models/user.rb
49
- updated 10 times since -4712-01-01
50
- offences: RSpec/AroundBlock - 8
51
- refactoring value: 110 (55%)
52
- --------------------
53
- spec/models/order.rb
54
- updated 20 times since -4712-01-01
55
- offences: Rspec/BeEql - 4
56
- refactoring value: 90 (45%)
46
+
47
+ Path: app/controllers/user_controller.rb
48
+ Updated 99 times since 2023-01-01
49
+ Offenses:
50
+ ๐Ÿš“ Rails/SomeCop - 2
51
+ Refactoring value: 1.5431217598108933 (54.79575%)
52
+
53
+ Path: app/models/user.rb
54
+ Updated 136 times since 2023-01-01
55
+ Offenses:
56
+ ๐Ÿš“ Rails/SomeCop - 1
57
+ ๐Ÿš“ Rails/AnotherCop - 1
58
+ Refactoring value: 1.2730122208719792 (45.20425%)
57
59
  ```
58
60
 
59
61
  > Want a different output format (e.g., CSV)? Let me know, open an issue!
60
62
 
61
- Value is calculated using a formula: `sum of value from each cop (<number of offences> * <cop weight> * <number of file updates> * <update weight>)`.
63
+ Value is calculated using a formula: `sum of value from each cop (<count of offences> * <cop weight> * (<count of file updates> / <total count of updates>) ** <update weight>)`.
62
64
 
63
65
  If you need to count updates from a specific dateโ€”use `--since`:
64
66
 
@@ -68,7 +70,7 @@ bundle exec rubocop-director --since=2023-01-01
68
70
 
69
71
  ## Development
70
72
 
71
- After checking out the repo, run `bundle insatll` to install dependencies
73
+ After checking out the repo, run `bundle install` to install dependencies
72
74
 
73
75
  ## Contributing
74
76
 
@@ -9,20 +9,10 @@ module RubocopDirector
9
9
  RUBOCOP_TODO = ".rubocop_todo.yml"
10
10
 
11
11
  def run
12
- todo = yield load_config
12
+ rubocop_todo = yield load_config
13
+ yield check_config_already_exists
13
14
 
14
- weights = todo.keys.each_with_object({}).each do |cop, acc|
15
- acc.merge!(cop => 1)
16
- end
17
-
18
- # TODO: warn if file exists
19
- File.write(".rubocop-director.yml", {
20
- "update_weight" => 1,
21
- "default_cop_weight" => 1,
22
- "weights" => weights
23
- }.to_yaml)
24
-
25
- Success("Config generated")
15
+ create_config(rubocop_todo)
26
16
  end
27
17
 
28
18
  private
@@ -32,6 +22,33 @@ module RubocopDirector
32
22
  rescue Errno::ENOENT
33
23
  Failure("#{RUBOCOP_TODO} not found, generate it using `rubocop --regenerate-todo`")
34
24
  end
25
+
26
+ def check_config_already_exists
27
+ return Success() if config_not_exists? || override_config?
28
+
29
+ Failure("previous version of #{CONFIG_NAME} was preserved.")
30
+ end
31
+
32
+ def config_not_exists?
33
+ !File.file?(CONFIG_NAME)
34
+ end
35
+
36
+ def override_config?
37
+ puts("#{CONFIG_NAME} already exists, do you want to override it? (y/n)")
38
+ gets.chomp == "y"
39
+ end
40
+
41
+ def create_config(rubocop_todo)
42
+ weights = rubocop_todo.keys.to_h { |key| [key, 1] }
43
+
44
+ File.write(CONFIG_NAME, {
45
+ "update_weight" => 1,
46
+ "default_cop_weight" => 1,
47
+ "weights" => weights
48
+ }.to_yaml)
49
+
50
+ Success("Config generated")
51
+ end
35
52
  end
36
53
  end
37
54
  end
@@ -20,8 +20,8 @@ module RubocopDirector
20
20
 
21
21
  def run
22
22
  config = yield load_config
23
- update_counts = yield load_git_stats
24
23
  rubocop_json = yield load_rubocop_json
24
+ update_counts = yield load_git_stats
25
25
  ranged_files = yield range_files(rubocop_json: rubocop_json, update_counts: update_counts, config: config)
26
26
 
27
27
  OutputFormatter.new(ranged_files: ranged_files, since: @since).call
@@ -35,19 +35,19 @@ module RubocopDirector
35
35
  Failure("#{CONFIG_NAME} not found, generate it using `rubocop-director --generate-config`")
36
36
  end
37
37
 
38
- def load_git_stats
39
- puts "๐Ÿ’ก Checking git history since #{@since} to find hot files..."
40
- GitLogStats.new(@since).fetch
41
- end
42
-
43
38
  def load_rubocop_json
44
- puts "๐Ÿ’ก๐ŸŽฅ Running rubocop to get the list of offences to fix..."
39
+ puts "๐Ÿ’ก Running rubocop to get the list of offences to fix..."
45
40
  RubocopStats.new.fetch
46
41
  end
47
42
 
43
+ def load_git_stats
44
+ puts "๐Ÿ’ก๐ŸŽฅ Checking git history since #{@since} to find hot files..."
45
+ GitLogStats.new(@since).fetch
46
+ end
47
+
48
48
  def range_files(rubocop_json:, update_counts:, config:)
49
49
  puts "๐Ÿ’ก๐ŸŽฅ๐ŸŽฌ Calculating a list of files to refactor..."
50
- RubocopDirector::FileStatsBuilder.new(rubocop_json: rubocop_json, update_counts: update_counts, config: config).build
50
+ FileStatsBuilder.new(rubocop_json: rubocop_json, update_counts: update_counts, config: config).build
51
51
  end
52
52
  end
53
53
  end
@@ -10,6 +10,8 @@ module RubocopDirector
10
10
  end
11
11
 
12
12
  def build
13
+ update_weight = yield fetch_update_weight
14
+
13
15
  file_stats = files_with_offenses.map do |file|
14
16
  stats = {
15
17
  path: file["path"],
@@ -17,11 +19,17 @@ module RubocopDirector
17
19
  offense_counts: file["offenses"].group_by { |offense| offense["cop_name"] }.transform_values(&:count)
18
20
  }
19
21
 
20
- stats[:value] = yield find_refactoring_value(stats)
22
+ stats[:cop_value] = yield find_refactoring_value(stats)
21
23
 
22
24
  stats
23
25
  end
24
26
 
27
+ total_updates_count = file_stats.sum { |f| f[:updates_count] }
28
+
29
+ file_stats = file_stats.each do |stats|
30
+ stats[:value] = stats[:cop_value] * ((stats[:updates_count] / total_updates_count.to_f)**update_weight)
31
+ end
32
+
25
33
  Success(file_stats.sort_by { _1[:value] }.reverse)
26
34
  end
27
35
 
@@ -32,14 +40,12 @@ module RubocopDirector
32
40
  def files_with_offenses = rubocop_json.select { |file| file["offenses"].any? }
33
41
 
34
42
  def find_refactoring_value(file)
35
- update_weight = yield fetch_update_weight
36
-
37
- offence_sum = file[:offense_counts].sum do |cop_name, count|
43
+ value = file[:offense_counts].sum do |cop_name, count|
38
44
  cop_weight = yield fetch_cop_weight(cop_name)
39
45
  cop_weight * count
40
46
  end
41
47
 
42
- Success((offence_sum * file[:updates_count] * update_weight).to_f)
48
+ Success(value)
43
49
  end
44
50
 
45
51
  def fetch_cop_weight(cop_name)
@@ -15,11 +15,13 @@ module RubocopDirector
15
15
 
16
16
  def call
17
17
  result = @ranged_files.each_with_object([]) do |file, result|
18
- result << "-" * 20
19
- result << file[:path]
20
- result << "updated #{file[:updates_count]} times since #{@since}"
21
- result << "offences: #{file[:offense_counts].map { |cop, count| "#{cop} - #{count}" }.join(", ")}"
22
- result << "refactoring value: #{file[:value]} (#{(100 * file[:value] / total_value.to_f).round(5)}%)"
18
+ result << ""
19
+
20
+ result << "Path: #{file[:path]}"
21
+ result << "Updated #{file[:updates_count]} times since #{@since}"
22
+ result << "Offenses:"
23
+ file[:offense_counts].each { |cop, count| result << " ๐Ÿš“ #{cop} - #{count}" }
24
+ result << "Refactoring value: #{file[:value]} (#{(100 * file[:value] / total_value.to_f).round(5)}%)"
23
25
  end
24
26
 
25
27
  Success(result)
@@ -7,22 +7,48 @@ require "dry/monads"
7
7
  module RubocopDirector
8
8
  class RubocopStats
9
9
  include Dry::Monads[:result]
10
+ include Dry::Monads::Do.for(:fetch)
11
+
12
+ TEMP_CONFIG_PATH = "./.temp_rubocop.yml"
10
13
 
11
14
  def fetch
12
- _, stderr = Open3.capture3("sed '/todo/d' ./.rubocop.yml > tmpfile; mv tmpfile ./.rubocop.yml")
13
- if stderr.length > 0
14
- return Failure("Failed to remove TODO from rubocop config: #{stderr}")
15
- end
15
+ config = yield load_config
16
+ yield generate_temp_rubocop_config_without_todo(initial_config: config)
17
+
18
+ stats = yield generate_stats
19
+ yield remove_temp_config
20
+
21
+ Success(stats)
22
+ end
23
+
24
+ private
16
25
 
17
- stdout, stderr = Open3.capture3("bundle exec rubocop --format json")
26
+ def load_config
27
+ Success(YAML.load_file("./.rubocop.yml"))
28
+ rescue Errno::ENOENT
29
+ Failure("unable to load rubocop config. Please ensure .rubocop.yml file is present at your project's root directory")
30
+ end
31
+
32
+ def generate_temp_rubocop_config_without_todo(initial_config:)
33
+ initial_config.dig("inherit_from")&.delete(".rubocop_todo.yml")
34
+
35
+ Success(File.write(TEMP_CONFIG_PATH, initial_config.to_yaml))
36
+ rescue IOError => e
37
+ Failure("Failed to create a temporary config file to generate stats: #{e}")
38
+ end
39
+
40
+ def generate_stats
41
+ stdout, stderr = Open3.capture3("bundle exec rubocop -c #{TEMP_CONFIG_PATH} --format json")
18
42
 
19
43
  if stderr.length > 0
20
44
  Failure("Failed to fetch rubocop stats: #{stderr}")
21
45
  else
22
46
  Success(JSON.parse(stdout)["files"])
23
47
  end
24
- ensure
25
- Open3.capture3("git checkout ./.rubocop.yml")
48
+ end
49
+
50
+ def remove_temp_config
51
+ Success(File.delete(TEMP_CONFIG_PATH))
26
52
  end
27
53
  end
28
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubocopDirector
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop_director
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-13 00:00:00.000000000 Z
11
+ date: 2023-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-monads