turbulence 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +51 -0
- data/CHANGELOG.md +208 -0
- data/Gemfile.lock +47 -24
- data/LICENSE.txt +7 -0
- data/README.md +100 -31
- data/bin/bule +1 -1
- data/docs/scatter-plot.png +0 -0
- data/docs/treemap.png +0 -0
- data/lib/turbulence/calculators/churn.rb +46 -34
- data/lib/turbulence/calculators/complexity.rb +19 -27
- data/lib/turbulence/checks_environment.rb +2 -1
- data/lib/turbulence/cli_parser.rb +53 -0
- data/lib/turbulence/command_line_interface.rb +30 -48
- data/lib/turbulence/configuration.rb +30 -0
- data/lib/turbulence/generators/scatterplot.rb +2 -2
- data/lib/turbulence/generators/treemap.rb +2 -2
- data/lib/turbulence/scm/git.rb +1 -1
- data/lib/turbulence/version.rb +1 -1
- data/lib/turbulence.rb +39 -20
- data/spec/turbulence/calculators/churn_spec.rb +53 -63
- data/spec/turbulence/calculators/complexity_spec.rb +5 -6
- data/spec/turbulence/cli_parser_spec.rb +50 -0
- data/spec/turbulence/command_line_interface_spec.rb +34 -19
- data/spec/turbulence/configuration_spec.rb +19 -0
- data/spec/turbulence/generators/scatter_plot_spec.rb +49 -44
- data/spec/turbulence/generators/treemap_spec.rb +7 -7
- data/spec/turbulence/scm/git_spec.rb +7 -6
- data/spec/turbulence/scm/perforce_spec.rb +44 -40
- data/spec/turbulence/turbulence_spec.rb +15 -7
- data/turbulence.gemspec +13 -9
- metadata +65 -32
- data/.travis.yml +0 -6
- data/spec/turbulence/checks_environoment_spec.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a2023fddd852d412e27f0a4daab0755c0e30bc611ac27656f9a2c2a0a8b20ace
|
|
4
|
+
data.tar.gz: 94460572613993d657687a63a62a367c8c3365aafde2cf6e702cef55d20b4cd1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a12aacc43132911890a7b4af5129c09bc89cf95399e1f21387da40207be20efbb97a8234d479a884792fcbacf873049be4188d4593fcb57d607f9c0c164cd3ce
|
|
7
|
+
data.tar.gz: 6d29af820ff2e2bcc533b69e78453c856956c3e83f2448dee95a55ea0bd2608015ec2f97f458735a06552b41af9acb666bc563cdf9f2d439d2970ef91e9b702b
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
ruby-version:
|
|
17
|
+
- '3.0'
|
|
18
|
+
- '3.1'
|
|
19
|
+
- '3.2'
|
|
20
|
+
- '3.3'
|
|
21
|
+
- 'head'
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Remove Gemfile.lock for fresh resolution
|
|
27
|
+
run: rm -f Gemfile.lock
|
|
28
|
+
|
|
29
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
30
|
+
uses: ruby/setup-ruby@v1
|
|
31
|
+
with:
|
|
32
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
33
|
+
bundler-cache: true
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
run: bundle exec rspec
|
|
37
|
+
|
|
38
|
+
lint:
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- name: Set up Ruby
|
|
45
|
+
uses: ruby/setup-ruby@v1
|
|
46
|
+
with:
|
|
47
|
+
ruby-version: '3.3'
|
|
48
|
+
bundler-cache: true
|
|
49
|
+
|
|
50
|
+
- name: Check gemspec
|
|
51
|
+
run: gem build turbulence.gemspec
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.3.0] - 2026-06-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `--output DIR` option to specify custom output directory for reports ([#39])
|
|
14
|
+
- `--no-open` flag to skip automatic browser launch, useful for CI/headless environments ([#42])
|
|
15
|
+
- GitHub Actions CI testing Ruby 3.0, 3.1, 3.2, 3.3, and head ([#52])
|
|
16
|
+
- CI status badge and RubyGems version badge in README ([#52])
|
|
17
|
+
- LICENSE.txt file (MIT License)
|
|
18
|
+
- Screenshots of scatter plot and treemap visualizations in README ([#58])
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Ruby 3.0+ / Ruby 4.0 compatibility** ([#50])
|
|
22
|
+
- Update minimum Ruby version to 3.0
|
|
23
|
+
- Add `racc` as runtime dependency (removed from stdlib in Ruby 3.3+)
|
|
24
|
+
- Remove obsolete `Ruby19Parser`/`Flog19` workaround classes
|
|
25
|
+
- Remove unused `ostruct` require
|
|
26
|
+
- Fix git repo detection with case-insensitive regex
|
|
27
|
+
- Update RSpec from 2.x to 3.x and modernize all specs ([#50])
|
|
28
|
+
- Modernize README with table format, examples, and requirements ([#56])
|
|
29
|
+
- Drop defunct `rubyforge_project` property from gemspec ([#46])
|
|
30
|
+
- Update referenced URL in gemspec description ([#44])
|
|
31
|
+
- Use symbols instead of class names as metrics_hash keys ([#34])
|
|
32
|
+
|
|
33
|
+
### Removed
|
|
34
|
+
- Travis CI configuration (replaced by GitHub Actions) ([#56])
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Fix typo: "cummulative" → "cumulative" ([#54])
|
|
38
|
+
|
|
39
|
+
## [1.2.4] - 2014-10-15
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- Fix missing stats bug where metrics hash keys were inconsistent ([#33])
|
|
43
|
+
- Fix missing file bug by creating Calculator::Churn instance to access config ([#32])
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
- Major refactoring to extract configuration into dedicated class ([#27])
|
|
47
|
+
- Use instances instead of classes for calculators
|
|
48
|
+
- Move CLI option parsing to separate file
|
|
49
|
+
- Improved code organization and reduced complexity
|
|
50
|
+
|
|
51
|
+
## [1.2.3] - 2014-09-22
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
- Revert autogenerated bin changes that caused "stack level too deep" errors for many users ([#30], [#29])
|
|
55
|
+
|
|
56
|
+
## [1.2.2] - 2014-09-18
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
- Fix leaky specs by stubbing ENV settings ([#26])
|
|
60
|
+
- Remove deprecation notices
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
- Update RSpec dependency
|
|
64
|
+
- Inject output to CLI for cleaner spec output ([#24])
|
|
65
|
+
|
|
66
|
+
## [1.2.0] - 2014-03-15
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
- Fix file opening on Windows by using `file:///` protocol ([#21])
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
- Treemap visualization option (`--treemap` flag) ([#18])
|
|
73
|
+
- Generator classes for scatter plot and treemap visualizations
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
- Move scatter plot code into dedicated generator class
|
|
77
|
+
- Update flog and launchy gem versions
|
|
78
|
+
|
|
79
|
+
## [1.1.0] - 2013-08-20
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
- Fix parsing of Ruby 1.9 style hashes ([#14])
|
|
83
|
+
- Fix syntax errors under Ruby 1.8.7-352 ([#16])
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
- Additional standard Rails folders for file filtering
|
|
87
|
+
- Support for both Ruby 1.8 and 1.9 syntax
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
- Update gem dependencies
|
|
91
|
+
|
|
92
|
+
## [1.0.0] - 2013-03-10
|
|
93
|
+
|
|
94
|
+
### Added
|
|
95
|
+
- Perforce SCM support with `--scm p4` option ([#11])
|
|
96
|
+
- `--exclude` feature to filter out files matching a pattern ([#13])
|
|
97
|
+
- Commit range option (`--churn-range`) for analyzing specific ranges
|
|
98
|
+
- Mean churn calculation option (`--churn-mean`)
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
- Extract Git-specific code to separate SCM class
|
|
102
|
+
- Use .gemspec for gem dependencies
|
|
103
|
+
- Upgrade to latest Launchy
|
|
104
|
+
- Allow use of more recent json gems ([#12])
|
|
105
|
+
|
|
106
|
+
### Fixed
|
|
107
|
+
- Reduced code complexity through refactoring
|
|
108
|
+
|
|
109
|
+
## [0.0.9] - 2012-11-15
|
|
110
|
+
|
|
111
|
+
### Changed
|
|
112
|
+
- Refactored metrics loop and output display
|
|
113
|
+
- Move complexity calculation into Calculator::Complexity
|
|
114
|
+
- Move churn work into Calculators::Churn
|
|
115
|
+
- Created CLI class for better organization
|
|
116
|
+
|
|
117
|
+
### Fixed
|
|
118
|
+
- Bug in churn calculation
|
|
119
|
+
- Filter files to Rails directories
|
|
120
|
+
|
|
121
|
+
## [0.0.8] - 2012-10-20
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
- Name mangler for anonymization of file names
|
|
125
|
+
- Command line options support
|
|
126
|
+
|
|
127
|
+
### Changed
|
|
128
|
+
- Lines changed instead of commits for churn metric
|
|
129
|
+
- Total flog score instead of average
|
|
130
|
+
- General code cleanup and refactoring
|
|
131
|
+
|
|
132
|
+
## [0.0.6] - 2012-09-15
|
|
133
|
+
|
|
134
|
+
### Added
|
|
135
|
+
- Filename to graph tooltip
|
|
136
|
+
- Churn and complexity to tooltip
|
|
137
|
+
|
|
138
|
+
### Fixed
|
|
139
|
+
- Exclude nonexistent files from analysis
|
|
140
|
+
|
|
141
|
+
## [0.0.5] - 2012-08-20
|
|
142
|
+
|
|
143
|
+
### Added
|
|
144
|
+
- Highcharts integration for visualization
|
|
145
|
+
- Separate series by directory (up to two levels)
|
|
146
|
+
|
|
147
|
+
### Changed
|
|
148
|
+
- Renamed project from "chuggle" to "turbulence"
|
|
149
|
+
|
|
150
|
+
## [0.0.4] - 2012-07-15
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
- Basic graphing functionality
|
|
154
|
+
- Gemfile and gemspec
|
|
155
|
+
|
|
156
|
+
## [0.0.3] - 2012-06-20
|
|
157
|
+
|
|
158
|
+
### Added
|
|
159
|
+
- Initial working version
|
|
160
|
+
- Churn and flog scoring
|
|
161
|
+
- Basic command line interface
|
|
162
|
+
|
|
163
|
+
## [0.0.2] - 2012-06-10
|
|
164
|
+
|
|
165
|
+
### Added
|
|
166
|
+
- Initial gem structure
|
|
167
|
+
- README with basic documentation
|
|
168
|
+
|
|
169
|
+
[Unreleased]: https://github.com/chad/turbulence/compare/1.2.4...HEAD
|
|
170
|
+
[1.3.0]: https://github.com/chad/turbulence/compare/1.2.4...1.3.0
|
|
171
|
+
[1.2.4]: https://github.com/chad/turbulence/compare/1.2.3...1.2.4
|
|
172
|
+
[1.2.3]: https://github.com/chad/turbulence/compare/1.2.2...1.2.3
|
|
173
|
+
[1.2.2]: https://github.com/chad/turbulence/compare/1.2.0...1.2.2
|
|
174
|
+
[1.2.0]: https://github.com/chad/turbulence/compare/1.1.0...1.2.0
|
|
175
|
+
[1.1.0]: https://github.com/chad/turbulence/compare/v1.0.0...1.1.0
|
|
176
|
+
[1.0.0]: https://github.com/chad/turbulence/compare/0.0.9...v1.0.0
|
|
177
|
+
[0.0.9]: https://github.com/chad/turbulence/compare/0.0.8...0.0.9
|
|
178
|
+
[0.0.8]: https://github.com/chad/turbulence/compare/0.0.6...0.0.8
|
|
179
|
+
[0.0.6]: https://github.com/chad/turbulence/compare/0.0.5...0.0.6
|
|
180
|
+
[0.0.5]: https://github.com/chad/turbulence/compare/0.0.4...0.0.5
|
|
181
|
+
[0.0.4]: https://github.com/chad/turbulence/compare/0.0.3...0.0.4
|
|
182
|
+
[0.0.3]: https://github.com/chad/turbulence/compare/0.0.2...0.0.3
|
|
183
|
+
[0.0.2]: https://github.com/chad/turbulence/releases/tag/0.0.2
|
|
184
|
+
|
|
185
|
+
[#58]: https://github.com/chad/turbulence/pull/58
|
|
186
|
+
[#56]: https://github.com/chad/turbulence/pull/56
|
|
187
|
+
[#54]: https://github.com/chad/turbulence/pull/54
|
|
188
|
+
[#52]: https://github.com/chad/turbulence/pull/52
|
|
189
|
+
[#50]: https://github.com/chad/turbulence/pull/50
|
|
190
|
+
[#46]: https://github.com/chad/turbulence/pull/46
|
|
191
|
+
[#44]: https://github.com/chad/turbulence/pull/44
|
|
192
|
+
[#42]: https://github.com/chad/turbulence/issues/42
|
|
193
|
+
[#39]: https://github.com/chad/turbulence/issues/39
|
|
194
|
+
[#34]: https://github.com/chad/turbulence/pull/34
|
|
195
|
+
[#33]: https://github.com/chad/turbulence/pull/33
|
|
196
|
+
[#32]: https://github.com/chad/turbulence/pull/32
|
|
197
|
+
[#30]: https://github.com/chad/turbulence/pull/30
|
|
198
|
+
[#29]: https://github.com/chad/turbulence/issues/29
|
|
199
|
+
[#27]: https://github.com/chad/turbulence/pull/27
|
|
200
|
+
[#26]: https://github.com/chad/turbulence/pull/26
|
|
201
|
+
[#24]: https://github.com/chad/turbulence/pull/24
|
|
202
|
+
[#21]: https://github.com/chad/turbulence/pull/21
|
|
203
|
+
[#18]: https://github.com/chad/turbulence/pull/18
|
|
204
|
+
[#16]: https://github.com/chad/turbulence/pull/16
|
|
205
|
+
[#14]: https://github.com/chad/turbulence/pull/14
|
|
206
|
+
[#13]: https://github.com/chad/turbulence/pull/13
|
|
207
|
+
[#12]: https://github.com/chad/turbulence/pull/12
|
|
208
|
+
[#11]: https://github.com/chad/turbulence/pull/11
|
data/Gemfile.lock
CHANGED
|
@@ -1,39 +1,62 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
turbulence (1.
|
|
5
|
-
flog (
|
|
6
|
-
json
|
|
4
|
+
turbulence (1.3.0)
|
|
5
|
+
flog (>= 4.1)
|
|
6
|
+
json
|
|
7
7
|
launchy (>= 2.0.0)
|
|
8
|
+
racc
|
|
8
9
|
|
|
9
10
|
GEM
|
|
10
11
|
remote: http://rubygems.org/
|
|
11
12
|
specs:
|
|
12
|
-
addressable (2.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
addressable (2.9.0)
|
|
14
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
15
|
+
childprocess (5.1.0)
|
|
16
|
+
logger (~> 1.5)
|
|
17
|
+
diff-lcs (1.6.2)
|
|
18
|
+
flog (4.9.4)
|
|
19
|
+
path_expander (~> 2.0)
|
|
20
|
+
prism (~> 1.7)
|
|
21
|
+
sexp_processor (~> 4.8)
|
|
22
|
+
json (2.19.9)
|
|
23
|
+
launchy (3.1.1)
|
|
24
|
+
addressable (~> 2.8)
|
|
25
|
+
childprocess (~> 5.0)
|
|
26
|
+
logger (~> 1.6)
|
|
27
|
+
logger (1.7.0)
|
|
28
|
+
path_expander (2.0.1)
|
|
29
|
+
prism (1.9.0)
|
|
30
|
+
public_suffix (7.0.5)
|
|
31
|
+
racc (1.8.1)
|
|
32
|
+
rake (13.4.2)
|
|
33
|
+
rspec (3.13.2)
|
|
34
|
+
rspec-core (~> 3.13.0)
|
|
35
|
+
rspec-expectations (~> 3.13.0)
|
|
36
|
+
rspec-mocks (~> 3.13.0)
|
|
37
|
+
rspec-core (3.13.6)
|
|
38
|
+
rspec-support (~> 3.13.0)
|
|
39
|
+
rspec-expectations (3.13.5)
|
|
40
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
41
|
+
rspec-support (~> 3.13.0)
|
|
42
|
+
rspec-its (2.0.0)
|
|
43
|
+
rspec-core (>= 3.13.0)
|
|
44
|
+
rspec-expectations (>= 3.13.0)
|
|
45
|
+
rspec-mocks (3.13.8)
|
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
47
|
+
rspec-support (~> 3.13.0)
|
|
48
|
+
rspec-support (3.13.7)
|
|
49
|
+
sexp_processor (4.17.5)
|
|
32
50
|
|
|
33
51
|
PLATFORMS
|
|
52
|
+
arm64-darwin-25
|
|
34
53
|
ruby
|
|
35
54
|
|
|
36
55
|
DEPENDENCIES
|
|
37
56
|
rake
|
|
38
|
-
rspec (~>
|
|
57
|
+
rspec (~> 3.0)
|
|
58
|
+
rspec-its
|
|
39
59
|
turbulence!
|
|
60
|
+
|
|
61
|
+
BUNDLED WITH
|
|
62
|
+
4.0.13
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2012-2026 Kerri Miller, Chad Fowler
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,48 +1,117 @@
|
|
|
1
|
-
|
|
2
|
-
============================
|
|
1
|
+
# Turbulence
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
[](https://github.com/chad/turbulence/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/rb/turbulence)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Turbulence visualizes churn vs complexity for your Ruby codebase, helping you identify files that are both highly complex and frequently changed - prime candidates for refactoring.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
These files have a high degree of complexity, and they change quite frequently.
|
|
10
|
-
There are a number of reasons why this can happen.
|
|
11
|
-
The one to look out for, though, is something I call runaway conditionals.
|
|
12
|
-
Sometimes a class becomes so complex that refactoring seems too difficult.
|
|
13
|
-
Developers hack if-then-elses into if-then-elses, and the rat’s nest grows. These classes are particularly ripe for a refactoring investment.
|
|
8
|
+
Based on Michael Feathers' work on [getting empirical about refactoring](https://www.stickyminds.com/article/getting-empirical-about-refactoring).
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
Abstractions here have low complexity and don't change much.
|
|
10
|
+
## Screenshots
|
|
17
11
|
|
|
18
|
-
|
|
12
|
+
### Scatter Plot (default)
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
It can consist of files that are somewhat configurational, but often there are also files that act as incubators for new abstractions.
|
|
22
|
-
People add code, it grows, and then they factor outward, extracting new classes. The files churn frequently, but their complexity remains low.
|
|
14
|
+

|
|
23
15
|
|
|
16
|
+
### Treemap
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
------------
|
|
18
|
+

|
|
27
19
|
|
|
28
|
-
|
|
20
|
+
## How to Read the Graph
|
|
21
|
+
|
|
22
|
+
The scatter plot places each file according to its **churn** (x-axis) and **complexity** (y-axis):
|
|
23
|
+
|
|
24
|
+
| Quadrant | Location | What it means |
|
|
25
|
+
|----------|----------|---------------|
|
|
26
|
+
| **Danger Zone** | Upper right | High complexity + high churn. These files change often and are hard to work with. Prime refactoring candidates! |
|
|
27
|
+
| **Healthy Closure** | Lower left | Low complexity + low churn. Stable, well-factored code. Leave it alone. |
|
|
28
|
+
| **Cowboy Code** | Upper left | High complexity + low churn. Complex code that sprang from someone's head fully formed. May need attention if it starts changing. |
|
|
29
|
+
| **Fertile Ground** | Lower right | Low complexity + high churn. Often configuration or incubators for new abstractions. Code grows here, then gets extracted. |
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Ruby 3.0 or later
|
|
34
|
+
- Git or Perforce
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
gem install turbulence
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
29
43
|
|
|
30
|
-
Usage
|
|
31
|
-
-----
|
|
32
44
|
In your project directory, run:
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
```bash
|
|
47
|
+
bule
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This generates and opens `turbulence/turbulence.html` with an interactive scatter plot.
|
|
51
|
+
|
|
52
|
+
### Options
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bule [options] [directory]
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
--scm p4|git SCM to use (default: git)
|
|
59
|
+
--churn-range A..B Commit range to compute file churn
|
|
60
|
+
--churn-mean Calculate mean churn instead of cumulative
|
|
61
|
+
--exclude PATTERN Exclude files matching pattern
|
|
62
|
+
--treemap Output treemap graph instead of scatter plot
|
|
63
|
+
--no-open Skip opening the report in a browser
|
|
64
|
+
--output DIR Output directory for reports (default: ./turbulence)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Examples
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Analyze current directory
|
|
71
|
+
bule
|
|
72
|
+
|
|
73
|
+
# Analyze a specific directory
|
|
74
|
+
bule path/to/project
|
|
75
|
+
|
|
76
|
+
# Use Perforce instead of Git
|
|
77
|
+
bule --scm p4
|
|
78
|
+
|
|
79
|
+
# Analyze only recent changes
|
|
80
|
+
bule --churn-range HEAD~100..HEAD
|
|
81
|
+
|
|
82
|
+
# Generate report without opening browser (useful for CI)
|
|
83
|
+
bule --no-open
|
|
84
|
+
|
|
85
|
+
# Output to a custom directory
|
|
86
|
+
bule --output spec/reports/turbulence
|
|
87
|
+
|
|
88
|
+
# Exclude test files
|
|
89
|
+
bule --exclude spec
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Perforce Support
|
|
93
|
+
|
|
94
|
+
For Perforce, set the `P4CLIENT` environment variable to your client workspace name:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
export P4CLIENT=my-workspace
|
|
98
|
+
bule --scm p4
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Privacy Warning
|
|
102
|
+
|
|
103
|
+
Turbulence generates a JavaScript file containing your file paths and names. If these are sensitive, be careful where you put the generated files and who you share them with.
|
|
104
|
+
|
|
105
|
+
## Contributing
|
|
35
106
|
|
|
36
|
-
and
|
|
107
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/chad/turbulence).
|
|
37
108
|
|
|
38
|
-
|
|
39
|
-
---------------------
|
|
40
|
-
Currently, bule defaults to using git. If you are using Perforce, call it like so:
|
|
109
|
+
## License
|
|
41
110
|
|
|
42
|
-
|
|
111
|
+
[MIT License](LICENSE.txt)
|
|
43
112
|
|
|
44
|
-
|
|
113
|
+
## Authors
|
|
45
114
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
115
|
+
- [Chad Fowler](https://github.com/chad)
|
|
116
|
+
- [Michael Feathers](https://github.com/michaelfeathers)
|
|
117
|
+
- [Corey Haines](https://github.com/coreyhaines)
|
data/bin/bule
CHANGED
|
Binary file
|
data/docs/treemap.png
ADDED
|
Binary file
|
|
@@ -1,55 +1,67 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
1
3
|
class Turbulence
|
|
2
4
|
module Calculators
|
|
3
5
|
class Churn
|
|
4
6
|
RUBY_FILE_EXTENSION = ".rb"
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
attr_accessor :scm, :compute_mean, :commit_range
|
|
8
|
+
attr_reader :config, :type
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end
|
|
10
|
+
def initialize(config = nil)
|
|
11
|
+
@config = config || Turbulence.config
|
|
12
|
+
@type = :churn
|
|
13
|
+
end
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
extend Forwardable
|
|
16
|
+
def_delegators :config, *[
|
|
17
|
+
:scm, :scm=,
|
|
18
|
+
:commit_range, :commit_range=,
|
|
19
|
+
:compute_mean, :compute_mean=,
|
|
20
|
+
]
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
[filename, churn]
|
|
22
|
+
def for_these_files(files)
|
|
23
|
+
changes_by_ruby_file.each do |filename, count|
|
|
24
|
+
yield filename, count if files.include?(filename)
|
|
25
25
|
end
|
|
26
|
+
end
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
def changes_by_ruby_file
|
|
29
|
+
ruby_files_changed_in_scm.group_by(&:first).map do |filename, stats|
|
|
30
|
+
churn_for_file(filename,stats)
|
|
30
31
|
end
|
|
32
|
+
end
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
def churn_for_file(filename,stats)
|
|
35
|
+
churn = stats[0..-2].map(&:last).inject(0){|running_total, changes| running_total + changes}
|
|
36
|
+
churn = calculate_mean_of_churn(churn, stats.size - 1) if compute_mean
|
|
37
|
+
[filename, churn]
|
|
38
|
+
end
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
end
|
|
40
|
+
def calculate_mean_of_churn(churn, sample_size)
|
|
41
|
+
return churn if sample_size < 1
|
|
42
|
+
churn /= sample_size
|
|
43
|
+
end
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
def ruby_files_changed_in_scm
|
|
46
|
+
counted_line_changes_by_file_by_commit.select do |filename, _|
|
|
47
|
+
filename.end_with?(RUBY_FILE_EXTENSION) && File.exist?(filename)
|
|
47
48
|
end
|
|
49
|
+
end
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
def counted_line_changes_by_file_by_commit
|
|
52
|
+
scm_log_file_lines.map do |line|
|
|
53
|
+
adds, deletes, filename = line.split(/\t/)
|
|
54
|
+
[filename, adds.to_i + deletes.to_i]
|
|
51
55
|
end
|
|
52
56
|
end
|
|
57
|
+
|
|
58
|
+
def scm_log_file_lines
|
|
59
|
+
scm_log_command.each_line.reject{|line| line == "\n"}.map(&:chomp)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def scm_log_command
|
|
63
|
+
scm.log_command(commit_range)
|
|
64
|
+
end
|
|
53
65
|
end
|
|
54
66
|
end
|
|
55
67
|
end
|