codeclimate_diff 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a23228123e37e3d3cec3d4ffaf33e22d56389ae6d620034a93670e91a9e2d6b5
4
+ data.tar.gz: 888dc1e3758dc9272b3ac4f163361628cff56c435357d23c9ae23c94e5064ab0
5
+ SHA512:
6
+ metadata.gz: 6e00f89bfe0a1a3e9fce4dc05ad0a58faa0b47afe36a89aa22dfefd7716683e82efda84719eb74f63f25bb97973fff8e6c11bec33fc7d0f36692a593b6042ec6
7
+ data.tar.gz: f748b2e9af9ac14a80e9949b5688f857b4b2f15adc5396ea2eadaf314180152948be6d35245c3c49dc69152f1a44336ca040484b0a894dc0a779ee0ac804357c
data/.codeclimate.yml ADDED
@@ -0,0 +1,16 @@
1
+ ---
2
+ version: "2"
3
+ plugins:
4
+ rubocop:
5
+ enabled: true
6
+ channel: rubocop-1-36-0
7
+ reek:
8
+ enabled: true
9
+
10
+ checks:
11
+ method-complexity:
12
+ config:
13
+ threshold: 10 # defaults to 5. Cognitive complexity rather than cyclomatic complexity
14
+
15
+ exclude_patterns:
16
+ - "**/spec/"
@@ -0,0 +1,4 @@
1
+ main_branch_name: main
2
+ threshold_to_run_on_all_files: 10
3
+ # For ARM based processors, use linux/x86_64
4
+ docker_platform: linux/amd64
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,16 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
14
+
15
+ Style/Documentation:
16
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-11-11
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in codeclimate_diff.gemspec
6
+ gemspec
7
+
8
+ gem "colorize"
9
+ gem "json"
10
+ gem "pry-byebug"
11
+ gem "rake", "~> 13.0"
12
+ gem "rest-client"
13
+ gem "rspec", "~> 3.0"
14
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,95 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ codeclimate_diff (0.1.10)
5
+ colorize
6
+ json
7
+ optparse
8
+ pry-byebug
9
+ rest-client
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ ast (2.4.2)
15
+ byebug (11.1.3)
16
+ coderay (1.1.3)
17
+ colorize (0.8.1)
18
+ diff-lcs (1.5.0)
19
+ domain_name (0.5.20190701)
20
+ unf (>= 0.0.5, < 1.0.0)
21
+ http-accept (1.7.0)
22
+ http-cookie (1.0.5)
23
+ domain_name (~> 0.5)
24
+ json (2.6.2)
25
+ method_source (1.0.0)
26
+ mime-types (3.4.1)
27
+ mime-types-data (~> 3.2015)
28
+ mime-types-data (3.2022.0105)
29
+ netrc (0.11.0)
30
+ optparse (0.3.1)
31
+ parallel (1.22.1)
32
+ parser (3.1.2.1)
33
+ ast (~> 2.4.1)
34
+ pry (0.14.1)
35
+ coderay (~> 1.1)
36
+ method_source (~> 1.0)
37
+ pry-byebug (3.10.1)
38
+ byebug (~> 11.0)
39
+ pry (>= 0.13, < 0.15)
40
+ rainbow (3.1.1)
41
+ rake (13.0.6)
42
+ regexp_parser (2.6.0)
43
+ rest-client (2.1.0)
44
+ http-accept (>= 1.7.0, < 2.0)
45
+ http-cookie (>= 1.0.2, < 2.0)
46
+ mime-types (>= 1.16, < 4.0)
47
+ netrc (~> 0.8)
48
+ rexml (3.2.5)
49
+ rspec (3.11.0)
50
+ rspec-core (~> 3.11.0)
51
+ rspec-expectations (~> 3.11.0)
52
+ rspec-mocks (~> 3.11.0)
53
+ rspec-core (3.11.0)
54
+ rspec-support (~> 3.11.0)
55
+ rspec-expectations (3.11.1)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.11.0)
58
+ rspec-mocks (3.11.1)
59
+ diff-lcs (>= 1.2.0, < 2.0)
60
+ rspec-support (~> 3.11.0)
61
+ rspec-support (3.11.1)
62
+ rubocop (1.36.0)
63
+ json (~> 2.3)
64
+ parallel (~> 1.10)
65
+ parser (>= 3.1.2.1)
66
+ rainbow (>= 2.2.2, < 4.0)
67
+ regexp_parser (>= 1.8, < 3.0)
68
+ rexml (>= 3.2.5, < 4.0)
69
+ rubocop-ast (>= 1.20.1, < 2.0)
70
+ ruby-progressbar (~> 1.7)
71
+ unicode-display_width (>= 1.4.0, < 3.0)
72
+ rubocop-ast (1.22.0)
73
+ parser (>= 3.1.1.0)
74
+ ruby-progressbar (1.11.0)
75
+ unf (0.1.4)
76
+ unf_ext
77
+ unf_ext (0.0.8.2)
78
+ unicode-display_width (2.3.0)
79
+
80
+ PLATFORMS
81
+ ruby
82
+ x86_64-darwin-19
83
+
84
+ DEPENDENCIES
85
+ codeclimate_diff!
86
+ colorize
87
+ json
88
+ pry-byebug
89
+ rake (~> 13.0)
90
+ rest-client
91
+ rspec (~> 3.0)
92
+ rubocop (~> 1.21)
93
+
94
+ BUNDLED WITH
95
+ 2.3.7
data/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # CodeclimateDiff
2
+
3
+ This tool lets you see how changes in your branch will affect the code quality (what issues you've added, fixed, and what issues are outstanding in the files you've touched that could be fixed while you're in the area.)
4
+
5
+ It runs the https://hub.docker.com/r/codeclimate/codeclimate docker image under the hood, which pays attention to all the normal Code Climate configurations.
6
+
7
+
8
+ ## Initial setup
9
+
10
+ 1. Make sure docker is installed and running
11
+
12
+ 2. Add a `.codeclimate.yml` config file eg:
13
+ ```yml
14
+ ---
15
+ version: "2"
16
+ plugins:
17
+ rubocop:
18
+ enabled: true
19
+ channel: rubocop-1-36-0
20
+ reek:
21
+ enabled: true
22
+
23
+ exclude_patterns:
24
+ - config/
25
+ - db/
26
+ - dist/
27
+ - features/
28
+ - public/
29
+ - "**/node_modules/"
30
+ - script/
31
+ - "**/spec/"
32
+ - "**/test/"
33
+ - "**/tests/"
34
+ - Tests/
35
+ - "**/vendor/"
36
+ - "**/*_test.go"
37
+ - "**/*.d.ts"
38
+ - "**/*.min.js"
39
+ - "**/*.min.css"
40
+ - "**/__tests__/"
41
+ - "**/__mocks__/"
42
+ - "/.gitlab/"
43
+ - coverage/ . # simple cov
44
+ ```
45
+
46
+ 3. Add a `.reek.yml` config file eg:
47
+
48
+ See https://github.com/troessner/reek#working-with-rails
49
+ ```yml
50
+ detectors:
51
+ IrresponsibleModule:
52
+ enabled: false
53
+
54
+ LongParameterList:
55
+ max_params: 4 # defaults to 3. You want this number realistic but stretchy so we can move it down
56
+
57
+ TooManyStatements:
58
+ max_statements: 10 # defaults to 5. You want this number realistic but stretchy so we can move it down
59
+
60
+ directories:
61
+ "app/controllers":
62
+ IrresponsibleModule:
63
+ enabled: false
64
+ NestedIterators:
65
+ max_allowed_nesting: 2
66
+ UnusedPrivateMethod:
67
+ enabled: false
68
+ InstanceVariableAssumption:
69
+ enabled: false
70
+ "app/helpers":
71
+ IrresponsibleModule:
72
+ enabled: false
73
+ UtilityFunction:
74
+ enabled: false
75
+ FeatureEnvy:
76
+ enabled: false
77
+ "app/mailers":
78
+ InstanceVariableAssumption:
79
+ enabled: false
80
+ "app/models":
81
+ InstanceVariableAssumption:
82
+ enabled: false
83
+ ```
84
+
85
+ 4. Add a `.codeclimate_diff.yml` configuration file
86
+ ```
87
+ main_branch_name: master # defaults to main
88
+ threshold_to_run_on_all_files: 8 # when you reach a certain number of files changed, it becomes faster to analyze all files rather than analyze them one by one.
89
+ ```
90
+
91
+ 5. Install the gem
92
+
93
+ Add this line to your application's Gemfile:
94
+
95
+ ```ruby
96
+ gem 'codeclimate_diff', github: 'boost/codeclimate_diff'
97
+ ```
98
+
99
+ Install the gem:
100
+
101
+ ```bash
102
+ $ bundle install
103
+ ```
104
+
105
+ Then generate the executable:
106
+
107
+ $ bundle binstubs codeclimate_diff
108
+
109
+
110
+ 6. Run the baseline and commit the result to the repo
111
+
112
+ ```
113
+ ./bin/codeclimate_diff --baseline
114
+ ```
115
+
116
+ ## Usage
117
+
118
+ 1. Create a feature branch for your work, and reset the baseline + commit (5 mins)
119
+
120
+ 2. Do some work
121
+
122
+ 3. Check if you've added any issues (about 10 secs per code file changed on your branch):
123
+
124
+ ```bash
125
+ # runs on each file changed in your branch (about 10 secs per code file changed on your branch)
126
+ ./bin/codeclimate_diff
127
+
128
+ OR
129
+
130
+ # filters the changed files in your branch futher by a grep pattern
131
+ ./bin/codeclimate_diff --pattern places
132
+
133
+ OR
134
+
135
+ # only shows the new and fixed issues
136
+ ./bin/codeclimate_diff --new-only
137
+
138
+ OR
139
+
140
+ # always analyzes all files rather than the changed files one by one, even if below the 'threshold_to_run_on_all_files' setting.
141
+ # NOTE: similar code issues will only work 100% correctly if you use this setting (otherwise it might miss a similarity with a file you didn't change and think you fixed it)
142
+ ./bin/codeclimate_diff --all
143
+ ```
144
+
145
+ 4. Now you have time to fix the issues, horray!
146
+
147
+
148
+ ## Setting it up to download the latest baseline from your CI Pipeline (Gitlab only)
149
+
150
+ Gitlab has a codeclimate template you can add to your pipeline that runs on main builds and then runs on your branch and outputs a difference (see https://docs.gitlab.com/ee/ci/testing/code_quality.html).
151
+
152
+ With a few tweaks to your CI configuration, we can pull down the main build baseline from the job so we don't have to do it locally.
153
+
154
+ 1. In your Gitlab CI Configuration where you include the `Code-Quality.gitlab-ci.yml` template:
155
+
156
+ ```yml
157
+ include:
158
+ - template: Code-Quality.gitlab-ci.yml
159
+
160
+ # add this bit:
161
+ code_quality:
162
+ artifacts:
163
+ paths: [gl-code-quality-report.json] . # without this, the artifact can't be downloaded
164
+ ```
165
+
166
+ 2. Add your project settings to the `.codecimate_diff.yml` configuration file:
167
+ ```yml
168
+ main_branch_name: main
169
+
170
+ # settings to pull down the baseline from the pipeline in Gitlab before checking your branch
171
+ gitlab:
172
+ download_baseline_from_pipeline: true # If false or excluded, you will need to generate the baseline manually
173
+ project_id: '<project id>'
174
+ host: https://gitlab.digitalnz.org/
175
+ baseline_filename: 'gl-code-quality-report.json'
176
+ ```
177
+
178
+ 3. Create a personal access token with `read_api` access and save it in the `CODECLIMATE_DIFF_GITLAB_PERSONAL_ACCESS_TOKEN` env variable
179
+
180
+ Now when you run it on the changed files in your branch, it will download the latest baseline first!
181
+
182
+ ## Development
183
+
184
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
185
+
186
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
187
+
188
+ ## Contributing
189
+
190
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/codeclimate_diff.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/codeclimate_diff/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "codeclimate_diff"
7
+ spec.version = CodeclimateDiff::VERSION
8
+ spec.authors = ["Isabel Anastasiadis"]
9
+ spec.email = ["isabel@boost.co.nz"]
10
+
11
+ spec.summary = "A developer command line tool to see how your branch has affected code quality"
12
+ spec.required_ruby_version = ">= 2.6.0"
13
+
14
+ spec.metadata["source_code_uri"] = "https://github.com/boost/codeclimate_diff"
15
+ spec.metadata["changelog_uri"] = "https://github.com/boost/codeclimate_diff/CHANGELOG.md"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
20
+ `git ls-files -z`.split("\x0").reject do |f|
21
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
22
+ end
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ # Uncomment to register a new dependency of your gem
29
+ spec.add_dependency("colorize")
30
+ spec.add_dependency("json")
31
+ spec.add_dependency("optparse")
32
+ spec.add_dependency("pry-byebug")
33
+ spec.add_dependency("rest-client")
34
+
35
+ # For more information and examples about making a new gem, check out our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ end
@@ -0,0 +1,61 @@
1
+ [{"engine_name":"structure","fingerprint":"f8ccebfc07d1a0c9fa77b6325a61b82c","categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `refresh_baseline_if_configured` has a Cognitive Complexity of 13 (exceeds 10 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":7,"end":39}},"other_locations":[],"remediation_points":450000,"severity":"minor","type":"issue"},
2
+ {"engine_name":"structure","fingerprint":"4890c3e24aa5f22bc928ccddd6a60a14","categories":["Complexity"],"check_name":"method_lines","content":{"body":""},"description":"Method `sort_issues` has 26 lines of code (exceeds 25 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":33,"end":73}},"other_locations":[],"remediation_points":624000,"severity":"minor","type":"issue"},
3
+ {"engine_name":"structure","fingerprint":"5989a7d02618f9eba49266f1c5473861","categories":["Complexity"],"check_name":"argument_count","content":{"body":""},"description":"Method `print_category` has 5 arguments (exceeds 4 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":20,"end":20}},"other_locations":[],"remediation_points":375000,"severity":"minor","type":"issue"},
4
+ {"engine_name":"structure","fingerprint":"79ec8f3ef3737357ed2c43ab0a065ef3","categories":["Complexity"],"check_name":"method_complexity","content":{"body":"# Cognitive Complexity\nCognitive Complexity is a measure of how difficult a unit of code is to intuitively understand. Unlike Cyclomatic Complexity, which determines how difficult your code will be to test, Cognitive Complexity tells you how difficult your code will be to read and comprehend.\n\n### A method's cognitive complexity is based on a few simple rules:\n* Code is not considered more complex when it uses shorthand that the language provides for collapsing multiple statements into one\n* Code is considered more complex for each \"break in the linear flow of the code\"\n* Code is considered more complex when \"flow breaking structures are nested\"\n\n### Further reading\n* [Cognitive Complexity docs](https://docs.codeclimate.com/v1.0/docs/cognitive-complexity)\n* [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf)\n"},"description":"Method `calculate_issues_in_changed_files` has a Cognitive Complexity of 20 (exceeds 10 allowed). Consider refactoring.","location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":39,"end":69}},"other_locations":[],"remediation_points":1150000,"severity":"minor","type":"issue"},
5
+ {"name":"ruby.parse.succeeded","type":"measurement","value":7,"engine_name":"structure"},
6
+ {"engine_name":"reek","fingerprint":"054589dd54a8aef188941b688d099978","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::CodeclimateWrapper has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/codeclimate_wrapper.rb","lines":{"begin":7,"end":7}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
7
+ {"engine_name":"reek","fingerprint":"0ab3c6e125bef6686b3c821c59933e08","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'CodeclimateDiff.configuration' 6 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":8,"end":22}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
8
+ {"engine_name":"reek","fingerprint":"585621642e3917aabb584a8dee9dc5b9","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'CodeclimateDiff.configuration[\"gitlab\"]' 5 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":8,"end":22}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
9
+ {"engine_name":"reek","fingerprint":"a90c8153eeb16896f80233d15786037a","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'puts \"Using current baseline.\"' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":33,"end":38}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
10
+ {"engine_name":"reek","fingerprint":"4dade4b82ae3a3c900dd5cbd4baa48c0","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'response.body' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":29,"end":32}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
11
+ {"engine_name":"reek","fingerprint":"eb7c72ed624bdafbdbfee6ceed64db54","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured calls 'response.code' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":28,"end":32}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
12
+ {"engine_name":"reek","fingerprint":"f54441e8533c9bf76e64b72596931f02","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::Downloader has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":6,"end":6}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
13
+ {"engine_name":"reek","fingerprint":"d2ddd39e281760ccf0b0918fe08e7e7c","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured has approx 19 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":7,"end":7}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
14
+ {"engine_name":"reek","fingerprint":"211287e4b5857d557ac53a56826d6fa0","type":"issue","check_name":"UncommunicativeVariableName","description":"CodeclimateDiff::Downloader#self.refresh_baseline_if_configured has the variable name 'e'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/downloader.rb","lines":{"begin":36,"end":36}},"remediation_points":150000,"content":{"body":"An `Uncommunicative Variable Name` is a variable name that doesn't communicate its intent well enough.\n\nPoor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.\n"},"severity":"minor"},
15
+ {"engine_name":"reek","fingerprint":"6387eea8958bb1f9b03eb1a4d1a7d3d7","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"description\"] == issue_to_match[\"description\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":10,"end":21}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
16
+ {"engine_name":"reek","fingerprint":"26e97835cc4b485f5758c3fcf6c9aaea","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"description\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":10,"end":21}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
17
+ {"engine_name":"reek","fingerprint":"22d5da8bce52124b5a9c808004f82019","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"fingerprint\"] == issue_to_match[\"fingerprint\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":8,"end":20}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
18
+ {"engine_name":"reek","fingerprint":"cc510276d86e62d3aaf8388d5bfa2b19","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue[\"fingerprint\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":8,"end":20}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
19
+ {"engine_name":"reek","fingerprint":"0b23ee28cb8bdc33b6f2f06a482b5267","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue_to_match[\"description\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":10,"end":21}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
20
+ {"engine_name":"reek","fingerprint":"30a514924733303a35f7ae8c8de71a95","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'issue_to_match[\"fingerprint\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":8,"end":20}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
21
+ {"engine_name":"reek","fingerprint":"e1a894e2e680a8948d6a5bee61911741","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'list.delete_at(index)' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":14,"end":25}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
22
+ {"engine_name":"reek","fingerprint":"2fefd889dbaf81b11b2bea400c8d6deb","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list calls 'list.index' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":7,"end":19}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
23
+ {"engine_name":"reek","fingerprint":"ca6ea3faac869baf424f72669048c899","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'baseline_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":50,"end":53}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
24
+ {"engine_name":"reek","fingerprint":"fe0d4f54334ad664a42b1018ae44a8ca","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'current_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":50,"end":53}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
25
+ {"engine_name":"reek","fingerprint":"26083f936211b5167cc5f6809b9c9ade","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'issue[\"fingerprint\"] == fingerprint' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":47,"end":48}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
26
+ {"engine_name":"reek","fingerprint":"e879665bca8ca2a6bc3f789248ff8b39","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::IssueSorter#self.sort_issues calls 'issue[\"fingerprint\"]' 3 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":44,"end":48}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
27
+ {"engine_name":"reek","fingerprint":"d90684590df5d9e0fe73c8b42d49fb87","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::IssueSorter has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":4,"end":4}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
28
+ {"engine_name":"reek","fingerprint":"980375b6536d455e110ca4c16fb8385a","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::IssueSorter#self.sort_issues contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":47,"end":61}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
29
+ {"engine_name":"reek","fingerprint":"c43a4fd2dc6f9f2900cf0b0a0702294e","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::IssueSorter#self.remove_closest_match_from_list has approx 9 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":5,"end":5}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
30
+ {"engine_name":"reek","fingerprint":"c8711ff1c512044d0e938787698e5f45","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::IssueSorter#self.sort_issues has approx 22 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/issue_sorter.rb","lines":{"begin":33,"end":33}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
31
+ {"engine_name":"reek","fingerprint":"7b8cb5858d38666042143f4f0ad0e173","type":"issue","check_name":"ControlParameter","description":"CodeclimateDiff::ResultPrinter#self.print_category is controlled by argument 'color'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":23,"end":23}},"remediation_points":500000,"content":{"body":"`Control Parameter` is a special case of `Control Couple`\n\n## Example\n\nA simple example would be the \"quoted\" parameter in the following method:\n\n```Ruby\ndef write(quoted)\n if quoted\n write_quoted @value\n else\n write_unquoted @value\n end\nend\n```\n\nFixing those problems is out of the scope of this document but an easy solution could be to remove the \"write\" method alltogether and to move the calls to \"write_quoted\" / \"write_unquoted\" in the initial caller of \"write\".\n"},"severity":"minor"},
32
+ {"engine_name":"reek","fingerprint":"83771ef70121c9192872a09a66ee7b0c","type":"issue","check_name":"ControlParameter","description":"CodeclimateDiff::ResultPrinter#self.print_result is controlled by argument 'show_preexisting'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":52,"end":52}},"remediation_points":500000,"content":{"body":"`Control Parameter` is a special case of `Control Couple`\n\n## Example\n\nA simple example would be the \"quoted\" parameter in the following method:\n\n```Ruby\ndef write(quoted)\n if quoted\n write_quoted @value\n else\n write_unquoted @value\n end\nend\n```\n\nFixing those problems is out of the scope of this document but an easy solution could be to remove the \"write\" method alltogether and to move the calls to \"write_quoted\" / \"write_unquoted\" in the initial caller of \"write\".\n"},"severity":"minor"},
33
+ {"engine_name":"reek","fingerprint":"f188df8774fe06f10f92ea3ced203a90","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues calls 'issue[\"check_name\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":36,"end":43}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
34
+ {"engine_name":"reek","fingerprint":"da9c16763266a2ee7c4acd27b473f431","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues calls 'issue[\"engine_name\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":36,"end":42}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
35
+ {"engine_name":"reek","fingerprint":"e9462c1d7525075446ba64f4bf4ddc2b","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues calls 'issue[\"severity\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":36,"end":44}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
36
+ {"engine_name":"reek","fingerprint":"a6d4b886f1137834a7895847fda2a405","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_issues_in_category calls 'issue[\"location\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":10,"end":11}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
37
+ {"engine_name":"reek","fingerprint":"c426eed4fc2a91238bf2a338dc0ac631","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_result calls 'fixed_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":71,"end":72}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
38
+ {"engine_name":"reek","fingerprint":"9f6643562758be7d65fc46249ba74212","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_result calls 'new_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":63,"end":64}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
39
+ {"engine_name":"reek","fingerprint":"66c9f93f83039d5ff8b90e5f5f57156a","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::ResultPrinter#self.print_result calls 'preexisting_issues.count' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":54,"end":55}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
40
+ {"engine_name":"reek","fingerprint":"524d4bedfc2cef6b38a753be561516ef","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::ResultPrinter has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":7,"end":7}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
41
+ {"engine_name":"reek","fingerprint":"b96b36b5b941ac84266107f82a75ecb6","type":"issue","check_name":"LongParameterList","description":"CodeclimateDiff::ResultPrinter#self.print_category has 5 parameters","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":20,"end":20}},"remediation_points":500000,"content":{"body":"A `Long Parameter List` occurs when a method has a lot of parameters.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n def long_list(foo,bar,baz,fling,flung)\n puts foo,bar,baz,fling,flung\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [2]:Dummy#long_list has 5 parameters (LongParameterList)\n```\n\nA common solution to this problem would be the introduction of parameter objects.\n"},"severity":"minor"},
42
+ {"engine_name":"reek","fingerprint":"24256875bc239c7ee72e7bf5162a5cf1","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::ResultPrinter#self.print_issues contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":41,"end":41}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
43
+ {"engine_name":"reek","fingerprint":"dba788bce2d4037f442a07834141f748","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_call_to_action has approx 7 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":79,"end":79}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
44
+ {"engine_name":"reek","fingerprint":"847c6524aee19aaaa5c9f75d2af55034","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_issues has approx 10 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":35,"end":35}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
45
+ {"engine_name":"reek","fingerprint":"0e1b58c41f946011380b520b782497f6","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_issues_in_category has approx 7 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":8,"end":8}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
46
+ {"engine_name":"reek","fingerprint":"659011304f57ba6821ee6ecd179e2fe3","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::ResultPrinter#self.print_result has approx 12 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/result_printer.rb","lines":{"begin":51,"end":51}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
47
+ {"engine_name":"reek","fingerprint":"eebfb3495de5ce6c32cdbdedd3733169","type":"issue","check_name":"BooleanParameter","description":"CodeclimateDiff::Runner#self.run_diff_on_branch has boolean parameter 'always_analyze_all_files'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":89,"end":89}},"remediation_points":500000,"content":{"body":"`Boolean Parameter` is a special case of `Control Couple`, where a method parameter is defaulted to true or false. A _Boolean Parameter_ effectively permits a method's caller to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n def hit_the_switch(switch = true)\n if switch\n puts 'Hitting the switch'\n # do other things...\n else\n puts 'Not hitting the switch'\n # do other things...\n end\n end\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 3 warnings:\n [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)\n [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)\n```\n\nNote that both smells are reported, `Boolean Parameter` and `Control Parameter`.\n\n## Getting rid of the smell\n\nThis is highly dependent on your exact architecture, but looking at the example above what you could do is:\n\n* Move everything in the `if` branch into a separate method\n* Move everything in the `else` branch into a separate method\n* Get rid of the `hit_the_switch` method alltogether\n* Make the decision what method to call in the initial caller of `hit_the_switch`\n"},"severity":"minor"},
48
+ {"engine_name":"reek","fingerprint":"f69f9414e9b677df0fe0fa88a56e09e6","type":"issue","check_name":"BooleanParameter","description":"CodeclimateDiff::Runner#self.run_diff_on_branch has boolean parameter 'show_preexisting'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":89,"end":89}},"remediation_points":500000,"content":{"body":"`Boolean Parameter` is a special case of `Control Couple`, where a method parameter is defaulted to true or false. A _Boolean Parameter_ effectively permits a method's caller to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n def hit_the_switch(switch = true)\n if switch\n puts 'Hitting the switch'\n # do other things...\n else\n puts 'Not hitting the switch'\n # do other things...\n end\n end\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 3 warnings:\n [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)\n [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)\n```\n\nNote that both smells are reported, `Boolean Parameter` and `Control Parameter`.\n\n## Getting rid of the smell\n\nThis is highly dependent on your exact architecture, but looking at the example above what you could do is:\n\n* Move everything in the `if` branch into a separate method\n* Move everything in the `else` branch into a separate method\n* Get rid of the `hit_the_switch` method alltogether\n* Make the decision what method to call in the initial caller of `hit_the_switch`\n"},"severity":"minor"},
49
+ {"engine_name":"reek","fingerprint":"c9d1604f71aa792148908051a6c30a03","type":"issue","check_name":"ControlParameter","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files is controlled by argument 'always_analyze_all_files'","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":43,"end":45}},"remediation_points":500000,"content":{"body":"`Control Parameter` is a special case of `Control Couple`\n\n## Example\n\nA simple example would be the \"quoted\" parameter in the following method:\n\n```Ruby\ndef write(quoted)\n if quoted\n write_quoted @value\n else\n write_unquoted @value\n end\nend\n```\n\nFixing those problems is out of the scope of this document but an easy solution could be to remove the \"write\" method alltogether and to move the calls to \"write_quoted\" / \"write_unquoted\" in the initial caller of \"write\".\n"},"severity":"minor"},
50
+ {"engine_name":"reek","fingerprint":"3ff6caf742702e28c65517be34dc4a90","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'JSON.parse(result)' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":49,"end":60}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
51
+ {"engine_name":"reek","fingerprint":"2ace891a3d054cd1d6b178902b0aa4b9","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'JSON.parse(result).each' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":49,"end":60}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
52
+ {"engine_name":"reek","fingerprint":"ec3e2cc4884fd40b46e5fcd406caed9d","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'changed_file_issues.append(issue)' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":53,"end":63}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
53
+ {"engine_name":"reek","fingerprint":"974a71566e027ab872e3496e1782f85a","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'issue[\"type\"] != \"issue\"' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":50,"end":61}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
54
+ {"engine_name":"reek","fingerprint":"ececcf3944ab70f551f1c57fc5f8989f","type":"issue","check_name":"DuplicateMethodCall","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files calls 'issue[\"type\"]' 2 times","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":50,"end":61}},"remediation_points":350000,"content":{"body":"Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.\n\nReek implements a check for _Duplicate Method Call_.\n\n## Example\n\nHere's a very much simplified and contrived example. The following method will report a warning:\n\n```Ruby\ndef double_thing()\n @other.thing + @other.thing\nend\n```\n\nOne quick approach to silence Reek would be to refactor the code thus:\n\n```Ruby\ndef double_thing()\n thing = @other.thing\n thing + thing\nend\n```\n\nA slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:\n\n```Ruby\nclass Other\n def double_thing()\n thing + thing\n end\nend\n```\n\nThe approach you take will depend on balancing other factors in your code.\n"},"severity":"minor"},
55
+ {"engine_name":"reek","fingerprint":"cd318842f19b32d930607d78221cf5c5","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff::Runner has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":12,"end":12}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"},
56
+ {"engine_name":"reek","fingerprint":"eaaa4ef4913d7d7dedf4596bf06edb8b","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::Runner#self.calculate_changed_filenames contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":33,"end":33}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
57
+ {"engine_name":"reek","fingerprint":"e5fe8233608d51d516fe9c9662f3bce0","type":"issue","check_name":"NestedIterators","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files contains iterators nested 2 deep","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":60,"end":60}},"remediation_points":500000,"content":{"body":"A `Nested Iterator` occurs when a block contains another block.\n\n## Example\n\nGiven\n\n```Ruby\nclass Duck\n class \u003c\u003c self\n def duck_names\n %i!tick trick track!.each do |surname|\n %i!duck!.each do |last_name|\n puts \"full name is #{surname} #{last_name}\"\n end\n end\n end\n end\nend\n```\n\nReek would report the following warning:\n\n```\ntest.rb -- 1 warning:\n [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)\n```\n"},"severity":"minor"},
58
+ {"engine_name":"reek","fingerprint":"545f4e2fbe74902028bcf6c047db975b","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Runner#self.calculate_changed_filenames has approx 16 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":13,"end":13}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
59
+ {"engine_name":"reek","fingerprint":"911d8c2cd9713eb42fe09220efd48ca7","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Runner#self.calculate_issues_in_changed_files has approx 18 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":39,"end":39}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
60
+ {"engine_name":"reek","fingerprint":"b3db1a66f15bef1e6a1a5d9bbb519f26","type":"issue","check_name":"TooManyStatements","description":"CodeclimateDiff::Runner#self.run_diff_on_branch has approx 7 statements","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff/runner.rb","lines":{"begin":89,"end":89}},"remediation_points":500000,"content":{"body":"A method with `Too Many Statements` is any method that has a large number of lines.\n\n`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.\n\nSo the following method would score +6 in Reek's statement-counting algorithm:\n\n```Ruby\ndef parse(arg, argv, \u0026error)\n if !(val = arg) and (argv.empty? or /\\A-/ =~ (val = argv[0]))\n return nil, block, nil # +1\n end\n opt = (val = parse_arg(val, \u0026error))[1] # +2\n val = conv_arg(*val) # +3\n if opt and !arg\n argv.shift # +4\n else\n val[0] = nil # +5\n end\n val # +6\nend\n```\n\n(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)\n"},"severity":"minor"},
61
+ {"engine_name":"reek","fingerprint":"91c4af205f1f773f532585f9dd580548","type":"issue","check_name":"IrresponsibleModule","description":"CodeclimateDiff has no descriptive comment","categories":["Complexity"],"location":{"path":"lib/codeclimate_diff.rb","lines":{"begin":6,"end":6}},"remediation_points":350000,"content":{"body":"Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.\n\n## Example\n\nGiven\n\n```Ruby\nclass Dummy\n # Do things...\nend\n```\n\nReek would emit the following warning:\n\n```\ntest.rb -- 1 warning:\n [1]:Dummy has no descriptive comment (IrresponsibleModule)\n```\n\nFixing this is simple - just an explaining comment:\n\n```Ruby\n# The Dummy class is responsible for ...\nclass Dummy\n # Do things...\nend\n```\n"},"severity":"minor"}]
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift("#{__dir__}/../lib")
5
+
6
+ require "optparse"
7
+ require "codeclimate_diff/runner"
8
+ require "codeclimate_diff"
9
+
10
+ options = {}
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: codeclimate_diff [options]"
13
+
14
+ opts.on("-b", "--baseline", "Means it will regenerate the baseline.")
15
+
16
+ opts.on("-n", "--new-only",
17
+ "It will only show what you have changed and not existing issues in files you have touched.")
18
+
19
+ opts.on("-a", "--all",
20
+ "It will always analyze all files, and not the changed files one by one, even if below the 'threshold_to_run_on_all_files' setting.")
21
+
22
+ opts.on("-pPATTERN", "--pattern=PATTERN",
23
+ "Grep pattern to filter files. If provided, will filter the files changed on your branch further.")
24
+ end.parse!(into: options)
25
+
26
+ if options[:baseline]
27
+ CodeclimateDiff::Runner.generate_baseline
28
+ elsif options[:"new-only"]
29
+ CodeclimateDiff::Runner.run_diff_on_branch(options[:pattern], always_analyze_all_files: options[:all], show_preexisting: false)
30
+ else
31
+ CodeclimateDiff::Runner.run_diff_on_branch(options[:pattern], always_analyze_all_files: options[:all], show_preexisting: true)
32
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "colorize"
5
+
6
+ module CodeclimateDiff
7
+ class CodeclimateWrapper
8
+
9
+ def run_codeclimate(filename = "")
10
+ docker_platform = CodeclimateDiff.configuration["docker_platform"] || "linux/amd64"
11
+
12
+ `docker run \
13
+ --interactive --tty --rm \
14
+ --env CODECLIMATE_CODE="$PWD" \
15
+ --volume "$PWD":/code \
16
+ --volume /var/run/docker.sock:/var/run/docker.sock \
17
+ --volume /tmp/cc:/tmp/cc \
18
+ --platform #{docker_platform} \
19
+ codeclimate/codeclimate analyze -f json #{filename}`
20
+ end
21
+
22
+ def pull_latest_image
23
+ puts "Downloading latest codeclimate docker image..."
24
+ `docker pull codeclimate/codeclimate`
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rest-client"
4
+
5
+ module CodeclimateDiff
6
+ class Downloader
7
+ def self.refresh_baseline_if_configured
8
+ return unless CodeclimateDiff.configuration["gitlab"]
9
+ return unless CodeclimateDiff.configuration["gitlab"]["download_baseline_from_pipeline"]
10
+
11
+ personal_access_token = ENV.fetch("CODECLIMATE_DIFF_GITLAB_PERSONAL_ACCESS_TOKEN")
12
+
13
+ if !personal_access_token
14
+ puts "Missing environment variable 'CODECLIMATE_DIFF_GITLAB_PERSONAL_ACCESS_TOKEN'. Using current baseline."
15
+ return
16
+ end
17
+
18
+ puts "Downloading baseline file from gitlab..."
19
+ branch_name = CodeclimateDiff.configuration["main_branch_name"] || "main"
20
+ project_id = CodeclimateDiff.configuration["gitlab"]["project_id"]
21
+ host = CodeclimateDiff.configuration["gitlab"]["host"]
22
+ baseline_filename = CodeclimateDiff.configuration["gitlab"]["baseline_filename"]
23
+
24
+ # curl --output codeclimate_diff_baseline.json --header "PRIVATE-TOKEN: MYTOKEN" "https://gitlab.digitalnz.org/api/v4/projects/85/jobs/artifacts/main/raw/codeclimate_diff_baseline.json?job=code_quality"
25
+ url = "#{host}/api/v4/projects/#{project_id}/jobs/artifacts/#{branch_name}/raw/#{baseline_filename}?job=code_quality"
26
+ response = RestClient.get(url, { "PRIVATE-TOKEN": personal_access_token })
27
+
28
+ if response.code < 300
29
+ File.write("codeclimate_diff_baseline.json", response.body)
30
+ puts "Successfully updated the baseline."
31
+ else
32
+ puts "Downloading baseline file failed with status code #{response.code}: #{response.body}"
33
+ puts "Using current baseline."
34
+ end
35
+
36
+ rescue StandardError => e
37
+ puts e
38
+ puts "Using current baseline."
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CodeclimateDiff
4
+ class IssueSorter
5
+ def self.remove_closest_match_from_list(issue_to_match, list)
6
+ # check for exact match first
7
+ index = list.index do |issue|
8
+ issue["fingerprint"] == issue_to_match["fingerprint"] &&
9
+ issue["location"]["lines"]["begin"] == issue_to_match["location"]["lines"]["begin"] &&
10
+ issue["description"] == issue_to_match["description"]
11
+ end
12
+
13
+ if index
14
+ list.delete_at(index)
15
+ return
16
+ end
17
+
18
+ # check for same method name (description often has method name or variable name in it)
19
+ index = list.index do |issue|
20
+ issue["fingerprint"] == issue_to_match["fingerprint"] &&
21
+ issue["description"] == issue_to_match["description"]
22
+ end
23
+
24
+ if index
25
+ list.delete_at(index)
26
+ return
27
+ end
28
+
29
+ # otherwise just remove the first one
30
+ list.pop
31
+ end
32
+
33
+ def self.sort_issues(preexisting_issues, changed_file_issues)
34
+ puts "Sorting into :preexisting, :new and :fixed lists..."
35
+
36
+ result = {}
37
+ result[:preexisting] = []
38
+ result[:new] = []
39
+ result[:fixed] = []
40
+
41
+ # fingerprints are unique per issue type and file
42
+ # so there could be multiple if the same issue shows up multiple times
43
+ # plus line numbers and method names could have changed
44
+ unique_fingerprints = (preexisting_issues + changed_file_issues).map { |issue| issue["fingerprint"] }.uniq
45
+
46
+ unique_fingerprints.each do |fingerprint|
47
+ baseline_issues = preexisting_issues.filter { |issue| issue["fingerprint"] == fingerprint }
48
+ current_issues = changed_file_issues.filter { |issue| issue["fingerprint"] == fingerprint }
49
+
50
+ if baseline_issues.count == current_issues.count
51
+ # current issues are most up to date (line numbers could have changed etc.)
52
+ result[:preexisting] += current_issues
53
+ elsif current_issues.count < baseline_issues.count
54
+ # less issues than there were before
55
+ current_issues.each do |issue_to_match|
56
+ CodeclimateDiff.remove_closest_match_from_list(issue_to_match, baseline_issues)
57
+ end
58
+ result[:fixed] += baseline_issues
59
+ else
60
+ # more issues than there were before
61
+ baseline_issues.each do |issue_to_match|
62
+ CodeclimateDiff.remove_closest_match_from_list(issue_to_match, current_issues)
63
+ end
64
+ result[:new] += current_issues
65
+ end
66
+ end
67
+
68
+ # do a check to make sure the maths works out
69
+ puts "#{preexisting_issues.count} issues in matching files in baseline"
70
+ puts "#{changed_file_issues.count} current issues in matching files"
71
+
72
+ result
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "colorize"
5
+
6
+ module CodeclimateDiff
7
+ class ResultPrinter
8
+ def self.print_issues_in_category(issues_list)
9
+ issues_list.each do |issue|
10
+ filename = issue["location"]["path"]
11
+ line_number = issue["location"]["lines"]["begin"]
12
+ description = issue["description"]
13
+
14
+ print "\u2022 #{filename}:#{line_number}".encode("utf-8").bold
15
+ puts " #{description}"
16
+ end
17
+ puts "\n"
18
+ end
19
+
20
+ def self.print_category(bullet_emoji, severity, engine_name, check_name, color)
21
+ message = "#{bullet_emoji} [#{severity}] #{engine_name} #{check_name}:".encode("utf-8")
22
+
23
+ case color
24
+ when "red"
25
+ puts message.red
26
+ when "yellow"
27
+ puts message.yellow
28
+ when "green"
29
+ puts message.green
30
+ else
31
+ puts message
32
+ end
33
+ end
34
+
35
+ def self.print_issues(issues_list, color, bullet_emoji)
36
+ issue_categories = issues_list.map { |issue| [issue["engine_name"], issue["check_name"], issue["severity"]] }.uniq
37
+ issue_categories.each do |issue_category|
38
+ engine_name = issue_category[0]
39
+ check_name = issue_category[1]
40
+ severity = issue_category[2]
41
+ issues = issues_list.filter do |issue|
42
+ issue["engine_name"] == engine_name &&
43
+ issue["check_name"] == check_name &&
44
+ issue["severity"] == severity
45
+ end
46
+ print_category(bullet_emoji, severity, engine_name, check_name, color)
47
+ print_issues_in_category(issues)
48
+ end
49
+ end
50
+
51
+ def self.print_result(sorted_issues, show_preexisting)
52
+ if show_preexisting
53
+ preexisting_issues = sorted_issues[:preexisting]
54
+ if preexisting_issues.count.positive?
55
+ puts "\n#{preexisting_issues.count} preexisting issues in changed files:\n".bold.yellow
56
+ print_issues(preexisting_issues, "yellow", "\u2718")
57
+ else
58
+ puts "\n0 issues in changed files!".encode("utf-8").bold.green
59
+ end
60
+ end
61
+
62
+ new_issues = sorted_issues[:new]
63
+ if new_issues.count.positive?
64
+ puts "\n#{new_issues.count} new issues:\n".bold.red
65
+ print_issues(new_issues, "red", "\u2718")
66
+ else
67
+ puts "\n0 new issues :)\n".encode("utf-8").bold
68
+ end
69
+
70
+ fixed_issues = sorted_issues[:fixed]
71
+ if fixed_issues.count.positive?
72
+ puts "\n#{fixed_issues.count} fixed issues: \n".encode("utf-8").bold.green
73
+ print_issues(fixed_issues, "green", "\u2714")
74
+ else
75
+ puts "\n0 fixed issues\n".bold
76
+ end
77
+ end
78
+
79
+ def self.print_call_to_action(sorted_issues)
80
+ fixed_count = sorted_issues[:fixed].count
81
+ new_count = sorted_issues[:new].count
82
+ outstanding_count = sorted_issues[:preexisting].count + new_count
83
+ if fixed_count > new_count
84
+ puts "\n\u{1F389}\u{1F389} Well done! You made the code even better!! \u{1F389}\u{1F389} \n".bold.green.encode("utf-8")
85
+ elsif new_count > fixed_count
86
+ puts "\n\ Uh oh, you've introduced more issues than you've fixed. Better fix that! \n".bold.red.encode("utf-8")
87
+ elsif outstanding_count.positive?
88
+ puts "\n\ Why don't you see if you can fix some of those outstanding issues while you're here? \n".bold.encode("utf-8")
89
+ else
90
+ puts "\n\u{1F389}\u{1F389} Nothing to do here, the code is immaculate!! \u{1F389}\u{1F389} \n".bold.green.encode("utf-8")
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "colorize"
5
+ require "pry-byebug"
6
+ require_relative "./codeclimate_wrapper"
7
+ require_relative "./result_printer"
8
+ require_relative "./issue_sorter"
9
+ require_relative "./downloader"
10
+
11
+ module CodeclimateDiff
12
+ class Runner
13
+ def self.calculate_changed_filenames(pattern)
14
+ extra_grep_filter = pattern ? " | grep '#{pattern}'" : ""
15
+ branch_name = CodeclimateDiff.configuration["main_branch_name"] || "main"
16
+ all_files_changed_str = `git diff --name-only #{branch_name} | grep --extended-regexp '.js$|.rb$'#{extra_grep_filter}`
17
+ all_files_changed = all_files_changed_str.split("\n")
18
+ .filter { |filename| File.exist?(filename) }
19
+
20
+ # load the exclude patterns list from .codeclimate.yml
21
+ exclude_patterns = []
22
+ if File.exist?(".codeclimate.yml")
23
+ config = YAML.load_file(".codeclimate.yml")
24
+ exclude_patterns = config["exclude_patterns"]
25
+ end
26
+
27
+ files_and_directories_excluded = exclude_patterns.map { |exclude_pattern| Dir.glob(exclude_pattern) }.flatten
28
+
29
+ # filter out any files that match the excluded ones
30
+ all_files_changed.filter do |filename|
31
+ next if files_and_directories_excluded.include? filename
32
+
33
+ next if files_and_directories_excluded.any? { |excluded_filename| filename.start_with?(excluded_filename) }
34
+
35
+ true
36
+ end
37
+ end
38
+
39
+ def self.calculate_issues_in_changed_files(changed_filenames, always_analyze_all_files)
40
+ changed_file_issues = []
41
+
42
+ threshold_to_run_on_all_files = CodeclimateDiff.configuration["threshold_to_run_on_all_files"] || 8
43
+ analyze_all_files = always_analyze_all_files || changed_filenames.count > threshold_to_run_on_all_files
44
+ if analyze_all_files
45
+ message = always_analyze_all_files ? "Analyzing all files..." : "The number of changed files is greater than the threshold '#{threshold_to_run_on_all_files}', so analyzing all files..."
46
+ puts message
47
+
48
+ result = CodeclimateWrapper.new.run_codeclimate
49
+ JSON.parse(result).each do |issue|
50
+ next if issue["type"] != "issue"
51
+ next unless changed_filenames.include? issue["location"]["path"]
52
+
53
+ changed_file_issues.append(issue)
54
+ end
55
+
56
+ else
57
+ changed_filenames.each do |filename|
58
+ puts "Analysing '#{filename}'..."
59
+ result = CodeclimateWrapper.new.run_codeclimate(filename)
60
+ JSON.parse(result).each do |issue|
61
+ next if issue["type"] != "issue"
62
+
63
+ changed_file_issues.append(issue)
64
+ end
65
+ end
66
+ end
67
+
68
+ changed_file_issues
69
+ end
70
+
71
+ def self.calculate_preexisting_issues_in_changed_files(changed_filenames)
72
+ Downloader.refresh_baseline_if_configured
73
+
74
+ puts "Extracting relevant preexisting issues..."
75
+ all_issues = JSON.parse(File.read("./codeclimate_diff_baseline.json"))
76
+
77
+ all_issues.filter { |issue| issue.key?("location") && changed_filenames.include?(issue["location"]["path"]) }
78
+ end
79
+
80
+ def self.generate_baseline
81
+ CodeclimateWrapper.new.pull_latest_image
82
+
83
+ puts "Generating the baseline. Should take about 5 minutes..."
84
+ result = CodeclimateWrapper.new.run_codeclimate
85
+ File.write("codeclimate_diff_baseline.json", result)
86
+ puts "Done!"
87
+ end
88
+
89
+ def self.run_diff_on_branch(pattern, always_analyze_all_files: false, show_preexisting: true)
90
+ CodeclimateWrapper.new.pull_latest_image
91
+
92
+ changed_filenames = calculate_changed_filenames(pattern)
93
+
94
+ changed_file_issues = calculate_issues_in_changed_files(changed_filenames, always_analyze_all_files)
95
+
96
+ preexisting_issues = calculate_preexisting_issues_in_changed_files(changed_filenames)
97
+
98
+ sorted_issues = IssueSorter.sort_issues(preexisting_issues, changed_file_issues)
99
+
100
+ ResultPrinter.print_result(sorted_issues, show_preexisting)
101
+ ResultPrinter.print_call_to_action(sorted_issues)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CodeclimateDiff
4
+ VERSION = "0.1.10"
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "codeclimate_diff/version"
4
+ require "yaml"
5
+
6
+ module CodeclimateDiff
7
+ class << self
8
+ def configuration
9
+ YAML.load_file("./.codeclimate_diff.yml")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ module CodeclimateDiff
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: codeclimate_diff
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.10
5
+ platform: ruby
6
+ authors:
7
+ - Isabel Anastasiadis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-07-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: optparse
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rest-client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - isabel@boost.co.nz
86
+ executables:
87
+ - codeclimate_diff
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".codeclimate.yml"
92
+ - ".codeclimate_diff.yml"
93
+ - ".rspec"
94
+ - ".rubocop.yml"
95
+ - CHANGELOG.md
96
+ - Gemfile
97
+ - Gemfile.lock
98
+ - README.md
99
+ - Rakefile
100
+ - codeclimate_diff.gemspec
101
+ - codeclimate_diff_baseline.json
102
+ - exe/codeclimate_diff
103
+ - lib/codeclimate_diff.rb
104
+ - lib/codeclimate_diff/codeclimate_wrapper.rb
105
+ - lib/codeclimate_diff/downloader.rb
106
+ - lib/codeclimate_diff/issue_sorter.rb
107
+ - lib/codeclimate_diff/result_printer.rb
108
+ - lib/codeclimate_diff/runner.rb
109
+ - lib/codeclimate_diff/version.rb
110
+ - sig/codeclimate_diff.rbs
111
+ homepage:
112
+ licenses: []
113
+ metadata:
114
+ source_code_uri: https://github.com/boost/codeclimate_diff
115
+ changelog_uri: https://github.com/boost/codeclimate_diff/CHANGELOG.md
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 2.6.0
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubygems_version: 3.2.32
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: A developer command line tool to see how your branch has affected code quality
135
+ test_files: []