rubocop_director 0.1.0 โ†’ 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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